清空input[type='file']的文本域

本文介绍两种有效清除HTML中file控件选中文件的方法:一种是通过移除原有控件并重新插入新的控件;另一种是利用form表单的reset方法,并结合临时表单实现仅清除文件而不影响其他输入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

file是没有value的概念的,所以不能直接清除file的文本域。现在提供两种方法来解决这个问题。
一、先移除,在插入。
就是先移除原有的demo里的file控件,然后在插入一个新的file控件。
二、利用form的reset方法。
form的reset方法可以将已上传的文件删除,但是其他输入也会被清空。
我们可以构建一个临时的form表单,将file控件放在这个临时的form表单里。
然后在重置这个表单,最后再将file控件放在原有的表单里并且移除临时的form表单就可以了。
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <title>file-text</title>
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</style>
</head>

<body>
<form class="myForm">
  <input type="file" name="myFile" id="myFile">
</form>
  <input type="button" name="myButton" value="删除">
<script type="text/javascript" src="js/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
  $('input[name="myButton"]').click(function(){
     //第一种
     $("#myfile").remove();
     $('.myForm').append("<input type='file' name='myFile' id='myFile'>");
     //第二种
     //clearFileInput($('#myFile'));
  });
  function clearFileInput($file){
    var $form=$('<form></form>');//创建临时表单,返回的是jq对象
    $('body').append($form);
    var $fileNext = $file.next();//记录file在原表单中的下一个兄弟节点
    if($fileNext.length==0)
      var $fileParent = $file.parent();//如果file在原表单中是最后一个节点,则记录其父节点
    $form.append($file);//相当于将原表单中的file剪切到信表单中
    $form[0].reset();//这时file的文本域就清空了
    if($fileNext.length>0){
      $file.insertBefore($fileNext);//在$fileNext前插入$file,也相当于是剪切
    }
    else{
      $fileParent.append($file);
    }
    $form.remove();
  }
  </script>
</body>
</html>
<template> <div> <el-dialog :visible.sync="dialogVisible" width="70%" :show-close="false" :close-on-click-modal="false" append-to-body @open="handleOpen" @close="handleClose" > <template slot="title"> <div style="text-align: center; font-size: 22px;"> {{ title }} </div> </template> <el-form :model="detailForm" label-width="130px" v-loading="loading"> <el-row> <el-col :span="8"> <el-form-item label="日期:"> <el-input readonly v-model="detailForm.recordDate"></el-input> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="班别:"> <el-input readonly v-model="detailForm.workClass"></el-input> </el-form-item> </el-col> </el-row> <el-form-item label="异常机况:"> <el-table :data="eqpList" style="width: 100%" id="eqpTable"> <el-table-column prop="area" label="Area"></el-table-column> <el-table-column prop="eqpId" label="Eqp Id"></el-table-column> <el-table-column prop="eqpName" label="Eqp Name"></el-table-column> <el-table-column prop="eqpStatus" label="Eqp Status"></el-table-column> <el-table-column prop="eqpHour" label="Hour"></el-table-column> <el-table-column prop="eqpUser" label="User"></el-table-column> <el-table-column prop="comments" label="Comment"></el-table-column> </el-table> <el-input type="textarea" v-model="detailForm.abnormal" :autosize="{ minRows: 2 }" placeholder="请输入异常机况 或 选中粘贴图片" @paste="handlePaste($event, abnormalImageRef)" :readonly="!detailForm.editAuth" ></el-input> <el-upload ref="abnormalImageRef" v-model="detailForm.abnormalImageList" action="#" :http-request="handleHttpUpload" list-type="picture-card" :before-upload="beforeUpload" :on-success="() => uploadSuccess('abnormalImageList')" :on-error="uploadError" :on-remove="(file, fileList) => handleRemove('abnormalImageList', fileList)" :accept="fileType.join(',')" :on-preview="handlePreview" :disabled="!detailForm.editAuth" > <i class="el-icon-plus"></i> </el-upload> </el-form-item> <el-form-item label="产片:"> <el-table :data="lotList" style="width: 100%" id="lotTable"> <el-table-column prop="eqp" label="机台"></el-table-column> <el-table-column prop="priority" label="Priority"></el-table-column> <el-table-column prop="lotId" label="Lot ID"></el-table-column> <el-table-column prop="maskTitle" label="Mask Title"></el-table-column> <el-table-column prop="dueDay" label="Due Day"></el-table-column> </el-table> <el-input type="textarea" v-model="detailForm.product" :autosize="{ minRows: 2 }" placeholder="请输入产片 或 选中粘贴图片" @paste="handlePaste($event, productImageRef)" :readonly="!detailForm.editAuth" ></el-input> <el-upload ref="productImageRef" v-model="detailForm.productImageList" action="#" :http-request="handleHttpUpload" list-type="picture-card" :before-upload="beforeUpload" :on-success="() => uploadSuccess('productImageList')" :on-error="uploadError" :on-remove="(file, fileList) => handleRemove('productImageList', fileList)" :accept="fileType.join(',')" :on-preview="handlePreview" :disabled="!detailForm.editAuth" > <i class="el-icon-plus"></i> </el-upload> </el-form-item> <el-form-item label="工程师交接事项:"> <el-input type="textarea" v-model="detailForm.engHandover" :autosize="{ minRows: 2 }" placeholder="请输入工程师交接事项 或 选中粘贴图片" @paste="handlePaste($event, engHandoverImageRef)" :readonly="!detailForm.editAuth" ></el-input> <el-upload ref="engHandoverImageRef" v-model="detailForm.engHandoverImageList" action="#" :http-request="handleHttpUpload" list-type="picture-card" :before-upload="beforeUpload" :on-success="() => uploadSuccess('engHandoverImageList')" :on-error="uploadError" :accept="fileType.join(',')" :on-remove="(file, fileList) => handleRemove('engHandoverImageList', fileList)" :on-preview="handlePreview" :disabled="!detailForm.editAuth" > <i class="el-icon-plus"></i> </el-upload> </el-form-item> <el-form-item label="课长:" v-if="detailForm.managerName !== null"> <el-input v-model="detailForm.managerName" readonly></el-input> </el-form-item> <el-form-item label="接班人:" v-if="detailForm.assigneeName !== null"> <el-input type="textarea" v-model="detailForm.assigneeName" readonly autosize></el-input> </el-form-item> </el-form> <template slot="footer"> <el-button type="primary" @click="save" v-if="detailForm.editAuth">保存</el-button> <el-button @click="closeDialog">取消</el-button> <el-button type="primary" @click="submit" v-if="detailForm.submitAuth">提交</el-button> <el-button type="primary" @click="audit" v-if="detailForm.auditAuth">审核</el-button> <el-button type="primary" @click="assign" v-if="detailForm.assignAuth">签核</el-button> </template> </el-dialog> <el-dialog :visible.sync="previewVisible" :show-close="false"> <img width="100%" :src="previewImageUrl" alt="Preview Image"/> </el-dialog> </div> </template> <script> import { getJournalForEdit, saveJournal, submitJournal, auditJournal, assignJournal, clearEditUser } from '@/api/mfgLog/area'; import request from '@/utils/request'; import {Message, MessageBox } from "element-ui"; import dayjs from 'dayjs'; import { getToken } from '@/utils/auth' export default { data() { return { loading: false, dialogVisible: false, title: '光罩厂制造部站点交接记录簿', fileType: ['image/jpeg', 'image/png', 'image/gif'], abnormalImageRef: null, engHandoverImageRef: null, productImageRef: null, previewVisible: false, previewImageUrl: '', lotList: [], eqpList: [], uploadInstances: {}, detailForm: { recordDate: '', workClass: '', workArea: '', abnormal: '', lotList: [], eqpList: [], abnormalImage: '', abnormalImageList: [], product: '', productImage: '', productImageList: [], engHandover: '', engHandoverImage: '', engHandoverImageList: [], managerName: '', assigneeName: '', editAuth: false, submitAuth: false, auditAuth: false, assignAuth: false, id: '' }, timer: null }; }, mounted() { // 监听事件以重置计时器 window.addEventListener('keydown', this.resetTimer); window.addEventListener('mousemove', this.resetTimer); window.addEventListener('mouseenter', this.resetTimer); // 浏览器关闭事件 window.addEventListener('beforeunload', (e) => { this.userInactive(); }); }, beforeDestroy() { // 清除事件监听和计时器 window.removeEventListener('keydown', this.resetTimer); window.removeEventListener('mousemove', this.resetTimer); window.removeEventListener('mouseenter', this.resetTimer); clearTimeout(this.timer); }, methods: { handleOpen() { this.resetTimer(); }, handleClose() { this.detailForm = { recordDate: '', workClass: '', workArea: '', abnormal: '', abnormalImage: '', abnormalImageList: [], product: '', productImage: '', productImageList: [], engHandover: '', engHandoverImage: '', engHandoverImageList: [], managerName: '', assigneeName: '', editAuth: false, submitAuth: false, auditAuth: false, assignAuth: false, id: '' }; this.lotList = []; this.eqpList = []; this.uploadInstances = {}; // Clear upload instances clearTimeout(this.timer); }, resetTimer() { // 用户进行了操作,重置计时器 if (this.timer) { clearTimeout(this.timer); } this.startTimer(); }, startTimer() { // 设置5分钟后的无操作处理 this.timer = setTimeout(() => { // 用户5分钟内无操作,执行相关逻辑 if (this.dialogVisible) { this.userInactive(); } }, 5 * 60 * 1000); }, userInactive() { // 用户处于非活跃状态的逻辑处理 if (this.detailForm.editAuth) { this.save(); } this.closeDialog(); }, showDialog(row) { this.loading = true; getJournalForEdit(row).then(res => { Object.assign(this.detailForm, res.data); // 显示提示信息 if (!this.detailForm.editAuth && !this.detailForm.assignAuth && this.detailForm.editUser) { Message.warning({ message: "当前日志正在被 " + this.detailForm.editUserName + " 编辑", position: 'top', // 确保消息显示在页面顶部 duration: 5000 // 消息显示时间为 5 秒 }); return 0; } this.detailForm.abnormalImageList = []; if (this.detailForm.abnormalImage?.length > 0) { let abnormalList = this.detailForm.abnormalImage.split(","); for (let index in abnormalList) { this.detailForm.abnormalImageList.push({ url: abnormalList[index] }); } } this.detailForm.productImageList = []; if (this.detailForm.productImage?.length > 0) { let productList = this.detailForm.productImage.split(","); for (let index in productList) { this.detailForm.productImageList.push({ url: productList[index] }); } } this.detailForm.engHandoverImageList = []; if (this.detailForm.engHandoverImage?.length > 0) { let engHandoverList = this.detailForm.engHandoverImage?.split(","); for (let index in engHandoverList) { this.detailForm.engHandoverImageList.push({ url: engHandoverList[index] }); } } if (this.detailForm.lotList && Array.isArray(this.detailForm.lotList)) { this.lotList = this.detailForm.lotList; } else { this.lotList = []; // 否则初始化为空数组 } if (this.detailForm.eqpList && Array.isArray(this.detailForm.eqpList)) { this.eqpList = this.detailForm.eqpList; } else { this.eqpList = []; // 否则初始化为空数组 } this.title = "光罩厂制造部 " + this.detailForm.workArea + " 站点交接记录簿"; // 判断当前时间是否允许提交/审核日志 if (this.detailForm.submitAuth || this.detailForm.auditAuth) { // 白班18点30分之前不允许提交和审核 let authTime = this.detailForm.recordDate + " 18:30:00"; if ('N' == this.detailForm.workClass.substring(0, 1)) { // 晚班06点30分之前不允许提交和审核 authTime = dayjs(this.detailForm.recordDate).add(1, 'day').format("YYYY-MM-DD") + " 06:30:00"; } if (dayjs().isBefore(dayjs(authTime))) { this.detailForm.submitAuth = false; this.detailForm.auditAuth = false; } } // 父系统页面打开遮罩 window.parent.postMessage("open", '*'); this.dialogVisible = true; // 设置初始计时器 this.loading = false; }).catch(error => { console.log(error); this.loading = false; }); }, closeDialog() { clearEditUser({ id: this.detailForm.id }).catch(error => { Message.error(error.msg ? error.msg : error); }); this.$emit('close'); // 父系统页面关闭遮罩 window.parent.postMessage("close", '*'); this.dialogVisible = false; }, save() { this.detailForm.abnormalImage = this.detailForm.abnormalImageList.map(item => item.url).join(","); this.detailForm.productImage = this.detailForm.productImageList.map(item => item.url).join(","); this.detailForm.engHandoverImage = this.detailForm.engHandoverImageList.map(item => item.url).join(","); this.loading = true; saveJournal(this.detailForm).then(res => { this.loading = false; Message.success("保存成功"); this.closeDialog(); }).catch(error => { this.loading = false; }); }, submit() { MessageBox.confirm("确认要提交吗?", "提示", { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning', }).then(res => { this.detailForm.abnormalImage = this.detailForm.abnormalImageList.map(item => item.url).join(","); this.detailForm.productImage = this.detailForm.productImageList.map(item => item.url).join(","); this.detailForm.engHandoverImage = this.detailForm.engHandoverImageList.map(item => item.url).join(","); this.loading = true; submitJournal(this.detailForm).then(res => { this.loading = false; Message.success("提交成功"); this.closeDialog(); }).catch(error => { this.loading = false; Message.error(error.msg ? error.msg : error); }); }).catch(error => { console.log(error); }); }, audit() { MessageBox.confirm("确认要提交审核吗?", "提示", { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning', }).then(res => { this.detailForm.abnormalImage = this.detailForm.abnormalImageList.map(item => item.url).join(","); this.detailForm.productImage = this.detailForm.productImageList.map(item => item.url).join(","); this.detailForm.engHandoverImage = this.detailForm.engHandoverImageList.map(item => item.url).join(","); this.detailForm.lotList = this.detailForm.lotList this.detailForm.eqpList = this.detailForm.eqpList this.loading = true; auditJournal(this.detailForm).then(res => { this.loading = false; Message.success("审核成功"); this.closeDialog(); }).catch(error => { this.loading = false; Message.error(error.msg ? error.msg : error); }); }).catch(error => { console.log(error); }); }, assign() { MessageBox.confirm("确认要签核吗?", "提示", { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning', }).then(res => { this.loading = true; assignJournal({ id: this.detailForm.id }).then(res => { this.loading = false; Message.success("签核成功"); this.closeDialog(); }).catch(error => { this.loading = false; Message.error(error.msg ? error.msg : error); }); }).catch(error => { console.log(error); }); }, handleHttpUpload(options) { const formData = new FormData(); formData.append('file', options.file); const controller = new AbortController(); const uid = options.file.uid; this.uploadInstances[uid] = controller; request.post(process.env.VUE_APP_BASE_API + '/viewException/uploadImage', formData, { headers: { 'Content-Type': 'multipart/form-data' }, signal: controller.signal }) .then(response => { options.onSuccess({ ...response.data, uid: options.file.uid }); }) .catch(error => { if (error.name === 'CanceledError') { console.log('Request canceled'); } else { options.onError(error); } }); }, handleRemove(listName, file) { // 找到对应的文件索引 const index = this.detailForm[listName].findIndex(item => item.uid === file.uid); if (index !== -1) { this.detailForm[listName].splice(index, 1); } this.handleRemoveFile(file); }, handleRemoveFile(file) { const uid = file.uid; if (this.uploadInstances[uid]) { this.uploadInstances[uid].abort(); delete this.uploadInstances[uid]; } }, headers(){ return { "Authorization": 'Bearer ' + getToken() } }, beforeUpload(rawFile) { const imgType = this.fileType.includes(rawFile.type); if (!imgType) { Message.warning({ title: '温馨提示', message: '上传图片不符合所需的格式!', }); } return imgType; }, uploadSuccess(imageListName) { Message.success({ title: '温馨提示', message: '图片上传成功!', }); this.detailForm[imageListName].forEach(item => { if (item.url.indexOf("/api/") < 0) item.url = item.response.url; }); }, uploadError() { Message.error({ title: '温馨提示', message: '图片上传失败,请您重新上传!', }); }, handlePaste(event, uploadRef) { const items = (event.clipboardData || window.clipboardData).items; let file = null; if (!items || items.length === 0) { Message.error("当前浏览器不支持粘贴板"); return; } // 搜索剪切板items for (let i = 0; i < items.length; i++) { if (items[i].type.indexOf("image") !== -1) { file = items[i].getAsFile(); break; } } if (!file) { return; } uploadRef.handleStart(file); // 将粘贴过来的图片加入预上传队列 uploadRef.submit(); // 提交图片上传队列 }, handlePreview(file) { this.previewImageUrl = file.url; this.previewVisible = true; } } }; </script> <style scoped> .layout-padding-custom { padding: 20px; } .border-vxe-table { margin-top: 20px; } </style> 其中 <el-input type="textarea" v-model="detailForm.engHandover" :autosize="{ minRows: 2 }" placeholder="请输入工程师交接事项 或 选中粘贴图片" @paste="handlePaste($event, engHandoverImageRef)" :readonly="!detailForm.editAuth" ></el-input>这些无法贴图贴
最新发布
08-09
<template> <el-main> <!-- 查询条件 --> <el-form :model="searchModel" ref="searchForm" label-width="80px" :inline="true" size="small" > <el-form-item> <el-input v-model="searchModel.name" placeholder="请输入姓名" style="width: 220px" ></el-input> </el-form-item> <el-form-item> <el-input v-model="searchModel.category" placeholder="请输入事项类别" style="width: 220px" ></el-input> </el-form-item> <!-- 审核状态下拉框查询条件 --> <el-form-item label="审核状态"> <el-select v-model="searchModel.auditStatus" placeholder="全部" style="width: 180px" @change="search()" > <el-option label="全部" value="" /> <el-option label="待审核" value="0" /> <el-option label="已初审" value="1" /> <el-option label="初审驳回" value="2" /> <el-option label="已终审" value="3" /> <el-option label="终审驳回" value="4" /> </el-select> </el-form-item> <el-form-item> <div style="display: flex; align-items: center; gap: 10px"> <!-- 查询/重置/新增按钮组 --> <div> <el-button type="primary" icon="el-icon-search" @click="search()" >查询</el-button > <el-button icon="el-icon-refresh-right" @click="resetValue()" >重置</el-button > <el-button type="success" icon="el-icon-plus" @click="openAddWindow()" >积分申请</el-button > </div> <!-- 导出/导入按钮组 --> <div style="display: flex; gap: 10px; margin-left: 10px"> <el-button type="primary" icon="el-icon-download" @click="exportExcel()" >导出Excel</el-button > <el-upload :action="importExcelUrl" :headers="uploadHeaders" :show-file-list="false" :on-success="importExcel" :before-upload="beforeUpload" > <el-button type="success" icon="el-icon-upload2" >导入Excel</el-button > </el-upload> </div> </div> </el-form-item> </el-form> <!-- 数据表格 --> <el-table :data="scoreDetailList" :height="tableHeight" border stripe class="el-table-ellipsis" style="width: 100%; margin-bottom: 10px" show-summary :summary-method="getSummaries" > <!-- 原有列保持不变 --> <el-table-column prop="id" label="序号" width="80" align="center" fixed="left" ></el-table-column> <el-table-column prop="recordDate" label="日期" width="120" header-align="center" align="center" fixed="left" ></el-table-column> <el-table-column prop="name" label="姓名" width="100" header-align="center" align="center" fixed="left" ></el-table-column> <!-- 新增图像列 --> <el-table-column prop="avatar" label="图像" width="120" align="center" header-align="center" > <template slot-scope="scope"> <el-image v-if="scope.row.avatar" style="width: 50px; height: 50px; border-radius: 10%" :src="getAvatarUrl(scope.row.avatar)" :preview-src-list="[getAvatarUrl(scope.row.avatar)]" @error="() => handleImageError(scope.row)" > <div slot="error" class="image-error" /> </el-image> </template> </el-table-column> <!-- 剩余列保持不变 --> <el-table-column prop="category" label="事项类别" width="180" header-align="center" ></el-table-column> <el-table-column prop="score" label="奖惩积分" width="120" header-align="center" align="center" ></el-table-column> <el-table-column prop="originalScore" label="原始分值" width="120" header-align="center" align="center" ></el-table-column> <el-table-column prop="executor" label="执行扣分人" width="100" header-align="center" align="center" ></el-table-column> <el-table-column prop="errorType" label="错误类型" width="180" header-align="center" ></el-table-column> <el-table-column prop="mistakeCount" label="犯错次数" width="80" header-align="center" align="center" ></el-table-column> <el-table-column prop="redEnvelope" label="红包" width="80" header-align="center" align="center" ></el-table-column> <el-table-column prop="details" label="具体事情" width="250" header-align="center" ></el-table-column> <el-table-column prop="creator" label="制单人" width="120" header-align="center" ></el-table-column> <el-table-column prop="createDate" label="制单日期" width="120" header-align="center" align="center" ></el-table-column> <el-table-column prop="remarks" label="备注" width="200" header-align="center" ></el-table-column> <el-table-column prop="auditStatus" label="审核状态" width="100" align="center" header-align="center" > <template slot-scope="scope"> {{ formatAuditStatus(scope.row.auditStatus) }} </template> </el-table-column> <el-table-column prop="firstAuditOpinion" label="初审意见" width="200" header-align="center" ></el-table-column> <el-table-column prop="finalAuditOpinion" label="终审意见" width="200" header-align="center" ></el-table-column> <!-- 操作列 --> <el-table-column label="操作" align="center" width="400" fixed="right"> <template slot-scope="scope"> <!-- 编辑按钮:仅待审核、初审驳回、终审驳回时可见 --> <el-button icon="el-icon-edit" type="primary" size="small" @click="handleEdit(scope.row)" :disabled="scope.row.auditStatus === 3" v-if="[0, 2, 4].includes(scope.row.auditStatus)" >编辑</el-button > <!-- 删除按钮:仅待审核、终审驳回时可见 --> <el-button icon="el-icon-delete" type="danger" size="small" @click="handleDelete(scope.row)" :disabled=" scope.row.auditStatus === 1 || scope.row.auditStatus === 3 " v-if="[0, 4].includes(scope.row.auditStatus)" >删除</el-button > <!-- 初审按钮:仅待审核、初审驳回时可见,已初审状态显示为浅蓝色 --> <el-button icon="el-icon-edit" :type="scope.row.auditStatus === 1 ? 'info' : 'primary'" size="small" @click="firstAudit(scope.row)" v-if=" hasPermission('api:scoreDetail:audit:save') && [0, 1, 2].includes(scope.row.auditStatus) " >初审</el-button > <!-- 终审按钮:仅初审通过、终审驳回时可见,已终审状态显示为浅蓝色 --> <el-button icon="el-icon-edit" :type="scope.row.auditStatus === 3 ? 'info' : 'success'" size="small" @click="finalAudit(scope.row)" v-if=" hasPermission('api:scoreDetail:audit:save') && [1, 3, 4].includes(scope.row.auditStatus) " >终审</el-button > <!-- 行内上传图像按钮 --> <el-upload :action="uploadFileUrl" :show-file-list="false" :headers="uploadHeaders" :before-upload="(file) => beforeAvatarUpload(file, scope.row)" :on-success=" (response, file, fileList) => handleRowAvatarUpload(scope.row, response) " :on-error=" (error, file, fileList) => handleAvatarUploadError(scope.row, error) " style="display: inline-flex; align-items: center" > <el-button type="primary" size="small" icon="el-icon-upload" >上传图像</el-button > </el-upload> </template> </el-table-column> </el-table> <!-- 分页工具栏 --> <el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="pageNum" :page-sizes="[10, 20, 30, 40, 50]" :page-size="10" layout="total, sizes, prev, pager, next, jumper" :total="total" ></el-pagination> <!-- 添加和修改分数详情窗口 --> <el-dialog :title="scoreDetailDialog.title" :visible.sync="scoreDetailDialog.visible" width="80%" > <el-form :model="scoreDetail" ref="scoreDetailForm" :rules="rules" label-width="80px" :inline="false" size="small" > <!-- 第1行:增加奖惩类型单选按钮 --> <el-row> <el-col :span="24"> <el-form-item label="奖惩类型"> <el-radio-group v-model="scoreType" @change="handleScoreTypeChange" > <el-radio label="reward">奖分</el-radio> <el-radio label="punish">扣分</el-radio> </el-radio-group> </el-form-item> </el-col> </el-row> <!-- 第2行 --> <el-row> <el-col :span="6"> <el-form-item label="日期" prop="recordDate"> <el-date-picker v-model="scoreDetail.recordDate" type="date" placeholder="选择日期" style="width: 180px" ></el-date-picker> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="姓名" prop="name"> <el-input v-model="scoreDetail.name" style="width: 150px" ></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="事项类别" prop="category"> <el-input v-model="scoreDetail.category" style="width: 180px" ></el-input> <el-button type="success" size="small" @click="openStandardDialog()" style="margin-left: 5px" > 积分标准 </el-button> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="奖惩积分" prop="score"> <el-input v-model.number="scoreDetail.score" style="width: 180px" ></el-input> </el-form-item> </el-col> </el-row> <!-- 第3行 --> <el-row> <!-- 左侧12列区域 - 跨两行显示积分标准 --> <el-col :span="12"> <el-form-item label="积分标准" prop="standard"> <!-- 使用文本域并设置合适的高度,使其视觉上占据两行空间 --> <el-input v-model="scoreDetail.standard" type="textarea" :rows="5" style="width: 100%;" placeholder="请输入积分标准" ></el-input > </el-form-item> </el-col> <!-- 仅扣分时显示 --> <el-col :span="4" v-if="scoreType === 'punish'"> <el-form-item label="原始分值" prop="originalScore"> <el-input v-model.number="scoreDetail.originalScore" style="width: 180px" ></el-input> </el-form-item> </el-col> <el-col :span="4" v-if="scoreType === 'punish'"> <el-form-item label="犯错次数" prop="mistakeCount"> <el-input v-model.number="scoreDetail.mistakeCount" style="width: 100%" ></el-input> </el-form-item> </el-col> <el-col :span="4" v-if="scoreType === 'punish'"> <el-form-item label="红包" prop="redEnvelope"> <el-input v-model.number="scoreDetail.redEnvelope" style="width: 100%" ></el-input> </el-form-item> </el-col> </el-row> <!-- 第4行 --> <el-row> <el-col :span="12"> </el-col> <!-- 仅扣分时显示 --> <el-col :span="8" v-if="scoreType === 'punish'"> <el-form-item label="执行扣分人" prop="executor"> <el-input v-model="scoreDetail.executor" style="width: 200px" @click.native="openPersonSelectDialog" :readonly="true" ></el-input> </el-form-item> </el-col> <el-col :span="4" v-if="scoreType === 'punish'"> <el-form-item label="错误类型" prop="errorType"> <el-input v-model="scoreDetail.errorType" style="width: 180px" @click.native="openErrorTypeDialog" :readonly="true" ></el-input> </el-form-item> </el-col> </el-row> <!-- 第5行 --> <el-row> <el-col :span="12"> <el-form-item label="具体事情" prop="details"> <el-input v-model="scoreDetail.details" type="textarea" :rows="4" style="width: 100%" ></el-input> </el-form-item> </el-col> <el-col :span="12"> <!-- 图像容器:整体居中 --> <div style=" display: flex; flex-direction: column; align-items: center; height: 100%; " > <!-- 上传按钮:在图像上方居中 --> <el-form-item label="图像" prop="avatar" style="width: 100%; text-align: center; margin-bottom: 15px" > <el-upload :action="uploadFileUrl" :show-file-list="false" :headers="uploadHeaders" :before-upload="beforeAvatarUpload" :on-success="handleFormAvatarUpload" :on-error="handleAvatarUploadError" style="display: inline-block" > <el-button type="primary" size="small" icon="el-icon-upload" >上传图像</el-button > </el-upload> </el-form-item> <!-- 图像预览区:居中显示 --> <div style=" flex: 1; display: flex; align-items: center; justify-content: center; width: 100%; " > <el-image v-if="scoreDetail.avatar" style="width: 120px; height: 120px; border-radius: 10%" :src="getAvatarUrl(scoreDetail.avatar)" :preview-src-list="[getAvatarUrl(scoreDetail.avatar)]" @error="handleFormImageError" > <div slot="error" class="image-error" style=" width: 120px; height: 120px; display: flex; align-items: center; justify-content: center; " /> </el-image> <!-- 无图像时的占位符:居中显示 --> <div v-else style=" width: 120px; height: 120px; border-radius: 10%; background-color: #f5f5f5; display: flex; align-items: center; justify-content: center; color: #ccc; font-size: 14px; " > 暂无图像 </div> </div> </div> </el-col> </el-row> <!-- 第6行 --> <el-row> <el-col :span="6"> <el-form-item label="制单人" prop="creator"> <el-input v-model="scoreDetail.creator" style="width: 180px" ></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="制单日期" prop="createDate"> <el-date-picker v-model="scoreDetail.createDate" type="date" placeholder="选择日期" style="width: 180px" ></el-date-picker> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="备注" prop="remarks"> <el-input v-model="scoreDetail.remarks" style="width: 100%" ></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="审核状态" prop="auditStatus"> <!-- 显示汉字状态而非数字 --> <el-input v-model="currentAuditStatusText" style="width: 100px" disabled ></el-input> </el-form-item> </el-col> </el-row> <!-- 审核区域 --> <div v-if="['初审', '终审'].includes(scoreDetailDialog.title)"> <el-row> <el-col :span="8"> <el-form-item label="审核意见" prop="auditOpinion" :rules="getAuditOpinionRules()" > <el-input v-model="scoreDetail.auditOpinion" type="textarea" :rows="2" style="width: 100%" ></el-input> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="审核人员"> <el-input v-model="currentAuditUser" disabled style="width: 100%" ></el-input> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="审核时间"> <el-input v-model="currentAuditTime" disabled style="width: 100%" ></el-input> </el-form-item> </el-col> <el-col :span="8"> <el-form-item> <el-button type="success" @click="handleAuditApprove" >审核通过</el-button > <el-button @click="handleAuditCancel">取消审核</el-button> <el-button type="danger" @click="handleAuditReject" >审核驳回</el-button > </el-form-item> </el-col> </el-row> </div> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="scoreDetailDialog.visible = false">取消</el-button> <el-button type="primary" @click="onConfirm" v-if=" ['新增分数详情', '编辑分数详情'].includes(scoreDetailDialog.title) " >确定</el-button > </div> </el-dialog> <!-- 积分标准选择窗口 --> <el-dialog :title="standardSelectDialog.title" :visible.sync="standardSelectDialog.visible" width="60%" > <!-- 保持不变 --> <el-form :model="standardSearchModel" ref="standardSearchForm" label-width="80px" :inline="true" size="small" style="margin-bottom: 10px" > <el-form-item label="事项类别"> <el-input v-model="standardSearchModel.category" placeholder="请输入事项类别" style="width: 220px" ></el-input> </el-form-item> <el-form-item label="积分标准"> <el-input v-model="standardSearchModel.standard" placeholder="请输入积分标准" style="width: 220px" ></el-input> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" @click="searchStandards()" >查询</el-button > </el-form-item> </el-form> <el-table :data="standardList" :height="standardTableHeight" border stripe class="el-table-ellipsis" style="width: 100%; margin-bottom: 10px" > <el-table-column prop="id" label="序号" width="80" align="center" fixed="left" ></el-table-column> <el-table-column prop="category" label="事项类别" width="200" header-align="center" ></el-table-column> <el-table-column prop="standard" label="事项标准" header-align="center" ></el-table-column> <el-table-column prop="score" label="分值" width="120" header-align="center" ></el-table-column> <el-table-column prop="remarks" label="备注" header-align="center" ></el-table-column> <!-- 操作列 --> <el-table-column label="操作" align="center" width="120" fixed="right"> <template slot-scope="scope"> <el-button icon="el-icon-check" type="success" size="small" @click="selectStandard(scope.row)" >选择</el-button > </template> </el-table-column> </el-table> <!-- 积分标准选择窗口分页工具栏 --> <el-pagination v-if="standardTotal > 0" background @size-change="handleStandardSizeChange" @current-change="handleStandardCurrentChange" :current-page="standardPageNum" :page-sizes="[10, 20, 30, 40, 50]" :page-size="standardPageSize" layout="total, sizes, prev, pager, next, jumper" :total="standardTotal" ></el-pagination> <div slot="footer" class="dialog-footer"> <el-button @click="standardSelectDialog.visible = false" >取消</el-button > </div> </el-dialog> <!-- 新增:选择执行扣分人对话框 --> <el-dialog :title="personSelectDialog.title" :visible.sync="personSelectDialog.visible" width="20%" > <el-form :model="personSearchModel" ref="personSearchForm" label-width="80px" :inline="true" size="small" style="margin-bottom: 10px" > <el-form-item label="人员姓名"> <el-input v-model="personSearchModel.name" placeholder="请输入人员姓名" style="width: 220px" ></el-input> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" @click="searchPersons()" >查询</el-button > </el-form-item> </el-form> <el-table :data="personList" height="400" border stripe class="el-table-ellipsis" style="width: 100%; margin-bottom: 10px" > <el-table-column prop="id" label="序号" width="80" align="center" ></el-table-column> <el-table-column prop="name" label="姓名" width="120" header-align="center" > <template slot-scope="scope"> <el-button type="text" @click="selectPerson(scope.row)"> {{ scope.row.name }} </el-button> </template> </el-table-column> <!-- 操作列 --> </el-table> <div slot="footer" class="dialog-footer"> <el-button @click="personSelectDialog.visible = false">取消</el-button> </div> </el-dialog> <!-- 新增:错误类型选择对话框 --> <el-dialog :title="errorTypeDialog.title" :visible.sync="errorTypeDialog.visible" width="30%" > <el-form :model="errorTypeSearchModel" ref="errorTypeSearchForm" label-width="80px" :inline="true" size="small" style="margin-bottom: 10px" > <el-form-item label="错误类型"> <el-input v-model="errorTypeSearchModel.name" placeholder="请输入错误类型" style="width: 220px" ></el-input> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" @click="searchErrorTypes()" >查询</el-button > </el-form-item> </el-form> <el-table :data="errorTypeList" height="400" border stripe style="width: 100%; margin-bottom: 10px" > <el-table-column prop="id" label="序号" width="80" align="center" ></el-table-column> <el-table-column prop="name" label="错误类型" header-align="center"> <!-- 修改:点击文本即可选择 --> <template slot-scope="scope"> <el-button type="text" @click="selectErrorType(scope.row)"> {{ scope.row.name }} </el-button> </template> </el-table-column> </el-table> <div slot="footer" class="dialog-footer"> <el-button @click="errorTypeDialog.visible = false">取消</el-button> </div> </el-dialog> </el-main> </template> <script> import { getScoreDetails, addScoreDetail, updateScoreDetail, deleteScoreDetail, getErrorCount, getScoreErrorTypes, } from "@/api/company/hr/scoreDetail"; import { getScoreStandards } from "@/api/company/hr/scoreStandard"; import { getScorePersons } from "@/api/company/hr/scorePerson"; import { getToken, getUsername } from "@/utils/auth"; import axios from "axios"; export default { name: "scoreDetailList", data() { return { // 新增:奖惩类型,默认奖分 scoreType: "reward", // 错误类型对话框相关数据 errorTypeDialog: { title: "选择错误类型", visible: false, }, errorTypeSearchModel: { errorType: "", pageNum: 1, pageSize: 10, }, errorTypeList: [], errorTypePageNum: 1, errorTypePageSize: 10, errorTypeTotal: 0, // 当前审核记录在列表中的索引(用于自动跳转) currentAuditIndex: -1, // 审核状态映射(数字转汉字) auditStatusMap: { 0: "待审核", 1: "已初审", 2: "初审驳回", 3: "已终审", 4: "终审驳回", }, // 查询条件 searchModel: { name: "", category: "", auditStatus: "", // 审核状态:空-全部,0-待审核,1-已初审,2-初审驳回,3-已终审,4-终审驳回 pageNum: 1, pageSize: 10, }, // 表格数据 scoreDetailList: [], tableHeight: 0, pageNum: 1, pageSize: 10, total: 0, // 分数详情表单 scoreDetail: { id: "", recordDate: new Date(), name: this.$store.getters.name, // 当前登录用户 category: "", score: "", originalScore: "", executor: "", errorType: "", mistakeCount: "", redEnvelope: "", details: "", creator: "", createDate: new Date(), remarks: "", auditStatus: 0, auditOpinion: "", firstAuditUser: "", firstAuditTime: "", finalAuditUser: "", finalAuditTime: "", avatar: "", // 图像字段 }, // 积分标准选择对话框数据 standardSelectDialog: { title: "选择积分标准", visible: false, }, standardSearchModel: { category: "", standard: "", pageNum: 1, pageSize: 10, }, standardList: [], standardTableHeight: 0, standardPageNum: 1, standardPageSize: 10, standardTotal: 0, // 表单验证规则 rules: { name: [{ required: true, trigger: "blur", message: "请输入姓名" }], recordDate: [ { required: true, trigger: "blur", message: "请选择日期" }, ], category: [ { required: true, trigger: "blur", message: "请输入事项类别" }, ], score: [{ required: true, trigger: "blur", message: "请输入奖惩积分" }], }, // 对话框 scoreDetailDialog: { title: "", visible: false, }, // 导入导出相关 uploadHeaders: { token: "" }, importExcelUrl: process.env.VUE_APP_BASE_API + "api/scoreDetail/import/easyExcel", // 图像上传相关配置 uploadFileUrl: process.env.VUE_APP_BASE_API + "files/upload", // 图像上传接口 uploadAvatarFileUrl: process.env.VUE_APP_BASE_API + "files/", // 图像显示基础路径 // 审核相关数据 currentAuditType: "", // 'first' 或 'final' currentAuditUser: "", currentAuditTime: "", currentAuditAction: "", // 'approve' 或 'reject' // 审核状态控制 auditStatusControl: { firstAudit: { enabled: true, // 始终启用初审 status: 0, // 0:待审核, 1:已初审, 2:初审驳回 }, finalAudit: { enabled: true, // 始终启用终审 status: 0, // 0:待终审, 1:已终审, 2:终审驳回 }, }, // 新增:选择执行扣分人对话框数据 personSelectDialog: { title: "选择执行扣分人", visible: false, }, personSearchModel: { name: "", pageNum: 1, pageSize: 10, }, personList: [], personTotal: 0, personPageNum: 1, personPageSize: 10, }; }, computed: { currentToken() { return getToken(); }, currentUsername() { return this.$store.getters.name; }, // 计算属性:当前审核状态文本 currentAuditStatusText() { const statusMap = { 0: "待审核", 1: "已初审", 2: "初审驳回", 3: "已终审", 4: "终审驳回", }; // 处理新增时的默认状态 return this.scoreDetail.id ? statusMap[this.scoreDetail.auditStatus] || "未知状态" : "待审核"; }, }, watch: { currentToken(newVal) { this.uploadHeaders.token = newVal; }, // 监听对话框标题变化,更新审核相关数据 "scoreDetailDialog.title"(newVal) { if (newVal === "初审") { this.currentAuditType = "first"; this.currentAuditUser = this.scoreDetail.firstAuditUser || this.currentUsername; this.currentAuditTime = this.scoreDetail.firstAuditTime; this.scoreDetail.auditOpinion = this.scoreDetail.firstAuditOpinion || ""; // 更新审核状态控制 this.auditStatusControl.firstAudit.status = this.scoreDetail.auditStatus; } else if (newVal === "终审") { this.currentAuditType = "final"; this.currentAuditUser = this.scoreDetail.finalAuditUser || this.currentUsername; this.currentAuditTime = this.scoreDetail.finalAuditTime; this.scoreDetail.auditOpinion = this.scoreDetail.finalAuditOpinion || ""; // 更新审核状态控制 this.auditStatusControl.finalAudit.status = this.scoreDetail.auditStatus; } }, }, methods: { // 新增:表格合计方法 getSummaries(param) { const { columns, data } = param; const sums = []; columns.forEach((column, index) => { if (index === 0) { // 第一列显示"合计" sums[index] = '合计'; return; } // 只对奖惩积分列进行合计 if (column.property === 'score') { const values = data.map(item => Number(item.score)); if (!values.every(value => isNaN(value))) { const sum = values.reduce((prev, curr) => { const value = Number(curr); return isNaN(value) ? prev : prev + value; }, 0); // 格式化合计值(保留两位小数,千分位分隔) sums[index] = sum.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } else { sums[index] = ''; } } else { // 其他列不显示合计 sums[index] = ''; } }); return sums; }, // 新增:处理奖惩类型变更 handleScoreTypeChange(val) { // 当切换为奖分时清空扣分相关字段 if (val === "reward") { this.scoreDetail.originalScore = ""; this.scoreDetail.mistakeCount = ""; this.scoreDetail.redEnvelope = ""; this.scoreDetail.executor = ""; this.scoreDetail.errorType = ""; } }, // 打开错误类型对话框 openErrorTypeDialog() { this.errorTypeSearchModel = { errorType: "", pageNum: 1, pageSize: 10, }; this.searchErrorTypes(); this.errorTypeDialog.visible = true; }, // 查询错误类型列表 async searchErrorTypes() { try { const res = await getScoreErrorTypes({ ...this.errorTypeSearchModel, pageNum: this.errorTypePageNum, pageSize: this.errorTypePageSize, }); if (res.code === 200) { this.errorTypeList = res.data.rows || []; this.errorTypeTotal = res.data.total || 0; } else { this.$message.error(res.message || "查询错误类型失败"); } } catch (error) { this.$message.error("查询错误类型请求异常"); } }, // 选择错误类型 - 简化逻辑 async selectErrorType(row) { // 直接使用row.name作为错误类型值 this.scoreDetail.errorType = row.name; this.errorTypeDialog.visible = false; // 强制触发视图更新 this.$forceUpdate(); // 自动更新犯错次数 await this.updateMistakeCount(); }, // 更新犯错次数 async updateMistakeCount() { if ( this.scoreDetail.recordDate && this.scoreDetail.name && this.scoreDetail.errorType ) { try { // 从日期中提取年份 const year = new Date(this.scoreDetail.recordDate).getFullYear(); // 调用API获取错误次数 const res = await getErrorCount({ name: this.scoreDetail.name, year: year, errorType: this.scoreDetail.errorType, }); if (res.code === 200) { // 使用$set确保响应式更新 const newCount = (res.data || 0) + 1; this.$set(this.scoreDetail, "mistakeCount", newCount); // 再次强制更新,确保视图同步 this.$nextTick(() => { this.$forceUpdate(); }); } else { this.$message.error(res.message || "获取错误次数失败"); } } catch (error) { console.error("获取错误次数请求异常:", error); this.$message.error( `获取错误次数失败: ${error.message || "网络错误"}` ); } } else { this.$message.warning("请先填写日期、姓名和错误类型"); } }, // 日期格式化辅助函数 formatDate(date) { const d = new Date(date); let month = "" + (d.getMonth() + 1); let day = "" + d.getDate(); const year = d.getFullYear(); if (month.length < 2) month = "0" + month; if (day.length < 2) day = "0" + day; return [year, month, day].join("-"); }, // 新增:打开选择执行扣分人对话框 openPersonSelectDialog() { this.personSearchModel = { name: "", pageNum: 1, pageSize: 10, }; this.searchPersons(); this.personSelectDialog.visible = true; }, // 新增:查询执行扣分人列表 async searchPersons() { try { const res = await getScorePersons({ ...this.personSearchModel, pageNum: this.personPageNum, pageSize: this.personPageSize, }); if (res.code === 200) { this.personList = res.data.rows || []; this.personTotal = res.data.total || 0; } else { this.$message.error(res.message || "查询人员列表失败"); } } catch (error) { this.$message.error("查询人员列表请求异常"); } }, // 新增:选择执行扣分人 selectPerson(row) { this.scoreDetail.executor = row.name; this.personSelectDialog.visible = false; }, // 获取审核意见的验证规则 getAuditOpinionRules() { return this.currentAuditAction === "reject" ? { required: true, message: "请输入驳回意见", trigger: "blur" } : []; }, // 打开初审窗口 firstAudit(row) { this.scoreDetailDialog.title = "初审"; this.scoreDetailDialog.visible = true; this.scoreDetail = { ...row }; // 记录当前索引 this.currentAuditIndex = this.scoreDetailList.findIndex( (item) => item.id === row.id ); // 初始化当前审核人 this.currentAuditUser = this.currentUsername; this.currentAuditType = "first"; // 更新审核状态控制 this.auditStatusControl.firstAudit.status = this.scoreDetail.auditStatus; }, // 打开终审窗口 finalAudit(row) { // 允许对任何状态记录进行终审操作 this.scoreDetailDialog.title = "终审"; this.scoreDetailDialog.visible = true; this.scoreDetail = { ...row }; this.currentAuditIndex = this.scoreDetailList.findIndex( (item) => item.id === row.id ); this.currentAuditUser = this.currentUsername; this.currentAuditType = "final"; // 更新审核状态控制 this.auditStatusControl.finalAudit.status = this.scoreDetail.auditStatus; }, // 审核通过 async handleAuditApprove() { this.currentAuditAction = "approve"; // 设置当前操作为通过 this.$refs.scoreDetailForm.validate(async (valid) => { if (!valid) return; const now = new Date(); const auditTime = now.toISOString().slice(0, 19).replace("T", " "); if (this.currentAuditType === "first") { this.scoreDetail.auditStatus = 1; this.scoreDetail.firstAuditUser = this.currentUsername; this.scoreDetail.firstAuditTime = auditTime; this.scoreDetail.firstAuditOpinion = this.scoreDetail.auditOpinion; this.auditStatusControl.firstAudit.status = 1; // 更新状态为已初审 } else { this.scoreDetail.auditStatus = 3; this.scoreDetail.finalAuditUser = this.currentUsername; this.scoreDetail.finalAuditTime = auditTime; this.scoreDetail.finalAuditOpinion = this.scoreDetail.auditOpinion; this.auditStatusControl.finalAudit.status = 1; // 更新状态为已终审 } await this.saveAuditInfo(); this.$message.success(`${this.scoreDetailDialog.title}通过`); this.jumpToNextAuditRecord(this.currentAuditType === "first" ? 0 : 1); }); }, // 取消审核 async handleAuditCancel() { if (this.currentAuditType === "first") { this.scoreDetail.auditStatus = 0; this.scoreDetail.firstAuditOpinion = ""; this.scoreDetail.firstAuditTime = null; this.auditStatusControl.firstAudit.status = 0; // 更新状态为待审核 } else { // 终审取消,回到初审通过状态 this.scoreDetail.auditStatus = 1; this.scoreDetail.finalAuditOpinion = ""; this.scoreDetail.finalAuditTime = null; this.auditStatusControl.finalAudit.status = 0; // 更新状态为待终审 } await this.saveAuditInfo(); this.$message.success(`已取消${this.scoreDetailDialog.title}`); this.scoreDetailDialog.visible = false; }, // 审核驳回 async handleAuditReject() { this.currentAuditAction = "reject"; // 设置当前操作为驳回 this.$refs.scoreDetailForm.validate(async (valid) => { if (!valid) return; const now = new Date(); const auditTime = now.toISOString().slice(0, 19).replace("T", " "); if (this.currentAuditType === "first") { this.scoreDetail.auditStatus = 2; this.scoreDetail.firstAuditUser = this.currentUsername; this.scoreDetail.firstAuditTime = auditTime; this.scoreDetail.firstAuditOpinion = this.scoreDetail.auditOpinion; this.auditStatusControl.firstAudit.status = 2; // 更新状态为初审驳回 } else { this.scoreDetail.auditStatus = 4; this.scoreDetail.finalAuditUser = this.currentUsername; this.scoreDetail.finalAuditTime = auditTime; this.scoreDetail.finalAuditOpinion = this.scoreDetail.auditOpinion; this.auditStatusControl.finalAudit.status = 2; // 更新状态为终审驳回 } await this.saveAuditInfo(); this.$message.success(`${this.scoreDetailDialog.title}驳回`); this.jumpToNextAuditRecord(this.currentAuditType === "first" ? 0 : 1); }); }, // 保存审核信息到后端 async saveAuditInfo() { try { const res = await updateScoreDetail(this.scoreDetail); if (res.code === 200) { this.search(); // 刷新列表 } else { this.$message.error(res.message); } } catch (error) { this.$message.error("审核操作失败"); } }, // 自动跳转到下一个符合条件的记录 jumpToNextAuditRecord(targetStatus) { const nextIndex = this.scoreDetailList.findIndex( (item, index) => index > this.currentAuditIndex && item.auditStatus === targetStatus ); if (nextIndex > -1) { const nextRecord = this.scoreDetailList[nextIndex]; this.scoreDetailDialog.visible = false; // 根据目标状态决定打开初审还是终审 targetStatus === 0 ? this.firstAudit(nextRecord) : this.finalAudit(nextRecord); } else { this.scoreDetailDialog.visible = false; this.$message.info("已无符合条件的下一条记录"); } }, // 表格列格式化审核状态 formatAuditStatus(status) { return this.auditStatusMap[status] || "未知状态"; }, // 初始化组件时获取用户名 initUserInfo() { this.scoreDetail.name = this.currentUsername; this.scoreDetail.creator = this.currentUsername; }, // 分数详情列表查询 async search() { try { const res = await getScoreDetails(this.searchModel); if (res.code === 200) { this.scoreDetailList = res.data.rows || []; this.total = res.data.total || 0; } else { this.$message.error(res.message || "查询失败"); } } catch (error) { this.$message.error("请求异常"); } }, // 重置查询条件 resetValue() { this.searchModel = { name: "", category: "", auditStatus: "", // 重置为全部 pageNum: 1, pageSize: 10, }; this.search(); }, // 分页事件处理 handleSizeChange(size) { this.searchModel.pageSize = size; this.search(); }, handleCurrentChange(page) { this.searchModel.pageNum = page; this.search(); }, // 打开新增窗口(优化:确保表单完全重置) openAddWindow() { // 先销毁表单验证状态,再重置字段 if (this.$refs.scoreDetailForm) { this.$refs.scoreDetailForm.clearValidate(); // 清除所有验证状态 this.$refs.scoreDetailForm.resetFields(); // 重置字段值 } // 初始化新增表单数据 this.scoreType = "reward"; // 默认奖分 this.scoreDetail = { recordDate: new Date(), createDate: new Date(), name: this.currentUsername, creator: this.currentUsername, auditStatus: 0, category: "", score: "", avatar: "", }; this.scoreDetailDialog.title = "新增分数详情"; this.scoreDetailDialog.visible = true; }, // 编辑分数详情 handleEdit(row) { this.scoreDetail = { ...row }; // 设置奖惩类型 this.scoreType = row.originalScore || row.mistakeCount || row.redEnvelope ? "punish" : "reward"; // 日期格式转换 if (this.scoreDetail.recordDate) { this.scoreDetail.recordDate = new Date(this.scoreDetail.recordDate); } if (this.scoreDetail.createDate) { this.scoreDetail.createDate = new Date(this.scoreDetail.createDate); } this.scoreDetailDialog.title = "编辑分数详情"; this.scoreDetailDialog.visible = true; }, // 打开积分标准选择对话框 openStandardDialog() { this.standardSearchModel = { category: "", standard: "", pageNum: 1, pageSize: 10, }; this.searchStandards(); this.standardSelectDialog.visible = true; }, // 查询积分标准 async searchStandards() { try { const res = await getScoreStandards({ ...this.standardSearchModel, pageNum: this.standardPageNum, pageSize: this.standardPageSize, }); if (res.code === 200) { this.standardList = res.data.rows || []; this.standardTotal = res.data.total || 0; } else { this.$message.error(res.message || "查询积分标准失败"); } } catch (error) { this.$message.error("查询积分标准请求异常"); } }, // 积分标准分页大小变更 handleStandardSizeChange(size) { this.standardPageSize = size; this.standardSearchModel.pageSize = size; this.searchStandards(); }, // 积分标准分页页码变更 handleStandardCurrentChange(page) { this.standardPageNum = page; this.standardSearchModel.pageNum = page; this.searchStandards(); }, // 选择积分标准 selectStandard(row) { // 给事项类别、积分标准、奖惩积分赋值 this.scoreDetail.category = row.category; this.scoreDetail.standard = row.standard; this.scoreDetail.score = row.score; // 手动触发表单验证 if (this.$refs.scoreDetailForm) { this.$refs.scoreDetailForm.validateField(["category", "score"]); } // 关闭选择窗口 this.standardSelectDialog.visible = false; }, // 保存分数详情 async onConfirm() { this.$refs.scoreDetailForm.validate(async (valid) => { if (!valid) return; try { const res = this.scoreDetail.id ? await updateScoreDetail(this.scoreDetail) : await addScoreDetail(this.scoreDetail); if (res.code === 200) { this.$message.success(res.message); this.scoreDetailDialog.visible = false; this.search(); } else { this.$message.error(res.message); } } catch (error) { this.$message.error("操作失败"); } }); }, // 删除分数详情 async handleDelete(row) { try { await this.$confirm("确定删除该分数详情?", "提示", { type: "warning", }); const res = await deleteScoreDetail(row.id); if (res.code === 200) { this.$message.success(res.message); this.search(); } else { this.$message.error(res.message); } } catch (error) { // 取消删除 } }, // 导出Excel async exportExcel() { try { const instance = axios.create({ baseURL: process.env.VUE_APP_BASE_API, headers: { token: getToken() }, responseType: "blob", }); const response = await instance.get( "/api/scoreDetail/export/easyExcel", { params: { name: this.searchModel.name, }, } ); const fileName = response.headers["content-disposition"] ? decodeURIComponent( response.headers["content-disposition"].split("filename=")[1] ).replace(/"/g, "") : "scoreDetails_export.xls"; const link = document.createElement("a"); link.href = URL.createObjectURL(new Blob([response.data])); link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(link.href); } catch (error) { this.handleExportError(error); } }, // 导入Excel importExcel(response) { if (response.code === 200) { this.search(); this.$message.success("导入成功"); } else { this.$message.error(response.message || "导入失败"); } }, // 导入文件校验 beforeUpload(file) { const allowedTypes = [ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.ms-excel", ]; if (!allowedTypes.includes(file.type)) { this.$message.error("仅支持 .xls 或 .xlsx 格式文件"); return false; } return true; }, // 导出错误处理 handleExportError(error) { if (error.response?.status === 401) { this.$message.error("登录已过期,请重新登录"); this.$store.dispatch("user/logout"); this.$router.push("/login"); } else if (error.response?.data instanceof Blob) { const reader = new FileReader(); reader.onload = () => { try { const errorData = JSON.parse(reader.result); this.$message.error(errorData.message || "导出失败"); } catch { this.$message.error("导出文件解析失败"); } }; reader.readAsText(error.response.data); } else { this.$message.error(error.message || "导出失败"); } }, // 图像上传相关方法 // 获取图像完整URL getAvatarUrl(avatar) { if (!avatar) return ""; const timestamp = Date.now(); return `${this.uploadAvatarFileUrl}${encodeURIComponent(avatar)}?token=${ this.currentToken }&t=${timestamp}`; }, // 处理图片加载错误 handleImageError(row) { console.error("图片加载失败:", row.avatar); // 简单的错误处理 this.$set(row, "avatar", ""); }, // 表单图片加载错误处理 handleFormImageError() { console.error("表单图片加载失败"); this.scoreDetail.avatar = ""; }, // 图像上传前验证 beforeAvatarUpload(file, row) { const isImage = file.type.startsWith("image/"); const isLt5M = file.size / 1024 / 1024 < 5; if (!isImage) { this.$message.error("只能上传图片文件!"); } if (!isLt5M) { this.$message.error("图像图片大小不能超过5MB!"); } return isImage && isLt5M; }, // 处理表单中的图像上传成功 handleFormAvatarUpload(response) { if (response.code === 200) { this.scoreDetail.avatar = response.data; this.$message.success("图像上传成功"); } else { this.$message.error(response.message || "图像上传失败"); } }, // 处理行内图像上传成功 handleRowAvatarUpload(row, response) { if (response.code === 200) { // 更新当前行的图像信息 this.$set(row, "avatar", response.data); this.$message.success("图像上传成功"); // 同步更新到后端 const updateData = { id: row.id, avatar: response.data, }; updateScoreDetail(updateData) .then((res) => { if (res.code !== 200) { this.$message.warning("图像信息同步失败"); } }) .catch(() => { this.$message.warning("图像信息同步失败"); }); } else { this.$message.error(response.message || "图像上传失败"); } }, // 处理图像上传失败 handleAvatarUploadError(row, error) { console.error("图像上传失败:", error); this.$message.error("图像上传失败,请重试"); }, }, created() { this.search(); }, mounted() { this.uploadHeaders.token = getToken(); this.initUserInfo(); this.$nextTick(() => { this.tableHeight = window.innerHeight - 220; this.standardTableHeight = window.innerHeight - 300; }); }, }; </script> <style> /* 全局单元格样式 */ .el-table-ellipsis .cell { white-space: nowrap !important; /* 强制不换行 */ overflow: hidden !important; text-overflow: ellipsis !important; } /* 禁用操作列的省略号效果 */ .el-table-ellipsis .no-ellipsis .cell { white-space: normal !important; /* 恢复默认换行 */ overflow: visible !important; text-overflow: clip !important; } /* 可选:表头对齐方式 */ .el-table-ellipsis th > .cell { display: block; /* 解决表头与内容列宽度轻微错位问题 */ } .dialog-footer { display: flex; justify-content: flex-end; gap: 10px; } /* 图片错误占位样式 */ .image-error { width: 100%; height: 100%; background-color: #f5f5f5; display: flex; align-items: center; justify-content: center; color: #ccc; } .image-error::after { content: "图片错误"; font-size: 12px; } /* 新增:合计行样式 */ .el-table .el-table__footer .cell { font-weight: bold; color: #409EFF; } </style> 报错: vue.runtime.esm.js:620 [Vue warn]: Property or method "showSettings1" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties. found in ---> <Layout> at src/layout/index.vue <App> at src/App.vue <Root> 6 vue.runtime.esm.js:620 [Vue warn]: Property or method "formatNumber" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties. found in ---> <ScoreTotal> at src/views/company/hr/scoreTotal.vue <AppMain> at src/layout/components/AppMain.vue <Layout> at src/layout/index.vue <App> at src/App.vue <Root> 合计数没有展示出来,优化代码
08-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值