From acd364fdf7b94a2bd3a5e414d366f05c009d315e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9A=93=E6=9C=88=E5=BD=92=E5=B0=98?= Date: Fri, 14 Feb 2025 21:06:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en.yaml | 5 + locales/zh-CN.yaml | 5 + src/api/code.ts | 90 +++++ src/api/file.ts | 17 + src/api/i18n.ts | 39 +-- src/views/codes/admin/components/form.vue | 61 ++++ src/views/codes/admin/index.vue | 244 +++++++++++++ src/views/codes/admin/utils/hook.tsx | 405 ++++++++++++++++++++++ types/code.d.ts | 56 +++ 9 files changed, 897 insertions(+), 25 deletions(-) create mode 100644 src/api/code.ts create mode 100644 src/api/file.ts create mode 100644 src/views/codes/admin/components/form.vue create mode 100644 src/views/codes/admin/index.vue create mode 100644 src/views/codes/admin/utils/hook.tsx create mode 100644 types/code.d.ts diff --git a/locales/en.yaml b/locales/en.yaml index 50ee227..910893c 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -26,6 +26,11 @@ buttons:Add: Add buttons:Update: Update buttons:Delete: Delete buttons:Export: Export +buttons:Import: Import +buttons:DownLoded: DownLoded +buttons:DownLodedTemplate: DownLoded Template +buttons:Hide: Hide +buttons:ConfirmUpload: Confirm Upload buttons:Save: Save buttons:Permission: Permission buttons:ExpandOrCollapse: Expand Or Collapse diff --git a/locales/zh-CN.yaml b/locales/zh-CN.yaml index be636b9..5cab03a 100644 --- a/locales/zh-CN.yaml +++ b/locales/zh-CN.yaml @@ -26,6 +26,11 @@ buttons:Add: 添加 buttons:Update: 修改 buttons:Delete: 删除 buttons:Export: 导出 +buttons:Import: 导入 +buttons:DownLoded: 下载 +buttons:DownLodedTemplate: 下载模版 +buttons:Hide: 隐藏 +buttons:ConfirmUpload: 确认上传 buttons:Save: 保存 buttons:Permission: 权限 buttons:ExpandOrCollapse: 展开或折叠 diff --git a/src/api/code.ts b/src/api/code.ts new file mode 100644 index 0000000..0099669 --- /dev/null +++ b/src/api/code.ts @@ -0,0 +1,90 @@ +import { http } from "@/utils/http"; +import type { CodeInfo, QueryCodeResult } from "types/code"; + +/** + * 获取编码模版 + */ +export const getCodeTemplateAPI = () => { + return http.request("get", "/api/code/template", { + responseType: "blob" // 设置响应类型为 blob + }); +}; + +/** + * 导入编码 + */ +export const getAddCodeAPI = (id: string) => { + return http.request("get", `/api/code/addCode/${id}`); +}; + +/**导入编码 */ +export const postAddCodeAPI = (data: AddCodeParams) => { + return http.request("post", `/api/code/add`, { + data + }); +}; + +/**删除编码 */ +export const deleteCodeAPI = (id: string) => { + return http.request("delete", `/api/code/delete/${id}`); +}; + +/** + * 批量删除编码 + */ +export const deleteCodeListAPI = (data: { ids: string[] }) => { + return http.request("post", `/api/code/deleteList`, { data }); +}; + +/** + * 更新编码参数 + */ +interface AddCodeParams { + /**编码 */ + code: string; + /** 编码描述 */ + description: string; +} + +/**更新编码 */ +export const putUpdateCodeAPI = (data: AddCodeParams, id: string) => { + return http.request("put", `/api/code/update/${id}`, { + data + }); +}; + +/**获取编码详情 */ +export const getCodeInfoAPI = (id: string) => { + return http.request("get", `/api/code/info/${id}`); +}; + +/** + * 获取编码列表参数 + */ +interface GetCodeListParams { + /** 当前页码 */ + page: number; + /** 每页条数 */ + pageSize: number; + /** 编码 */ + code: string; + /** 编码描述 */ + description: string; +} +/** + * 获取编码列表 + * @param params + * @returns + */ +export const getCodeListAPI = (params: GetCodeListParams) => { + return http.request>("get", "/api/code/list", { + params + }); +}; + +/**查询编码 */ +export const postCodeInfoAPI = (data: { query_text: string }) => { + return http.request("post", `/api/code/query`, { + data + }); +}; diff --git a/src/api/file.ts b/src/api/file.ts new file mode 100644 index 0000000..5652a2b --- /dev/null +++ b/src/api/file.ts @@ -0,0 +1,17 @@ +import { http } from "@/utils/http"; +import type { FileInfo } from "types/file"; + +/**上传文件 */ +export const postUploadFileAPI = (data: { file: Blob }) => { + return http.request("post", "/api/file/upload", { + data, + headers: { + "Content-Type": "multipart/form-data" + } + }); +}; + +/**删除文件 */ +export const deleteFileAPI = (id: string) => { + return http.request("delete", `/api/file/delete/${id}`); +}; diff --git a/src/api/i18n.ts b/src/api/i18n.ts index 8128632..272e7a6 100644 --- a/src/api/i18n.ts +++ b/src/api/i18n.ts @@ -59,18 +59,14 @@ type GetLoacleListParams = { code?: string; }; -type GetLocaleListResult = { - /**语言列表 */ - result: LanguageInfo[]; - /**总条数 */ - total: number; - /**页码 */ - page: number; -}; export const getLocaleListAPI = (params: GetLoacleListParams) => { - return http.request("get", "/api/i18n/locale/list", { - params - }); + return http.request>( + "get", + "/api/i18n/locale/list", + { + params + } + ); }; /** @@ -111,17 +107,6 @@ type GetI18nListParams = { /**翻译内容 */ translation?: string; }; -/** - * 获取翻译列表 - */ -type GetI18nListResult = { - /**翻译列表 */ - result: TranslationInfo[]; - /**总条数 */ - total: number; - /**页码 */ - page: number; -}; /** * 获取翻译列表 @@ -129,9 +114,13 @@ type GetI18nListResult = { * @returns */ export const getI18nListAPI = (params: GetI18nListParams) => { - return http.request("get", "/api/i18n/list", { - params - }); + return http.request>( + "get", + "/api/i18n/list", + { + params + } + ); }; /** diff --git a/src/views/codes/admin/components/form.vue b/src/views/codes/admin/components/form.vue new file mode 100644 index 0000000..3d711da --- /dev/null +++ b/src/views/codes/admin/components/form.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/views/codes/admin/index.vue b/src/views/codes/admin/index.vue new file mode 100644 index 0000000..fe361be --- /dev/null +++ b/src/views/codes/admin/index.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/src/views/codes/admin/utils/hook.tsx b/src/views/codes/admin/utils/hook.tsx new file mode 100644 index 0000000..5e5760f --- /dev/null +++ b/src/views/codes/admin/utils/hook.tsx @@ -0,0 +1,405 @@ +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 } from "@/components/ReDialog"; +import type { PaginationProps } from "@pureadmin/table"; +import type { CodeInfo } from "types/code"; +import { + deleteCodeAPI, + deleteCodeListAPI, + getAddCodeAPI, + getCodeListAPI, + getCodeTemplateAPI, + postAddCodeAPI, + putUpdateCodeAPI +} from "@/api/code"; +import { getKeyList } from "@pureadmin/utils"; +import { + ElMessageBox, + type UploadProps, + type UploadUserFile +} from "element-plus"; +import { deleteFileAPI, postUploadFileAPI } from "@/api/file"; + +export const useCode = (tableRef: Ref) => { + /** + * 查询表单 + */ + const form = reactive({ + code: "", + description: "" + }); + /** + * 表单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 columns: TableColumnList = [ + { + label: "勾选列", // 如果需要表格多选,此处label必须设置 + type: "selection", + fixed: "left", + reserveSelection: true // 数据刷新后保留选项 + }, + { + label: "编码", + prop: "code" + }, + { + label: "编码描述", + prop: "description" + }, + { + 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 getCodeListAPI({ + page: pagination.currentPage, + pageSize: pagination.pageSize, + description: form.description, + code: form.code + }); + 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: CodeInfo) => { + const res = await deleteCodeAPI(row.id); + if (res.success) { + onSearch(); + } + message(res.msg, { + type: res.success ? "success" : "error" + }); + }; + /** + * 处理每页数量变化 + */ + const handleSizeChange = async (val: number) => { + const res = await getCodeListAPI({ + page: pagination.currentPage, + pageSize: val, + description: form.description, + code: form.code + }); + 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 getCodeListAPI({ + page: val, + pageSize: pagination.pageSize, + description: form.description, + code: form.code + }); + 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 deleteCodeListAPI({ + 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?: CodeInfo) => { + addDialog({ + title: `${title}编码项`, + props: { + formInline: { + title: title, + description: row?.description ?? "", + code: row?.code ?? "" + } + }, + width: "45%", + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => + h(editForm, { + formInline: { + description: row?.description ?? "", + code: row?.code ?? "" + }, + ref: formRef + }), + beforeSure: async (done, {}) => { + const FormData = formRef.value.newFormInline; + let form = { + description: FormData.name ?? "", + 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 putUpdateCodeAPI(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 + ); + }; + + /** + * 页面加载执行 + */ + onMounted(async () => { + await onSearch(); + }); + return { + form, + dataList, + loading, + pagination, + columns, + selectedNum, + showUploadArea, + fileIds, + fileList, + uploadStatus, + beforeUpload, + handleUpload, + beforeRemove, + openDialog, + onSearch, + resetForm, + handleDelete, + handleSizeChange, + handleCurrentChange, + handleSelectionChange, + onSelectionCancel, + onbatchDel, + onDownloadTemplate + }; +}; diff --git a/types/code.d.ts b/types/code.d.ts new file mode 100644 index 0000000..fc2458e --- /dev/null +++ b/types/code.d.ts @@ -0,0 +1,56 @@ +/**编码详情 */ +export interface CodeInfo { + /** 编码ID */ + id: string; + /** 编码 */ + code: string; + /** 编码描述 */ + description: string; + /** 创建时间 */ + create_time: string; + /** 更新时间 */ + update_time: string; + /** 创建人 */ + create_by: string; + /** 更新人 */ + update_by: string; +} + +/**查询结果项 */ +export interface QueryResultItem { + /** 编码ID */ + id: string; + /** 编码 */ + code: string; + /** 编码描述 */ + description: string; + /**匹配度 */ + match_rate: number; +} + +/**查询结果 */ +export interface QueryResult { + /**查询文本 */ + query_text: string; + /**查询状态 */ + status: number; + /**会话ID */ + id: string; + /**结果 */ + result: QueryResultItem[]; +} +/**查询编码结果 */ +export interface QueryCodeResult { + /**查询文本 */ + query: string; + /**查询统计 */ + query_count: number; + /**查询耗时 */ + cost_time: number; + /**结果统计 */ + result_count: number; + /**查询结果 */ + response_result: QueryResult[]; + /**查询状态 */ + status: number; +}