feat(workspace): authorized workspace model UI

This commit is contained in:
dataeaseShu
2026-06-04 16:51:01 +08:00
committed by dataeaseShu
parent 98dba5f517
commit f54a7cc7d5
12 changed files with 907 additions and 10 deletions
+4
View File
@@ -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

+3 -1
View File
@@ -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",
+3 -1
View File
@@ -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": "로그아웃",
+4 -2
View File
@@ -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": "退出登录",
+4 -2
View File
@@ -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": "登出",
+2 -3
View File
@@ -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"
+4 -1
View File
@@ -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>