| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 |
- <template>
- <main class="safety-platform-container__main">
- <el-form
- ref="formRef"
- :model="ruleFormData"
- :rules="formRules"
- label-width="auto"
- class="evaluation-form"
- >
- <el-form-item label="考核表名称:" prop="evaluationTableName">
- <el-input
- v-model="ruleFormData.evaluationTableName"
- placeholder="请输入考核表名称"
- :disabled="isViewMode"
- />
- </el-form-item>
- <el-form-item label="上传附件文档:" prop="attachmentDocument">
- <UploadFiles
- label="上传附件"
- :file-list="ruleFormData.attachmentDocument"
- @uploadSuccess="handleUploadSuccess"
- />
- </el-form-item>
- <el-form-item label="评分说明:" prop="scoringDescription">
- <el-input
- v-model="ruleFormData.scoringDescription"
- type="textarea"
- :rows="5"
- placeholder="请输入评分说明"
- :disabled="isViewMode"
- />
- </el-form-item>
- </el-form>
- <div class="evaluation-items-section">
- <div class="section-header">
- <!-- <el-button plain @click="handleDownloadTemplate">模板下载</el-button> -->
- <!-- <el-button plain @click="handleImport">导入</el-button> -->
- </div>
- <div class="evaluation-items-table">
- <el-table :data="evaluationItems" border :span-method="handleSpanMethod">
- <el-table-column label="编号" type="index" width="80" align="center" />
- <el-table-column label="考核项目" min-width="150">
- <template #header>
- <span>考核项目<span style="color: red">*</span></span>
- </template>
- <template #default="scope">
- <el-input
- :model-value="getCurrentEvaluationItem(scope.$index)"
- @update:model-value="(val) => handleEvaluationItemInput(scope.$index, val)"
- @blur="handleEvaluationItemBlur(scope.$index)"
- placeholder="请输入考核项目"
- :disabled="isViewMode"
- />
- </template>
- </el-table-column>
- <el-table-column label="考核内容" min-width="200">
- <template #header>
- <span>考核内容<span style="color: red">*</span></span>
- </template>
- <template #default="scope">
- <el-input
- v-model="scope.row.evaluationContent"
- placeholder="请输入考核内容"
- :disabled="isViewMode"
- />
- </template>
- </el-table-column>
- <el-table-column label="评分方式" min-width="150">
- <template #default="scope">
- <el-input
- v-model="scope.row.scoringMethod"
- placeholder="请输入评分方式"
- :disabled="isViewMode"
- />
- </template>
- </el-table-column>
- <el-table-column label="加减分项" min-width="150" align="center">
- <template #default="scope">
- <el-select
- v-model="scope.row.isAdd"
- placeholder="请选择"
- :disabled="isViewMode"
- style="width: 100%"
- >
- <el-option label="基础分" :value="2" />
- <el-option label="加分项" :value="1" />
- <el-option label="减分项" :value="0" />
- </el-select>
- </template>
- </el-table-column>
- <el-table-column label="复评人" min-width="150">
- <template #default="scope">
- <el-select
- v-model="scope.row.reviewUserId"
- placeholder="请选择复评人"
- filterable
- clearable
- :disabled="isViewMode"
- style="width: 100%"
- @change="(val) => handleReviewUserChange(scope.$index, val)"
- >
- <el-option
- v-for="user in reviewUserList"
- :key="user.id"
- :label="user.realname"
- :value="user.id"
- />
- </el-select>
- </template>
- </el-table-column>
- <el-table-column v-if="!isViewMode" label="操作" fixed="right" width="350" align="center">
- <template #default="scope">
- <el-button type="primary" link @click="handleAddItem(scope.$index)">新增</el-button>
- <el-button type="primary" link @click="handleMoveUp(scope.$index)">向上插入分类</el-button>
- <el-button type="primary" link @click="handleMoveDown(scope.$index)">向下插入分类</el-button>
- <el-button type="danger" link @click="handleDeleteItem(scope.$index)">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </div>
- <!-- 导入弹窗 -->
- <el-dialog v-model="importDialogVisible" title="导入考核项目" width="500px" destroy-on-close>
- <el-upload
- :file-list="importFileList"
- :auto-upload="false"
- :on-change="handleFileChange"
- :on-remove="handleFileRemove"
- :limit="1"
- drag
- accept=".xlsx,.xls"
- >
- <el-icon class="el-icon--upload"><Document /></el-icon>
- <div class="el-upload__text">
- <div style="font-size: 12px; color: red; margin-bottom: 5px">请下载模板并按要求填写后上传</div>
- <div style="font-size: 16px">点击或将文件拖拽到这里上传</div>
- <div style="font-size: 12px; color: rgba(0, 0, 0, 0.45); margin-top: 5px">
- 文件支持.xlsx .xls格式,仅支持上传一个文件
- </div>
- </div>
- </el-upload>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="importDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleConfirmImport" :disabled="importFileList.length === 0">
- 导入
- </el-button>
- </span>
- </template>
- </el-dialog>
- </main>
- <footer class="safety-platform-container__footer">
- <el-button @click="router.back()">取消</el-button>
- <el-button v-if="!isViewMode" type="primary" @click="handleSubmit">
- {{ isCreateMode ? '提交' : '保存' }}
- </el-button>
- </footer>
- </template>
- <script setup lang="ts">
- import { computed, nextTick, onMounted, ref } from 'vue';
- import { useRoute, useRouter } from 'vue-router';
- import { ElMessage } from 'element-plus';
- import type { FormInstance } from 'element-plus';
- import { Document } from '@element-plus/icons-vue';
- import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
- import { EVALUATION_SYSTEM_FORM_DATA, EVALUATION_SYSTEM_FORM_RULES } from '../configs/form';
- import { importSecurityExamineDet, saveSecurityExamine, querySecurityExamineDetail, updateSecurityExamine } from '@/api/evaluationSystem';
- import type { EvaluationContent } from '@/api/evaluationSystem';
- import type { FileItem } from '@/components/UploadFiles/types';
- import { useUserInfoHook } from '@/hooks/useUserInfoHook';
- import { formatAttachmentList } from '@/components/UploadFiles/utils';
- import { queryAvailableUserList } from '@/api/production-safety/responsibility-implementation';
- const props = defineProps<{
- id?: number;
- }>();
- const router = useRouter();
- const route = useRoute();
- const operate = computed(() => (route.query.operate as string) || 'evaluationSystem-create');
- const isCreateMode = computed(() => operate.value === 'evaluationSystem-create');
- const isEditMode = computed(() => operate.value === 'evaluationSystem-edit');
- const isViewMode = computed(() => operate.value === 'evaluationSystem-view');
- const formRef = ref<FormInstance>();
- const ruleFormData = ref({ ...EVALUATION_SYSTEM_FORM_DATA });
- const formRules = EVALUATION_SYSTEM_FORM_RULES;
- // 获取当前用户信息
- const { id: userId, realname: userName } = useUserInfoHook();
- // 初始化一条空白数据
- const evaluationItems = ref<any[]>([
- {
- id: 0,
- psemId: 0,
- evaluationItem: '',
- evaluationContent: '',
- scoringMethod: '',
- isAdd: 1, // 是否加分项(0-否,1-是),默认为1(加分项)
- reviewUserId: null as number | null, // 复评人ID
- reviewUserName: '', // 复评人姓名
- },
- ]);
- // 复评人用户列表
- const reviewUserList = ref<UserLisItem[]>([]);
- const getReviewUserList = async () => {
- try {
- const res = await queryAvailableUserList({
- pageNumber: 1,
- pageSize: 9999,
- queryParam: {}, // 不传递 deptId 参数
- });
- reviewUserList.value = res?.records || [];
- } catch (e) {
- console.error('获取复评人列表失败:', e);
- reviewUserList.value = [];
- }
- };
- // 处理复评人选择变化
- const handleReviewUserChange = (index: number, userId: number | null) => {
- if (userId) {
- const selectedUser = reviewUserList.value.find((user) => user.id === userId);
- if (selectedUser) {
- evaluationItems.value[index].reviewUserName = selectedUser.realname || '';
- }
- } else {
- evaluationItems.value[index].reviewUserName = '';
- }
- };
- // 用于存储输入过程中的临时值,避免实时合并
- const tempEvaluationItems = ref<Record<number, string>>({});
- const handleValidate = async () => {
- if (!formRef.value) return;
- return new Promise((resolve) => {
- formRef.value?.validate((valid: boolean) => {
- resolve(valid);
- });
- });
- };
- const handleUploadSuccess = (files: any[]) => {
- ruleFormData.value.attachmentDocument = files;
- };
- const handleDownloadTemplate = () => {
- // TODO: 下载模板
- console.log('download template');
- };
- // 导入弹窗相关
- const importDialogVisible = ref(false);
- const importFileList = ref<any[]>([]);
- const handleImport = () => {
- importDialogVisible.value = true;
- importFileList.value = [];
- };
- // 处理导入成功
- const handleImportSuccess = async (response: any) => {
- try {
- // 接口返回的数据格式:{ code: 0, message: "string", data: EvaluationContent[] }
- if (response && response.data && Array.isArray(response.data)) {
- // 将导入的数据映射到 evaluationItems 格式
- // 导入的数据视为新增数据,不带后端已保存的 id / psemId
- evaluationItems.value = response.data.map((item: any) => ({
- id: 0,
- psemId: 0,
- evaluationItem: item.exProgram || '', // 考核项目
- evaluationContent: item.exContent || '', // 考核内容
- scoringMethod: item.scoringWay || '', // 评分方式
- isAdd: item.isAdd !== undefined ? item.isAdd : 1, // 是否加分项(0-否,1-是),默认为1
- reviewUserId: item.reviewUserId || null, // 复评人ID
- reviewUserName: item.reviewUserName || '', // 复评人姓名
- }));
- // 如果导入后列表为空,至少保留一条空白数据
- if (evaluationItems.value.length === 0) {
- evaluationItems.value = [
- {
- evaluationItem: '',
- evaluationContent: '',
- scoringMethod: '',
- },
- ];
- }
- ElMessage.success(`导入成功,共导入 ${response.data.length} 条数据`);
- importDialogVisible.value = false;
- } else {
- ElMessage.error('导入失败:返回数据格式错误');
- }
- } catch (e) {
- console.error('导入处理失败:', e);
- ElMessage.error(e?.message || e?.data || '导入处理失败');
- }
- };
- // 处理文件上传
- const handleFileChange = (file: any) => {
- importFileList.value = [file];
- };
- // 处理文件移除
- const handleFileRemove = () => {
- importFileList.value = [];
- };
- // 确认导入
- const handleConfirmImport = async () => {
- if (importFileList.value.length === 0) {
- ElMessage.warning('请先选择要导入的文件');
- return;
- }
- const file = importFileList.value[0].raw || importFileList.value[0];
- if (!file) {
- ElMessage.warning('文件不存在');
- return;
- }
- // 检查文件类型
- const isExcel = /\.(xlsx|xls)$/.test(file.name.toLowerCase());
- if (!isExcel) {
- ElMessage.error('仅支持上传.xlsx .xls格式文件');
- return;
- }
- try {
- const formData = new FormData();
- formData.append('file', file);
- const res = await importSecurityExamineDet(formData);
- if (res) {
- handleImportSuccess({ data: res });
- }
- } catch (e: any) {
- console.error('导入失败:', e);
- ElMessage.error(e?.message || e?.data || '导入失败,请重试');
- }
- };
- // 获取当前行的考核项目值(考虑临时输入值)
- const getCurrentEvaluationItem = (rowIndex: number): string => {
- // 如果正在输入(有临时值),使用临时值;否则使用实际值
- if (tempEvaluationItems.value[rowIndex] !== undefined) {
- return tempEvaluationItems.value[rowIndex];
- }
- return evaluationItems.value[rowIndex]?.evaluationItem || '';
- };
- // 处理单元格合并(合并考核项目列)
- const handleSpanMethod = ({ row, column, rowIndex, columnIndex }: any) => {
- // 只合并考核项目列(columnIndex === 1,因为编号列是 0)
- if (columnIndex === 1) {
- const currentItem = row.evaluationItem;
- if (!currentItem) {
- // 如果当前行的考核项目为空,不合并
- return {
- rowspan: 1,
- colspan: 1,
- };
- }
- // 查找相同考核项目的连续行
- let rowspan = 1;
- for (let i = rowIndex + 1; i < evaluationItems.value.length; i++) {
- if (evaluationItems.value[i].evaluationItem === currentItem) {
- rowspan++;
- } else {
- break;
- }
- }
- // 检查是否是同一组的第一行
- if (rowIndex > 0 && evaluationItems.value[rowIndex - 1].evaluationItem === currentItem) {
- // 不是第一行,不显示(被合并)
- return {
- rowspan: 0,
- colspan: 0,
- };
- }
- return {
- rowspan,
- colspan: 1,
- };
- }
- // 其他列不合并
- return {
- rowspan: 1,
- colspan: 1,
- };
- };
- // 检查是否是相邻行的相同内容(用于合并)
- const isAdjacentSameValue = (index: number, value: string): boolean => {
- if (!value) return false;
-
- // 检查上一行或下一行是否有相同的值
- const prevRow = evaluationItems.value[index - 1];
- const nextRow = evaluationItems.value[index + 1];
-
- return (
- (prevRow && prevRow.evaluationItem === value) ||
- (nextRow && nextRow.evaluationItem === value)
- );
- };
- // 检查是否有跨行的相同考核项目(非相邻的)
- const checkNonConsecutiveDuplicate = (index: number, value: string): boolean => {
- if (!value) return false;
- // 检查是否有非相邻行的相同值
- for (let i = 0; i < evaluationItems.value.length; i++) {
- if (i === index) continue; // 跳过当前行
- if (evaluationItems.value[i].evaluationItem === value) {
- // 检查是否是相邻行
- const isAdjacent = i === index - 1 || i === index + 1;
-
- if (!isAdjacent) {
- return true; // 发现跨行的重复
- }
- }
- }
- return false;
- };
- // 考核项目输入时的处理(只更新临时值,不触发合并)
- const handleEvaluationItemInput = (index: number, value: string) => {
- // 只更新临时值,不更新实际数据,避免实时合并
- tempEvaluationItems.value[index] = value;
- };
- // 考核项目失去焦点时的处理
- const handleEvaluationItemBlur = async (index: number) => {
- // 获取临时输入值
- const tempValue = tempEvaluationItems.value[index];
- const finalValue = tempValue !== undefined ? tempValue : evaluationItems.value[index].evaluationItem;
-
- // 清除临时值
- delete tempEvaluationItems.value[index];
- // 如果值为空,直接更新并返回
- if (!finalValue || !finalValue.trim()) {
- evaluationItems.value[index].evaluationItem = '';
- await nextTick();
- return;
- }
- // 先检查是否是相邻行的相同内容(允许合并)
- const isAdjacent = isAdjacentSameValue(index, finalValue);
-
- if (isAdjacent) {
- // 相邻行相同,允许合并,直接更新数据
- evaluationItems.value[index].evaluationItem = finalValue;
- await nextTick();
- return;
- }
- // 检查是否有跨行的相同考核项目(非相邻的)
- if (checkNonConsecutiveDuplicate(index, finalValue)) {
- ElMessage.warning('考核项目一致,请使用连续的相同考核项目');
- evaluationItems.value[index].evaluationItem = '';
- await nextTick();
- return;
- }
- // 没有冲突,更新实际数据
- evaluationItems.value[index].evaluationItem = finalValue;
- // 等待 DOM 更新后,强制表格重新计算合并
- await nextTick();
- };
- // 新增:在当前行下面插入一条新数据,如果当前行有考核项目值,新行也使用相同的值
- const handleAddItem = (index: number) => {
- const currentItem = evaluationItems.value[index];
- evaluationItems.value.splice(index + 1, 0, {
- id: 0,
- psemId: 0,
- evaluationItem: currentItem.evaluationItem || '', // 合并考核项目字段
- evaluationContent: '',
- scoringMethod: '',
- isAdd: currentItem.isAdd !== undefined ? currentItem.isAdd : 1, // 是否加分项,继承当前行的值
- reviewUserId: null, // 复评人ID
- reviewUserName: '', // 复评人姓名
- });
- };
- // 删除当前行
- const handleDeleteItem = (index: number) => {
- if (evaluationItems.value.length <= 1) {
- ElMessage.warning('至少需要保留一条数据');
- return;
- }
- evaluationItems.value.splice(index, 1);
- };
- // 查找当前行所在合并组的起始位置
- const findGroupStartIndex = (index: number): number => {
- const currentItem = evaluationItems.value[index].evaluationItem;
- if (!currentItem) {
- return index; // 如果当前行考核项目为空,直接返回当前索引
- }
- // 向上查找,找到相同考核项目的第一行
- let startIndex = index;
- for (let i = index - 1; i >= 0; i--) {
- if (evaluationItems.value[i].evaluationItem === currentItem) {
- startIndex = i;
- } else {
- break;
- }
- }
- return startIndex;
- };
- // 查找当前行所在合并组的结束位置
- const findGroupEndIndex = (index: number): number => {
- const currentItem = evaluationItems.value[index].evaluationItem;
- if (!currentItem) {
- return index; // 如果当前行考核项目为空,直接返回当前索引
- }
- // 向下查找,找到相同考核项目的最后一行
- let endIndex = index;
- for (let i = index + 1; i < evaluationItems.value.length; i++) {
- if (evaluationItems.value[i].evaluationItem === currentItem) {
- endIndex = i;
- } else {
- break;
- }
- }
- return endIndex;
- };
- // 向上插入分类:在当前行所在合并组的第一行之前插入一条空白数据
- const handleMoveUp = (index: number) => {
- const groupStartIndex = findGroupStartIndex(index);
- evaluationItems.value.splice(groupStartIndex, 0, {
- id: 0,
- psemId: 0,
- evaluationItem: '',
- evaluationContent: '',
- scoringMethod: '',
- isAdd: 1, // 是否加分项(0-否,1-是),默认为1
- reviewUserId: null, // 复评人ID
- reviewUserName: '', // 复评人姓名
- });
- };
- // 向下插入分类:在当前行所在合并组的最后一行之后插入一条空白数据
- const handleMoveDown = (index: number) => {
- const groupEndIndex = findGroupEndIndex(index);
- evaluationItems.value.splice(groupEndIndex + 1, 0, {
- id: 0,
- psemId: 0,
- evaluationItem: '',
- evaluationContent: '',
- scoringMethod: '',
- isAdd: 1, // 是否加分项(0-否,1-是),默认为1
- reviewUserId: null, // 复评人ID
- reviewUserName: '', // 复评人姓名
- });
- };
- // 将逗号分隔的URL字符串转换为FileItem数组
- const convertAttachmentsToFileItems = (attachmentsStr: string): FileItem[] => {
- if (!attachmentsStr || !attachmentsStr.trim()) {
- return [];
- }
-
- // 按逗号分割URL
- const urls = attachmentsStr.split(',').map(url => url.trim()).filter(url => url);
-
- return urls.map((url, index) => {
- // 从URL中提取文件名
- const urlParts = url.split('/');
- const fileName = urlParts[urlParts.length - 1] || `附件${index + 1}`;
-
- // 根据文件扩展名判断文件类型
- const extension = fileName.split('.').pop()?.toLowerCase() || '';
- let fileType = 'pdf';
- if (extension === 'doc' || extension === 'docx') {
- fileType = 'word';
- } else if (extension === 'xls' || extension === 'xlsx') {
- fileType = 'excel';
- } else if (extension === 'ppt' || extension === 'pptx') {
- fileType = 'ppt';
- }
-
- return {
- fileId: 0,
- fileName,
- fileType,
- fileSize: '0',
- fileUrl: url,
- };
- });
- };
- const getDetail = async () => {
- if (!props.id) return;
-
- try {
- const detail = await querySecurityExamineDetail(props.id);
- if (!detail) return;
-
- // 填充表单数据
- ruleFormData.value.evaluationTableName = detail.exName || '';
- ruleFormData.value.scoringDescription = detail.ratingDescribe || '';
-
- // 转换附件文档:将逗号分隔的URL字符串转换为FileItem数组
- ruleFormData.value.attachmentDocument = convertAttachmentsToFileItems(detail.attachments || '');
-
- // 填充考核项目列表
- if (detail.exContents && detail.exContents.length > 0) {
- // 详情接口返回的数据视为已持久化的数据,保留 id / psemId
- evaluationItems.value = detail.exContents.map((item: any) => ({
- id: item.id || 0,
- psemId: item.psemId || 0,
- evaluationItem: item.exProgram || '',
- evaluationContent: item.exContent || '',
- scoringMethod: item.scoringWay || '',
- isAdd: item.isAdd !== undefined ? item.isAdd : 1, // 是否加分项(0-否,1-是),默认为1
- reviewUserId: item.reviewUserId || null, // 复评人ID
- reviewUserName: item.reviewUserName || '', // 复评人姓名
- }));
- } else {
- // 如果没有数据,至少保留一条空白数据
- evaluationItems.value = [
- {
- id: 0,
- psemId: 0,
- evaluationItem: '',
- evaluationContent: '',
- scoringMethod: '',
- isAdd: 1, // 是否加分项(0-否,1-是),默认为1
- reviewUserId: null, // 复评人ID
- reviewUserName: '', // 复评人姓名
- },
- ];
- }
- } catch (e: any) {
- console.error('获取详情失败:', e);
- ElMessage.error(e?.message || e?.data || '获取详情失败,请重试');
- }
- };
- // 验证考核项目列表
- const validateEvaluationItems = (): boolean => {
- for (let i = 0; i < evaluationItems.value.length; i++) {
- const item = evaluationItems.value[i];
- if (!item.evaluationItem || !item.evaluationItem.trim()) {
- ElMessage.warning(`第 ${i + 1} 行的考核项目不能为空`);
- return false;
- }
- if (!item.evaluationContent || !item.evaluationContent.trim()) {
- ElMessage.warning(`第 ${i + 1} 行的考核内容不能为空`);
- return false;
- }
- }
- return true;
- };
- const handleSubmit = async () => {
- const res = await handleValidate();
- if (!res) return;
- // 验证考核项目列表
- if (!validateEvaluationItems()) {
- return;
- }
- try {
- // 处理附件文档:先上传文件获取 URL,然后提取 fileUrl,多个用逗号分隔
- let attachments = '';
- if (ruleFormData.value.attachmentDocument && ruleFormData.value.attachmentDocument.length > 0) {
- // 分离已有URL的文件和新上传的文件
- const existingFiles: string[] = [];
- const newFiles: any[] = [];
-
- ruleFormData.value.attachmentDocument.forEach((file: any) => {
- // 如果文件已经有 fileUrl 且没有 file 对象,说明是已有文件
- if (file.fileUrl && !file.file) {
- existingFiles.push(file.fileUrl);
- } else {
- // 否则是需要上传的新文件
- newFiles.push(file);
- }
- });
- // 上传新文件
- let uploadedUrls: string[] = [];
- if (newFiles.length > 0) {
- const uploadedFiles = await formatAttachmentList(newFiles);
- uploadedUrls = uploadedFiles
- .map((file: any) => file.fileUrl || file.url || '')
- .filter((url: string) => url);
- }
- // 合并已有URL和新上传的URL
- attachments = [...existingFiles, ...uploadedUrls].filter((url: string) => url).join(',');
- }
- // 映射考核项目列表,添加序号
- const exContents: EvaluationContent[] = evaluationItems.value.map((item, index) => {
- const base: any = {
- serialNum: index + 1, // 序号从1开始
- exProgram: item.evaluationItem || '', // 考核项目
- exContent: item.evaluationContent || '', // 考核内容
- scoringWay: item.scoringMethod || '', // 评分方式
- isAdd: item.isAdd !== undefined ? item.isAdd : 1, // 是否加分项(0-否,1-是)
- reviewUserId: item.reviewUserId || null, // 复评人ID
- reviewUserName: item.reviewUserName || '', // 复评人姓名
- };
- // 只有详情接口返回的已存在数据才携带 id / psemId
- if (item.id && item.psemId) {
- base.id = item.id;
- base.psemId = item.psemId;
- }
- return base as EvaluationContent;
- });
- if (isEditMode.value && props.id) {
- // 编辑模式:调用更新接口
- const payload = {
- id: props.id, // 编辑时必须传ID
- exName: ruleFormData.value.evaluationTableName || '', // 考核表名称
- attachments, // 考核文档
- ratingDescribe: ruleFormData.value.scoringDescription || '', // 评分说明
- deptNames: '', // 下发部门(编辑时为空)
- deptIds: [], // 下发部门ID数组(编辑时为空)
- getUserGroupId: 0,
- planStartTime: '', // 计划开始时间(编辑时为空)
- planEndTime: '', // 计划结束时间(编辑时为空)
- status: 0, // 状态:0-未下发
- createdUserId: userId || 0, // 创建人ID
- createdUserName: userName || '', // 创建人名称
- exContents, // 考核项目列表
- };
- await updateSecurityExamine(payload);
- ElMessage.success('保存成功');
- } else {
- // 创建模式:调用保存接口
- const payload = {
- id: 0, // 新增时为0
- exName: ruleFormData.value.evaluationTableName || '', // 考核表名称
- attachments, // 考核文档
- ratingDescribe: ruleFormData.value.scoringDescription || '', // 评分说明
- deptNames: '', // 下发部门(创建时为空)
- deptIds: [], // 下发部门ID数组(创建时为空)
- getUserGroupId: 0,
- planStartTime: '', // 计划开始时间(创建时为空)
- planEndTime: '', // 计划结束时间(创建时为空)
- status: 0, // 状态:0-未下发
- createdUserId: userId || 0, // 创建人ID
- createdUserName: userName || '', // 创建人名称
- exContents, // 考核项目列表
- };
- await saveSecurityExamine(payload);
- ElMessage.success('创建成功');
- }
-
- router.back();
- } catch (e: any) {
- console.error('保存失败:', e);
- ElMessage.error(e?.message || e?.data || '保存失败,请重试');
- }
- };
- onMounted(() => {
- // 获取复评人列表
- getReviewUserList();
-
- if (isEditMode.value || isViewMode.value) {
- getDetail();
- } else {
- // 创建模式下,确保附件文档字段为空
- ruleFormData.value.attachmentDocument = [];
- // 创建模式下,确保至少有一条空白数据
- if (evaluationItems.value.length === 0) {
- evaluationItems.value = [
- {
- id: 0,
- psemId: 0,
- evaluationItem: '',
- evaluationContent: '',
- scoringMethod: '',
- isAdd: 1, // 是否加分项(0-否,1-是),默认为1
- reviewUserId: null, // 复评人ID
- reviewUserName: '', // 复评人姓名
- },
- ];
- }
- }
- });
- </script>
- <style scoped lang="scss">
- @use '@/styles/page-details-layout.scss' as *;
- .evaluation-form {
- display: flex;
- flex-direction: column;
- width: 600px;
- gap: 32px;
- :deep(.el-form-item) {
- margin-bottom: 0;
- }
- :deep(.el-form-item__label) {
- padding: 0;
- }
- }
- .evaluation-items-section {
- margin-top: 32px;
- }
- .section-header {
- display: flex;
- gap: 10px;
- margin-bottom: 20px;
- }
- .evaluation-items-table {
- width: 100%;
- }
- </style>
|