mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat(workspace): authorized workspace model UI
This commit is contained in:
@@ -20,3 +20,7 @@ export const workspaceModelMapping = (aiModelId: any) =>
|
||||
request.get(`/system/aimodel/${aiModelId}/ws_mapping`)
|
||||
export const workspaceModelMappingUpdate = (aiModelId: any, data: any) =>
|
||||
request.put(`/system/aimodel/${aiModelId}/ws_mapping`, data)
|
||||
export const workspaceModelMappingAdd = (aiModelId: any, data: any) =>
|
||||
request.post(`/system/aimodel/${aiModelId}/ws_mapping`, data)
|
||||
export const workspaceModelMappingDelete = (aiModelId: any, data: any) =>
|
||||
request.delete(`/system/aimodel/${aiModelId}/ws_mapping`, { data })
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_18854_4748)">
|
||||
<path d="M7.99984 13.9994C4.68613 13.9994 1.99984 11.3132 1.99984 7.99965C1.99984 4.68608 4.68613 1.9999 7.99984 1.9999C11.3135 1.9999 13.9998 4.68608 13.9998 7.99965C13.9998 11.3132 11.3135 13.9994 7.99984 13.9994ZM7.99984 15.3327C12.0499 15.3327 15.3332 12.0496 15.3332 7.99965C15.3332 3.94973 12.0499 0.666626 7.99984 0.666626C3.94975 0.666626 0.666504 3.94973 0.666504 7.99965C0.666504 12.0496 3.94975 15.3327 7.99984 15.3327Z" fill="#646A73"/>
|
||||
<path d="M4.6665 7.66633C4.6665 7.48225 4.81574 7.33302 4.99984 7.33302H10.9998C11.1839 7.33302 11.3332 7.48225 11.3332 7.66633V8.33297C11.3332 8.51706 11.1839 8.66629 10.9998 8.66629H4.99984C4.81574 8.66629 4.6665 8.51706 4.6665 8.33297V7.66633Z" fill="#646A73"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_18854_4748">
|
||||
<rect width="16" height="15.9993" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 970 B |
@@ -40,7 +40,8 @@
|
||||
"workspaces_authorized": "Authorized {num} Workspaces",
|
||||
"number_of_members": "Number of Members",
|
||||
"delete_selected_workspaces": "Are you sure you want to delete the selected {msg} workspaces?",
|
||||
"delete_workspace": "Are you sure you want to delete the workspace: {msg}?"
|
||||
"delete_workspace": "Are you sure you want to delete the workspace: {msg}?",
|
||||
"no_workspace": "No Workspaces"
|
||||
},
|
||||
"sync": {
|
||||
"records": "Displaying {num} records out of {total}",
|
||||
@@ -189,6 +190,7 @@
|
||||
"system_manage": "System Management",
|
||||
"update_success": "Update",
|
||||
"save_success": "Save Successful",
|
||||
"operation_success": "Operation successful",
|
||||
"next": "Next",
|
||||
"save": "Save",
|
||||
"logout": "Logout",
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
"workspaces_authorized": "{num}개의 권한 있는 작업 공간",
|
||||
"number_of_members": "회원 수",
|
||||
"delete_selected_workspaces": "선택한 {msg}개의 작업 공간을 삭제하시겠습니까?",
|
||||
"delete_workspace": "작업 공간을 삭제하시겠습니까: {msg}?"
|
||||
"delete_workspace": "작업 공간을 삭제하시겠습니까: {msg}?",
|
||||
"no_workspace": "작업 공간 없음"
|
||||
},
|
||||
"sync": {
|
||||
"records": "{total}개 중 {num}개의 레코드 표시",
|
||||
@@ -189,6 +190,7 @@
|
||||
"system_manage": "시스템 관리",
|
||||
"update_success": "업데이트 성공",
|
||||
"save_success": "저장 성공",
|
||||
"operation_success": "작업 성공",
|
||||
"next": "다음 단계",
|
||||
"save": "저장",
|
||||
"logout": "로그아웃",
|
||||
|
||||
@@ -39,8 +39,9 @@
|
||||
"modify_authorized_space": "修改授权空间",
|
||||
"workspaces_authorized": "已授权 {num} 个工作空间",
|
||||
"number_of_members": "成员数量",
|
||||
"delete_selected_workspaces": "是否删除选中的 {msg} 个工作空间?",
|
||||
"delete_workspace": "是否删除工作空间:{msg}?"
|
||||
"delete_selected_workspaces": "是否移除选中的 {msg} 个工作空间?",
|
||||
"delete_workspace": "是否移除工作空间:{msg}?",
|
||||
"no_workspace": "暂无工作空间"
|
||||
},
|
||||
"sync": {
|
||||
"records": "显示 {num} 条数据,共 {total} 条",
|
||||
@@ -189,6 +190,7 @@
|
||||
"system_manage": "系统管理",
|
||||
"update_success": "更新成功",
|
||||
"save_success": "保存成功",
|
||||
"operation_success": "操作成功",
|
||||
"next": "下一步",
|
||||
"save": "保存",
|
||||
"logout": "退出登录",
|
||||
|
||||
@@ -39,8 +39,9 @@
|
||||
"modify_authorized_space": "修改授權空間",
|
||||
"workspaces_authorized": "已授權 {num} 個工作空間",
|
||||
"number_of_members": "成員數量",
|
||||
"delete_selected_workspaces": "是否刪除選中的 {msg} 個工作空間?",
|
||||
"delete_workspace": "是否刪除工作空間:{msg}?"
|
||||
"delete_selected_workspaces": "是否移除選中的 {msg} 個工作空間?",
|
||||
"delete_workspace": "是否移除工作空間:{msg}?",
|
||||
"no_workspace": "暫無工作空間"
|
||||
},
|
||||
"sync": {
|
||||
"records": "顯示 {num} 筆資料,共 {total} 筆",
|
||||
@@ -189,6 +190,7 @@
|
||||
"system_manage": "系統管理",
|
||||
"update_success": "更新成功",
|
||||
"save_success": "儲存成功",
|
||||
"operation_success": "操作成功",
|
||||
"next": "下一步",
|
||||
"save": "儲存",
|
||||
"logout": "登出",
|
||||
|
||||
@@ -43,7 +43,7 @@ const showErrorMask = (msg?: string) => {
|
||||
errorMsg.value = ''
|
||||
}, 3000)
|
||||
}
|
||||
const emits = defineEmits(['edit', 'del', 'default', 'authorizedSpace', 'editworkspaceList'])
|
||||
const emits = defineEmits(['edit', 'del', 'default', 'authorizedSpace', 'editWorkspaceList'])
|
||||
|
||||
const handleDefault = () => {
|
||||
emits('default')
|
||||
@@ -58,7 +58,7 @@ const handleEdit = () => {
|
||||
}
|
||||
|
||||
const handleEditWorkspaceList = () => {
|
||||
emits('editworkspaceList')
|
||||
emits('editWorkspaceList')
|
||||
}
|
||||
const handleAuthorizedSpace = () => {
|
||||
emits('authorizedSpace')
|
||||
@@ -99,7 +99,6 @@ defineExpose({ showErrorMask })
|
||||
<span class="value" style="display: flex; align-items: center">
|
||||
{{ $t('permission.2', { msg: num }) }}
|
||||
<el-tooltip
|
||||
v-if="false"
|
||||
effect="dark"
|
||||
:content="$t('authorized_space.authorized_space_list')"
|
||||
placement="top"
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { get_supplier } from '@/entity/supplier'
|
||||
import { highlightKeyword } from '@/utils/xss'
|
||||
import AuthorizedWorkspaceDialogForModel from '@/views/system/workspace/AuthorizedWorkspaceDialogForModel.vue'
|
||||
import AuthorizedWorkspaceDraw from '@/views/system/workspace/AuthorizedWorkspaceDraw.vue'
|
||||
|
||||
interface Model {
|
||||
ws_mapping_count: number | undefined
|
||||
@@ -252,12 +253,13 @@ const deleteHandler = (item: any) => {
|
||||
}
|
||||
|
||||
const AuthorizedWorkspaceDialogForModelRef = ref()
|
||||
const AuthorizedWorkspaceDrawRef = ref()
|
||||
|
||||
const handleAuthorizedSpace = (item: any) => {
|
||||
AuthorizedWorkspaceDialogForModelRef.value.open(item.id)
|
||||
}
|
||||
const handleEditWorkspaceList = (item: any) => {
|
||||
AuthorizedWorkspaceDialogForModelRef.value.open(item.id)
|
||||
AuthorizedWorkspaceDrawRef.value.open(item.id)
|
||||
}
|
||||
|
||||
const clickModel = (ele: any) => {
|
||||
@@ -483,6 +485,7 @@ const submit = (item: any) => {
|
||||
</el-drawer>
|
||||
</div>
|
||||
<AuthorizedWorkspaceDialogForModel ref="AuthorizedWorkspaceDialogForModelRef" @refresh="search" />
|
||||
<AuthorizedWorkspaceDraw ref="AuthorizedWorkspaceDrawRef" @refresh="search" />
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
</el-input>
|
||||
<div class="mt-8 max-height_workspace">
|
||||
<el-checkbox
|
||||
v-if="workspaceWithKeywords.length"
|
||||
v-model="checkAll"
|
||||
class="mb-8"
|
||||
style="margin-left: 16px"
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
</el-input>
|
||||
<div class="mt-8 max-height_workspace">
|
||||
<el-checkbox
|
||||
v-if="workspaceWithKeywords.length"
|
||||
v-model="checkAll"
|
||||
class="mb-8"
|
||||
style="margin-left: 16px"
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="centerDialogVisible"
|
||||
:title="$t('authorized_space.authorized_space')"
|
||||
modal-class="authorized-workspace"
|
||||
width="840"
|
||||
:before-close="beforeClose"
|
||||
>
|
||||
<p class="mb-8 lighter" style="margin-top: 8px">{{ $t('authorized_space.select_space') }}</p>
|
||||
<div v-loading="loading" class="flex border" style="height: 428px; border-radius: 6px">
|
||||
<div class="p-16 border-r">
|
||||
<el-input
|
||||
v-model="search"
|
||||
:validate-event="false"
|
||||
:placeholder="$t('datasource.search')"
|
||||
style="width: 364px; margin-left: 16px"
|
||||
clearable
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Search></Search>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<div class="mt-8 max-height_workspace">
|
||||
<el-checkbox
|
||||
v-if="workspaceWithKeywords.length"
|
||||
v-model="checkAll"
|
||||
class="mb-8"
|
||||
style="margin-left: 16px"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
{{ $t('datasource.select_all') }}
|
||||
</el-checkbox>
|
||||
<el-checkbox-group
|
||||
v-model="checkedWorkspace"
|
||||
class="checkbox-group-block"
|
||||
@change="handleCheckedWorkspaceChange"
|
||||
>
|
||||
<el-checkbox
|
||||
v-for="space in workspaceWithKeywords"
|
||||
:key="space.id"
|
||||
:label="space.name"
|
||||
:value="space"
|
||||
class="hover-bg"
|
||||
>
|
||||
<div class="flex">
|
||||
<el-icon size="28">
|
||||
<avatar_personal></avatar_personal>
|
||||
</el-icon>
|
||||
<span class="ml-4 ellipsis" style="max-width: 40%" :title="space.name">
|
||||
{{ space.name }}</span
|
||||
>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-16 w-full">
|
||||
<div class="flex-between mb-16" style="margin: 0 16px">
|
||||
<span class="lighter">
|
||||
{{ $t('workspace.selected_number', { msg: checkTableList.length }) }}
|
||||
</span>
|
||||
|
||||
<el-button text @click="clearWorkspaceAll">
|
||||
{{ $t('workspace.clear') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div
|
||||
v-for="ele in checkTableList"
|
||||
:key="ele.name"
|
||||
style="margin: 0 16px; position: relative"
|
||||
class="flex-between align-center hover-bg_select"
|
||||
>
|
||||
<div :title="ele.name" class="flex align-center ellipsis" style="width: 100%">
|
||||
<el-icon size="28">
|
||||
<avatar_personal></avatar_personal>
|
||||
</el-icon>
|
||||
<span class="ml-4 lighter ellipsis" style="max-width: 40%" :title="ele.name">{{
|
||||
ele.name
|
||||
}}</span>
|
||||
</div>
|
||||
<el-button class="close-btn" text>
|
||||
<el-icon size="16" @click="clearWorkspace(ele)"><Close /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button secondary @click="centerDialogVisible = false">
|
||||
{{ $t('common.cancel') }}</el-button
|
||||
>
|
||||
<el-button type="primary" @click="handleConfirm">
|
||||
{{ $t('common.confirm2') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { workspaceList, workspaceModelMappingAdd } from '@/api/workspace'
|
||||
import avatar_personal from '@/assets/svg/workspace-white.svg'
|
||||
import Close from '@/assets/svg/icon_close_outlined_w.svg'
|
||||
import Search from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import type { CheckboxValueType } from 'element-plus-secondary'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
const checkAll = ref(false)
|
||||
const isIndeterminate = ref(false)
|
||||
const checkedWorkspace = ref<any[]>([])
|
||||
const workspace = ref<any[]>([])
|
||||
const search = ref('')
|
||||
const loading = ref(false)
|
||||
const centerDialogVisible = ref(false)
|
||||
const checkTableList = ref([] as any[])
|
||||
|
||||
const workspaceWithKeywords = computed(() => {
|
||||
return workspace.value.filter((ele: any) =>
|
||||
(ele.name.toLowerCase() as string).includes(search.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
watch(search, () => {
|
||||
const tableNameArr = workspaceWithKeywords.value.map((ele: any) => ele.name)
|
||||
checkedWorkspace.value = checkTableList.value.filter((ele: any) =>
|
||||
tableNameArr.includes(ele.name)
|
||||
)
|
||||
const checkedCount = checkedWorkspace.value.length
|
||||
checkAll.value = checkedCount === workspaceWithKeywords.value.length
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < workspaceWithKeywords.value.length
|
||||
})
|
||||
const handleCheckAllChange = (val: CheckboxValueType) => {
|
||||
const tableNameArr = workspaceWithKeywords.value.map((ele: any) => ele.name)
|
||||
checkedWorkspace.value = val
|
||||
? [
|
||||
...new Set([
|
||||
...workspaceWithKeywords.value,
|
||||
...checkedWorkspace.value.filter((ele: any) => !tableNameArr.includes(ele.name)),
|
||||
]),
|
||||
]
|
||||
: []
|
||||
isIndeterminate.value = false
|
||||
checkTableList.value = val
|
||||
? [
|
||||
...new Set([
|
||||
...workspaceWithKeywords.value,
|
||||
...checkTableList.value.filter((ele: any) => !tableNameArr.includes(ele.name)),
|
||||
]),
|
||||
]
|
||||
: checkTableList.value.filter((ele: any) => !tableNameArr.includes(ele.name))
|
||||
}
|
||||
const handleCheckedWorkspaceChange = (value: CheckboxValueType[]) => {
|
||||
const checkedCount = value.length
|
||||
checkAll.value = checkedCount === workspaceWithKeywords.value.length
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < workspaceWithKeywords.value.length
|
||||
const tableNameArr = workspaceWithKeywords.value.map((ele: any) => ele.name)
|
||||
checkTableList.value = [
|
||||
...new Set([
|
||||
...checkTableList.value.filter((ele: any) => !tableNameArr.includes(ele.name)),
|
||||
...value,
|
||||
]),
|
||||
]
|
||||
}
|
||||
let oid: any = null
|
||||
|
||||
const open = async (id: any) => {
|
||||
loading.value = true
|
||||
search.value = ''
|
||||
oid = id
|
||||
checkedWorkspace.value = []
|
||||
checkTableList.value = []
|
||||
checkAll.value = false
|
||||
isIndeterminate.value = false
|
||||
const workspaceListResult = await workspaceList()
|
||||
workspace.value = JSON.parse(JSON.stringify(workspaceListResult))
|
||||
loading.value = false
|
||||
centerDialogVisible.value = true
|
||||
}
|
||||
const emits = defineEmits(['refresh'])
|
||||
const handleConfirm = () => {
|
||||
workspaceModelMappingAdd(
|
||||
oid,
|
||||
checkTableList.value.map((ele: any) => `${ele.id}`)
|
||||
).then(() => {
|
||||
beforeClose()
|
||||
emits('refresh')
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: t('common.operation_success'),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const beforeClose = () => {
|
||||
checkedWorkspace.value = []
|
||||
workspace.value = []
|
||||
oid = null
|
||||
checkTableList.value = []
|
||||
centerDialogVisible.value = false
|
||||
}
|
||||
|
||||
const clearWorkspace = (val: any) => {
|
||||
checkedWorkspace.value = checkedWorkspace.value.filter((ele: any) => ele.id !== val.id)
|
||||
checkTableList.value = checkTableList.value.filter((ele: any) => ele.id !== val.id)
|
||||
handleCheckedWorkspaceChange(checkedWorkspace.value)
|
||||
}
|
||||
|
||||
const clearWorkspaceAll = () => {
|
||||
checkedWorkspace.value = []
|
||||
handleCheckedWorkspaceChange([])
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
})
|
||||
</script>
|
||||
<style lang="less">
|
||||
.authorized-workspace {
|
||||
.mb-8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.ed-checkbox {
|
||||
margin-right: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hover-bg,
|
||||
.hover-bg_select {
|
||||
&:hover {
|
||||
&::after {
|
||||
content: '';
|
||||
height: 44px;
|
||||
width: calc(100% + 34px);
|
||||
background: #1f23291a;
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: -8px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hover-bg_select {
|
||||
&:hover {
|
||||
&::after {
|
||||
width: calc(100% + 16px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mt-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.p-16 {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.lighter {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.checkbox-group-block {
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
.checkbox-group-block {
|
||||
.ed-checkbox,
|
||||
.ed-checkbox__label,
|
||||
.flex {
|
||||
width: 96%;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
align-items: center;
|
||||
.account {
|
||||
color: #8f959e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background: #1f23291a !important;
|
||||
}
|
||||
}
|
||||
|
||||
.border {
|
||||
border: 1px solid #dee0e3;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
overflow-y: auto;
|
||||
|
||||
.flex-between {
|
||||
height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.mt-8 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.max-height_workspace {
|
||||
max-height: calc(100% - 24px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ml-4 {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.border-r {
|
||||
border-right: 1px solid #dee0e3;
|
||||
width: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,521 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="drawerVisible"
|
||||
:title="$t('authorized_space.authorized_space_list')"
|
||||
size="840px"
|
||||
class="authorized-workspace-drawer"
|
||||
@closed="handleDrawerClosed"
|
||||
>
|
||||
<div v-loading="loading" class="drawer-body">
|
||||
<div class="top-row">
|
||||
<span class="lighter">
|
||||
{{ $t('authorized_space.workspaces_authorized', { num: filteredList.length }) }}
|
||||
</span>
|
||||
|
||||
<div class="detail" @click="handleAddWorkspace">
|
||||
<el-icon style="margin-right: 4px" size="16">
|
||||
<icon_add_outlined></icon_add_outlined>
|
||||
</el-icon>
|
||||
{{ $t('authorized_space.authorized_space') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-row">
|
||||
<el-input
|
||||
v-model="keywords"
|
||||
clearable
|
||||
:placeholder="$t('datasource.search')"
|
||||
@input="onKeywordChange"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<icon_searchOutline_outlined />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="table-content"
|
||||
:class="{ 'show-pagination-height': multipleSelectionAll.length > 0 }"
|
||||
>
|
||||
<el-table
|
||||
ref="multipleTableRef"
|
||||
:data="pageList"
|
||||
:row-key="getRowId"
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" :reserve-selection="true" width="55" />
|
||||
<el-table-column prop="name" :label="$t('user.workspace')" min-width="220" />
|
||||
<el-table-column
|
||||
v-if="false"
|
||||
prop="memberCount"
|
||||
:label="$t('authorized_space.number_of_members')"
|
||||
width="140"
|
||||
/>
|
||||
<el-table-column :label="t('ds.actions')" width="100" fixed="right">
|
||||
<template #default="scope">
|
||||
<div class="field-comment">
|
||||
<el-tooltip
|
||||
:offset="14"
|
||||
effect="dark"
|
||||
:content="$t('workspace.remove')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon class="action-btn" size="16" @click="deleteHandler(scope.row)">
|
||||
<icon_block_outlined />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<EmptyBackground
|
||||
v-if="!oldKeywordsMember && !filteredList.length"
|
||||
:description="$t('authorized_space.no_workspace')"
|
||||
img-type="noneWhite"
|
||||
/>
|
||||
|
||||
<EmptyBackground
|
||||
v-if="!!oldKeywordsMember && !filteredList.length"
|
||||
:description="$t('datasource.relevant_content_found')"
|
||||
img-type="tree"
|
||||
/>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div v-if="filteredList.length" class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="pageInfo.currentPage"
|
||||
v-model:page-size="pageInfo.pageSize"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:background="true"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pageInfo.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="multipleSelectionAll.length" class="bottom-select">
|
||||
<el-checkbox
|
||||
v-model="checkAll"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
{{ $t('datasource.select_all') }}
|
||||
</el-checkbox>
|
||||
|
||||
<button class="danger-button" @click="deleteBatchUser">{{ $t('workspace.remove') }}</button>
|
||||
|
||||
<span class="selected">{{
|
||||
$t('user.selected_2_items', { msg: multipleSelectionAll.length })
|
||||
}}</span>
|
||||
|
||||
<el-button text @click="cancelDelete">{{ $t('common.cancel') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<AuthorizedWorkspaceDialogForModelAdd
|
||||
ref="authorizedWorkspaceDialogForModelAdd"
|
||||
@refresh="refresh"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, reactive, nextTick } from 'vue'
|
||||
import { workspaceModelMapping, workspaceModelMappingDelete, workspaceList } from '@/api/workspace'
|
||||
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
|
||||
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
|
||||
import icon_block_outlined from '@/assets/svg/icon_block_outlined.svg'
|
||||
import AuthorizedWorkspaceDialogForModelAdd from './AuthorizedWorkspaceDialogForModelAdd.vue'
|
||||
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'refresh'): void
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const drawerVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const keywords = ref('')
|
||||
const oldKeywordsMember = ref('')
|
||||
const aiModelId = ref<string | null>(null)
|
||||
|
||||
const rawList = ref<any[]>([])
|
||||
const multipleSelectionAll = ref<any[]>([])
|
||||
const multipleTableRef = ref()
|
||||
const authorizedWorkspaceDialogForModelAdd = ref()
|
||||
const selectionSyncing = ref(false)
|
||||
const checkAll = ref(false)
|
||||
const isIndeterminate = ref(false)
|
||||
const selectedRowMap = ref<Map<string, any>>(new Map())
|
||||
|
||||
const pageInfo = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const filteredList = computed(() => {
|
||||
if (!keywords.value) return rawList.value
|
||||
return rawList.value.filter((ele: any) =>
|
||||
`${ele.name || ''}`.toLowerCase().includes(keywords.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
|
||||
const pageList = computed(() => {
|
||||
const start = (pageInfo.currentPage - 1) * pageInfo.pageSize
|
||||
const end = start + pageInfo.pageSize
|
||||
return filteredList.value.slice(start, end)
|
||||
})
|
||||
|
||||
const getRowId = (row: any) => `${row.id}`
|
||||
|
||||
const syncSelectedRows = () => {
|
||||
multipleSelectionAll.value = Array.from(selectedRowMap.value.values())
|
||||
}
|
||||
|
||||
const setPageTotal = () => {
|
||||
pageInfo.total = filteredList.value.length
|
||||
const maxPage = Math.max(1, Math.ceil(pageInfo.total / pageInfo.pageSize))
|
||||
if (pageInfo.currentPage > maxPage) {
|
||||
pageInfo.currentPage = maxPage
|
||||
}
|
||||
}
|
||||
|
||||
const refreshSelectionState = () => {
|
||||
const checkedCount = pageList.value.filter((row: any) =>
|
||||
selectedRowMap.value.has(getRowId(row))
|
||||
).length
|
||||
checkAll.value = pageList.value.length > 0 && checkedCount === pageList.value.length
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < pageList.value.length
|
||||
}
|
||||
|
||||
const handleToggleRowSelection = (check = true) => {
|
||||
if (!multipleTableRef.value) return
|
||||
selectionSyncing.value = true
|
||||
multipleTableRef.value.clearSelection()
|
||||
if (check) {
|
||||
for (const row of pageList.value) {
|
||||
if (selectedRowMap.value.has(getRowId(row))) {
|
||||
multipleTableRef.value.toggleRowSelection(row, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
selectionSyncing.value = false
|
||||
refreshSelectionState()
|
||||
}
|
||||
|
||||
const handleSelectionChange = (val: any[]) => {
|
||||
if (selectionSyncing.value) return
|
||||
const pageIdSet = new Set(pageList.value.map((row: any) => getRowId(row)))
|
||||
for (const id of pageIdSet) {
|
||||
selectedRowMap.value.delete(id)
|
||||
}
|
||||
for (const row of val) {
|
||||
selectedRowMap.value.set(getRowId(row), row)
|
||||
}
|
||||
syncSelectedRows()
|
||||
refreshSelectionState()
|
||||
}
|
||||
|
||||
const handleCheckAllChange = (val: boolean) => {
|
||||
for (const row of pageList.value) {
|
||||
const id = getRowId(row)
|
||||
if (val) {
|
||||
selectedRowMap.value.set(id, row)
|
||||
} else {
|
||||
selectedRowMap.value.delete(id)
|
||||
}
|
||||
}
|
||||
syncSelectedRows()
|
||||
handleToggleRowSelection()
|
||||
}
|
||||
|
||||
const cancelDelete = () => {
|
||||
selectedRowMap.value.clear()
|
||||
multipleSelectionAll.value = []
|
||||
checkAll.value = false
|
||||
isIndeterminate.value = false
|
||||
handleToggleRowSelection(false)
|
||||
}
|
||||
|
||||
const toMemberCount = (row: any) => {
|
||||
return row.memberCount ?? row.member_count ?? row.userCount ?? row.user_count ?? row.count ?? 0
|
||||
}
|
||||
|
||||
const loadData = async () => {
|
||||
if (!aiModelId.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
const [mappingResult, workspaceResult] = await Promise.all([
|
||||
workspaceModelMapping(aiModelId.value),
|
||||
workspaceList(),
|
||||
])
|
||||
const ids = new Set((mappingResult || []).map((id: any) => `${id}`))
|
||||
rawList.value = (workspaceResult || [])
|
||||
.filter((ele: any) => ids.has(`${ele.id}`))
|
||||
.map((ele: any) => ({
|
||||
...ele,
|
||||
memberCount: toMemberCount(ele),
|
||||
}))
|
||||
const latestRowMap = new Map(rawList.value.map((row: any) => [getRowId(row), row]))
|
||||
for (const id of Array.from(selectedRowMap.value.keys())) {
|
||||
const latest = latestRowMap.get(id)
|
||||
if (!latest) {
|
||||
selectedRowMap.value.delete(id)
|
||||
} else {
|
||||
selectedRowMap.value.set(id, latest)
|
||||
}
|
||||
}
|
||||
syncSelectedRows()
|
||||
setPageTotal()
|
||||
await nextTick()
|
||||
handleToggleRowSelection()
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const deleteByIds = (ids: string[]) => {
|
||||
if (!aiModelId.value || !ids.length) return
|
||||
workspaceModelMappingDelete(aiModelId.value, ids).then(async () => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: t('workspace.removed_successfully'),
|
||||
})
|
||||
for (const id of ids) {
|
||||
selectedRowMap.value.delete(id)
|
||||
}
|
||||
syncSelectedRows()
|
||||
await loadData()
|
||||
})
|
||||
}
|
||||
|
||||
const deleteHandler = (row: any) => {
|
||||
ElMessageBox.confirm(t('authorized_space.delete_workspace', { msg: row.name }), {
|
||||
confirmButtonType: 'danger',
|
||||
confirmButtonText: t('workspace.remove'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
}).then(() => {
|
||||
deleteByIds([`${row.id}`])
|
||||
})
|
||||
}
|
||||
|
||||
const deleteBatchUser = () => {
|
||||
ElMessageBox.confirm(
|
||||
t('authorized_space.delete_selected_workspaces', { msg: multipleSelectionAll.value.length }),
|
||||
{
|
||||
confirmButtonType: 'danger',
|
||||
confirmButtonText: t('workspace.remove'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
}
|
||||
).then(() => {
|
||||
deleteByIds(multipleSelectionAll.value.map((ele: any) => `${ele.id}`))
|
||||
})
|
||||
}
|
||||
|
||||
const handleSizeChange = (val: number) => {
|
||||
pageInfo.pageSize = val
|
||||
pageInfo.currentPage = 1
|
||||
setPageTotal()
|
||||
nextTick(() => handleToggleRowSelection())
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val: number) => {
|
||||
pageInfo.currentPage = val
|
||||
nextTick(() => handleToggleRowSelection())
|
||||
}
|
||||
|
||||
const onKeywordChange = () => {
|
||||
pageInfo.currentPage = 1
|
||||
setPageTotal()
|
||||
oldKeywordsMember.value = keywords.value
|
||||
nextTick(() => {
|
||||
handleToggleRowSelection()
|
||||
})
|
||||
}
|
||||
|
||||
const handleAddWorkspace = () => {
|
||||
authorizedWorkspaceDialogForModelAdd.value?.open(aiModelId.value)
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
loadData()
|
||||
}
|
||||
|
||||
const handleDrawerClosed = () => {
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
const open = async (id: string | number) => {
|
||||
aiModelId.value = `${id}`
|
||||
keywords.value = ''
|
||||
oldKeywordsMember.value = ''
|
||||
selectedRowMap.value.clear()
|
||||
multipleSelectionAll.value = []
|
||||
checkAll.value = false
|
||||
isIndeterminate.value = false
|
||||
pageInfo.currentPage = 1
|
||||
drawerVisible.value = true
|
||||
await loadData()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.authorized-workspace-drawer {
|
||||
.drawer-body {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.detail {
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
&:hover {
|
||||
&::after {
|
||||
content: '';
|
||||
background: #1f23291a;
|
||||
border-radius: 6px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 84px;
|
||||
height: 26px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lighter {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-row {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.table-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding-bottom: 64px;
|
||||
|
||||
&.show-pagination-height {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 64px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.field-comment {
|
||||
height: 24px;
|
||||
|
||||
.action-btn {
|
||||
position: relative;
|
||||
margin-top: 4px;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background-color: #1f23291a;
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transform: translate(-50%, -50%);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
color: #8f959e;
|
||||
}
|
||||
|
||||
.bottom-select {
|
||||
position: absolute;
|
||||
height: 64px;
|
||||
width: calc(100% + 48px);
|
||||
left: -24px;
|
||||
bottom: -79px;
|
||||
border-top: 1px solid #1f232926;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
padding-left: 24px;
|
||||
z-index: 10;
|
||||
|
||||
.danger-button {
|
||||
border: 1px solid var(--ed-color-danger);
|
||||
color: var(--ed-color-danger);
|
||||
border-radius: var(--ed-border-radius-base);
|
||||
min-width: 80px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin: 0 16px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.selected {
|
||||
color: #646a73;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.ed-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user