diff --git a/ui/src/views/home/components/ranking/HomeRankings.vue b/ui/src/views/home/components/ranking/HomeRankings.vue index b91cde855fa..a66bf21573c 100644 --- a/ui/src/views/home/components/ranking/HomeRankings.vue +++ b/ui/src/views/home/components/ranking/HomeRankings.vue @@ -83,6 +83,9 @@ function handleRangeChange({ startTime, endTime }: MkDateRangeValue) { function handleOpenRanking(kind: HomeRankingKind) { selectedKind.value = kind } +function handleRankingDrawerClosed() { + selectedKind.value = undefined +} onMounted(loadRankings) @@ -131,6 +134,13 @@ onMounted(loadRankings) 对话 {{ numberFormat(record.chat_record_count) }} 次 - + diff --git a/ui/src/views/tool/tool-form/DataSourceFormDrawer.vue b/ui/src/views/tool/tool-form/DataSourceFormDrawer.vue index 47ef9c2174e..3a9a50c4b94 100644 --- a/ui/src/views/tool/tool-form/DataSourceFormDrawer.vue +++ b/ui/src/views/tool/tool-form/DataSourceFormDrawer.vue @@ -5,8 +5,10 @@ import type { FormInstance, FormRules } from 'element-plus' import type ToolApi from '@/api/admin/workspace/tool/tool' import type SystemResourceToolApi from '@/api/admin/system/resource-management/tool/tool' import type SystemSharedToolApi from '@/api/admin/system/shared-resources/tool/tool' -import { TOOL_TYPE } from '@/api/enums' +import { FILE_SOURCE_TYPE, TOOL_TYPE } from '@/api/enums' +import FileApi from '@/api/admin/file' import type { DynamicFormField, ToolInputField, ToolItem, ToolPayload } from '@/api/types' +import MkEditAvatar from '@/components/mk-edit-avatar/index.vue' import { useStore } from '@/stores' import { MsgConfirm, MsgSuccess } from '@/utils/message' import InitFieldTable from './component/init-field/InitFieldTable.vue' @@ -56,28 +58,44 @@ const formRules: FormRules = { name: [{ whitespace: true, required: true, message: '请输入数据源名称', trigger: 'blur' }], } +// 暂存确认后的头像文件,恢复默认时清空。 +const iconFile = ref(null) +function handleIconChange(file: File | null) { + iconFile.value = file +} + function handleSubmit() { - formRef.value?.validate((valid) => { - if (!valid) return + return formRef.value?.validate((valid) => { + if (!valid || loading.value) return - const payload: ToolPayload = { ...cloneDeep(dataSourceForm), tool_type: TOOL_TYPE.DATA_SOURCE } loading.value = true const currentEditId = editId.value const isEdit = Boolean(currentEditId) - const request = currentEditId - ? props.api.putTool(currentEditId, payload) - : 'postTool' in props.api - ? props.api.postTool({ ...payload, folder_id: props.folderId || null }) - : Promise.reject(new Error('资源管理不支持创建工具')) - - request - .then((savedTool) => { - const refreshCurrentUser = isEdit ? Promise.resolve() : auth.loadAuthBaseProfile() - return refreshCurrentUser.then(() => { - MsgSuccess(isEdit ? '保存成功' : '创建成功') - visible.value = false - if (isEdit) emit('update', savedTool) - else emit('refresh') + // 新头像先上传并替换预览地址,再提交完整工具表单。 + const uploadIcon = iconFile.value + ? FileApi.postUploadFile(iconFile.value, currentEditId, FILE_SOURCE_TYPE.TOOL).request.then((icon) => { + dataSourceForm.icon = icon + iconFile.value = null + }) + : Promise.resolve() + + return uploadIcon + .then(() => { + const payload: ToolPayload = { ...cloneDeep(dataSourceForm), tool_type: TOOL_TYPE.DATA_SOURCE } + const request = currentEditId + ? props.api.putTool(currentEditId, payload) + : 'postTool' in props.api + ? props.api.postTool({ ...payload, folder_id: props.folderId || null }) + : Promise.reject(new Error('资源管理不支持创建工具')) + + return request.then((savedTool) => { + const refreshCurrentUser = isEdit ? Promise.resolve() : auth.loadAuthBaseProfile() + return refreshCurrentUser.then(() => { + MsgSuccess(isEdit ? '保存成功' : '创建成功') + visible.value = false + if (isEdit) emit('update', savedTool) + else emit('refresh') + }) }) }) .finally(() => { @@ -123,6 +141,7 @@ function open(tool?: ToolItem, asCopy = false) { } function handleBeforeClose() { + if (loading.value) return if (JSON.stringify(dataSourceForm) === originalForm.value) { visible.value = false return @@ -136,6 +155,7 @@ function handleBeforeClose() { function resetData() { Object.assign(dataSourceForm, { code: codeTemplate, desc: '', icon: '', init_field_list: [], input_field_list: [], name: '' }) + iconFile.value = null editId.value = undefined originalForm.value = '' loading.value = false @@ -155,7 +175,7 @@ defineExpose({ open }) 基本信息 - - + + + + + + - 取消 + 取消 {{ editId ? '保存' : '创建' }} diff --git a/ui/src/views/tool/tool-form/McpFormDrawer.vue b/ui/src/views/tool/tool-form/McpFormDrawer.vue index 0b4cbf34d59..0ea2333b97e 100644 --- a/ui/src/views/tool/tool-form/McpFormDrawer.vue +++ b/ui/src/views/tool/tool-form/McpFormDrawer.vue @@ -5,8 +5,10 @@ import type { FormInstance, FormRules } from 'element-plus' import type ToolApi from '@/api/admin/workspace/tool/tool' import type SystemResourceToolApi from '@/api/admin/system/resource-management/tool/tool' import type SystemSharedToolApi from '@/api/admin/system/shared-resources/tool/tool' -import { TOOL_TYPE } from '@/api/enums' +import { FILE_SOURCE_TYPE, TOOL_TYPE } from '@/api/enums' +import FileApi from '@/api/admin/file' import type { ToolItem, ToolPayload } from '@/api/types' +import MkEditAvatar from '@/components/mk-edit-avatar/index.vue' import { useStore } from '@/stores' import { MsgConfirm, MsgError, MsgSuccess } from '@/utils/message' @@ -43,6 +45,12 @@ const formRules: FormRules = { name: [{ whitespace: true, required: true, message: '请输入 MCP 名称', trigger: 'blur' }], } +// 暂存确认后的头像文件,恢复默认时清空。 +const iconFile = ref(null) +function handleIconChange(file: File | null) { + iconFile.value = file +} + function isValidConfig() { try { const config: unknown = JSON.parse(mcpForm.code) @@ -55,27 +63,37 @@ function isValidConfig() { } function handleSubmit() { - formRef.value?.validate((valid) => { - if (!valid || !isValidConfig()) return + return formRef.value?.validate((valid) => { + if (!valid || loading.value || !isValidConfig()) return - const payload: ToolPayload = { ...cloneDeep(mcpForm), tool_type: TOOL_TYPE.MCP } loading.value = true const currentEditId = editId.value const isEdit = Boolean(currentEditId) - const request = currentEditId - ? props.api.putTool(currentEditId, payload) - : 'postTool' in props.api - ? props.api.postTool({ ...payload, folder_id: props.folderId || null }) - : Promise.reject(new Error('资源管理不支持创建工具')) - - request - .then((savedTool) => { - const refreshCurrentUser = isEdit ? Promise.resolve() : auth.loadAuthBaseProfile() - return refreshCurrentUser.then(() => { - MsgSuccess(isEdit ? '保存成功' : '创建成功') - visible.value = false - if (isEdit) emit('update', savedTool) - else emit('refresh') + // 新头像先上传并替换预览地址,再提交完整工具表单。 + const uploadIcon = iconFile.value + ? FileApi.postUploadFile(iconFile.value, currentEditId, FILE_SOURCE_TYPE.TOOL).request.then((icon) => { + mcpForm.icon = icon + iconFile.value = null + }) + : Promise.resolve() + + return uploadIcon + .then(() => { + const payload: ToolPayload = { ...cloneDeep(mcpForm), tool_type: TOOL_TYPE.MCP } + const request = currentEditId + ? props.api.putTool(currentEditId, payload) + : 'postTool' in props.api + ? props.api.postTool({ ...payload, folder_id: props.folderId || null }) + : Promise.reject(new Error('资源管理不支持创建工具')) + + return request.then((savedTool) => { + const refreshCurrentUser = isEdit ? Promise.resolve() : auth.loadAuthBaseProfile() + return refreshCurrentUser.then(() => { + MsgSuccess(isEdit ? '保存成功' : '创建成功') + visible.value = false + if (isEdit) emit('update', savedTool) + else emit('refresh') + }) }) }) .finally(() => { @@ -128,6 +146,7 @@ function open(tool?: ToolItem, asCopy = false) { } function handleBeforeClose() { + if (loading.value) return if (JSON.stringify(mcpForm) === originalForm.value) { visible.value = false return @@ -141,6 +160,7 @@ function handleBeforeClose() { function resetData() { Object.assign(mcpForm, { code: '', desc: '', icon: '', name: '' }) + iconFile.value = null editId.value = undefined originalForm.value = '' loading.value = false @@ -160,7 +180,7 @@ defineExpose({ open }) 基本信息 - - + + + + + + @@ -199,7 +223,7 @@ defineExpose({ open }) - 取消 + 取消 测试连接 {{ editId ? '保存' : '创建' }} diff --git a/ui/src/views/tool/tool-form/SkillToolFormDrawer.vue b/ui/src/views/tool/tool-form/SkillToolFormDrawer.vue index 604275fad0a..22d78a7b529 100644 --- a/ui/src/views/tool/tool-form/SkillToolFormDrawer.vue +++ b/ui/src/views/tool/tool-form/SkillToolFormDrawer.vue @@ -5,8 +5,10 @@ import type { FormInstance, FormRules, UploadFile, UploadFiles, UploadUserFile } import type ToolApi from '@/api/admin/workspace/tool/tool' import type SystemResourceToolApi from '@/api/admin/system/resource-management/tool/tool' import type SystemSharedToolApi from '@/api/admin/system/shared-resources/tool/tool' -import { TOOL_TYPE } from '@/api/enums' +import { FILE_SOURCE_TYPE, TOOL_TYPE } from '@/api/enums' +import FileApi from '@/api/admin/file' import type { DynamicFormField, ToolItem, ToolPayload } from '@/api/types' +import MkEditAvatar from '@/components/mk-edit-avatar/index.vue' import MkDragUpload from '@/components/mk-drag-upload/index.vue' import { useStore } from '@/stores' import { MsgConfirm, MsgError, MsgSuccess } from '@/utils/message' @@ -44,6 +46,12 @@ const formRules: FormRules = { name: [{ whitespace: true, required: true, message: '请输入 Skill 名称', trigger: 'blur' }], } +// 暂存确认后的头像文件,恢复默认时清空。 +const iconFile = ref(null) +function handleIconChange(file: File | null) { + iconFile.value = file +} + function removeUploadFile(file: UploadFile, fileList: UploadFiles) { const fileIndex = fileList.findIndex((item) => item.uid === file.uid) if (fileIndex >= 0) fileList.splice(fileIndex, 1) @@ -94,34 +102,44 @@ function handleRemoveFile() { } function handleSubmit() { - formRef.value?.validate((valid) => { - if (!valid) return + return formRef.value?.validate((valid) => { + if (!valid || loading.value) return - const payload: ToolPayload = { - code: skillForm.code, - desc: skillForm.desc, - icon: skillForm.icon, - init_field_list: cloneDeep(skillForm.init_field_list), - name: skillForm.name, - tool_type: TOOL_TYPE.SKILL, - } loading.value = true const currentEditId = editId.value const isEdit = Boolean(currentEditId) - const request = currentEditId - ? props.api.putTool(currentEditId, payload) - : 'postTool' in props.api - ? props.api.postTool({ ...payload, folder_id: props.folderId || null }) - : Promise.reject(new Error('资源管理不支持创建工具')) + // 新头像先上传并替换预览地址,再提交完整工具表单。 + const uploadIcon = iconFile.value + ? FileApi.postUploadFile(iconFile.value, currentEditId, FILE_SOURCE_TYPE.TOOL).request.then((icon) => { + skillForm.icon = icon + iconFile.value = null + }) + : Promise.resolve() - request - .then((savedTool) => { - const refreshCurrentUser = isEdit ? Promise.resolve() : auth.loadAuthBaseProfile() - return refreshCurrentUser.then(() => { - MsgSuccess(isEdit ? '保存成功' : '创建成功') - visible.value = false - if (isEdit) emit('update', savedTool) - else emit('refresh') + return uploadIcon + .then(() => { + const payload: ToolPayload = { + code: skillForm.code, + desc: skillForm.desc, + icon: skillForm.icon, + init_field_list: cloneDeep(skillForm.init_field_list), + name: skillForm.name, + tool_type: TOOL_TYPE.SKILL, + } + const request = currentEditId + ? props.api.putTool(currentEditId, payload) + : 'postTool' in props.api + ? props.api.postTool({ ...payload, folder_id: props.folderId || null }) + : Promise.reject(new Error('资源管理不支持创建工具')) + + return request.then((savedTool) => { + const refreshCurrentUser = isEdit ? Promise.resolve() : auth.loadAuthBaseProfile() + return refreshCurrentUser.then(() => { + MsgSuccess(isEdit ? '保存成功' : '创建成功') + visible.value = false + if (isEdit) emit('update', savedTool) + else emit('refresh') + }) }) }) .finally(() => { @@ -167,6 +185,7 @@ function open(tool?: ToolItem, asCopy = false) { } function handleBeforeClose() { + if (loading.value) return if (JSON.stringify(skillForm) === originalForm.value) { visible.value = false return @@ -180,6 +199,7 @@ function handleBeforeClose() { function resetData() { Object.assign(skillForm, { code: '', desc: '', fileList: [], icon: '', init_field_list: [], name: '' }) + iconFile.value = null editId.value = undefined originalForm.value = '' loading.value = false @@ -200,7 +220,7 @@ defineExpose({ open }) 基本信息 - - + + + + + + - 取消 + 取消 {{ editId ? '保存' : '创建' }} diff --git a/ui/src/views/tool/tool-form/WorkflowFormDialog.vue b/ui/src/views/tool/tool-form/WorkflowFormDialog.vue index 63a453fad44..470b6c08144 100644 --- a/ui/src/views/tool/tool-form/WorkflowFormDialog.vue +++ b/ui/src/views/tool/tool-form/WorkflowFormDialog.vue @@ -6,8 +6,10 @@ import type { FormInstance, FormRules } from 'element-plus' import type ToolApi from '@/api/admin/workspace/tool/tool' import type SystemResourceToolApi from '@/api/admin/system/resource-management/tool/tool' import type SystemSharedToolApi from '@/api/admin/system/shared-resources/tool/tool' -import { TOOL_TYPE } from '@/api/enums' +import { FILE_SOURCE_TYPE, TOOL_TYPE } from '@/api/enums' +import FileApi from '@/api/admin/file' import type { ToolItem, ToolPayload } from '@/api/types' +import MkEditAvatar from '@/components/mk-edit-avatar/index.vue' import { useStore } from '@/stores' import { MsgSuccess } from '@/utils/message' import { isSystemSharedResource } from '@/utils/resource-context' @@ -37,34 +39,52 @@ const editId = ref() const workflowForm = reactive({ desc: '', icon: '', name: '', work_flow: {} }) const formRules: FormRules = { name: [{ whitespace: true, required: true, message: '请输入工作流名称', trigger: 'blur' }] } +// 暂存确认后的头像文件,恢复默认时清空。 +const iconFile = ref(null) +function handleIconChange(file: File | null) { + iconFile.value = file +} + function handleSubmit() { - formRef.value?.validate((valid) => { - if (!valid) return + return formRef.value?.validate((valid) => { + if (!valid || loading.value) return - const payload: ToolPayload = { ...cloneDeep(workflowForm), code: 'None', tool_type: TOOL_TYPE.WORKFLOW } loading.value = true const currentEditId = editId.value const isEdit = Boolean(currentEditId) - const request = currentEditId - ? props.api.putTool(currentEditId, payload) - : 'postTool' in props.api - ? props.api.postTool({ ...payload, folder_id: props.folderId || null }) - : Promise.reject(new Error('资源管理不支持创建工具')) - - request - .then((savedTool) => { - const refreshCurrentUser = isEdit ? Promise.resolve() : auth.loadAuthBaseProfile() - return refreshCurrentUser.then(() => { - MsgSuccess(isEdit ? '保存成功' : '创建成功') - visible.value = false - if (isEdit) { - emit('update', savedTool) - return - } - - return router.push({ - name: isSystemSharedResource() ? 'system-shared-workflow-tool' : 'workflow-tool', - params: isSystemSharedResource() ? { toolId: savedTool.id } : { toolId: savedTool.id, workspaceId: route.params.workspaceId }, + // 新头像先上传并替换预览地址,再提交完整工具表单。 + const uploadIcon = iconFile.value + ? FileApi.postUploadFile(iconFile.value, currentEditId, FILE_SOURCE_TYPE.TOOL).request.then((icon) => { + workflowForm.icon = icon + iconFile.value = null + }) + : Promise.resolve() + + return uploadIcon + .then(() => { + const payload: ToolPayload = { ...cloneDeep(workflowForm), code: 'None', tool_type: TOOL_TYPE.WORKFLOW } + const request = currentEditId + ? props.api.putTool(currentEditId, payload) + : 'postTool' in props.api + ? props.api.postTool({ ...payload, folder_id: props.folderId || null }) + : Promise.reject(new Error('资源管理不支持创建工具')) + + return request.then((savedTool) => { + const refreshCurrentUser = isEdit ? Promise.resolve() : auth.loadAuthBaseProfile() + return refreshCurrentUser.then(() => { + MsgSuccess(isEdit ? '保存成功' : '创建成功') + visible.value = false + if (isEdit) { + emit('update', savedTool) + return + } + + return router + .push({ + name: isSystemSharedResource() ? 'system-shared-workflow-tool' : 'workflow-tool', + params: isSystemSharedResource() ? { toolId: savedTool.id } : { toolId: savedTool.id, workspaceId: route.params.workspaceId }, + }) + .then(() => {}) }) }) }) @@ -100,8 +120,14 @@ function open(tool?: ToolItem, asCopy = false) { }) } +function handleBeforeClose(done: () => void) { + if (loading.value) return + done() +} + function resetData() { Object.assign(workflowForm, { desc: '', icon: '', name: '', work_flow: {} }) + iconFile.value = null editId.value = undefined loading.value = false formLoading.value = false @@ -117,10 +143,10 @@ defineExpose({ open }) - + - - + + + + + + - 取消 + 取消 {{ editId ? '保存' : '创建' }}