mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 10:12:51 +00:00
feat: workspace
This commit is contained in:
parent
625b4d6b5d
commit
64b1f3d33a
|
|
@ -1,9 +1,9 @@
|
|||
import { get, post, del } from '@/request/index'
|
||||
import type { Ref } from 'vue'
|
||||
import { Result } from '@/request/Result'
|
||||
import type { RoleItem, RolePermissionItem, CreateOrUpdateParams, PageList, RoleMemberItem, CreateMemberParamsItem } from '@/api/type/role'
|
||||
import type { RoleItem, RolePermissionItem, CreateOrUpdateParams, RoleMemberItem, CreateMemberParamsItem } from '@/api/type/role'
|
||||
import { RoleTypeEnum } from '@/enums/system'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
import type { pageRequest, PageList } from '@/api/type/common'
|
||||
|
||||
const prefix = '/system/role'
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,4 +11,11 @@ interface pageRequest {
|
|||
page_size: number
|
||||
}
|
||||
|
||||
export type { KeyValue, Dict, pageRequest }
|
||||
interface PageList<T> {
|
||||
current: number,
|
||||
size: number,
|
||||
total: number,
|
||||
records: T
|
||||
}
|
||||
|
||||
export type { KeyValue, Dict, pageRequest, PageList }
|
||||
|
|
|
|||
|
|
@ -51,14 +51,7 @@ interface RoleMemberItem {
|
|||
|
||||
interface CreateMemberParamsItem {
|
||||
user_ids: string[],
|
||||
workspace_ids: string[]
|
||||
}
|
||||
|
||||
interface PageList<T> {
|
||||
current: number,
|
||||
size: number,
|
||||
total: number,
|
||||
records: T
|
||||
workspace_ids?: string[]
|
||||
}
|
||||
|
||||
type Arrayable<T> = T | T[]
|
||||
|
|
@ -72,4 +65,4 @@ interface FormItemModel {
|
|||
}
|
||||
}
|
||||
|
||||
export type { RoleItem, FormItemModel, RolePermissionItem, RoleTableDataItem, CreateOrUpdateParams, PageList, ChildrenPermissionItem, RoleMemberItem, CreateMemberParamsItem }
|
||||
export type { RoleItem, FormItemModel, RolePermissionItem, RoleTableDataItem, CreateOrUpdateParams, ChildrenPermissionItem, RoleMemberItem, CreateMemberParamsItem }
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
interface WorkspaceItem {
|
||||
name: string,
|
||||
id?: string
|
||||
}
|
||||
|
||||
interface CreateWorkspaceMemberParamsItem {
|
||||
user_ids: string[],
|
||||
role_ids: string[]
|
||||
}
|
||||
|
||||
interface WorkspaceMemberItem {
|
||||
user_relation_id: string,
|
||||
user_id: string,
|
||||
username: string,
|
||||
nick_name: string,
|
||||
role_id: string,
|
||||
role_name: string,
|
||||
}
|
||||
export type { WorkspaceItem, CreateWorkspaceMemberParamsItem, WorkspaceMemberItem }
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import type { Ref } from 'vue'
|
||||
import { get, post, del } from '@/request/index'
|
||||
import type { WorkspaceItem, CreateWorkspaceMemberParamsItem, WorkspaceMemberItem } from '@/api/type/workspace'
|
||||
import type { pageRequest, PageList } from '@/api/type/common'
|
||||
|
||||
const prefix = '/system/workspace'
|
||||
|
||||
/**
|
||||
* 获取添加成员时的工作空间下拉列表
|
||||
*/
|
||||
const getWorkspaceList: (loading?: Ref<boolean>) => Promise<Result<Record<string, any>[]>> = (loading) => {
|
||||
return get('/workspace/current_user', undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工作空间列表
|
||||
*/
|
||||
const getSystemWorkspaceList: (loading?: Ref<boolean>) => Promise<Result<WorkspaceItem[]>> = (loading) => {
|
||||
return get(`${prefix}`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建或更新工作空间
|
||||
*/
|
||||
const CreateOrUpdateWorkspace: (
|
||||
data: WorkspaceItem,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (data, loading) => {
|
||||
return post(`${prefix}`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工作空间
|
||||
*/
|
||||
const deleteWorkspace: (workspace_id: string, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
|
||||
workspace_id,
|
||||
loading,
|
||||
) => {
|
||||
return del(`${prefix}/${workspace_id}`, undefined, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工作空间成员列表
|
||||
*/
|
||||
const getWorkspaceMemberList: (
|
||||
workspace_id: string,
|
||||
page: pageRequest,
|
||||
param: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<PageList<WorkspaceMemberItem[]>>> = (workspace_id, page, param, loading) => {
|
||||
return get(
|
||||
`${prefix}/${workspace_id}/user_list/${page.current_page}/${page.page_size}`,
|
||||
param,
|
||||
loading,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建工作空间成员
|
||||
*/
|
||||
const CreateWorkspaceMember: (
|
||||
workspace_id: string,
|
||||
data: CreateWorkspaceMemberParamsItem[],
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<any>> = (workspace_id, data, loading) => {
|
||||
return post(`${prefix}/${workspace_id}/add_member`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除工作空间成员
|
||||
*/
|
||||
const deleteWorkspaceMember: (workspace_id: string, user_relation_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
workspace_id,
|
||||
user_relation_id,
|
||||
loading,
|
||||
) => {
|
||||
return post(`${prefix}/${workspace_id}/remove_member/${user_relation_id}`, undefined, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取添加成员时的角色下拉列表
|
||||
*/
|
||||
const getWorkspaceRoleList: (loading?: Ref<boolean>) => Promise<Result<Record<string, any>[]>> = (loading) => {
|
||||
return get('/role_list/current_user', undefined, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
getWorkspaceList,
|
||||
getSystemWorkspaceList,
|
||||
CreateOrUpdateWorkspace,
|
||||
deleteWorkspace,
|
||||
getWorkspaceMemberList,
|
||||
CreateWorkspaceMember,
|
||||
deleteWorkspaceMember,
|
||||
getWorkspaceRoleList,
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import notFound from './404'
|
||||
import application from './application'
|
||||
import role from './role'
|
||||
import workspace from './workspace'
|
||||
import applicationOverview from './application-overview'
|
||||
import knowledge from './knowledge'
|
||||
import system from './system'
|
||||
|
|
@ -34,5 +35,6 @@ export default {
|
|||
chatLog,
|
||||
login,
|
||||
operateLog,
|
||||
role
|
||||
role,
|
||||
workspace
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
export default {
|
||||
// TODO
|
||||
title: '工作空间',
|
||||
list: '工作空间列表',
|
||||
name: '工作空间名称',
|
||||
member: {
|
||||
delete: {
|
||||
confirmTitle: '是否移除成员:',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import system from './system'
|
|||
import userManage from './user-manage'
|
||||
import resourceAuthorization from './resource-authorization'
|
||||
import role from './role'
|
||||
import workspace from './workspace'
|
||||
import application from './application'
|
||||
import problem from './problem'
|
||||
import applicationOverview from './application-overview'
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
userManage,
|
||||
resourceAuthorization,
|
||||
role,
|
||||
workspace,
|
||||
application,
|
||||
problem,
|
||||
applicationOverview,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
export default {
|
||||
title: '工作空间',
|
||||
list: '工作空间列表',
|
||||
name: '工作空间名称',
|
||||
member: {
|
||||
delete: {
|
||||
confirmTitle: '是否移除成员:',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import notFound from './404'
|
||||
import application from './application'
|
||||
import role from './role'
|
||||
import workspace from './workspace'
|
||||
import applicationOverview from './application-overview'
|
||||
import knowledge from './knowledge'
|
||||
import system from './system'
|
||||
|
|
@ -34,5 +35,6 @@ export default {
|
|||
chatLog,
|
||||
login,
|
||||
operateLog,
|
||||
role
|
||||
role,
|
||||
workspace
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
export default {
|
||||
// TODO
|
||||
title: '工作空间',
|
||||
list: '工作空间列表',
|
||||
name: '工作空间名称',
|
||||
member: {
|
||||
delete: {
|
||||
confirmTitle: '是否移除成员:',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,19 @@ const systemRouter = {
|
|||
},
|
||||
component: () => import('@/views/role/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system/workspace',
|
||||
name: 'workspace',
|
||||
meta: {
|
||||
icon: 'app-resource-authorization', // TODO
|
||||
iconActive: 'app-resource-authorization-active', // TODO
|
||||
title: 'views.workspace.title',
|
||||
activeMenu: '/system',
|
||||
parentPath: '/system',
|
||||
parentName: 'system',
|
||||
},
|
||||
component: () => import('@/views/workspace/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/system/shared',
|
||||
name: 'shared',
|
||||
|
|
|
|||
|
|
@ -109,6 +109,9 @@ h5 {
|
|||
.bold {
|
||||
font-weight: 600;
|
||||
}
|
||||
.medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
.lighter {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
<h4>{{ $t('views.role.member.add') }}</h4>
|
||||
</template>
|
||||
<template #default>
|
||||
<MemberFormContent ref="memberFormContentRef" :models="formItemModel" v-model:form="list" />
|
||||
<MemberFormContent ref="memberFormContentRef" :models="formItemModel" v-model:form="list"
|
||||
v-loading="memberFormContentLoading" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<div style="flex: auto">
|
||||
|
|
@ -22,12 +23,15 @@ import { onBeforeMount, ref } from 'vue'
|
|||
import type { CreateMemberParamsItem, FormItemModel } from '@/api/type/role'
|
||||
import RoleApi from '@/api/system/role'
|
||||
import UserApi from '@/api/user/user'
|
||||
import WorkspaceApi from '@/api/workspace'
|
||||
import MemberFormContent from './MemberFormContent.vue'
|
||||
import { t } from '@/locales'
|
||||
import type { RoleItem } from '@/api/type/role'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
import { RoleTypeEnum } from '@/enums/system'
|
||||
|
||||
const props = defineProps<{
|
||||
roleId: string
|
||||
currentRole?: RoleItem
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
|
@ -38,55 +42,95 @@ const loading = ref(false)
|
|||
const visible = ref(false)
|
||||
const list = ref<CreateMemberParamsItem[]>([]);
|
||||
|
||||
const formItemModel = ref<FormItemModel[]>([
|
||||
{
|
||||
path: 'user_ids',
|
||||
label: t('views.role.member.title'),
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: `${t('common.selectPlaceholder')}${t('views.role.member.title')}`,
|
||||
},
|
||||
],
|
||||
selectProps: {
|
||||
options: [],
|
||||
placeholder: `${t('common.selectPlaceholder')}${t('views.role.member.title')}`
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'workspace_ids',
|
||||
label: t('views.role.member.workspace'),
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: `${t('common.selectPlaceholder')}${t('views.role.member.workspace')}`,
|
||||
},
|
||||
],
|
||||
selectProps: {
|
||||
options: [], // TODO
|
||||
placeholder: `${t('common.selectPlaceholder')}${t('views.role.member.workspace')}`
|
||||
}
|
||||
},
|
||||
]);
|
||||
const memberFormContentLoading = ref(false);
|
||||
const formItemModel = ref<FormItemModel[]>([]);
|
||||
const userFormItem = ref<FormItemModel[]>([]);
|
||||
const workspaceFormItem = ref<FormItemModel[]>([]);
|
||||
|
||||
async function getUserFormItem() {
|
||||
try {
|
||||
const res = await UserApi.getUserList(memberFormContentLoading);
|
||||
userFormItem.value = [{
|
||||
path: 'user_ids',
|
||||
label: t('views.role.member.title'),
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: `${t('common.selectPlaceholder')}${t('views.role.member.title')}`,
|
||||
},
|
||||
],
|
||||
selectProps: {
|
||||
options: res.data?.map(item => ({
|
||||
label: item.nick_name,
|
||||
value: item.id
|
||||
})) || [],
|
||||
placeholder: `${t('common.selectPlaceholder')}${t('views.role.member.title')}`
|
||||
}
|
||||
}];
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function getWorkspaceFormItem() {
|
||||
try {
|
||||
const res = await WorkspaceApi.getWorkspaceList(memberFormContentLoading);
|
||||
workspaceFormItem.value = [{
|
||||
path: 'workspace_ids',
|
||||
label: t('views.role.member.workspace'),
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: `${t('common.selectPlaceholder')}${t('views.role.member.workspace')}`,
|
||||
},
|
||||
],
|
||||
selectProps: {
|
||||
options: res.data?.map(item => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
})) || [],
|
||||
placeholder: `${t('common.selectPlaceholder')}${t('views.role.member.workspace')}`
|
||||
}
|
||||
}]
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (props.currentRole?.type !== RoleTypeEnum.ADMIN) {
|
||||
formItemModel.value = [...userFormItem.value, ...workspaceFormItem.value]
|
||||
list.value = [{ user_ids: [], workspace_ids: [] }]
|
||||
} else {
|
||||
formItemModel.value = [...userFormItem.value]
|
||||
list.value = [{ user_ids: [] }]
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const res = await UserApi.getUserList();
|
||||
formItemModel.value[0].selectProps.options = res.data?.map(item => ({ label: item.nick_name, value: item.id }))
|
||||
await getUserFormItem();
|
||||
await getWorkspaceFormItem();
|
||||
init()
|
||||
})
|
||||
|
||||
function open() {
|
||||
init();
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
visible.value = false
|
||||
list.value = []
|
||||
}
|
||||
|
||||
const memberFormContentRef = ref<InstanceType<typeof MemberFormContent>>()
|
||||
function handleAdd() {
|
||||
memberFormContentRef.value?.validate().then(async (valid) => {
|
||||
if (valid) {
|
||||
await RoleApi.CreateMember(props.roleId, { members: list.value }, loading)
|
||||
let params;
|
||||
if (props.currentRole?.type === RoleTypeEnum.ADMIN) {
|
||||
params = list.value.map(item => ({ user_ids: item.user_ids, workspace_ids: ['None'] }))
|
||||
}
|
||||
await RoleApi.CreateMember(props.currentRole?.id as string, { members: params ?? list.value }, loading)
|
||||
MsgSuccess(t('common.addSuccess'))
|
||||
handleCancel();
|
||||
emit('refresh')
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
</el-table-column>
|
||||
</app-table>
|
||||
</div>
|
||||
<AddMemberDrawer ref="addMemberDrawerRef" :role-id="props.currentRole?.id as string" @refresh="getList" />
|
||||
<AddMemberDrawer ref="addMemberDrawerRef" :currentRole="props.currentRole" @refresh="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
:rules="model.rules" :label="index === 0 && model.label ? model.label : ''" class="mr-8" style="flex: 1">
|
||||
<el-select v-model="element[model.path]"
|
||||
:placeholder="model.selectProps.placeholder ?? $t('common.selectPlaceholder')" clearable filterable multiple
|
||||
style="width: 100%">
|
||||
style="width: 100%" collapse-tags collapse-tags-tooltip>
|
||||
<el-option v-for="opt in model.selectProps.options" :key="opt.value" :label="opt.label"
|
||||
:value="opt.value" />
|
||||
</el-select>
|
||||
|
|
@ -53,7 +53,6 @@ watch(() => props.models, () => {
|
|||
props.models.forEach((e) => {
|
||||
formItem[e.path] = [];
|
||||
});
|
||||
handleAdd();
|
||||
}, { immediate: true })
|
||||
|
||||
function handleDelete(index: number) {
|
||||
|
|
@ -68,5 +67,4 @@ const validate = () => {
|
|||
}
|
||||
|
||||
defineExpose({ validate })
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -25,9 +25,11 @@
|
|||
<template #dropdown>
|
||||
<el-dropdown-menu style="min-width: 80px">
|
||||
<el-dropdown-item @click.stop="createOrUpdateRole(row)" class="p-8">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
{{ $t('common.rename') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.stop="deleteRole(row)" class="border-t p-8">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
{{ $t('common.delete') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
|
|
@ -66,10 +68,13 @@
|
|||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu style="min-width: 80px">
|
||||
<el-dropdown-item @click.stop="createOrUpdateRole(row)" class="p-8"> {{ $t('common.rename') }}
|
||||
<el-dropdown-item @click.stop="createOrUpdateRole(row)" class="p-8">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
{{ $t('common.rename') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.stop="deleteRole(row)" class="border-t p-8"> {{ $t('common.delete')
|
||||
}}
|
||||
<el-dropdown-item @click.stop="deleteRole(row)" class="border-t p-8">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
{{ $t('common.delete') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
<template>
|
||||
<el-drawer v-model="visible" size="600" :destroy-on-close="true" :before-close="handleCancel">
|
||||
<template #header>
|
||||
<h4>{{ $t('views.role.member.add') }}</h4>
|
||||
</template>
|
||||
<template #default>
|
||||
<MemberFormContent ref="memberFormContentRef" :models="formItemModel" v-model:form="list"
|
||||
v-loading="memberFormContentLoading" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<div style="flex: auto">
|
||||
<el-button @click="handleCancel">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="handleAdd()" :loading="loading">
|
||||
{{ $t('common.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeMount, ref } from 'vue'
|
||||
import UserApi from '@/api/user/user'
|
||||
import WorkspaceApi from '@/api/workspace'
|
||||
import MemberFormContent from '@/views/role/component/MemberFormContent.vue'
|
||||
import { t } from '@/locales'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
import type { CreateWorkspaceMemberParamsItem, WorkspaceItem } from '@/api/type/workspace'
|
||||
import type { FormItemModel } from '@/api/type/role'
|
||||
|
||||
const props = defineProps<{
|
||||
currentWorkspace?: WorkspaceItem
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'refresh'): void;
|
||||
}>();
|
||||
|
||||
const loading = ref(false)
|
||||
const visible = ref(false)
|
||||
const list = ref<CreateWorkspaceMemberParamsItem[]>([]);
|
||||
|
||||
const memberFormContentLoading = ref(false);
|
||||
const formItemModel = ref<FormItemModel[]>([]);
|
||||
const userFormItem = ref<FormItemModel[]>([]);
|
||||
const roleFormItem = ref<FormItemModel[]>([]);
|
||||
|
||||
async function getUserFormItem() {
|
||||
try {
|
||||
const res = await UserApi.getUserList(memberFormContentLoading);
|
||||
userFormItem.value = [{
|
||||
path: 'user_ids',
|
||||
label: t('views.role.member.title'),
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: `${t('common.selectPlaceholder')}${t('views.role.member.title')}`,
|
||||
},
|
||||
],
|
||||
selectProps: {
|
||||
options: res.data?.map(item => ({
|
||||
label: item.nick_name,
|
||||
value: item.id
|
||||
})) || [],
|
||||
placeholder: `${t('common.selectPlaceholder')}${t('views.role.member.title')}`
|
||||
}
|
||||
}];
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function getRoleFormItem() {
|
||||
try {
|
||||
const res = await WorkspaceApi.getWorkspaceRoleList(memberFormContentLoading);
|
||||
roleFormItem.value = [{
|
||||
path: 'role_ids',
|
||||
label: t('views.role.member.role'),
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: `${t('common.selectPlaceholder')}${t('views.role.member.role')}`,
|
||||
},
|
||||
],
|
||||
selectProps: {
|
||||
options: res.data?.map(item => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
})) || [],
|
||||
placeholder: `${t('common.selectPlaceholder')}${t('views.role.member.role')}`
|
||||
}
|
||||
}]
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
formItemModel.value = [...userFormItem.value, ...roleFormItem.value]
|
||||
list.value = [{ user_ids: [], role_ids: [] }]
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
await getUserFormItem();
|
||||
await getRoleFormItem();
|
||||
init()
|
||||
})
|
||||
|
||||
function open() {
|
||||
init();
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
const memberFormContentRef = ref<InstanceType<typeof MemberFormContent>>()
|
||||
function handleAdd() {
|
||||
memberFormContentRef.value?.validate().then(async (valid) => {
|
||||
if (valid) {
|
||||
await WorkspaceApi.CreateWorkspaceMember(props.currentWorkspace?.id as string, list.value, loading)
|
||||
MsgSuccess(t('common.addSuccess'))
|
||||
handleCancel();
|
||||
emit('refresh')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<el-dialog :title="`${!form.id ? $t('common.create') : $t('common.rename')}${$t('views.workspace.title')}`"
|
||||
v-model="dialogVisible" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true">
|
||||
<el-form label-position="top" ref="formRef" :rules="rules" :model="form" require-asterisk-position="right">
|
||||
<el-form-item :label="$t('views.workspace.name')" prop="name">
|
||||
<el-input v-model="form.name" maxlength="64"
|
||||
:placeholder="`${$t('common.inputPlaceholder')}${$t('views.workspace.name')}`" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit(formRef)" :loading="loading">
|
||||
{{ !form.id ? $t('common.create') : $t('common.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
import type { WorkspaceItem } from '@/api/type/workspace'
|
||||
import WorkspaceApi from '@/api/workspace'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'refresh', currentWorkspace: WorkspaceItem): void;
|
||||
}>();
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const defaultForm = {
|
||||
name: ''
|
||||
}
|
||||
const form = ref<WorkspaceItem>({
|
||||
...defaultForm,
|
||||
})
|
||||
function open(item?: WorkspaceItem) {
|
||||
if (item) {
|
||||
form.value = { id: item.id, name: item.name }
|
||||
} else {
|
||||
form.value = { ...defaultForm }
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const rules = reactive({
|
||||
name: [{ required: true, message: `${t('common.inputPlaceholder')}${t('views.workspace.name')}`, trigger: 'blur' }],
|
||||
})
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
WorkspaceApi.CreateOrUpdateWorkspace(form.value, loading).then((res: any) => {
|
||||
MsgSuccess(!form.value.id ? t('common.createSuccess') : t('common.renameSuccess'))
|
||||
emit('refresh', res.data)
|
||||
dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<template>
|
||||
<div class="flex-between mb-16">
|
||||
<el-button type="primary" @click="handleAdd">
|
||||
{{ $t('views.role.member.add') }}
|
||||
</el-button>
|
||||
<div class="flex complex-search">
|
||||
<el-select class="complex-search__left" v-model="searchType" style="width: 120px">
|
||||
<el-option :label="$t('views.login.loginForm.username.label')" value="username" />
|
||||
</el-select>
|
||||
<el-input v-if="searchType === 'username'" v-model="searchForm.username" @change="getList"
|
||||
:placeholder="$t('common.inputPlaceholder')" style="width: 220px" clearable />
|
||||
</div>
|
||||
</div>
|
||||
<app-table :data="tableData" :pagination-config="paginationConfig" @sizeChange="handleSizeChange"
|
||||
@changePage="getList" v-loading="loading">
|
||||
<el-table-column prop="nick_name" :label="$t('views.userManage.form.nick_name.label')" />
|
||||
<el-table-column prop="username" :label="$t('views.userManage.form.username.label')" />
|
||||
<el-table-column prop="role_name" :label="$t('views.role.member.role')" />
|
||||
<el-table-column :label="$t('common.operation')" width="100" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip effect="dark" :content="`${$t('views.role.member.delete.button')}`" placement="top">
|
||||
<el-button type="primary" text @click.stop="handleDelete(row)">
|
||||
<el-icon>
|
||||
<EditPen />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</app-table>
|
||||
<AddMemberDrawer ref="addMemberDrawerRef" :currentWorkspace="props.currentWorkspace" @refresh="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, reactive, watch } from 'vue'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
import AddMemberDrawer from './AddMemberDrawer.vue'
|
||||
import WorkspaceApi from '@/api/workspace'
|
||||
import type { WorkspaceMemberItem, WorkspaceItem } from '@/api/type/workspace'
|
||||
|
||||
const props = defineProps<{
|
||||
currentWorkspace?: WorkspaceItem
|
||||
}>()
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const searchType = ref('username')
|
||||
const searchForm = ref<Record<string, any>>({
|
||||
username: '',
|
||||
})
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 20,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const tableData = ref<WorkspaceMemberItem[]>([])
|
||||
|
||||
async function getList() {
|
||||
if (!props.currentWorkspace?.id) return
|
||||
try {
|
||||
const params = {
|
||||
[searchType.value]: searchForm.value[searchType.value],
|
||||
}
|
||||
const res = await WorkspaceApi.getWorkspaceMemberList(props.currentWorkspace?.id, paginationConfig, params, loading)
|
||||
tableData.value = res.data.records
|
||||
paginationConfig.total = res.data.total
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
function handleSizeChange() {
|
||||
paginationConfig.current_page = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
watch(() => props.currentWorkspace?.id, () => {
|
||||
getList()
|
||||
})
|
||||
|
||||
const addMemberDrawerRef = ref<InstanceType<typeof AddMemberDrawer>>()
|
||||
function handleAdd() {
|
||||
addMemberDrawerRef.value?.open();
|
||||
}
|
||||
|
||||
function handleDelete(row: WorkspaceMemberItem) {
|
||||
MsgConfirm(
|
||||
`${t('views.workspace.member.delete.confirmTitle')}${row.nick_name} ?`, '',
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
loading.value = true
|
||||
WorkspaceApi.deleteWorkspaceMember(props.currentWorkspace?.id as string, row.user_relation_id, loading).then(() => {
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
getList()
|
||||
})
|
||||
})
|
||||
.catch(() => { })
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
<template>
|
||||
<div class="workspace">
|
||||
<h2 class="mb-16">{{ $t('views.workspace.title') }}</h2>
|
||||
<el-card style="--el-card-padding: 0" body-class="workspace-card">
|
||||
<div class="flex h-full">
|
||||
<div class="workspace-left border-r p-16">
|
||||
<div class="workspace-left_title">
|
||||
<h4 class="medium">{{ $t('views.workspace.list') }}</h4>
|
||||
<el-tooltip effect="dark" :content="`${$t('common.create')}${$t('views.workspace.title')}`" placement="top">
|
||||
<el-button type="primary" text @click="createOrUpdateWorkspace()">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="p-8">
|
||||
<el-input v-model="filterText" :placeholder="$t('common.search')" prefix-icon="Search" clearable />
|
||||
</div>
|
||||
<div class="list-height-left">
|
||||
<el-scrollbar v-loading="loading">
|
||||
<common-list :data="filterList" @click="clickWorkspace" :default-active="currentWorkspace?.id">
|
||||
<template #default="{ row }">
|
||||
<div class="flex-between">
|
||||
<span>{{ row.name }}</span>
|
||||
<el-dropdown :teleported="false">
|
||||
<el-button text>
|
||||
<el-icon class="color-secondary">
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu style="min-width: 80px">
|
||||
<el-dropdown-item @click.stop="createOrUpdateWorkspace(row)" class="p-8">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
{{
|
||||
$t('common.rename') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.stop="deleteWorkspace(row)" class="border-t p-8">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
{{
|
||||
$t('common.delete')
|
||||
}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<template #empty>
|
||||
<span></span>
|
||||
</template>
|
||||
</common-list>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右边 -->
|
||||
<div class="workspace-right" v-loading="loading">
|
||||
<div class="flex align-center" style="margin-bottom: 20px;">
|
||||
<h4 class="medium">{{ currentWorkspace?.name }}</h4>
|
||||
<el-divider direction="vertical" class="mr-8 ml-8" />
|
||||
<AppIcon iconName="app-wordspace" style="font-size: 16px" class="color-input-placeholder"></AppIcon>
|
||||
<span class="color-input-placeholder ml-4">
|
||||
数字
|
||||
</span>
|
||||
</div>
|
||||
<Member :currentWorkspace="currentWorkspace" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<CreateOrUpdateWorkspaceDialog ref="createOrUpdateWorkspaceDialogRef" @refresh="refresh" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import WorkspaceApi from '@/api/workspace'
|
||||
import { t } from '@/locales'
|
||||
import Member from './component/Member.vue'
|
||||
import CreateOrUpdateWorkspaceDialog from './component/CreateOrUpdateWorkspaceDialog.vue'
|
||||
import type { WorkspaceItem } from '@/api/type/workspace'
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
|
||||
const filterText = ref('')
|
||||
const loading = ref(false)
|
||||
const list = ref<WorkspaceItem[]>([])
|
||||
const filterList = ref<WorkspaceItem[]>([]) // 搜索过滤后列表
|
||||
const currentWorkspace = ref<WorkspaceItem>()
|
||||
|
||||
async function getWorkspace() {
|
||||
try {
|
||||
const res = await WorkspaceApi.getSystemWorkspaceList(loading)
|
||||
list.value = res.data
|
||||
filterList.value = filter(list.value, filterText.value)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getWorkspace()
|
||||
currentWorkspace.value = list.value[0]
|
||||
})
|
||||
|
||||
async function refresh(workspace?: WorkspaceItem) {
|
||||
await getWorkspace();
|
||||
// 创建角色后选中新建的角色
|
||||
currentWorkspace.value = workspace ? workspace : currentWorkspace.value
|
||||
}
|
||||
|
||||
function filter(list: WorkspaceItem[], filterText: string) {
|
||||
if (!filterText.length) {
|
||||
return list
|
||||
}
|
||||
return list.filter((v: WorkspaceItem) =>
|
||||
v.name.toLowerCase().includes(filterText.toLowerCase()),
|
||||
)
|
||||
}
|
||||
|
||||
watch(filterText, (val: string) => {
|
||||
filterList.value = filter(list.value, val)
|
||||
})
|
||||
|
||||
function clickWorkspace(item: WorkspaceItem) {
|
||||
currentWorkspace.value = item
|
||||
}
|
||||
|
||||
const createOrUpdateWorkspaceDialogRef = ref<InstanceType<typeof CreateOrUpdateWorkspaceDialog>>()
|
||||
function createOrUpdateWorkspace(item?: WorkspaceItem) {
|
||||
createOrUpdateWorkspaceDialogRef.value?.open(item);
|
||||
}
|
||||
|
||||
// TODO 该工作空间下存在 知识库资源、应用资源,无法删除
|
||||
function deleteWorkspace(item: WorkspaceItem) {
|
||||
MsgConfirm(
|
||||
`${t('views.workspace.delete.confirmTitle')}${item.name} ?`,
|
||||
t('views.workspace.delete.confirmMessage'),
|
||||
{
|
||||
confirmButtonText: t('common.confirm'),
|
||||
confirmButtonClass: 'danger',
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
WorkspaceApi.deleteWorkspace(item.id as string, loading).then(async () => {
|
||||
MsgSuccess(t('common.deleteSuccess'))
|
||||
await getWorkspace()
|
||||
currentWorkspace.value = item.id === currentWorkspace.value?.id ? list.value[0] : currentWorkspace.value
|
||||
})
|
||||
})
|
||||
.catch(() => { })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.workspace {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 16px 24px;
|
||||
|
||||
:deep(.workspace-card) {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.workspace-left {
|
||||
box-sizing: border-box;
|
||||
width: var(--setting-left-width);
|
||||
min-width: var(--setting-left-width);
|
||||
|
||||
.workspace-left_title {
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.list-height-left {
|
||||
height: calc(100vh - 255px);
|
||||
|
||||
:deep(.common-list li) {
|
||||
padding-right: 4px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-left_divider {
|
||||
padding: 0 8px;
|
||||
|
||||
:deep(.el-divider) {
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-right {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue