From 1168b0f2e32d6af56e5967425ccf86c7cdfd51fe Mon Sep 17 00:00:00 2001 From: AaronWu <2463371514@qq.com> Date: Mon, 19 Jan 2026 10:07:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B7=A5=E5=8D=95=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=8C=E6=8F=90=E4=BA=A4=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=98=B2=E6=8A=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/client/issue/columns.tsx | 4 +- src/views/client/issue/data.ts | 40 ++-- src/views/client/issue/index.vue | 14 +- src/views/question/issue/columns.tsx | 4 +- src/views/question/issue/data.ts | 25 +-- src/views/question/issue/index.vue | 247 +++++++++++++++---------- src/views/question/knowledge/index.vue | 171 ++++++++++------- vite.config.ts | 2 +- 8 files changed, 299 insertions(+), 208 deletions(-) diff --git a/src/views/client/issue/columns.tsx b/src/views/client/issue/columns.tsx index 2d69038..d5a09b5 100644 --- a/src/views/client/issue/columns.tsx +++ b/src/views/client/issue/columns.tsx @@ -157,7 +157,9 @@ export const baseColumns: TableColumnItem[] = [ }, }, customRender: ({ record }) => { - const { label, color } = stateTypeList.find((e) => e.value === record.state); + const obj = stateTypeList.find((e) => e.value === record.state); + const label = obj?.label; + const color = obj?.color; return {label}; }, diff --git a/src/views/client/issue/data.ts b/src/views/client/issue/data.ts index 2d8cc6f..6228ac9 100644 --- a/src/views/client/issue/data.ts +++ b/src/views/client/issue/data.ts @@ -1,32 +1,46 @@ +/* + * @Author: AaronWu 2463371514@qq.com + * @Date: 2025-04-16 14:22:07 + * @LastEditors: AaronWu 2463371514@qq.com + * @LastEditTime: 2026-01-14 10:57:35 + * @FilePath: /IssueSupportManage/src/views/client/issue/data.ts + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ export const stateTypeList: any = [ - // Init(0,"待处理"), - // Back(1,"退回"), - // Develop(2,"开发中"), - // Test(3,"测试中"), - // End(4,"结束"), + { + label: '退回', + value: -1, + color: '#2db7f5', + }, { label: '待处理', value: 0, color: '#f50', }, { - label: '退回', + label: '开发中', value: 1, - color: '#2db7f5', + color: '#87d068', }, { - label: '开发中', + label: '测试中', value: 2, - color: '#87d068', + color: '#108ee9', }, + // 待客户确认 { - label: '测试中', + label: '待客户确认', value: 3, - color: '#108ee9', + color: '#2db7f5', }, { label: '已解决', value: 4, - color: '#f50', + color: '#87d068', + }, + { + label: '关闭', + value: 5, + color: '#d9d9d9', }, -]; +]; \ No newline at end of file diff --git a/src/views/client/issue/index.vue b/src/views/client/issue/index.vue index cdd094f..94840f4 100644 --- a/src/views/client/issue/index.vue +++ b/src/views/client/issue/index.vue @@ -275,7 +275,10 @@ ...e, }; }); - values.fileIds = values.files.filter((e) => e.id).map((e) => e.id).join(','); + values.fileIds = values.files + .filter((e) => e.id) + .map((e) => e.id) + .join(','); } if (values?.fileIds === ',') { values.fileIds = ''; @@ -529,8 +532,9 @@ if (records && Array.isArray(records) && records.length) { childItem.children = (records as any).map((e) => { - const { label, color } = stateTypeList.find((d) => d.value === e.state); - + const obj = stateTypeList.find((i) => i.value === e.state); + const label = obj?.label; + const color = obj?.color; return { title: e.title, key: e.id, @@ -567,7 +571,9 @@ if (records && Array.isArray(records) && records.length) { tree = (records as any).map((e) => { - const { label, color } = stateTypeList.find((d) => d.value === e.state); + const obj = stateTypeList.find((i) => i.value === e.state); + const label = obj?.label; + const color = obj?.color; return { title: e.title, diff --git a/src/views/question/issue/columns.tsx b/src/views/question/issue/columns.tsx index 6c269fb..7abe19f 100644 --- a/src/views/question/issue/columns.tsx +++ b/src/views/question/issue/columns.tsx @@ -242,7 +242,9 @@ export const baseColumns: TableColumnItem[] = [ }, }, customRender: ({ record }) => { - const { label, color } = stateTypeList.find((e) => e.value === record.state); + const obj = stateTypeList.find((e) => e.value === record.state); + const label = obj?.label; + const color = obj?.color; return {label}; }, diff --git a/src/views/question/issue/data.ts b/src/views/question/issue/data.ts index a27f752..da8e6b1 100644 --- a/src/views/question/issue/data.ts +++ b/src/views/question/issue/data.ts @@ -1,43 +1,38 @@ export const stateTypeList: any = [ - // Init(0,"待处理"), - // Back(1,"退回"), - // Develop(2,"开发中"), - // Test(3,"测试中"), - // End(4,"结束"), + { + label: '退回', + value: -1, + color: '#2db7f5', + }, { label: '待处理', value: 0, color: '#f50', }, - { - label: '退回', - value: 1, - color: '#2db7f5', - }, { label: '开发中', - value: 2, + value: 1, color: '#87d068', }, { label: '测试中', - value: 3, + value: 2, color: '#108ee9', }, // 待客户确认 { label: '待客户确认', - value: 4, + value: 3, color: '#2db7f5', }, { label: '已解决', - value: 5, + value: 4, color: '#87d068', }, { label: '关闭', - value: 6, + value: 5, color: '#d9d9d9', }, ]; diff --git a/src/views/question/issue/index.vue b/src/views/question/issue/index.vue index 973f62c..2ba879c 100644 --- a/src/views/question/issue/index.vue +++ b/src/views/question/issue/index.vue @@ -126,6 +126,7 @@ const curRowId = ref(''); const visible = ref(false); const quillEditor = ref | null>(null); + const submitting = ref(false); // 表单提交状态 const editorOptions = ref({ theme: 'snow', modules: { @@ -205,7 +206,11 @@ // quillImageUploadCustom(quillEditor.value); }); - const handleOk = async () => { + const handleOk = useDebounceFn(async () => { + if (submitting.value) { + return; + } + const values = await formRef?.validate(); if (values) { console.log('values: ', values); @@ -214,43 +219,51 @@ message.warning('请等待附件上传完成'); return; } - values.id = curRecord.value.id; - - if (values.files && Array.isArray(values.files) && values.files.length) { - values.files = values.files.map((e) => { - if (e.response) { + + submitting.value = true; + try { + values.id = curRecord.value.id; + + if (values.files && Array.isArray(values.files) && values.files.length) { + values.files = values.files.map((e) => { + if (e.response) { + return { + name: e.name, + url: e.response.url, + id: e.response.id, + }; + } return { - name: e.name, - url: e.response.url, - id: e.response.id, + ...e, }; - } - return { - ...e, - }; - }); - values.fileIds = values.files - .filter((e) => e.id) - .map((e) => e.id) - .join(','); - } else { - values.fileIds = ''; - } - if (values?.fileIds === ',') { - values.fileIds = ''; - } - - if (values?.tags && Array.isArray(values.tags) && values.tags.length) { - values.tags = values.tags.join(','); + }); + values.fileIds = values.files + .filter((e) => e.id) + .map((e) => e.id) + .join(','); + } else { + values.fileIds = ''; + } + if (values?.fileIds === ',') { + values.fileIds = ''; + } + + if (values?.tags && Array.isArray(values.tags) && values.tags.length) { + values.tags = values.tags.join(','); + } + + await (values.id ? updateIssue : createIssue)(values); + message.success(`${values.id ? '编辑' : '新增'}成功`); + visible.value = false; + resetFormFields(); + dynamicTableInstance?.reload(); + } catch (error) { + console.error('提交失败:', error); + } finally { + submitting.value = false; } - - await (values.id ? updateIssue : createIssue)(values); - message.success(`${values.id ? '编辑' : '新增'}成功`); - visible.value = false; - resetFormFields(); - dynamicTableInstance?.reload(); } - }; + }, 300); const handleCancel = () => { visible.value = false; @@ -311,17 +324,17 @@ handleEdit(record); }, }, - { - icon: 'init', - size: '15', - title: '处理', - label: '处理', - ifShow: stateText === '退回', - onClick: (e) => { - e.stopPropagation(); - changeState(record, 0); - }, - }, + // { + // icon: 'init', + // size: '15', + // title: '处理', + // label: '处理', + // ifShow: stateText === '退回', + // onClick: (e) => { + // e.stopPropagation(); + // changeState(record, 0); + // }, + // }, { icon: 'dev', size: '15', @@ -330,7 +343,7 @@ ifShow: stateText === '待处理', onClick: (e) => { e.stopPropagation(); - changeState(record, 2); + changeState(record, 1); }, }, { @@ -338,10 +351,10 @@ size: '20', title: '退回', label: '退回', - ifShow: stateText === '开发中' || stateText === '测试中', + ifShow: stateText !== '待处理' && stateText !== '关闭', onClick: (e) => { e.stopPropagation(); - changeState(record, 1); + changeState(record, record.state - 1); }, }, { @@ -352,7 +365,7 @@ ifShow: stateText === '开发中', onClick: (e) => { e.stopPropagation(); - changeState(record, 3); + changeState(record, 2); }, }, // 待客户确认 4 @@ -364,7 +377,7 @@ ifShow: stateText === '测试中', onClick: (e) => { e.stopPropagation(); - changeState(record, 4); + changeState(record, 3); }, }, { @@ -375,7 +388,7 @@ ifShow: stateText === '待客户确认', onClick: (e) => { e.stopPropagation(); - changeState(record, 5); + changeState(record, 4); }, }, { @@ -397,7 +410,7 @@ ifShow: stateText !== '关闭', onClick: (e) => { e.stopPropagation(); - changeState(record, 6); + changeState(record, 5); }, }, { @@ -425,44 +438,62 @@ /** * @description 打开问题工单弹窗 */ + // 创建防抖的提交处理函数 + const createSubmitHandler = (record: Partial, isModal = false) => { + let isSubmitting = false; + return useDebounceFn(async (values: any) => { + if (isSubmitting) { + return; + } + isSubmitting = true; + try { + console.log('新增/编辑问题工单', values); + values.id = record.id; + + if (values.files && Array.isArray(values.files) && values.files.length) { + values.files = values.files.map((e) => { + if (e.response) { + return { + name: e.name, + url: e.response.url, + id: e.response.id, + }; + } + return { + ...e, + }; + }); + values.fileIds = values.files + .filter((e) => e.id) + .map((e) => e.id) + .join(','); + } else { + values.fileIds = ''; + } + + if (values?.tags && Array.isArray(values.tags) && values.tags.length) { + values.tags = values.tags.join(','); + } + + await (record.id ? updateIssue : createIssue)(values); + message.success(`${record.id ? '编辑' : '新增'}成功`); + dynamicTableInstance?.reload(); + } catch (error) { + console.error('提交失败:', error); + } finally { + isSubmitting = false; + } + }, 300); + }; + const openIssueModal = async (record: Partial = {}, isReadOnly = false) => { + const handleSubmit = createSubmitHandler(record); + const [formRef] = await showModal({ modalProps: { title: `${isReadOnly ? '查看' : record.id ? '编辑' : '新增'}问题工单`, width: 700, - onFinish: async (values) => { - console.log('新增/编辑问题工单', values); - values.id = record.id; - - if (values.files && Array.isArray(values.files) && values.files.length) { - values.files = values.files.map((e) => { - if (e.response) { - return { - name: e.name, - url: e.response.url, - id: e.response.id, - }; - } - return { - ...e, - }; - }); - values.fileIds = values.files - .filter((e) => e.id) - .map((e) => e.id) - .join(','); - } else { - values.fileIds = ''; - } - - if (values?.tags && Array.isArray(values.tags) && values.tags.length) { - values.tags = values.tags.join(','); - } - - await (record.id ? updateIssue : createIssue)(values); - message.success(`${record.id ? '编辑' : '新增'}成功`); - dynamicTableInstance?.reload(); - }, + onFinish: handleSubmit, footer: isReadOnly ? null : undefined, }, formProps: { @@ -499,26 +530,40 @@ const openFlowModal = async (record: Partial = {}, state) => { const flowTitle = stateTypeList.find((item) => item.value === state)?.label; + + let isFlowSubmitting = false; + const handleFlowSubmit = useDebounceFn(async (values: any) => { + if (isFlowSubmitting) { + return; + } + isFlowSubmitting = true; + try { + await updateIssueState({ + id: record.id, + state: state, + ...values, + }); + message.success(`操作成功`); + dynamicTableInstance?.reload(); + // 判断当前行是否有选中 + if (curRowId.value === record.id) { + curRowId.value = ''; + nextTick(() => { + curRowId.value = record.id; + }); + } + } catch (error) { + console.error('提交失败:', error); + } finally { + isFlowSubmitting = false; + } + }, 300); + const [formRef] = await showFlowModal({ modalProps: { title: `${flowTitle}问题工单`, width: 700, - onFinish: async (values) => { - await updateIssueState({ - id: record.id, - state: state, - ...values, - }); - message.success(`操作成功`); - dynamicTableInstance?.reload(); - // 判断当前行是否有选中 - if (curRowId.value === record.id) { - curRowId.value = ''; - nextTick(() => { - curRowId.value = record.id; - }); - } - }, + onFinish: handleFlowSubmit, }, formProps: { labelWidth: 100, diff --git a/src/views/question/knowledge/index.vue b/src/views/question/knowledge/index.vue index 903681c..eac977a 100644 --- a/src/views/question/knowledge/index.vue +++ b/src/views/question/knowledge/index.vue @@ -98,6 +98,7 @@ import { createDictValue } from '@/api/dict'; import { DictEnum } from '@/enums/dictEnum'; import { getDictionaryByTypeName } from '@/utils/dict'; + import { useDebounceFn } from '@vueuse/core'; defineOptions({ name: 'knowledge', }); @@ -106,6 +107,7 @@ const curRecord = ref>({}); const visible = ref(false); + const submitting = ref(false); // 表单提交状态 const quillEditor = ref | null>(null); @@ -170,7 +172,11 @@ // quillImageUploadCustom(quillEditor.value); }); - const handleOk = async () => { + const handleOk = useDebounceFn(async () => { + if (submitting.value) { + return; + } + const values = await formRef?.validate(); if (values) { console.log('values: ', values); @@ -179,57 +185,65 @@ message.warning('请等待附件上传完成'); return; } - values.id = curRecord.value.id; - if (values.files && Array.isArray(values.files) && values.files.length) { - values.files = values.files.map((e) => { - if (e.response) { + submitting.value = true; + try { + values.id = curRecord.value.id; + + if (values.files && Array.isArray(values.files) && values.files.length) { + values.files = values.files.map((e) => { + if (e.response) { + return { + name: e.name, + url: e.response.url, + id: e.response.id, + }; + } return { - name: e.name, - url: e.response.url, - id: e.response.id, + ...e, }; - } - return { - ...e, - }; - }); - values.fileIds = values.files - .filter((e) => e.id) - .map((e) => e.id) - .join(','); - } else { - values.fileIds = ''; - } + }); + values.fileIds = values.files + .filter((e) => e.id) + .map((e) => e.id) + .join(','); + } else { + values.fileIds = ''; + } - if (values?.fileIds === ',') { - values.fileIds = ''; - } + if (values?.fileIds === ',') { + values.fileIds = ''; + } - if (values?.tags && Array.isArray(values.tags) && values.tags.length) { - const allTags = (await getDictionaryByTypeName(DictEnum.TAG_TYPE)) || []; - // 找出values.tags中不在allTags中的值 - const notInTags = values.tags.filter((tag) => { - return !allTags.some((e) => e.value === tag); - }); - console.log('notInTags: ', notInTags); - for (const tag of notInTags) { - await createDictValue({ - dictValue: tag, - typeId: 33, - enable: 1, + if (values?.tags && Array.isArray(values.tags) && values.tags.length) { + const allTags = (await getDictionaryByTypeName(DictEnum.TAG_TYPE)) || []; + // 找出values.tags中不在allTags中的值 + const notInTags = values.tags.filter((tag) => { + return !allTags.some((e) => e.value === tag); }); + console.log('notInTags: ', notInTags); + for (const tag of notInTags) { + await createDictValue({ + dictValue: tag, + typeId: 33, + enable: 1, + }); + } + values.tags = values.tags.join(','); } - values.tags = values.tags.join(','); - } - await (values.id ? updateKnowledgeBase : createKnowledgeBase)(values); - message.success(`${values.id ? '编辑' : '新增'}成功`); - visible.value = false; - resetFormFields(); - dynamicTableInstance?.reload(); + await (values.id ? updateKnowledgeBase : createKnowledgeBase)(values); + message.success(`${values.id ? '编辑' : '新增'}成功`); + visible.value = false; + resetFormFields(); + dynamicTableInstance?.reload(); + } catch (error) { + console.error('提交失败:', error); + } finally { + submitting.value = false; + } } - }; + }, 300); const handleCancel = () => { visible.value = false; @@ -283,39 +297,52 @@ record: Partial = {}, isReadOnly = false, ) => { + let isModalSubmitting = false; + const handleModalSubmit = useDebounceFn(async (values: any) => { + if (isModalSubmitting) { + return; + } + isModalSubmitting = true; + try { + console.log('新增/编辑知识库', values); + values.id = record.id; + + if (values.files && Array.isArray(values.files) && values.files.length) { + values.files = values.files.map((e) => { + if (e.response) { + return { + name: e.name, + url: e.response.url, + id: e.response.id, + }; + } + return { + ...e, + }; + }); + values.fileIds = values.files + .filter((e) => e.id) + .map((e) => e.id) + .join(','); + } else { + values.fileIds = ''; + } + + await (record.id ? updateKnowledgeBase : createKnowledgeBase)(values); + message.success(`${record.id ? '编辑' : '新增'}成功`); + dynamicTableInstance?.reload(); + } catch (error) { + console.error('提交失败:', error); + } finally { + isModalSubmitting = false; + } + }, 300); + const [formRef] = await showModal({ modalProps: { title: `${isReadOnly ? '查看' : record.id ? '编辑' : '新增'}知识库`, width: 700, - onFinish: async (values) => { - console.log('新增/编辑知识库', values); - values.id = record.id; - - if (values.files && Array.isArray(values.files) && values.files.length) { - values.files = values.files.map((e) => { - if (e.response) { - return { - name: e.name, - url: e.response.url, - id: e.response.id, - }; - } - return { - ...e, - }; - }); - values.fileIds = values.files - .filter((e) => e.id) - .map((e) => e.id) - .join(','); - } else { - values.fileIds = ''; - } - - await (record.id ? updateKnowledgeBase : createKnowledgeBase)(values); - message.success(`${record.id ? '编辑' : '新增'}成功`); - dynamicTableInstance?.reload(); - }, + onFinish: handleModalSubmit, footer: isReadOnly ? null : undefined, }, formProps: { diff --git a/vite.config.ts b/vite.config.ts index 80883d9..e954652 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -126,7 +126,7 @@ export default defineConfig({ // }, '/server': { target: 'http://43.137.2.78:8085', - // target: 'http://192.168.2.22:8089', + // target: 'http://192.168.2.27:8089', changeOrigin: true, rewrite: (path) => path.replace(/^\/server/, '/server'), },