feat(Model Configuration): Supported Authorized Space

This commit is contained in:
dataeaseShu
2026-06-02 15:15:36 +08:00
committed by dataeaseShu
parent 4b0d4ce663
commit 4a9f4e2187
10 changed files with 597 additions and 9 deletions
+5
View File
@@ -15,3 +15,8 @@ export const workspaceDelete = (id: any) => request.delete(`/system/workspace/${
export const workspaceList = () => request.get('/system/workspace')
export const workspaceDetail = (id: any) => request.get(`/system/workspace/${id}`)
export const uwsOption = (params: any) => request.get('system/workspace/uws/option', { params })
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)
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.33333 1.9999V13.9994H12.6667V1.9999H3.33333ZM2.66667 0.666626H13.3333C13.7015 0.666626 14 0.979303 14 1.36501V14.6343C14 15.02 13.7015 15.3327 13.3333 15.3327H2.66667C2.29848 15.3327 2 15.02 2 14.6343V1.36501C2 0.979303 2.29848 0.666626 2.66667 0.666626ZM5 4.66646H7.66667C7.85076 4.66646 8 4.81569 8 4.99978V5.66642C8 5.85051 7.85076 5.99974 7.66667 5.99974H5C4.81591 5.99974 4.66667 5.85051 4.66667 5.66642V4.99978C4.66667 4.81569 4.81591 4.66646 5 4.66646ZM5 7.33302H11C11.1841 7.33302 11.3333 7.48225 11.3333 7.66633V8.33297C11.3333 8.51706 11.1841 8.66629 11 8.66629H5C4.81591 8.66629 4.66667 8.51706 4.66667 8.33297V7.66633C4.66667 7.48225 4.81591 7.33302 5 7.33302ZM5 9.99957H11C11.1841 9.99957 11.3333 10.1488 11.3333 10.3329V10.9995C11.3333 11.1836 11.1841 11.3328 11 11.3328H5C4.81591 11.3328 4.66667 11.1836 4.66667 10.9995V10.3329C4.66667 10.1488 4.81591 9.99957 5 9.99957Z" fill="#646A73"/>
</svg>

After

Width:  |  Height:  |  Size: 1019 B

@@ -0,0 +1,5 @@
<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
<circle cx="14" cy="14" r="14" class="icon-primary"/>
<path d="M14.1567 8.55799L20.0301 12.2956C20.2102 12.4102 20.2102 12.6731 20.0301 12.7877L14.1567 16.5254C14.0612 16.5862 13.9391 16.5862 13.8435 16.5254L7.97013 12.7877C7.79003 12.6731 7.79003 12.4102 7.97013 12.2956L13.8435 8.55799C13.9391 8.49719 14.0612 8.49719 14.1567 8.55799ZM9.75652 12.5417L14.0001 15.2421L18.2437 12.5417L14.0001 9.8412L9.75652 12.5417Z" fill="white"/>
<path d="M8.51471 15.1002C8.37738 15.016 8.1978 15.0591 8.1136 15.1964L7.80868 15.6937C7.72448 15.8311 7.76755 16.0106 7.90488 16.0948L13.2454 19.3693C13.6201 19.5991 14.0884 19.5991 14.4632 19.3693L19.8037 16.0948C19.941 16.0106 19.9841 15.8311 19.8999 15.6937L19.5949 15.1964C19.5107 15.0591 19.3312 15.016 19.1938 15.1002L13.9305 18.3274C13.8837 18.3561 13.8248 18.3561 13.778 18.3274L8.51471 15.1002Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 945 B

+10
View File
@@ -32,6 +32,16 @@
"enter_variable_name": "Please Enter Variable Name",
"enter_variable_value": "Please Enter Variable Value"
},
"authorized_space": {
"authorized_space": "Authorized Space",
"authorized_space_list": "Authorized Space List",
"select_space": "Select Space",
"modify_authorized_space": "Modify Authorized Space",
"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}?"
},
"sync": {
"records": "Displaying {num} records out of {total}",
"confirm_upload": "Confirm Upload",
+10
View File
@@ -32,6 +32,16 @@
"enter_variable_name": "변수 이름을 입력하세요",
"enter_variable_value": "변수 값을 입력하세요"
},
"authorized_space": {
"authorized_space": "권한 있는 공간",
"authorized_space_list": "권한 있는 공간 목록",
"select_space": "공간 선택",
"modify_authorized_space": "권한 있는 공간 수정",
"workspaces_authorized": "{num}개의 권한 있는 작업 공간",
"number_of_members": "회원 수",
"delete_selected_workspaces": "선택한 {msg}개의 작업 공간을 삭제하시겠습니까?",
"delete_workspace": "작업 공간을 삭제하시겠습니까: {msg}?"
},
"sync": {
"records": "{total}개 중 {num}개의 레코드 표시",
"confirm_upload": "업로드 확인",
+10
View File
@@ -32,6 +32,16 @@
"enter_variable_name": "请输入变量名称",
"enter_variable_value": "请输入变量值"
},
"authorized_space": {
"authorized_space": "授权空间",
"authorized_space_list": "授权空间列表",
"select_space": "选择空间",
"modify_authorized_space": "修改授权空间",
"workspaces_authorized": "已授权 {num} 个工作空间",
"number_of_members": "成员数量",
"delete_selected_workspaces": "是否删除选中的 {msg} 个工作空间?",
"delete_workspace": "是否删除工作空间:{msg}?"
},
"sync": {
"records": "显示 {num} 条数据,共 {total} 条",
"confirm_upload": "确定上传",
+10
View File
@@ -32,6 +32,16 @@
"enter_variable_name": "請輸入變數名稱",
"enter_variable_value": "請輸入變數值"
},
"authorized_space": {
"authorized_space": "授權空間",
"authorized_space_list": "授權空間列表",
"select_space": "選擇空間",
"modify_authorized_space": "修改授權空間",
"workspaces_authorized": "已授權 {num} 個工作空間",
"number_of_members": "成員數量",
"delete_selected_workspaces": "是否刪除選中的 {msg} 個工作空間?",
"delete_workspace": "是否刪除工作空間:{msg}?"
},
"sync": {
"records": "顯示 {num} 筆資料,共 {total} 筆",
"confirm_upload": "確定上傳",
+165 -9
View File
@@ -1,9 +1,12 @@
<script lang="ts" setup>
import delIcon from '@/assets/svg/icon_delete.svg'
import icon_more_outlined from '@/assets/svg/icon_more_outlined.svg'
import icon_moments_categories_outlined from '@/assets/svg/icon_moments-categories_outlined.svg'
import icon_admin_outlined from '@/assets/svg/icon_admin_outlined.svg'
import icon_describe_outlined from '@/assets/svg/icon_describe_outlined.svg'
import edit from '@/assets/svg/icon_edit_outlined.svg'
import { get_supplier } from '@/entity/supplier'
import { computed, ref } from 'vue'
import { computed, ref, unref } from 'vue'
const props = withDefaults(
defineProps<{
name: string
@@ -12,6 +15,7 @@ const props = withDefaults(
id?: string
isDefault?: boolean
supplier?: number
num?: number
}>(),
{
name: '-',
@@ -20,6 +24,7 @@ const props = withDefaults(
id: '-',
isDefault: false,
supplier: 0,
num: 0,
}
)
const errorMsg = ref('')
@@ -38,7 +43,7 @@ const showErrorMask = (msg?: string) => {
errorMsg.value = ''
}, 3000)
}
const emits = defineEmits(['edit', 'del', 'default'])
const emits = defineEmits(['edit', 'del', 'default', 'authorizedSpace', 'editworkspaceList'])
const handleDefault = () => {
emits('default')
@@ -52,6 +57,18 @@ const handleEdit = () => {
emits('edit')
}
const handleEditWorkspaceList = () => {
emits('editworkspaceList')
}
const handleAuthorizedSpace = () => {
emits('authorizedSpace')
}
const buttonRef = ref()
const popoverRef = ref()
const onClickOutside = () => {
unref(popoverRef).popperRef?.delayHide?.()
}
defineExpose({ showErrorMask })
</script>
@@ -77,6 +94,27 @@ defineExpose({ showErrorMask })
<span class="type">{{ $t('model.basic_model') }}</span>
<span class="value"> {{ baseModel }}</span>
</div>
<div v-if="false" class="type-value">
<span class="type">{{ $t('authorized_space.authorized_space') }}</span>
<span class="value" style="display: flex; align-items: center">
{{ $t('permission.2', { msg: num }) }}
<el-tooltip
effect="dark"
:content="$t('authorized_space.authorized_space_list')"
placement="top"
offset="12"
>
<el-icon
class="hover-icon_with_bg"
style="cursor: pointer; margin-left: 8px"
size="16"
@click="handleEditWorkspaceList"
>
<icon_describe_outlined></icon_describe_outlined>
</el-icon>
</el-tooltip>
</span>
</div>
<div class="methods">
<el-tooltip
v-if="isDefault"
@@ -104,12 +142,41 @@ defineExpose({ showErrorMask })
</el-icon>
{{ $t('dashboard.edit') }}
</el-button>
<el-button secondary @click="handleDel">
<el-icon style="margin-right: 4px" size="16">
<delIcon></delIcon>
</el-icon>
{{ $t('dashboard.delete') }}
</el-button>
<el-icon
ref="buttonRef"
v-click-outside="onClickOutside"
class="more"
size="16"
style="color: #646a73"
@click.stop
>
<icon_more_outlined></icon_more_outlined>
</el-icon>
<el-popover
ref="popoverRef"
:virtual-ref="buttonRef"
virtual-triggering
trigger="click"
:teleported="false"
popper-class="popover-card_workspace"
placement="bottom-start"
>
<div class="content">
<div class="item" @click.stop="handleAuthorizedSpace">
<el-icon size="16">
<icon_moments_categories_outlined></icon_moments_categories_outlined>
</el-icon>
{{ $t('authorized_space.authorized_space') }}
</div>
<div class="item" @click.stop="handleDel">
<el-icon size="16">
<delIcon></delIcon>
</el-icon>
{{ $t('dashboard.delete') }}
</div>
</div>
</el-popover>
</div>
</div>
</template>
@@ -167,7 +234,7 @@ defineExpose({ showErrorMask })
.methods {
margin-top: 16px;
align-items: center;
display: none;
display: flex;
.divide {
height: 14px;
@@ -175,6 +242,37 @@ defineExpose({ showErrorMask })
background-color: #1f232926;
margin: 0 12px;
}
.more {
position: relative;
cursor: pointer;
margin-left: 12px;
width: 32px;
height: 32px;
svg {
position: relative;
z-index: 10;
}
&::after {
content: '';
position: absolute;
border-radius: 6px;
width: 30px;
height: 30px;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
background: #fff;
border: 1px solid #d9dcdf;
}
&:hover {
&::after {
background-color: #f5f6f7;
}
}
}
}
&:hover {
@@ -208,3 +306,61 @@ defineExpose({ showErrorMask })
}
}
</style>
<style lang="less">
.popover-card_workspace.popover-card_workspace.popover-card_workspace {
box-shadow: 0px 4px 8px 0px #1f23291a;
border-radius: 6px;
border: 1px solid #dee0e3;
width: fit-content !important;
min-width: 120px !important;
padding: 0;
.content {
position: relative;
&::after {
position: absolute;
content: '';
top: 39px !important;
left: 0;
width: 100%;
height: 1px;
background: #dee0e3;
}
.item {
position: relative;
padding: 0 12px;
height: 40px;
display: flex;
align-items: center;
cursor: pointer;
.ed-icon {
margin-right: 8px;
color: #646a73;
}
&:hover {
&::after {
display: block;
}
}
&::after {
content: '';
width: calc(100% - 8px);
height: 32px;
border-radius: 6px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #1f23291a;
display: none;
}
}
}
}
</style>
+13
View File
@@ -16,6 +16,7 @@ import { getModelTypeName } from '@/entity/CommonEntity.ts'
import { useI18n } from 'vue-i18n'
import { get_supplier } from '@/entity/supplier'
import { highlightKeyword } from '@/utils/xss'
import AuthorizedWorkspaceDialogForModel from '@/views/system/workspace/AuthorizedWorkspaceDialogForModel.vue'
interface Model {
name: string
@@ -249,6 +250,15 @@ const deleteHandler = (item: any) => {
})
}
const AuthorizedWorkspaceDialogForModelRef = ref()
const handleAuthorizedSpace = (item: any) => {
AuthorizedWorkspaceDialogForModelRef.value.open(item.id)
}
const handleEditWorkspaceList = (item: any) => {
AuthorizedWorkspaceDialogForModelRef.value.open(item.id)
}
const clickModel = (ele: any) => {
activeStep.value = 1
supplierChang(ele)
@@ -394,6 +404,8 @@ const submit = (item: any) => {
@edit="handleEditModel(ele)"
@del="deleteHandler"
@default="handleDefault(ele)"
@authorized-space="handleAuthorizedSpace(ele)"
@edit-workspace-list="handleEditWorkspaceList(ele)"
></card>
</el-col>
</el-row>
@@ -468,6 +480,7 @@ const submit = (item: any) => {
</template>
</el-drawer>
</div>
<AuthorizedWorkspaceDialogForModel ref="AuthorizedWorkspaceDialogForModelRef" @refresh="search" />
</template>
<style lang="less" scoped>
@@ -0,0 +1,366 @@
<template>
<el-dialog
v-model="centerDialogVisible"
:title="$t('authorized_space.modify_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-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_2_people', { 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 v-if="!checkedWorkspace.length" disabled type="info">{{
$t('common.confirm2')
}}</el-button>
<el-button v-else 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, workspaceModelMapping, workspaceModelMappingUpdate } 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, workspaceMappingResult] = await Promise.allSettled([
workspaceList(),
workspaceModelMapping(id),
])
workspace.value =
workspaceListResult.status === 'fulfilled'
? JSON.parse(JSON.stringify(workspaceListResult.value))
: []
checkedWorkspace.value =
workspaceMappingResult.status === 'fulfilled'
? workspace.value.filter((ele: any) => workspaceMappingResult.value.includes(ele.id))
: []
handleCheckedWorkspaceChange(
workspaceMappingResult.status === 'fulfilled'
? workspace.value.filter((ele: any) => workspaceMappingResult.value.includes(ele.id))
: []
)
loading.value = false
centerDialogVisible.value = true
}
const emits = defineEmits(['refresh'])
const handleConfirm = () => {
workspaceModelMappingUpdate(
oid,
checkTableList.value.map((ele: any) => ele.id)
).then(() => {
beforeClose()
emits('refresh')
ElMessage({
type: 'success',
message: t('common.save_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>