From 88ceb0f63b0acdef21606cb73d6c6c16a79237db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9A=93=E6=9C=88=E5=BD=92=E5=B0=98?= Date: Thu, 27 Feb 2025 23:23:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=8F=8D=E9=A6=88=EF=BC=8C=E6=95=B0=E6=8D=AE=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en.yaml | 9 +- locales/zh-CN.yaml | 9 +- src/api/code.ts | 172 ++++- src/views/codes/index/components/form.vue | 107 +++ src/views/codes/index/index.vue | 11 +- src/views/codes/index/utils/hook.tsx | 54 +- src/views/codes/log/components/details.vue | 58 +- src/views/codes/log/components/form.vue | 107 +++ src/views/data/feedback/components/form.vue | 225 ++++++ src/views/data/feedback/index.vue | 298 ++++++++ src/views/data/feedback/utils/hook.tsx | 551 +++++++++++++++ src/views/data/import/components/form.vue | 180 +++++ src/views/data/import/index.vue | 375 ++++++++++ src/views/data/import/utils/hook.tsx | 659 ++++++++++++++++++ .../maintain}/components/form.vue | 0 .../{codes/admin => data/maintain}/index.vue | 64 +- .../admin => data/maintain}/utils/hook.tsx | 72 +- types/code.d.ts | 68 ++ 18 files changed, 2998 insertions(+), 21 deletions(-) create mode 100644 src/views/codes/index/components/form.vue create mode 100644 src/views/codes/log/components/form.vue create mode 100644 src/views/data/feedback/components/form.vue create mode 100644 src/views/data/feedback/index.vue create mode 100644 src/views/data/feedback/utils/hook.tsx create mode 100644 src/views/data/import/components/form.vue create mode 100644 src/views/data/import/index.vue create mode 100644 src/views/data/import/utils/hook.tsx rename src/views/{codes/admin => data/maintain}/components/form.vue (100%) rename src/views/{codes/admin => data/maintain}/index.vue (80%) rename src/views/{codes/admin => data/maintain}/utils/hook.tsx (81%) diff --git a/locales/en.yaml b/locales/en.yaml index 3417ed0..def87ef 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -43,6 +43,8 @@ buttons:More: More buttons:Deselect: Deselect buttons:DeleteInBatches: Delete In Batches buttons:ExportInBatches: Export In Batches +buttons:AgressInBatches: Agress In Batches +buttons:DisagressInBatches: Disagress In Batches buttons:ExitInBatches: Exit In Batches buttons:ExportAll: Export All buttons:UploadAvatar: Upload Avatar @@ -51,6 +53,8 @@ buttons:RoleAllocation: Role Allocation buttons:PermissionDetails: Permission Details buttons:ForceToExit: Force Exit buttons:DataAdmin: Data Admin +buttons:Audit: Audit +buttons:Feedback: Feedback search:Total: Total search:History: History search:Collect: Collect @@ -117,8 +121,11 @@ menus:SystemConfig: System Config menus:CodeManager: HTS Code Mgnt menus:CodeQueryIndex: HTS Code Query menus:CodeQueryCount: Query Statistics -menus:CodeQueryData: Data Maintenance menus:QueryCodedetails: Log Detail +menus:DataQuality: Data Quality +menus:DataImport: Data Import +menus:DataFeedback: Data Feedback +menus:DataMaintain: Data Export status:Load: Loading... status:Message: Message status:Notify: Notify diff --git a/locales/zh-CN.yaml b/locales/zh-CN.yaml index 8e3ac1f..902e692 100644 --- a/locales/zh-CN.yaml +++ b/locales/zh-CN.yaml @@ -43,6 +43,8 @@ buttons:More: 更多 buttons:Deselect: 取消选择 buttons:DeleteInBatches: 批量删除 buttons:ExportInBatches: 批量导出 +buttons:AgressInBatches: 批量通过 +buttons:DisagressInBatches: 批量驳回 buttons:ExportAll: 全部导出 buttons:UploadAvatar: 上传头像 buttons:ResetPassword: 重置密码 @@ -51,6 +53,8 @@ buttons:PermissionDetails: 权限详情 buttons:ForceToExit: 强制退出 buttons:ExitInBatches: 批量强退 buttons:DataAdmin: 数据管理 +buttons:Audit: 审核 +buttons:Feedback: 反馈 search:Total: 共 search:History: 搜索历史 search:Collect: 收藏 @@ -117,8 +121,11 @@ menus:SystemConfig: 系统配置 menus:CodeManager: 编码管理 menus:CodeQueryIndex: 编码查询 menus:CodeQueryCount: 查询统计 -menus:CodeQueryData: 数据维护 menus:QueryCodedetails: 日志详情 +menus:DataQuality: 数据质量 +menus:DataImport: 数据导入 +menus:DataFeedback: 数据反馈 +menus:DataMaintain: 数据维护 status:Load: 加载中... status:Message: 消息 status:Notify: 通知 diff --git a/src/api/code.ts b/src/api/code.ts index 9861848..7349454 100644 --- a/src/api/code.ts +++ b/src/api/code.ts @@ -1,5 +1,11 @@ import { http } from "@/utils/http"; -import type { CodeInfo, QueryCodeLogInfo, QueryCodeResult } from "types/code"; +import type { + CodeFeedbackInfo, + CodeImportInfo, + CodeInfo, + QueryCodeLogInfo, + QueryCodeResult +} from "types/code"; import { filterEmptyObject } from "./utils"; /** @@ -77,9 +83,19 @@ interface GetCodeListParams { /** 每页条数 */ pageSize: number; /** 编码 */ - code: string; + code?: string; /** 编码描述 */ - description: string; + description?: string; + /**用户账号 */ + username?: string; + /**用户昵称 */ + nickname?: string; + /**部门ID */ + department_id?: string; + /**开始时间 */ + startTime?: string | number; + /**结束时间 */ + endTime?: string | number; } /** * 获取编码列表 @@ -160,3 +176,153 @@ export const getCodeLogListAllAPI = (params: { } ); }; + +/**删除编码导入 */ +export const deleteCodeImportAPI = (id: string) => { + return http.request("delete", `/api/code/deleteCodeImport/${id}`); +}; + +/**批量删除编码导入 */ +export const deleteCodeImportListAPI = (data: { ids: string[] }) => { + return http.request("post", `/api/code/deleteCodeImportList`, { data }); +}; + +/**编码导入审核 */ +export const putCodeImportAuditAPI = (data: { + /**状态 */ + status: number; + /**编码ID列表 */ + ids: string[]; +}) => { + return http.request("put", `/api/code/codeImportAudit`, { data }); +}; + +/**获取编码导入列表参数 */ +interface GetCodeImportListParams { + /** 当前页码 */ + page: number; + /** 每页条数 */ + pageSize: number; + /** 状态 */ + status?: number | string; + /**编码 */ + code?: string; + /**编码描述 */ + description?: string; + /**用户账号 */ + username?: string; + /**用户昵称 */ + nickname?: string; + /**部门ID */ + department_id?: string; + /**开始时间 */ + startTime?: string | number; + /**结束时间 */ + endTime?: string | number; +} +/** + * 获取编码导入列表 + * @param params + * @returns + */ +export const getCodeImportListAPI = (params: GetCodeImportListParams) => { + return http.request>( + "get", + `/api/code/codeImportList`, + { + params: filterEmptyObject(params) + } + ); +}; + +/**修改编码导入 */ +export const putUpdateCodeImportAPI = (data: AddCodeParams, id: string) => { + return http.request("put", `/api/code/updateCodeImport/${id}`, { + data + }); +}; + +/**添加反馈参数 */ +interface AddCodeFeedbackParams { + /**反馈编码 */ + feedback_code: string; + /**反馈文本 */ + feedback_description: string; + /**编码ID */ + code_id: string; +} + +/**添加反馈 */ +export const postAddCodeFeedbackAPI = (data: AddCodeFeedbackParams) => { + return http.request("post", `/api/code/addFeedback`, { data }); +}; + +/**删除反馈 */ +export const deleteCodeFeedbackAPI = (id: string) => { + return http.request("delete", `/api/code/deleteFeedback/${id}`); +}; +/**批量删除反馈 */ +export const deleteCodeFeedbackListAPI = (data: { ids: string[] }) => { + return http.request("post", `/api/code/deleteFeedbackList`, { data }); +}; + +/**修改编码反馈 */ +export const putUpdateCodeFeedbackAPI = ( + data: AddCodeFeedbackParams, + id: string +) => { + return http.request("put", `/api/code/updateFeedback/${id}`, { + data + }); +}; + +/**获取编码反馈列表参数 */ +interface GetCodeFeedbackListParams { + /** 当前页码 */ + page: number; + /** 每页条数 */ + pageSize: number; + /** 状态 */ + status?: number | string; + /**编码 */ + code?: string; + /**反馈编码 */ + feedback_code?: string; + /**反馈文本 */ + feedback_description?: string; + /**用户账号 */ + username?: string; + /**用户昵称 */ + nickname?: string; + /**部门ID */ + department_id?: string; + /**开始时间 */ + startTime?: string | number; + /**结束时间 */ + endTime?: string | number; +} + +/** + * 获取编码反馈列表 + * @param params + * @returns + */ +export const getCodeFeedbackListAPI = (params: GetCodeFeedbackListParams) => { + return http.request>( + "get", + `/api/code/feedbackList`, + { + params: filterEmptyObject(params) + } + ); +}; + +/**反馈审核 */ +export const putCodeFeedbackAuditAPI = (data: { + /**状态 */ + status: number; + /**反馈ID列表 */ + ids: string[]; +}) => { + return http.request("put", `/api/code/feedbackAudit`, { data }); +}; diff --git a/src/views/codes/index/components/form.vue b/src/views/codes/index/components/form.vue new file mode 100644 index 0000000..21054ed --- /dev/null +++ b/src/views/codes/index/components/form.vue @@ -0,0 +1,107 @@ + + + diff --git a/src/views/codes/index/index.vue b/src/views/codes/index/index.vue index 43e930a..06e8b2f 100644 --- a/src/views/codes/index/index.vue +++ b/src/views/codes/index/index.vue @@ -210,6 +210,14 @@ item.match_rate ? `${item.match_rate}%` : "0%" }} + {{ t("buttons:Feedback") }} @@ -262,7 +270,8 @@ const { onDownloadTemplate, handleDetail, exportToExcel, - onbatchExport + onbatchExport, + handleFeedback } = useIndex(tableRef); diff --git a/src/views/codes/index/utils/hook.tsx b/src/views/codes/index/utils/hook.tsx index 411b7e2..e577316 100644 --- a/src/views/codes/index/utils/hook.tsx +++ b/src/views/codes/index/utils/hook.tsx @@ -1,5 +1,6 @@ import { message } from "@/utils/message"; -import { type Ref, ref, reactive } from "vue"; +import editForm from "../components/form.vue"; +import { type Ref, ref, reactive, h } from "vue"; import * as XLSX from "xlsx"; import type { PaginationProps } from "@pureadmin/table"; import type { QueryCodeResult, QueryResult, QueryResultItem } from "types/code"; @@ -11,10 +12,12 @@ import { import { getQueryCodeAPI, getQueryTemplateAPI, + postAddCodeFeedbackAPI, postCodeInfoAPI } from "@/api/code"; import { deleteFileAPI, postUploadFileAPI } from "@/api/file"; import { getKeyList, cloneDeep } from "@pureadmin/utils"; +import { addDialog } from "@/components/ReDialog"; export const useIndex = (tableRef: Ref) => { /** @@ -26,7 +29,7 @@ export const useIndex = (tableRef: Ref) => { /** * 表单Ref */ - // const formRef = ref(null); + const formRef = ref(null); /** * 数据列表 */ @@ -371,6 +374,50 @@ export const useIndex = (tableRef: Ref) => { exportToExcel([exportData], "查询结果"); }; + /**处理反馈 */ + const handleFeedback = async (row: QueryResultItem) => { + addDialog({ + title: `反馈错误`, + props: { + formInline: { + code_id: row?.id ?? "", + code: row?.code ?? "", + description: row?.description ?? "", + feedback_code: row?.code ?? "", + feedback_description: row?.description ?? "" + } + }, + width: "45%", + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => + h(editForm, { + formInline: { + code_id: row?.id ?? "", + code: row?.code ?? "", + description: row?.description ?? "", + feedback_code: row?.code ?? "", + feedback_description: row?.description ?? "" + }, + ref: formRef + }), + beforeSure: async (done, {}) => { + const FormData = formRef.value.newFormInline; + const res = await postAddCodeFeedbackAPI({ + code_id: row.id, + feedback_description: FormData.feedback_description, + feedback_code: FormData.feedback_code + }); + if (res.success) { + message("反馈成功!", { type: "success" }); + done(); + } else { + message("反馈失败!", { type: "error" }); + } + } + }); + }; return { form, dataList, @@ -396,6 +443,7 @@ export const useIndex = (tableRef: Ref) => { onDownloadTemplate, handleDetail, exportToExcel, - onbatchExport + onbatchExport, + handleFeedback }; }; diff --git a/src/views/codes/log/components/details.vue b/src/views/codes/log/components/details.vue index d107da8..d051a29 100644 --- a/src/views/codes/log/components/details.vue +++ b/src/views/codes/log/components/details.vue @@ -135,6 +135,14 @@ item.match_rate ? `${item.match_rate}%` : "0%" }} + {{ t("buttons:Feedback") }} @@ -145,6 +153,7 @@ defineOptions({ name: "QueryCodedetails" }); +import editForm from "./form.vue"; import { ref, reactive, onMounted, h } from "vue"; import { useRouter, useRoute } from "vue-router"; import dayjs from "dayjs"; @@ -160,15 +169,15 @@ import type { QueryResult, QueryResultItem } from "types/code"; -import { getCodeLogInfoAPI } from "@/api/code"; +import { getCodeLogInfoAPI, postAddCodeFeedbackAPI } from "@/api/code"; import { PaginationProps } from "@pureadmin/table"; import { message } from "@/utils/message"; import { hasAuth } from "@/utils/auth"; +import { addDialog } from "@/components/ReDialog"; const { t } = useI18n(); const route = useRoute(); const router = useRouter(); const getParameter = isEmpty(route.params) ? route.query : route.params; -console.log(getParameter); // /**初始化加载页面 */ // function init() { // Object.keys(getParameter).forEach(param => { @@ -203,6 +212,7 @@ const queryInfo = ref(); */ const dataList = ref([]); const tableRef = ref(); +const formRef = ref(); /**抽屉状态 */ const drawerStatus = ref(false); const loading = ref(true); @@ -403,6 +413,50 @@ const onbatchExport = async () => { exportToExcel([exportData], "查询结果"); }; +/**处理反馈 */ +const handleFeedback = async (row: QueryResultItem) => { + addDialog({ + title: `反馈错误`, + props: { + formInline: { + code_id: row?.id ?? "", + code: row?.code ?? "", + description: row?.description ?? "", + feedback_code: row?.code ?? "", + feedback_description: row?.description ?? "" + } + }, + width: "45%", + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => + h(editForm, { + formInline: { + code_id: row?.id ?? "", + code: row?.code ?? "", + description: row?.description ?? "", + feedback_code: row?.code ?? "", + feedback_description: row?.description ?? "" + }, + ref: formRef + }), + beforeSure: async (done, {}) => { + const FormData = formRef.value.newFormInline; + const res = await postAddCodeFeedbackAPI({ + code_id: row.id, + feedback_description: FormData.feedback_description, + feedback_code: FormData.feedback_code + }); + if (res.success) { + message("反馈成功!", { type: "success" }); + done(); + } else { + message("反馈失败!", { type: "error" }); + } + } + }); +}; onMounted(async () => { await getQueryInfo(); }); diff --git a/src/views/codes/log/components/form.vue b/src/views/codes/log/components/form.vue new file mode 100644 index 0000000..47db53a --- /dev/null +++ b/src/views/codes/log/components/form.vue @@ -0,0 +1,107 @@ + + + diff --git a/src/views/data/feedback/components/form.vue b/src/views/data/feedback/components/form.vue new file mode 100644 index 0000000..5e8a7d5 --- /dev/null +++ b/src/views/data/feedback/components/form.vue @@ -0,0 +1,225 @@ + + + diff --git a/src/views/data/feedback/index.vue b/src/views/data/feedback/index.vue new file mode 100644 index 0000000..ef5c36c --- /dev/null +++ b/src/views/data/feedback/index.vue @@ -0,0 +1,298 @@ + + + + + diff --git a/src/views/data/feedback/utils/hook.tsx b/src/views/data/feedback/utils/hook.tsx new file mode 100644 index 0000000..6cda089 --- /dev/null +++ b/src/views/data/feedback/utils/hook.tsx @@ -0,0 +1,551 @@ +import dayjs from "dayjs"; +import editForm from "../components/form.vue"; +import { message } from "@/utils/message"; +import { type Ref, ref, reactive, onMounted, h } from "vue"; +import { addDialog, closeDialog } from "@/components/ReDialog"; +import type { PaginationProps } from "@pureadmin/table"; +import type { CodeFeedbackInfo } from "types/code"; +import { + deleteCodeFeedbackAPI, + deleteCodeFeedbackListAPI, + getCodeFeedbackListAPI, + postAddCodeFeedbackAPI, + putCodeFeedbackAuditAPI, + putUpdateCodeFeedbackAPI +} from "@/api/code"; +import { getKeyList, handleTree } from "@pureadmin/utils"; +import type { DepartmentInfo } from "types/system"; +import { getDepartmentListAPI } from "@/api/system"; + +export const useCode = (tableRef: Ref) => { + /** + * 查询表单 + */ + const form = reactive({ + code: "", + feedback_code: "", + feedback_description: "", + username: "", + department_id: "", + nickname: "", + timeRange: [null, null], + status: "" + }); + /** + * 表单Ref + */ + const formRef = ref(null); + /** + * 数据列表 + */ + const dataList = ref([]); + /** + * 加载状态 + */ + const loading = ref(true); + /** + * 已选数量 + */ + const selectedNum = ref(0); + /** + * 分页参数 + */ + const pagination = reactive({ + total: 0, + pageSize: 10, + currentPage: 1, + background: true, + pageSizes: [10, 20, 30, 40, 50] + }); + + const getStatusTag = (status: number) => { + switch (status) { + case 1: + return "success"; + case 2: + return "danger"; + case 3: + return "warning"; + default: + return "info"; + } + }; + + const getStatusTagText = (status: number) => { + switch (status) { + case 1: + return "审核通过"; + case 2: + return "审核未通过"; + case 3: + return "待审核"; + default: + return "未知"; + } + }; + /** + * 表格列设置 + */ + const columns: TableColumnList = [ + { + label: "勾选列", // 如果需要表格多选,此处label必须设置 + type: "selection", + fixed: "left", + reserveSelection: true // 数据刷新后保留选项 + }, + { + label: "用户账号", + prop: "username", + minWidth: 100 + }, + { + label: "用户昵称", + prop: "nickname", + minWidth: 100 + }, + { + label: "所属部门", + prop: "department_name", + minWidth: 100 + }, + { + label: "编码", + prop: "code", + formatter: ({ code }) => code.replace(/(\d{2})/g, "$1.").slice(0, -1) + }, + { + label: "描述", + prop: "description" + }, + { + label: "反馈编码", + prop: "feedback_code" + }, + { + label: "反馈描述", + prop: "feedback_description" + }, + { + label: "审核状态", + prop: "status", + minWidth: 100, + cellRenderer: ({ row, props }) => ( + + {getStatusTagText(row.status)} + + ) + }, + { + label: "创建时间", + prop: "create_time", + formatter: ({ create_time }) => + dayjs(create_time).format("YYYY-MM-DD HH:mm:ss") + }, + { + label: "操作", + fixed: "right", + width: 250, + slot: "operation" + } + ]; + /** + * 初次查询 + */ + const onSearch = async () => { + loading.value = true; + const res = await getCodeFeedbackListAPI({ + page: pagination.currentPage, + pageSize: pagination.pageSize, + feedback_code: form.feedback_code, + feedback_description: form.feedback_description, + code: form.code, + status: form.status, + username: form.username, + department_id: form.department_id, + nickname: form.nickname, + startTime: form.timeRange[0] ? form.timeRange[0] : null, + endTime: form.timeRange[1] ? form.timeRange[1] : null + }); + if (res.success) { + dataList.value = res.data.result; + pagination.total = res.data.total; + pagination.currentPage = res.data.page; + pagination.pageSize = res.data.pageSize; + } + message(res.msg, { + type: res.success ? "success" : "error" + }); + loading.value = false; + }; + /** + * 重置表单 + * @param formEl 表单ref + * @returns + */ + const resetForm = (formEl: any) => { + if (!formEl) return; + formEl.resetFields(); + onSearch(); + }; + /** + * 处理删除 + * @param row + */ + const handleDelete = async (row: CodeFeedbackInfo) => { + const res = await deleteCodeFeedbackAPI(row.id); + if (res.success) { + onSearch(); + } + message(res.msg, { + type: res.success ? "success" : "error" + }); + }; + /** + * 处理每页数量变化 + */ + const handleSizeChange = async (val: number) => { + const res = await getCodeFeedbackListAPI({ + page: pagination.currentPage, + pageSize: val, + feedback_code: form.feedback_code, + feedback_description: form.feedback_description, + code: form.code, + status: form.status, + username: form.username, + department_id: form.department_id, + nickname: form.nickname, + startTime: form.timeRange[0] ? form.timeRange[0] : null, + endTime: form.timeRange[1] ? form.timeRange[1] : null + }); + if (res.success) { + dataList.value = res.data.result; + pagination.total = res.data.total; + pagination.currentPage = res.data.page; + pagination.pageSize = res.data.pageSize; + } + message(res.msg, { + type: res.success ? "success" : "error" + }); + }; + + /** + * 处理页码变化 + * @param val + */ + const handleCurrentChange = async (val: number) => { + const res = await getCodeFeedbackListAPI({ + page: val, + pageSize: pagination.pageSize, + feedback_code: form.feedback_code, + feedback_description: form.feedback_description, + code: form.code, + status: form.status, + username: form.username, + department_id: form.department_id, + nickname: form.nickname, + startTime: form.timeRange[0] ? form.timeRange[0] : null, + endTime: form.timeRange[1] ? form.timeRange[1] : null + }); + if (res.code === 200) { + dataList.value = res.data.result; + pagination.total = res.data.total; + pagination.currentPage = res.data.page; + pagination.pageSize = res.data.pageSize; + } + message(res.msg, { + type: res.success ? "success" : "error" + }); + }; + /** 当CheckBox选择项发生变化时会触发该事件 */ + const handleSelectionChange = async (val: any) => { + selectedNum.value = val.length; + // 重置表格高度 + tableRef.value.setAdaptive(); + }; + + /** 取消选择 */ + const onSelectionCancel = async () => { + selectedNum.value = 0; + // 用于多选表格,清空用户的选择 + tableRef.value.getTableRef().clearSelection(); + }; + /** + * 批量删除 + */ + const onbatchDel = async () => { + // 返回当前选中的行 + const curSelected = tableRef.value.getTableRef().getSelectionRows(); + const res = await deleteCodeFeedbackListAPI({ + ids: getKeyList(curSelected, "id") + }); + if (res.success) { + message(res.msg, { + type: "success" + }); + tableRef.value.getTableRef().clearSelection(); + onSearch(); + } else { + message(res.msg, { type: "error", duration: 5000 }); + } + }; + const openDialog = async (title = "新增", row?: CodeFeedbackInfo) => { + addDialog({ + title: `${title}编码项`, + props: { + formInline: { + way: title, + feedback_code: row?.feedback_code ?? "", + feedback_description: row?.feedback_description ?? "", + code_id: row?.code_id ?? "", + code: row?.code ?? "", + description: row?.description ?? "", + status: row?.status ?? 3, + user_id: row?.user_id ?? "", + username: row?.username ?? "", + nickname: row?.nickname ?? "", + department_id: row?.department_id ?? "", + department_name: row?.department_name ?? "", + id: row?.id ?? "", + create_time: row?.create_time ?? "", + update_time: row?.update_time ?? "" + } + }, + width: "45%", + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => + h(editForm, { + formInline: { + way: title, + feedback_description: row?.feedback_description ?? "", + feedback_code: row?.feedback_code ?? "", + code_id: row?.code_id ?? "", + code: row?.code ?? "", + description: row?.description ?? "", + status: row?.status ?? 3, + user_id: row?.user_id ?? "", + username: row?.username ?? "", + nickname: row?.nickname ?? "", + department_id: row?.department_id ?? "", + department_name: row?.department_name ?? "", + id: row?.id ?? "", + create_time: row?.create_time ?? "", + update_time: row?.update_time ?? "" + }, + ref: formRef + }), + beforeSure: async (done, {}) => { + const FormData = formRef.value.newFormInline; + let form = { + feedback_description: FormData.feedback_description ?? "", + feedback_code: FormData.feedback_code ?? "", + code_id: FormData.code_id ?? "" + }; + if (title === "新增") { + const res = await postAddCodeFeedbackAPI(form); + if (res.success) { + done(); + await onSearch(); + } + message(res.msg, { type: res.success ? "success" : "error" }); + } else { + const res = await putUpdateCodeFeedbackAPI(form, row.id); + if (res.success) { + done(); + await onSearch(); + } + message(res.msg, { type: res.success ? "success" : "error" }); + } + } + }); + }; + + /**部门列表 */ + const departments = ref([]); + /**获取部门列表 */ + const getDepartments = async () => { + const res = await getDepartmentListAPI({ page: 1, pageSize: 9999 }); + if (res.success) { + departments.value = formatHigherOptions( + handleTree(res.data.result, "id", "parent_id") + ); + } else { + departments.value = []; + } + }; + const formatHigherOptions = (treeList: any) => { + // 根据返回数据的status字段值判断追加是否禁用disabled字段,返回处理后的树结构,用于上级部门级联选择器的展示(实际开发中也是如此,不可能前端需要的每个字段后端都会返回,这时需要前端自行根据后端返回的某些字段做逻辑处理) + if (!treeList || !treeList.length) return; + const newTreeList = []; + for (let i = 0; i < treeList.length; i++) { + treeList[i].disabled = treeList[i].status === 0 ? true : false; + formatHigherOptions(treeList[i].children); + newTreeList.push(treeList[i]); + } + return newTreeList; + }; + /** 处理审核 */ + const handleAudit = async (row: CodeFeedbackInfo) => { + addDialog({ + title: `审核编码项`, + props: { + formInline: { + way: "审核", + feedback_code: row?.feedback_code ?? "", + feedback_description: row?.feedback_description ?? "", + code_id: row?.code_id ?? "", + code: row?.code ?? "", + description: row?.description ?? "", + status: row?.status ?? 3, + user_id: row?.user_id ?? "", + username: row?.username ?? "", + nickname: row?.nickname ?? "", + department_id: row?.department_id ?? "", + department_name: row?.department_name ?? "", + id: row?.id ?? "", + create_time: row?.create_time ?? "", + update_time: row?.update_time ?? "" + } + }, + width: "45%", + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + + contentRenderer: () => + h(editForm, { + formInline: { + way: "审核", + feedback_code: row?.feedback_code ?? "", + feedback_description: row?.feedback_description ?? "", + code_id: row?.code_id ?? "", + code: row?.code ?? "", + description: row?.description ?? "", + status: row?.status ?? 3, + user_id: row?.user_id ?? "", + username: row?.username ?? "", + nickname: row?.nickname ?? "", + department_id: row?.department_id ?? "", + department_name: row?.department_name ?? "", + id: row?.id ?? "", + create_time: row?.create_time ?? "", + update_time: row?.update_time ?? "" + }, + ref: formRef + }), + + footerButtons: [ + { + label: "取消", + type: "info", + btnClick: ({ dialog }) => { + console.log("取消审核"); + closeDialog(dialog.options!, dialog.index!); // ✅ 调用封装的 `closeDialog` + } + }, + { + label: "通过", + type: "success", + popconfirm: { + title: "确定要通过审核吗?", + confirmButtonText: "确认", + cancelButtonText: "取消" + }, + btnClick: async ({ dialog }) => { + const res = await putCodeFeedbackAuditAPI({ + status: 1, + ids: [row.id] + }); + if (res.success) { + closeDialog(dialog.options!, dialog.index!); // ✅ 调用封装的 `closeDialog` + await onSearch(); + } + message(res.msg, { type: res.success ? "success" : "error" }); + } + }, + { + label: "不通过", + type: "danger", + popconfirm: { + title: "确定要驳回审核吗?", + confirmButtonText: "确认", + cancelButtonText: "取消" + }, + btnClick: async ({ dialog }) => { + const res = await putCodeFeedbackAuditAPI({ + status: 2, + ids: [row.id] + }); + if (res.success) { + closeDialog(dialog.options!, dialog.index!); // ✅ 调用封装的 `closeDialog` + await onSearch(); + } + message(res.msg, { type: res.success ? "success" : "error" }); + } + } + ] + }); + }; + /**批量通过 */ + const onbatchAgree = async () => { + // 返回当前选中的行 + const curSelected = tableRef.value.getTableRef().getSelectionRows(); + const res = await putCodeFeedbackAuditAPI({ + status: 1, + ids: getKeyList(curSelected, "id") + }); + if (res.success) { + message(res.msg, { + type: "success" + }); + tableRef.value.getTableRef().clearSelection(); + onSearch(); + } else { + message(res.msg, { type: "error", duration: 5000 }); + } + }; + /**批量驳回 */ + const onbatchDisagree = async () => { + // 返回当前选中的行 + const curSelected = tableRef.value.getTableRef().getSelectionRows(); + const res = await putCodeFeedbackAuditAPI({ + status: 2, + ids: getKeyList(curSelected, "id") + }); + if (res.success) { + message(res.msg, { + type: "success" + }); + } + }; + + /** + * 页面加载执行 + */ + onMounted(async () => { + await onSearch(); + await getDepartments(); + }); + return { + form, + dataList, + loading, + pagination, + columns, + selectedNum, + departments, + openDialog, + onSearch, + resetForm, + handleDelete, + handleSizeChange, + handleCurrentChange, + handleSelectionChange, + onSelectionCancel, + onbatchDel, + handleAudit, + onbatchDisagree, + onbatchAgree + }; +}; diff --git a/src/views/data/import/components/form.vue b/src/views/data/import/components/form.vue new file mode 100644 index 0000000..b4d153b --- /dev/null +++ b/src/views/data/import/components/form.vue @@ -0,0 +1,180 @@ + + + diff --git a/src/views/data/import/index.vue b/src/views/data/import/index.vue new file mode 100644 index 0000000..8005901 --- /dev/null +++ b/src/views/data/import/index.vue @@ -0,0 +1,375 @@ + + + + + diff --git a/src/views/data/import/utils/hook.tsx b/src/views/data/import/utils/hook.tsx new file mode 100644 index 0000000..5e87090 --- /dev/null +++ b/src/views/data/import/utils/hook.tsx @@ -0,0 +1,659 @@ +import dayjs from "dayjs"; +import editForm from "../components/form.vue"; +import { message } from "@/utils/message"; +import { type Ref, ref, reactive, onMounted, h } from "vue"; +import { addDialog, closeDialog } from "@/components/ReDialog"; +import type { PaginationProps } from "@pureadmin/table"; +import type { CodeImportInfo } from "types/code"; +import { + deleteCodeImportAPI, + deleteCodeImportListAPI, + getAddCodeAPI, + getCodeImportListAPI, + getCodeTemplateAPI, + postAddCodeAPI, + putCodeImportAuditAPI, + putUpdateCodeImportAPI +} from "@/api/code"; +import { getKeyList, handleTree } from "@pureadmin/utils"; +import { + ElMessageBox, + type UploadProps, + type UploadUserFile +} from "element-plus"; +import { deleteFileAPI, postUploadFileAPI } from "@/api/file"; +import type { DepartmentInfo } from "types/system"; +import { getDepartmentListAPI } from "@/api/system"; + +export const useCode = (tableRef: Ref) => { + /** + * 查询表单 + */ + const form = reactive({ + code: "", + description: "", + username: "", + department_id: "", + nickname: "", + timeRange: [null, null], + status: "" + }); + /** + * 表单Ref + */ + const formRef = ref(null); + /** + * 数据列表 + */ + const dataList = ref([]); + /** + * 加载状态 + */ + const loading = ref(true); + /** + * 已选数量 + */ + const selectedNum = ref(0); + /** + * 分页参数 + */ + const pagination = reactive({ + total: 0, + pageSize: 10, + currentPage: 1, + background: true, + pageSizes: [10, 20, 30, 40, 50] + }); + // 上传文件区域显示状态 + const showUploadArea = ref(false); + const fileList = ref([]); + /**上传成功后文件列表 */ + const fileIds = ref([]); + const fileId = ref(""); + /**上传按钮状态 */ + const uploadStatus = ref(false); + + const getStatusTag = (status: number) => { + switch (status) { + case 1: + return "success"; + case 2: + return "danger"; + case 3: + return "warning"; + default: + return "info"; + } + }; + + const getStatusTagText = (status: number) => { + switch (status) { + case 1: + return "审核通过"; + case 2: + return "审核未通过"; + case 3: + return "待审核"; + default: + return "未知"; + } + }; + /** + * 表格列设置 + */ + const columns: TableColumnList = [ + { + label: "勾选列", // 如果需要表格多选,此处label必须设置 + type: "selection", + fixed: "left", + reserveSelection: true // 数据刷新后保留选项 + }, + { + label: "用户账号", + prop: "username", + minWidth: 100 + }, + { + label: "用户昵称", + prop: "nickname", + minWidth: 100 + }, + { + label: "所属部门", + prop: "department_name", + minWidth: 100 + }, + { + label: "编码", + prop: "code", + formatter: ({ code }) => code.replace(/(\d{2})/g, "$1.").slice(0, -1) + }, + { + label: "编码描述", + prop: "description" + }, + { + label: "审核状态", + prop: "status", + minWidth: 100, + cellRenderer: ({ row, props }) => ( + + {getStatusTagText(row.status)} + + ) + }, + { + label: "创建时间", + prop: "create_time", + formatter: ({ create_time }) => + dayjs(create_time).format("YYYY-MM-DD HH:mm:ss") + }, + { + label: "操作", + fixed: "right", + width: 250, + slot: "operation" + } + ]; + /** + * 初次查询 + */ + const onSearch = async () => { + loading.value = true; + const res = await getCodeImportListAPI({ + page: pagination.currentPage, + pageSize: pagination.pageSize, + description: form.description, + code: form.code, + status: form.status, + username: form.username, + department_id: form.department_id, + nickname: form.nickname, + startTime: form.timeRange[0] ? form.timeRange[0] : null, + endTime: form.timeRange[1] ? form.timeRange[1] : null + }); + if (res.success) { + dataList.value = res.data.result; + pagination.total = res.data.total; + pagination.currentPage = res.data.page; + pagination.pageSize = res.data.pageSize; + } + message(res.msg, { + type: res.success ? "success" : "error" + }); + loading.value = false; + }; + /** + * 重置表单 + * @param formEl 表单ref + * @returns + */ + const resetForm = (formEl: any) => { + if (!formEl) return; + formEl.resetFields(); + onSearch(); + }; + /** + * 处理删除 + * @param row + */ + const handleDelete = async (row: CodeImportInfo) => { + const res = await deleteCodeImportAPI(row.id); + if (res.success) { + onSearch(); + } + message(res.msg, { + type: res.success ? "success" : "error" + }); + }; + /** + * 处理每页数量变化 + */ + const handleSizeChange = async (val: number) => { + const res = await getCodeImportListAPI({ + page: pagination.currentPage, + pageSize: val, + description: form.description, + code: form.code, + status: form.status, + username: form.username, + department_id: form.department_id, + nickname: form.nickname, + startTime: form.timeRange[0] ? form.timeRange[0] : null, + endTime: form.timeRange[1] ? form.timeRange[1] : null + }); + if (res.success) { + dataList.value = res.data.result; + pagination.total = res.data.total; + pagination.currentPage = res.data.page; + pagination.pageSize = res.data.pageSize; + } + message(res.msg, { + type: res.success ? "success" : "error" + }); + }; + + /** + * 处理页码变化 + * @param val + */ + const handleCurrentChange = async (val: number) => { + const res = await getCodeImportListAPI({ + page: val, + pageSize: pagination.pageSize, + description: form.description, + code: form.code, + status: form.status, + username: form.username, + department_id: form.department_id, + nickname: form.nickname, + startTime: form.timeRange[0] ? form.timeRange[0] : null, + endTime: form.timeRange[1] ? form.timeRange[1] : null + }); + if (res.code === 200) { + dataList.value = res.data.result; + pagination.total = res.data.total; + pagination.currentPage = res.data.page; + pagination.pageSize = res.data.pageSize; + } + message(res.msg, { + type: res.success ? "success" : "error" + }); + }; + /** 当CheckBox选择项发生变化时会触发该事件 */ + const handleSelectionChange = async (val: any) => { + selectedNum.value = val.length; + // 重置表格高度 + tableRef.value.setAdaptive(); + }; + + /** 取消选择 */ + const onSelectionCancel = async () => { + selectedNum.value = 0; + // 用于多选表格,清空用户的选择 + tableRef.value.getTableRef().clearSelection(); + }; + /** + * 批量删除 + */ + const onbatchDel = async () => { + // 返回当前选中的行 + const curSelected = tableRef.value.getTableRef().getSelectionRows(); + const res = await deleteCodeImportListAPI({ + ids: getKeyList(curSelected, "id") + }); + if (res.success) { + message(res.msg, { + type: "success" + }); + tableRef.value.getTableRef().clearSelection(); + onSearch(); + } else { + message(res.msg, { type: "error", duration: 5000 }); + } + }; + const openDialog = async (title = "新增", row?: CodeImportInfo) => { + addDialog({ + title: `${title}编码项`, + props: { + formInline: { + way: title, + description: row?.description ?? "", + code: row?.code ?? "", + status: row?.status ?? 3, + user_id: row?.user_id ?? "", + username: row?.username ?? "", + nickname: row?.nickname ?? "", + department_id: row?.department_id ?? "", + department_name: row?.department_name ?? "", + id: row?.id ?? "", + create_time: row?.create_time ?? "", + update_time: row?.update_time ?? "" + } + }, + width: "45%", + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => + h(editForm, { + formInline: { + way: title, + description: row?.description ?? "", + code: row?.code ?? "", + status: row?.status ?? 3, + user_id: row?.user_id ?? "", + username: row?.username ?? "", + nickname: row?.nickname ?? "", + department_id: row?.department_id ?? "", + department_name: row?.department_name ?? "", + id: row?.id ?? "", + create_time: row?.create_time ?? "", + update_time: row?.update_time ?? "" + }, + ref: formRef + }), + beforeSure: async (done, {}) => { + const FormData = formRef.value.newFormInline; + let form = { + description: FormData.description ?? "", + code: FormData.code ?? "" + }; + if (title === "新增") { + const res = await postAddCodeAPI(form); + if (res.success) { + done(); + await onSearch(); + } + message(res.msg, { type: res.success ? "success" : "error" }); + } else { + const res = await putUpdateCodeImportAPI(form, row.id); + if (res.success) { + done(); + await onSearch(); + } + message(res.msg, { type: res.success ? "success" : "error" }); + } + } + }); + }; + /**下载模版文件 */ + const onDownloadTemplate = async () => { + try { + const blob = await getCodeTemplateAPI(); + + // 生成下载链接 + // @ts-ignore + const url = URL.createObjectURL(blob); + + // 创建 元素并触发下载 + const link = document.createElement("a"); + link.href = url; + link.download = "上传模版.xlsx"; // 设置下载文件名,确保后缀名正确 + document.body.appendChild(link); // 将 元素添加到 DOM 中 + link.click(); // 模拟点击下载 + + // 清理 URL 对象 + URL.revokeObjectURL(url); + document.body.removeChild(link); // 移除 元素 + } catch (error) { + console.error("下载模板失败:", error); + } + }; + /** 上传文件前 */ + const beforeUpload = async file => { + const isExcel = + file.type === + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || // xlsx + file.type === "application/vnd.ms-excel" || // xls + file.name.endsWith(".xlsx") || // 兼容部分浏览器 + file.name.endsWith(".xls"); // 兼容部分浏览器 + + // const maxSize = 20 * 1024 * 1024; // 20MB 限制 + + if (!isExcel) { + message("只能上传 xlsx 或 xls 文件!", { type: "error" }); + return false; + } + /* + if (file.size > maxSize) { + message("文件大小应在 20MB 以内!", { type: "error" }); + return false; + } + */ + + return true; + }; + + /**处理文件上传 */ + const handleUpload = async () => { + if (fileList.value.length === 0) { + message("请先上传文件!", { type: "error", duration: 5000 }); + return; + } + uploadStatus.value = true; + for (const file of fileList.value) { + if (file.status === "success") { + const data = await getAddCodeAPI(fileId.value); + if (data.success) { + message("导入成功!", { type: "success" }); + await onSearch(); + } + continue; + } + try { + const res = await postUploadFileAPI({ + file: file.raw + }); + if (res.success) { + file.status = "success"; + fileId.value = res.data.id; + message(`${res.data.name}上传成功!`, { type: "success" }); + fileIds.value.push(res.data); + const data = await getAddCodeAPI(fileId.value); + if (data.success) { + message(`导入成功!`, { type: "success" }); + await onSearch(); + } + } else { + file.status = "fail"; + } + } catch (error) { + console.error(error); + } + } + uploadStatus.value = false; + }; + /**移除文件 */ + const beforeRemove: UploadProps["beforeRemove"] = async uploadFile => { + return ElMessageBox.confirm(`是否移除 ${uploadFile.name} ?`).then( + async () => { + if (uploadFile.status === "success") { + const fileId = fileIds.value.filter( + item => item.filename === uploadFile.name + )[0]["fileId"]; + const res = await deleteFileAPI(fileId); + if (res.code === 200) { + message(res.msg, { type: "success" }); + return true; + } else { + message(res.msg, { type: "error" }); + return false; + } + } else { + return true; + } + }, + () => false + ); + }; + /**部门列表 */ + const departments = ref([]); + /**获取部门列表 */ + const getDepartments = async () => { + const res = await getDepartmentListAPI({ page: 1, pageSize: 9999 }); + if (res.success) { + departments.value = formatHigherOptions( + handleTree(res.data.result, "id", "parent_id") + ); + } else { + departments.value = []; + } + }; + const formatHigherOptions = (treeList: any) => { + // 根据返回数据的status字段值判断追加是否禁用disabled字段,返回处理后的树结构,用于上级部门级联选择器的展示(实际开发中也是如此,不可能前端需要的每个字段后端都会返回,这时需要前端自行根据后端返回的某些字段做逻辑处理) + if (!treeList || !treeList.length) return; + const newTreeList = []; + for (let i = 0; i < treeList.length; i++) { + treeList[i].disabled = treeList[i].status === 0 ? true : false; + formatHigherOptions(treeList[i].children); + newTreeList.push(treeList[i]); + } + return newTreeList; + }; + /** 处理审核 */ + const handleAudit = async (row: CodeImportInfo) => { + addDialog({ + title: `审核编码项`, + props: { + formInline: { + way: "审核", + description: row?.description ?? "", + code: row?.code ?? "", + status: row?.status ?? 3, + user_id: row?.user_id ?? "", + username: row?.username ?? "", + nickname: row?.nickname ?? "", + department_id: row?.department_id ?? "", + department_name: row?.department_name ?? "", + id: row?.id ?? "", + create_time: row?.create_time ?? "", + update_time: row?.update_time ?? "" + } + }, + width: "45%", + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + + contentRenderer: () => + h(editForm, { + formInline: { + way: "审核", + description: row?.description ?? "", + code: row?.code ?? "", + status: row?.status ?? 3, + user_id: row?.user_id ?? "", + username: row?.username ?? "", + nickname: row?.nickname ?? "", + department_id: row?.department_id ?? "", + department_name: row?.department_name ?? "", + id: row?.id ?? "", + create_time: row?.create_time ?? "", + update_time: row?.update_time ?? "" + }, + ref: formRef + }), + + footerButtons: [ + { + label: "取消", + type: "info", + btnClick: ({ dialog }) => { + console.log("取消审核"); + closeDialog(dialog.options!, dialog.index!); // ✅ 调用封装的 `closeDialog` + } + }, + { + label: "通过", + type: "success", + popconfirm: { + title: "确定要通过审核吗?", + confirmButtonText: "确认", + cancelButtonText: "取消" + }, + btnClick: async ({ dialog }) => { + const res = await putCodeImportAuditAPI({ + status: 1, + ids: [row.id] + }); + if (res.success) { + closeDialog(dialog.options!, dialog.index!); // ✅ 调用封装的 `closeDialog` + await onSearch(); + } + message(res.msg, { type: res.success ? "success" : "error" }); + } + }, + { + label: "不通过", + type: "danger", + popconfirm: { + title: "确定要驳回审核吗?", + confirmButtonText: "确认", + cancelButtonText: "取消" + }, + btnClick: async ({ dialog }) => { + const res = await putCodeImportAuditAPI({ + status: 2, + ids: [row.id] + }); + if (res.success) { + closeDialog(dialog.options!, dialog.index!); // ✅ 调用封装的 `closeDialog` + await onSearch(); + } + message(res.msg, { type: res.success ? "success" : "error" }); + } + } + ] + }); + }; + /**批量通过 */ + const onbatchAgree = async () => { + // 返回当前选中的行 + const curSelected = tableRef.value.getTableRef().getSelectionRows(); + const res = await putCodeImportAuditAPI({ + status: 1, + ids: getKeyList(curSelected, "id") + }); + if (res.success) { + message(res.msg, { + type: "success" + }); + tableRef.value.getTableRef().clearSelection(); + onSearch(); + } else { + message(res.msg, { type: "error", duration: 5000 }); + } + }; + /**批量驳回 */ + const onbatchDisagree = async () => { + // 返回当前选中的行 + const curSelected = tableRef.value.getTableRef().getSelectionRows(); + const res = await putCodeImportAuditAPI({ + status: 2, + ids: getKeyList(curSelected, "id") + }); + if (res.success) { + message(res.msg, { + type: "success" + }); + } + }; + + /** + * 页面加载执行 + */ + onMounted(async () => { + await onSearch(); + await getDepartments(); + }); + return { + form, + dataList, + loading, + pagination, + columns, + selectedNum, + showUploadArea, + departments, + fileIds, + fileList, + uploadStatus, + beforeUpload, + handleUpload, + beforeRemove, + openDialog, + onSearch, + resetForm, + handleDelete, + handleSizeChange, + handleCurrentChange, + handleSelectionChange, + onSelectionCancel, + onbatchDel, + onDownloadTemplate, + handleAudit, + onbatchDisagree, + onbatchAgree + }; +}; diff --git a/src/views/codes/admin/components/form.vue b/src/views/data/maintain/components/form.vue similarity index 100% rename from src/views/codes/admin/components/form.vue rename to src/views/data/maintain/components/form.vue diff --git a/src/views/codes/admin/index.vue b/src/views/data/maintain/index.vue similarity index 80% rename from src/views/codes/admin/index.vue rename to src/views/data/maintain/index.vue index 1cb398a..e7a88f8 100644 --- a/src/views/codes/admin/index.vue +++ b/src/views/data/maintain/index.vue @@ -6,12 +6,28 @@ :model="form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]" > + + + + + + @@ -19,7 +35,44 @@ v-model="form.description" clearable placeholder="请输入编码描述~" - class="!w-[180px]" + class="!w-[200px]" + /> + + + + + + + + @@ -36,7 +89,7 @@ - +