feat: 添加数据反馈,数据导入功能

This commit is contained in:
皓月归尘 2025-02-27 23:23:03 +08:00
parent 6e6bab05ac
commit 88ceb0f63b
18 changed files with 2998 additions and 21 deletions

View File

@ -43,6 +43,8 @@ buttons:More: More
buttons:Deselect: Deselect buttons:Deselect: Deselect
buttons:DeleteInBatches: Delete In Batches buttons:DeleteInBatches: Delete In Batches
buttons:ExportInBatches: Export In Batches buttons:ExportInBatches: Export In Batches
buttons:AgressInBatches: Agress In Batches
buttons:DisagressInBatches: Disagress In Batches
buttons:ExitInBatches: Exit In Batches buttons:ExitInBatches: Exit In Batches
buttons:ExportAll: Export All buttons:ExportAll: Export All
buttons:UploadAvatar: Upload Avatar buttons:UploadAvatar: Upload Avatar
@ -51,6 +53,8 @@ buttons:RoleAllocation: Role Allocation
buttons:PermissionDetails: Permission Details buttons:PermissionDetails: Permission Details
buttons:ForceToExit: Force Exit buttons:ForceToExit: Force Exit
buttons:DataAdmin: Data Admin buttons:DataAdmin: Data Admin
buttons:Audit: Audit
buttons:Feedback: Feedback
search:Total: Total search:Total: Total
search:History: History search:History: History
search:Collect: Collect search:Collect: Collect
@ -117,8 +121,11 @@ menus:SystemConfig: System Config
menus:CodeManager: HTS Code Mgnt menus:CodeManager: HTS Code Mgnt
menus:CodeQueryIndex: HTS Code Query menus:CodeQueryIndex: HTS Code Query
menus:CodeQueryCount: Query Statistics menus:CodeQueryCount: Query Statistics
menus:CodeQueryData: Data Maintenance
menus:QueryCodedetails: Log Detail menus:QueryCodedetails: Log Detail
menus:DataQuality: Data Quality
menus:DataImport: Data Import
menus:DataFeedback: Data Feedback
menus:DataMaintain: Data Export
status:Load: Loading... status:Load: Loading...
status:Message: Message status:Message: Message
status:Notify: Notify status:Notify: Notify

View File

@ -43,6 +43,8 @@ buttons:More: 更多
buttons:Deselect: 取消选择 buttons:Deselect: 取消选择
buttons:DeleteInBatches: 批量删除 buttons:DeleteInBatches: 批量删除
buttons:ExportInBatches: 批量导出 buttons:ExportInBatches: 批量导出
buttons:AgressInBatches: 批量通过
buttons:DisagressInBatches: 批量驳回
buttons:ExportAll: 全部导出 buttons:ExportAll: 全部导出
buttons:UploadAvatar: 上传头像 buttons:UploadAvatar: 上传头像
buttons:ResetPassword: 重置密码 buttons:ResetPassword: 重置密码
@ -51,6 +53,8 @@ buttons:PermissionDetails: 权限详情
buttons:ForceToExit: 强制退出 buttons:ForceToExit: 强制退出
buttons:ExitInBatches: 批量强退 buttons:ExitInBatches: 批量强退
buttons:DataAdmin: 数据管理 buttons:DataAdmin: 数据管理
buttons:Audit: 审核
buttons:Feedback: 反馈
search:Total: search:Total:
search:History: 搜索历史 search:History: 搜索历史
search:Collect: 收藏 search:Collect: 收藏
@ -117,8 +121,11 @@ menus:SystemConfig: 系统配置
menus:CodeManager: 编码管理 menus:CodeManager: 编码管理
menus:CodeQueryIndex: 编码查询 menus:CodeQueryIndex: 编码查询
menus:CodeQueryCount: 查询统计 menus:CodeQueryCount: 查询统计
menus:CodeQueryData: 数据维护
menus:QueryCodedetails: 日志详情 menus:QueryCodedetails: 日志详情
menus:DataQuality: 数据质量
menus:DataImport: 数据导入
menus:DataFeedback: 数据反馈
menus:DataMaintain: 数据维护
status:Load: 加载中... status:Load: 加载中...
status:Message: 消息 status:Message: 消息
status:Notify: 通知 status:Notify: 通知

View File

@ -1,5 +1,11 @@
import { http } from "@/utils/http"; 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"; import { filterEmptyObject } from "./utils";
/** /**
@ -77,9 +83,19 @@ interface GetCodeListParams {
/** 每页条数 */ /** 每页条数 */
pageSize: number; 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<null>("delete", `/api/code/deleteCodeImport/${id}`);
};
/**批量删除编码导入 */
export const deleteCodeImportListAPI = (data: { ids: string[] }) => {
return http.request<null>("post", `/api/code/deleteCodeImportList`, { data });
};
/**编码导入审核 */
export const putCodeImportAuditAPI = (data: {
/**状态 */
status: number;
/**编码ID列表 */
ids: string[];
}) => {
return http.request<null>("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<QueryListResult<CodeImportInfo>>(
"get",
`/api/code/codeImportList`,
{
params: filterEmptyObject(params)
}
);
};
/**修改编码导入 */
export const putUpdateCodeImportAPI = (data: AddCodeParams, id: string) => {
return http.request<null>("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<null>("post", `/api/code/addFeedback`, { data });
};
/**删除反馈 */
export const deleteCodeFeedbackAPI = (id: string) => {
return http.request<null>("delete", `/api/code/deleteFeedback/${id}`);
};
/**批量删除反馈 */
export const deleteCodeFeedbackListAPI = (data: { ids: string[] }) => {
return http.request<null>("post", `/api/code/deleteFeedbackList`, { data });
};
/**修改编码反馈 */
export const putUpdateCodeFeedbackAPI = (
data: AddCodeFeedbackParams,
id: string
) => {
return http.request<null>("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<QueryListResult<CodeFeedbackInfo>>(
"get",
`/api/code/feedbackList`,
{
params: filterEmptyObject(params)
}
);
};
/**反馈审核 */
export const putCodeFeedbackAuditAPI = (data: {
/**状态 */
status: number;
/**反馈ID列表 */
ids: string[];
}) => {
return http.request<null>("put", `/api/code/feedbackAudit`, { data });
};

View File

@ -0,0 +1,107 @@
<template>
<el-form
ref="ruleFormRef"
:model="newFormInline"
:rules="formRules"
label-width="82px"
>
<el-row :gutter="30">
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="编码ID" prop="code_id">
<el-input
v-model="newFormInline.code_id"
placeholder="请输入编码ID~"
clearable
disabled
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="当前编码" prop="code">
<el-input
v-model="newFormInline.code"
placeholder="请输入当前编码~"
clearable
disabled
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="当前描述" prop="description">
<el-input
v-model="newFormInline.description"
placeholder="请输入当前编码描述~"
clearable
disabled
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="正确编码" prop="feedback_code">
<el-input
v-model="newFormInline.feedback_code"
placeholder="请输入正确的编码描述~"
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="正确描述" prop="feedback_description">
<el-input
v-model="newFormInline.feedback_description"
placeholder="请输入正确的编码描述~"
clearable
class="w-full"
/>
</el-form-item>
</re-col>
</el-row>
</el-form>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
import ReCol from "@/components/ReCol";
import type { FormRules } from "element-plus";
const ruleFormRef = ref();
interface PropsInfo {
/**编码ID */
code_id: string;
/**编码 */
code: string;
/**编码描述 */
description: string;
/**反馈编码 */
feedback_code: string;
/**反馈文本 */
feedback_description: string;
}
type ProsData = {
formInline: PropsInfo;
};
/** 自定义表单规则校验 */
const formRules = reactive<FormRules>({
feedback_code: [
{ required: true, message: "反馈编码为必填项", trigger: "blur" }
],
feedback_description: [
{ required: true, message: "反馈描述为必填项", trigger: "blur" }
]
});
const props = withDefaults(defineProps<ProsData>(), {
formInline: () => ({
code_id: "",
code: "",
description: "",
feedback_code: "",
feedback_description: ""
})
});
const newFormInline = ref<PropsInfo>(props.formInline);
defineExpose({ newFormInline });
</script>

View File

@ -210,6 +210,14 @@
item.match_rate ? `${item.match_rate}%` : "0%" item.match_rate ? `${item.match_rate}%` : "0%"
}}</el-descriptions-item> }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<el-button
v-if="hasAuth('code:btn:addFeedback')"
class="w-full top-2"
type="primary"
:disabled="!hasAuth('code:btn:addFeedback')"
@click="handleFeedback(item)"
>{{ t("buttons:Feedback") }}</el-button
>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
</el-drawer> </el-drawer>
@ -262,7 +270,8 @@ const {
onDownloadTemplate, onDownloadTemplate,
handleDetail, handleDetail,
exportToExcel, exportToExcel,
onbatchExport onbatchExport,
handleFeedback
} = useIndex(tableRef); } = useIndex(tableRef);
</script> </script>

View File

@ -1,5 +1,6 @@
import { message } from "@/utils/message"; 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 * as XLSX from "xlsx";
import type { PaginationProps } from "@pureadmin/table"; import type { PaginationProps } from "@pureadmin/table";
import type { QueryCodeResult, QueryResult, QueryResultItem } from "types/code"; import type { QueryCodeResult, QueryResult, QueryResultItem } from "types/code";
@ -11,10 +12,12 @@ import {
import { import {
getQueryCodeAPI, getQueryCodeAPI,
getQueryTemplateAPI, getQueryTemplateAPI,
postAddCodeFeedbackAPI,
postCodeInfoAPI postCodeInfoAPI
} from "@/api/code"; } from "@/api/code";
import { deleteFileAPI, postUploadFileAPI } from "@/api/file"; import { deleteFileAPI, postUploadFileAPI } from "@/api/file";
import { getKeyList, cloneDeep } from "@pureadmin/utils"; import { getKeyList, cloneDeep } from "@pureadmin/utils";
import { addDialog } from "@/components/ReDialog";
export const useIndex = (tableRef: Ref) => { export const useIndex = (tableRef: Ref) => {
/** /**
@ -26,7 +29,7 @@ export const useIndex = (tableRef: Ref) => {
/** /**
* Ref * Ref
*/ */
// const formRef = ref(null); const formRef = ref(null);
/** /**
* *
*/ */
@ -371,6 +374,50 @@ export const useIndex = (tableRef: Ref) => {
exportToExcel([exportData], "查询结果"); 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 { return {
form, form,
dataList, dataList,
@ -396,6 +443,7 @@ export const useIndex = (tableRef: Ref) => {
onDownloadTemplate, onDownloadTemplate,
handleDetail, handleDetail,
exportToExcel, exportToExcel,
onbatchExport onbatchExport,
handleFeedback
}; };
}; };

View File

@ -135,6 +135,14 @@
item.match_rate ? `${item.match_rate}%` : "0%" item.match_rate ? `${item.match_rate}%` : "0%"
}}</el-descriptions-item> }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<el-button
v-if="hasAuth('code:btn:addFeedback')"
class="w-full top-2"
type="primary"
:disabled="!hasAuth('code:btn:addFeedback')"
@click="handleFeedback(item)"
>{{ t("buttons:Feedback") }}</el-button
>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
</el-drawer> </el-drawer>
@ -145,6 +153,7 @@
defineOptions({ defineOptions({
name: "QueryCodedetails" name: "QueryCodedetails"
}); });
import editForm from "./form.vue";
import { ref, reactive, onMounted, h } from "vue"; import { ref, reactive, onMounted, h } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -160,15 +169,15 @@ import type {
QueryResult, QueryResult,
QueryResultItem QueryResultItem
} from "types/code"; } from "types/code";
import { getCodeLogInfoAPI } from "@/api/code"; import { getCodeLogInfoAPI, postAddCodeFeedbackAPI } from "@/api/code";
import { PaginationProps } from "@pureadmin/table"; import { PaginationProps } from "@pureadmin/table";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import { hasAuth } from "@/utils/auth"; import { hasAuth } from "@/utils/auth";
import { addDialog } from "@/components/ReDialog";
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const getParameter = isEmpty(route.params) ? route.query : route.params; const getParameter = isEmpty(route.params) ? route.query : route.params;
console.log(getParameter);
// /** */ // /** */
// function init() { // function init() {
// Object.keys(getParameter).forEach(param => { // Object.keys(getParameter).forEach(param => {
@ -203,6 +212,7 @@ const queryInfo = ref<QueryCodeLogInfo>();
*/ */
const dataList = ref<QueryResult[]>([]); const dataList = ref<QueryResult[]>([]);
const tableRef = ref(); const tableRef = ref();
const formRef = ref();
/**抽屉状态 */ /**抽屉状态 */
const drawerStatus = ref<boolean>(false); const drawerStatus = ref<boolean>(false);
const loading = ref(true); const loading = ref(true);
@ -403,6 +413,50 @@ const onbatchExport = async () => {
exportToExcel([exportData], "查询结果"); 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 () => { onMounted(async () => {
await getQueryInfo(); await getQueryInfo();
}); });

View File

@ -0,0 +1,107 @@
<template>
<el-form
ref="ruleFormRef"
:model="newFormInline"
:rules="formRules"
label-width="82px"
>
<el-row :gutter="30">
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="编码ID" prop="code_id">
<el-input
v-model="newFormInline.code_id"
placeholder="请输入编码ID~"
clearable
disabled
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="反馈编码" prop="code">
<el-input
v-model="newFormInline.code"
placeholder="请输入反馈编码~"
clearable
disabled
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="编码描述" prop="description">
<el-input
v-model="newFormInline.description"
placeholder="请输入编码描述~"
clearable
disabled
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="正确编码" prop="feedback_code">
<el-input
v-model="newFormInline.feedback_code"
placeholder="请输入正确的编码~"
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="正确描述" prop="feedback_description">
<el-input
v-model="newFormInline.feedback_description"
placeholder="请输入正确的编码描述~"
clearable
class="w-full"
/>
</el-form-item>
</re-col>
</el-row>
</el-form>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
import ReCol from "@/components/ReCol";
import type { FormRules } from "element-plus";
const ruleFormRef = ref();
interface PropsInfo {
/**编码ID */
code_id: string;
/**编码 */
code: string;
/**编码描述 */
description: string;
/**反馈编码 */
feedback_code: string;
/**反馈文本 */
feedback_description: string;
}
type ProsData = {
formInline: PropsInfo;
};
/** 自定义表单规则校验 */
const formRules = reactive<FormRules>({
feedback_code: [
{ required: true, message: "反馈编码为必填项", trigger: "blur" }
],
feedback_description: [
{ required: true, message: "反馈内容为必填项", trigger: "blur" }
]
});
const props = withDefaults(defineProps<ProsData>(), {
formInline: () => ({
code_id: "",
code: "",
description: "",
feedback_code: "",
feedback_description: ""
})
});
const newFormInline = ref<PropsInfo>(props.formInline);
defineExpose({ newFormInline });
</script>

View File

@ -0,0 +1,225 @@
<template>
<el-form
ref="ruleFormRef"
:model="newFormInline"
:rules="formRules"
label-width="82px"
>
<el-row :gutter="30">
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="编码ID" prop="code_id">
<el-input
v-model="newFormInline.code_id"
placeholder="请输入编码ID~"
clearable
disabled
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="反馈编码" prop="code">
<el-input
v-model="newFormInline.code"
placeholder="请输入反馈编码~"
clearable
disabled
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="编码描述" prop="description">
<el-input
v-model="newFormInline.description"
placeholder="请输入编码描述~"
clearable
disabled
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="反馈编码" prop="feedback_code">
<el-input
v-model="newFormInline.feedback_code"
placeholder="请输入反馈编码~"
clearable
:disabled="newFormInline.way === '审核'"
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="反馈描述" prop="feedback_description">
<el-input
v-model="newFormInline.feedback_description"
placeholder="请输入反馈编码描述~"
clearable
:disabled="newFormInline.way === '审核'"
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="用户ID" prop="user_id">
<el-input
v-model="newFormInline.user_id"
placeholder="请输入用户ID~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="用户账号" prop="username">
<el-input
v-model="newFormInline.username"
placeholder="请输入用户账号~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="用户名称" prop="nickname">
<el-input
v-model="newFormInline.nickname"
placeholder="请输入用户名称~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="所属部门" prop="department_name">
<el-input
v-model="newFormInline.department_name"
placeholder="请输入所属部门~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="创建时间" prop="create_time">
<el-input
:value="
dayjs(newFormInline.create_time).format('YYYY-MM-DD HH:mm:ss')
"
placeholder="请输入创建时间~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="修改时间" prop="update_time">
<el-input
:value="
dayjs(newFormInline.update_time).format('YYYY-MM-DD HH:mm:ss')
"
placeholder="请输入修改时间~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="审核状态" prop="status">
<el-select
v-model="newFormInline.status"
placeholder="请选择状态~"
clearable
disabled
class="w-[full]"
>
<el-option label="审核通过" :value="1" />
<el-option label="审核未通过" :value="2" />
<el-option label="待审核" :value="3" />
</el-select>
</el-form-item>
</re-col>
</el-row>
</el-form>
</template>
<script setup lang="ts">
import dayjs from "dayjs";
import { ref, reactive } from "vue";
import ReCol from "@/components/ReCol";
import type { FormRules } from "element-plus";
const ruleFormRef = ref();
interface PropsInfo {
/**方式 */
way: string;
/**反馈ID */
id: string;
/**编码ID */
code_id: string;
/**编码 */
code: string;
/**编码描述 */
description: string;
/**反馈编码 */
feedback_code: string;
/**反馈文本 */
feedback_description: string;
/** 创建时间 */
create_time: string;
/** 更新时间 */
update_time: string;
/**用户ID */
user_id: string;
/**用户昵称 */
nickname: string;
/**用户账号 */
username: string;
/**部门ID */
department_id: string;
/**部门名称 */
department_name: string;
/**审核状态 */
status: number;
}
type ProsData = {
formInline: PropsInfo;
};
/** 自定义表单规则校验 */
const formRules = reactive<FormRules>({
feedback_code: [
{ required: true, message: "反馈编码为必填项", trigger: "blur" }
],
feedback_description: [
{ required: true, message: "反馈内容为必填项", trigger: "blur" }
]
});
const props = withDefaults(defineProps<ProsData>(), {
formInline: () => ({
id: "",
way: "新增",
code_id: "",
code: "",
description: "",
feedback_code: "",
feedback_description: "",
status: 0,
create_time: "",
update_time: "",
user_id: "",
nickname: "",
username: "",
department_id: "",
department_name: ""
})
});
const newFormInline = ref<PropsInfo>(props.formInline);
defineExpose({ newFormInline });
</script>

View File

@ -0,0 +1,298 @@
<template>
<div class="main">
<el-form
ref="formRef"
:inline="true"
:model="form"
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
>
<el-form-item label="用户账号" prop="usename">
<el-input
v-model="form.username"
placeholder="请输入用户账号~"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="用户名称" prop="nickname">
<el-input
v-model="form.nickname"
placeholder="请输入用户昵称~"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="反馈编码" prop="code">
<el-input
v-model="form.code"
placeholder="请输入反馈编码~"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="反馈内容" prop="feedback">
<el-input
v-model="form.feedback"
clearable
placeholder="请输入反馈内容~"
class="!w-[200px]"
/>
</el-form-item>
<el-form-item
v-if="hasAuth('code:data:feedbackAdmin')"
label="所属部门:"
prop="department_id"
>
<el-cascader
v-model="form.department_id"
class="!w-[200px]"
:options="departments"
:props="{
value: 'id',
label: 'name',
emitPath: false,
checkStrictly: true
}"
clearable
filterable
placeholder="请选择所属部门~"
>
<template #default="{ node, data }">
<span>{{ data.name }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
<el-form-item label="反馈时间" prop="timeRange">
<el-date-picker
v-model="form.timeRange"
:shortcuts="getPickerShortcuts()"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期时间"
end-placeholder="结束日期时间"
value-format="x"
unlink-panels
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
:icon="useRenderIcon('ri:search-line')"
:loading="loading"
@click="onSearch"
>
{{ t("buttons:Search") }}
</el-button>
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
{{ t("buttons:Reset") }}
</el-button>
</el-form-item>
</el-form>
<PureTableBar title="数据反馈" :columns="columns" @refresh="onSearch">
<template v-slot="{ size, dynamicColumns }">
<div
v-if="selectedNum > 0"
v-motion-fade
class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center"
>
<div class="flex-auto">
<span
style="font-size: var(--el-font-size-base)"
class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]"
>
已选 {{ selectedNum }}
</span>
<el-button type="primary" text @click="onSelectionCancel">
{{ t("buttons:Deselect") }}
</el-button>
</div>
<el-popconfirm
v-if="hasAuth('code:btn:feedbackAudit')"
title="是否确认通过审核?"
@confirm="onbatchAgree"
>
<template #reference>
<el-button
type="success"
text
class="mr-1"
:disabled="
selectedNum < 0 || !hasAuth('code:btn:feedbackAudit')
"
>
{{ t("buttons:AgressInBatches") }}
</el-button>
</template>
</el-popconfirm>
<el-popconfirm
v-if="hasAuth('code:btn:feedbackAudit')"
title="是否确认驳回?"
@confirm="onbatchDisagree"
>
<template #reference>
<el-button
type="danger"
text
class="mr-1"
:disabled="
selectedNum < 0 || !hasAuth('code:btn:feedbackAudit')
"
>
{{ t("buttons:DisagressInBatches") }}
</el-button>
</template>
</el-popconfirm>
<el-popconfirm
v-if="hasAuth('code:btn:deleteFeedback')"
title="是否确认删除?"
@confirm="onbatchDel"
>
<template #reference>
<el-button
type="danger"
text
class="mr-1"
:disabled="
selectedNum < 0 || !hasAuth('code:btn:deleteFeedback')
"
>
{{ t("buttons:DeleteInBatches") }}
</el-button>
</template>
</el-popconfirm>
</div>
<pure-table
ref="tableRef"
row-key="id"
adaptive
border
stripe
:adaptiveConfig="{ offsetBottom: 45 }"
align-whole="center"
table-layout="auto"
:loading="loading"
:size="size"
:data="dataList"
:columns="dynamicColumns"
:pagination="pagination"
:paginationSmall="size === 'small' ? true : false"
:header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}"
@selection-change="handleSelectionChange"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
>
<template #operation="{ row }">
<el-button
class="reset-margin"
link
type="primary"
:size="size"
:disabled="!hasAuth('code:btn:updateFeedback')"
:icon="useRenderIcon(EditPen)"
@click="openDialog('修改', row)"
>
{{ t("buttons:Update") }}
</el-button>
<el-button
class="reset-margin"
link
type="warning"
:size="size"
:disabled="!hasAuth('code:btn:feedbackAudit')"
:icon="useRenderIcon(Read)"
@click="handleAudit(row)"
>
{{ t("buttons:Audit") }}
</el-button>
<el-popconfirm
:title="`是否确认删除编码名为 ${row.code} 的这条数据`"
@confirm="handleDelete(row)"
>
<template #reference>
<el-button
class="reset-margin"
link
type="danger"
:size="size"
:disabled="!hasAuth('code:btn:deleteFeedback')"
:icon="useRenderIcon(Delete)"
>
{{ t("buttons:Delete") }}
</el-button>
</template>
</el-popconfirm>
</template>
</pure-table>
</template>
</PureTableBar>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: "DataFeedBack"
});
import { ref } from "vue";
import { useCode } from "./utils/hook";
import { useI18n } from "vue-i18n";
import { getPickerShortcuts } from "@/views/monitor/utils";
import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Delete from "@iconify-icons/ep/delete";
import EditPen from "@iconify-icons/ep/edit-pen";
import Refresh from "@iconify-icons/ep/refresh";
import Read from "@iconify-icons/ep/place";
import { hasAuth } from "@/utils/auth";
const { t } = useI18n();
/**
* 表格Ref
*/
const tableRef = ref();
const formRef = ref();
const {
form,
dataList,
loading,
pagination,
columns,
selectedNum,
departments,
openDialog,
onSearch,
resetForm,
handleDelete,
handleSizeChange,
handleCurrentChange,
handleSelectionChange,
onSelectionCancel,
onbatchDel,
handleAudit,
onbatchDisagree,
onbatchAgree
} = useCode(tableRef);
</script>
<style scoped lang="scss">
:deep(.el-dropdown-menu__item i) {
margin: 0;
}
:deep(.el-button:focus-visible) {
outline: none;
}
.main-content {
margin: 24px 24px 0 !important;
}
.search-form {
:deep(.el-form-item) {
margin-bottom: 12px;
}
}
</style>

View File

@ -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<CodeFeedbackInfo[]>([]);
/**
*
*/
const loading = ref(true);
/**
*
*/
const selectedNum = ref<number>(0);
/**
*
*/
const pagination = reactive<PaginationProps>({
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 }) => (
<el-tag size={props.size} type={getStatusTag(row.status)}>
{getStatusTagText(row.status)}
</el-tag>
)
},
{
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<DepartmentInfo[]>([]);
/**获取部门列表 */
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
};
};

View File

@ -0,0 +1,180 @@
<template>
<el-form
ref="ruleFormRef"
:model="newFormInline"
:rules="formRules"
label-width="82px"
>
<el-row :gutter="30">
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="编码" prop="code">
<el-input
v-model="newFormInline.code"
placeholder="请输入编码~"
clearable
:disabled="newFormInline.way === '审核'"
class="w-full"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="描述" prop="description">
<el-input
v-model="newFormInline.description"
placeholder="请输入编码描述~"
clearable
:disabled="newFormInline.way === '审核'"
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="用户ID" prop="user_id">
<el-input
v-model="newFormInline.user_id"
placeholder="请输入用户ID~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="用户账号" prop="username">
<el-input
v-model="newFormInline.username"
placeholder="请输入用户账号~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="用户名称" prop="nickname">
<el-input
v-model="newFormInline.nickname"
placeholder="请输入用户名称~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="所属部门" prop="department_name">
<el-input
v-model="newFormInline.department_name"
placeholder="请输入所属部门~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="创建时间" prop="create_time">
<el-input
:value="
dayjs(newFormInline.create_time).format('YYYY-MM-DD HH:mm:ss')
"
placeholder="请输入创建时间~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="修改时间" prop="update_time">
<el-input
:value="
dayjs(newFormInline.update_time).format('YYYY-MM-DD HH:mm:ss')
"
placeholder="请输入修改时间~"
disabled
clearable
class="w-full"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way !== '新增'" :value="24" :xm="24" :sm="24">
<el-form-item label="审核状态" prop="status">
<el-select
v-model="newFormInline.status"
placeholder="请选择状态~"
clearable
disabled
class="w-[full]"
>
<el-option label="审核通过" :value="1" />
<el-option label="审核未通过" :value="2" />
<el-option label="待审核" :value="3" />
</el-select>
</el-form-item>
</re-col>
</el-row>
</el-form>
</template>
<script setup lang="ts">
import dayjs from "dayjs";
import { ref, reactive } from "vue";
import ReCol from "@/components/ReCol";
import type { FormRules } from "element-plus";
const ruleFormRef = ref();
interface PropsInfo {
id: string;
/**方式 */
way: string;
/**导入状态 */
status: number;
/**编码 */
code: string;
/**编码描述 */
description: string;
/** 创建时间 */
create_time: string;
/** 更新时间 */
update_time: string;
/**用户ID */
user_id: string;
/**用户昵称 */
nickname: string;
/**用户账号 */
username: string;
/**部门ID */
department_id: string;
/**部门名称 */
department_name: string;
}
type ProsData = {
formInline: PropsInfo;
};
/** 自定义表单规则校验 */
const formRules = reactive<FormRules>({
code: [{ required: true, message: "编码为必填项", trigger: "blur" }],
description: [
{ required: true, message: "编码描述为必填项", trigger: "blur" }
]
});
const props = withDefaults(defineProps<ProsData>(), {
formInline: () => ({
id: "",
way: "新增",
code: "",
description: "",
status: 0,
create_time: "",
update_time: "",
user_id: "",
nickname: "",
username: "",
department_id: "",
department_name: ""
})
});
const newFormInline = ref<PropsInfo>(props.formInline);
defineExpose({ newFormInline });
</script>

View File

@ -0,0 +1,375 @@
<template>
<div class="main">
<el-form
ref="formRef"
:inline="true"
:model="form"
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
>
<el-form-item label="用户账号" prop="usename">
<el-input
v-model="form.username"
placeholder="请输入用户账号~"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="用户名称" prop="nickname">
<el-input
v-model="form.nickname"
placeholder="请输入用户昵称~"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="编码" prop="code">
<el-input
v-model="form.code"
placeholder="请输入编码"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="编码描述" prop="description">
<el-input
v-model="form.description"
clearable
placeholder="请输入编码描述~"
class="!w-[200px]"
/>
</el-form-item>
<el-form-item
v-if="hasAuth('code:data:importAdmin')"
label="所属部门:"
prop="department_id"
>
<el-cascader
v-model="form.department_id"
class="!w-[200px]"
:options="departments"
:props="{
value: 'id',
label: 'name',
emitPath: false,
checkStrictly: true
}"
clearable
filterable
placeholder="请选择所属部门~"
>
<template #default="{ node, data }">
<span>{{ data.name }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
<el-form-item label="导入时间" prop="timeRange">
<el-date-picker
v-model="form.timeRange"
:shortcuts="getPickerShortcuts()"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期时间"
end-placeholder="结束日期时间"
value-format="x"
unlink-panels
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
:icon="useRenderIcon('ri:search-line')"
:loading="loading"
@click="onSearch"
>
{{ t("buttons:Search") }}
</el-button>
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
{{ t("buttons:Reset") }}
</el-button>
</el-form-item>
</el-form>
<PureTableBar title="编码导入" :columns="columns" @refresh="onSearch">
<template #buttons>
<el-button
v-if="hasAuth('code:btn:add')"
type="primary"
:disabled="!hasAuth('code:btn:add')"
:icon="useRenderIcon(AddFill)"
@click="openDialog('新增')"
>
{{ t("buttons:Add") }}
</el-button>
<el-button
v-if="hasAuth('code:btn:import')"
type="primary"
:icon="useRenderIcon(AddFill)"
:disabled="!hasAuth('code:btn:import')"
@click="showUploadArea = !showUploadArea"
>
{{ showUploadArea ? t("buttons:Hide") : t("buttons:Import") }}
</el-button>
</template>
<template v-slot="{ size, dynamicColumns }">
<!-- 上传文件区域 -->
<el-card v-if="showUploadArea" shadow="never">
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
drag
action="#"
class="w-full"
:auto-upload="false"
:before-upload="beforeUpload"
:before-remove="beforeRemove"
>
<div class="el-upload__text">
<IconifyIconOffline
:icon="UploadIcon"
width="26"
:limit="1"
class="m-auto mb-2"
/>
可点击或拖拽上传
</div>
</el-upload>
<template #footer>
<div class="flex items-center justify-center">
<el-button
v-if="hasAuth('code:btn:uploadTemplate')"
class="w-full my-2"
:disabled="!hasAuth('code:btn:uploadTemplate')"
@click="onDownloadTemplate"
>{{ t("buttons:DownLodedTemplate") }}</el-button
>
<el-popconfirm
v-if="hasAuth('code:btn:import')"
title="是否确认上传?"
@confirm="handleUpload"
>
<template #reference>
<el-button
class="w-full my-2"
type="primary"
:disabled="uploadStatus || !hasAuth('code:btn:import')"
>{{ t("buttons:ConfirmUpload") }}</el-button
>
</template>
</el-popconfirm>
</div>
</template>
</el-card>
<div
v-if="selectedNum > 0"
v-motion-fade
class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center"
>
<div class="flex-auto">
<span
style="font-size: var(--el-font-size-base)"
class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]"
>
已选 {{ selectedNum }}
</span>
<el-button type="primary" text @click="onSelectionCancel">
{{ t("buttons:Deselect") }}
</el-button>
</div>
<el-popconfirm
v-if="hasAuth('code:btn:codeImportAudit')"
title="是否确认通过审核?"
@confirm="onbatchAgree"
>
<template #reference>
<el-button
type="success"
text
class="mr-1"
:disabled="
selectedNum < 0 || !hasAuth('code:btn:codeImportAudit')
"
>
{{ t("buttons:AgressInBatches") }}
</el-button>
</template>
</el-popconfirm>
<el-popconfirm
v-if="hasAuth('code:btn:codeImportAudit')"
title="是否确认驳回?"
@confirm="onbatchDisagree"
>
<template #reference>
<el-button
type="danger"
text
class="mr-1"
:disabled="
selectedNum < 0 || !hasAuth('code:btn:codeImportAudit')
"
>
{{ t("buttons:DisagressInBatches") }}
</el-button>
</template>
</el-popconfirm>
<el-popconfirm
v-if="hasAuth('code:btn:deleteCodeImport')"
title="是否确认删除?"
@confirm="onbatchDel"
>
<template #reference>
<el-button
type="danger"
text
class="mr-1"
:disabled="
selectedNum < 0 || !hasAuth('code:btn:deleteCodeImport')
"
>
{{ t("buttons:DeleteInBatches") }}
</el-button>
</template>
</el-popconfirm>
</div>
<pure-table
ref="tableRef"
row-key="id"
adaptive
border
stripe
:adaptiveConfig="{ offsetBottom: 45 }"
align-whole="center"
table-layout="auto"
:loading="loading"
:size="size"
:data="dataList"
:columns="dynamicColumns"
:pagination="pagination"
:paginationSmall="size === 'small' ? true : false"
:header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}"
@selection-change="handleSelectionChange"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
>
<template #operation="{ row }">
<el-button
class="reset-margin"
link
type="primary"
:size="size"
:disabled="!hasAuth('code:btn:updateCodeImport')"
:icon="useRenderIcon(EditPen)"
@click="openDialog('修改', row)"
>
{{ t("buttons:Update") }}
</el-button>
<el-button
class="reset-margin"
link
type="warning"
:size="size"
:disabled="!hasAuth('code:btn:codeImportAudit')"
:icon="useRenderIcon(Read)"
@click="handleAudit(row)"
>
{{ t("buttons:Audit") }}
</el-button>
<el-popconfirm
:title="`是否确认删除编码名为 ${row.code} 的这条数据`"
@confirm="handleDelete(row)"
>
<template #reference>
<el-button
class="reset-margin"
link
type="danger"
:size="size"
:disabled="!hasAuth('code:btn:deleteCodeImport')"
:icon="useRenderIcon(Delete)"
>
{{ t("buttons:Delete") }}
</el-button>
</template>
</el-popconfirm>
</template>
</pure-table>
</template>
</PureTableBar>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: "DataImport"
});
import { ref } from "vue";
import { useCode } from "./utils/hook";
import { useI18n } from "vue-i18n";
import { getPickerShortcuts } from "@/views/monitor/utils";
import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Delete from "@iconify-icons/ep/delete";
import EditPen from "@iconify-icons/ep/edit-pen";
import Refresh from "@iconify-icons/ep/refresh";
import AddFill from "@iconify-icons/ri/add-circle-line";
import UploadIcon from "@iconify-icons/ri/upload-2-line";
import Read from "@iconify-icons/ep/place";
import { hasAuth } from "@/utils/auth";
const { t } = useI18n();
/**
* 表格Ref
*/
const tableRef = ref();
const formRef = ref();
const {
form,
dataList,
loading,
pagination,
columns,
selectedNum,
showUploadArea,
departments,
fileList,
uploadStatus,
beforeUpload,
handleUpload,
beforeRemove,
openDialog,
onSearch,
resetForm,
handleDelete,
handleSizeChange,
handleCurrentChange,
handleSelectionChange,
onSelectionCancel,
onbatchDel,
onDownloadTemplate,
handleAudit,
onbatchDisagree,
onbatchAgree
} = useCode(tableRef);
</script>
<style scoped lang="scss">
:deep(.el-dropdown-menu__item i) {
margin: 0;
}
:deep(.el-button:focus-visible) {
outline: none;
}
.main-content {
margin: 24px 24px 0 !important;
}
.search-form {
:deep(.el-form-item) {
margin-bottom: 12px;
}
}
</style>

View File

@ -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<CodeImportInfo[]>([]);
/**
*
*/
const loading = ref(true);
/**
*
*/
const selectedNum = ref<number>(0);
/**
*
*/
const pagination = reactive<PaginationProps>({
total: 0,
pageSize: 10,
currentPage: 1,
background: true,
pageSizes: [10, 20, 30, 40, 50]
});
// 上传文件区域显示状态
const showUploadArea = ref(false);
const fileList = ref<UploadUserFile[]>([]);
/**上传成功后文件列表 */
const fileIds = ref([]);
const fileId = ref<string>("");
/**上传按钮状态 */
const uploadStatus = ref<boolean>(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 }) => (
<el-tag size={props.size} type={getStatusTag(row.status)}>
{getStatusTagText(row.status)}
</el-tag>
)
},
{
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);
// 创建 <a> 元素并触发下载
const link = document.createElement("a");
link.href = url;
link.download = "上传模版.xlsx"; // 设置下载文件名,确保后缀名正确
document.body.appendChild(link); // 将 <a> 元素添加到 DOM 中
link.click(); // 模拟点击下载
// 清理 URL 对象
URL.revokeObjectURL(url);
document.body.removeChild(link); // 移除 <a> 元素
} 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<DepartmentInfo[]>([]);
/**获取部门列表 */
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
};
};

View File

@ -6,12 +6,28 @@
:model="form" :model="form"
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
> >
<el-form-item label="用户账号" prop="usename">
<el-input
v-model="form.username"
placeholder="请输入用户账号~"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="用户名称" prop="nickname">
<el-input
v-model="form.nickname"
placeholder="请输入用户昵称~"
clearable
class="!w-[200px]"
/>
</el-form-item>
<el-form-item label="编码" prop="code"> <el-form-item label="编码" prop="code">
<el-input <el-input
v-model="form.code" v-model="form.code"
placeholder="请输入编码" placeholder="请输入编码"
clearable clearable
class="!w-[180px]" class="!w-[200px]"
/> />
</el-form-item> </el-form-item>
<el-form-item label="编码描述" prop="description"> <el-form-item label="编码描述" prop="description">
@ -19,7 +35,44 @@
v-model="form.description" v-model="form.description"
clearable clearable
placeholder="请输入编码描述~" placeholder="请输入编码描述~"
class="!w-[180px]" class="!w-[200px]"
/>
</el-form-item>
<el-form-item
v-if="hasAuth('code:data:importAdmin')"
label="所属部门:"
prop="department_id"
>
<el-cascader
v-model="form.department_id"
class="!w-[200px]"
:options="departments"
:props="{
value: 'id',
label: 'name',
emitPath: false,
checkStrictly: true
}"
clearable
filterable
placeholder="请选择所属部门~"
>
<template #default="{ node, data }">
<span>{{ data.name }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
<el-form-item label="导入时间" prop="timeRange">
<el-date-picker
v-model="form.timeRange"
:shortcuts="getPickerShortcuts()"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期时间"
end-placeholder="结束日期时间"
value-format="x"
unlink-panels
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -36,7 +89,7 @@
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<PureTableBar title="编码管理" :columns="columns" @refresh="onSearch"> <PureTableBar title="数据维护" :columns="columns" @refresh="onSearch">
<template #buttons> <template #buttons>
<el-button <el-button
v-if="hasAuth('code:btn:add')" v-if="hasAuth('code:btn:add')"
@ -200,10 +253,11 @@
<script setup lang="ts"> <script setup lang="ts">
defineOptions({ defineOptions({
name: "CodeAdmin" name: "DataMaintain"
}); });
import { ref } from "vue"; import { ref } from "vue";
import { useCode } from "./utils/hook"; import { useCode } from "./utils/hook";
import { getPickerShortcuts } from "@/views/monitor/utils";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { PureTableBar } from "@/components/RePureTableBar"; import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
@ -227,8 +281,8 @@ const {
pagination, pagination,
columns, columns,
selectedNum, selectedNum,
departments,
showUploadArea, showUploadArea,
fileIds,
fileList, fileList,
uploadStatus, uploadStatus,
beforeUpload, beforeUpload,

View File

@ -14,13 +14,15 @@ import {
postAddCodeAPI, postAddCodeAPI,
putUpdateCodeAPI putUpdateCodeAPI
} from "@/api/code"; } from "@/api/code";
import { getKeyList } from "@pureadmin/utils"; import { getKeyList, handleTree } from "@pureadmin/utils";
import { import {
ElMessageBox, ElMessageBox,
type UploadProps, type UploadProps,
type UploadUserFile type UploadUserFile
} from "element-plus"; } from "element-plus";
import { deleteFileAPI, postUploadFileAPI } from "@/api/file"; import { deleteFileAPI, postUploadFileAPI } from "@/api/file";
import type { DepartmentInfo } from "types/system";
import { getDepartmentListAPI } from "@/api/system";
export const useCode = (tableRef: Ref) => { export const useCode = (tableRef: Ref) => {
/** /**
@ -28,7 +30,11 @@ export const useCode = (tableRef: Ref) => {
*/ */
const form = reactive({ const form = reactive({
code: "", code: "",
description: "" description: "",
username: "",
department_id: "",
timeRange: [null, null],
nickname: ""
}); });
/** /**
* Ref * Ref
@ -74,6 +80,21 @@ export const useCode = (tableRef: Ref) => {
fixed: "left", fixed: "left",
reserveSelection: true // 数据刷新后保留选项 reserveSelection: true // 数据刷新后保留选项
}, },
{
label: "用户账号",
prop: "username",
minWidth: 100
},
{
label: "用户昵称",
prop: "nickname",
minWidth: 100
},
{
label: "所属部门",
prop: "department_name",
minWidth: 100
},
{ {
label: "编码", label: "编码",
prop: "code", prop: "code",
@ -105,7 +126,12 @@ export const useCode = (tableRef: Ref) => {
page: pagination.currentPage, page: pagination.currentPage,
pageSize: pagination.pageSize, pageSize: pagination.pageSize,
description: form.description, description: form.description,
code: form.code code: form.code,
department_id: form.department_id,
nickname: form.nickname,
username: form.username,
startTime: form.timeRange[0] ? form.timeRange[0] : null,
endTime: form.timeRange[1] ? form.timeRange[1] : null
}); });
if (res.success) { if (res.success) {
dataList.value = res.data.result; dataList.value = res.data.result;
@ -149,7 +175,12 @@ export const useCode = (tableRef: Ref) => {
page: pagination.currentPage, page: pagination.currentPage,
pageSize: val, pageSize: val,
description: form.description, description: form.description,
code: form.code code: form.code,
department_id: form.department_id,
nickname: form.nickname,
username: form.username,
startTime: form.timeRange[0] ? form.timeRange[0] : null,
endTime: form.timeRange[1] ? form.timeRange[1] : null
}); });
if (res.success) { if (res.success) {
dataList.value = res.data.result; dataList.value = res.data.result;
@ -171,7 +202,12 @@ export const useCode = (tableRef: Ref) => {
page: val, page: val,
pageSize: pagination.pageSize, pageSize: pagination.pageSize,
description: form.description, description: form.description,
code: form.code code: form.code,
department_id: form.department_id,
nickname: form.nickname,
username: form.username,
startTime: form.timeRange[0] ? form.timeRange[0] : null,
endTime: form.timeRange[1] ? form.timeRange[1] : null
}); });
if (res.code === 200) { if (res.code === 200) {
dataList.value = res.data.result; dataList.value = res.data.result;
@ -371,12 +407,37 @@ export const useCode = (tableRef: Ref) => {
() => false () => false
); );
}; };
/**部门列表 */
const departments = ref<DepartmentInfo[]>([]);
/**获取部门列表 */
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;
};
/** /**
* *
*/ */
onMounted(async () => { onMounted(async () => {
await onSearch(); await onSearch();
await getDepartments();
}); });
return { return {
form, form,
@ -385,6 +446,7 @@ export const useCode = (tableRef: Ref) => {
pagination, pagination,
columns, columns,
selectedNum, selectedNum,
departments,
showUploadArea, showUploadArea,
fileIds, fileIds,
fileList, fileList,

68
types/code.d.ts vendored
View File

@ -6,6 +6,16 @@ export interface CodeInfo {
code: string; code: string;
/** 编码描述 */ /** 编码描述 */
description: string; description: string;
/**用户ID */
user_id: string;
/**用户昵称 */
nickname: string;
/**用户账号 */
username: string;
/**部门ID */
department_id: string;
/**部门名称 */
department_name: string;
/** 创建时间 */ /** 创建时间 */
create_time: string; create_time: string;
/** 更新时间 */ /** 更新时间 */
@ -88,3 +98,61 @@ export interface QueryCodeLogInfo {
/** 部门名称 */ /** 部门名称 */
department_name: string; department_name: string;
} }
/**编码反馈信息 */
export interface CodeFeedbackInfo {
/**反馈ID */
id: string;
/**编码ID */
code_id: string;
/**编码 */
code: string;
/**编码描述 */
description: string;
/**反馈编码 */
feedback_code: string;
/**反馈文本 */
feedback_description: string;
/** 创建时间 */
create_time: string;
/** 更新时间 */
update_time: string;
/**用户ID */
user_id: string;
/**用户昵称 */
nickname: string;
/**用户账号 */
username: string;
/**部门ID */
department_id: string;
/**部门名称 */
department_name: string;
/**审核状态 */
status: number;
}
/**编码导入信息 */
export interface CodeImportInfo {
/**导入ID */
id: string;
/**导入状态 */
status: number;
/**编码 */
code: string;
/**编码描述 */
description: string;
/** 创建时间 */
create_time: string;
/** 更新时间 */
update_time: string;
/**用户ID */
user_id: string;
/**用户昵称 */
nickname: string;
/**用户账号 */
username: string;
/**部门ID */
department_id: string;
/**部门名称 */
department_name: string;
}