<template> <div class="project-list"> <div class="search-bar"> <el-form :inline="true" :model="searchForm" class="demo-form-inline" size="small" > <el-form-item label="任务名称" class="form-item"> <el-input v-model="searchForm.taskName" placeholder="任务名称" style="width: 300px;" ></el-input> </el-form-item> <el-form-item label="任务状态" class="form-item"> <el-select v-model="searchForm.taskStatus" placeholder="状态" clearable style="width: 300px;" > <el-option v-for="item in statusList" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <el-form-item label="任务年份" class="form-item"> <el-date-picker v-model="searchForm.year" type="year" style="width: 300px" placeholder="选择年份" value-format="yyyy" > </el-date-picker> </el-form-item> <el-form-item class="search-buttons"> <el-button type="primary" @click="onSearch">查询</el-button> <el-button @click="onReset">重置</el-button> </el-form-item> </el-form> </div> <div class="table-actions mb10"> <el-button type="primary" size="mini" @click="addTask" style="height: 36px" >+ 新增任务</el-button > </div> <div class="f1 df"> <CustomTable :columns="columns" :tableData="tableData" :total="total" :show-selection="false" :show-index="true" @size-change="handleSizeChange" @current-change="handleCurrentChange" tableHeight="495px" > <template slot="operation" slot-scope="{ row }"> <div class="operation-buttons"> <el-button @click="editTask(row)" type="text" size="mini" >编辑</el-button > <el-button v-if="row.taskStatus == 0" type="text" size="mini" @click="setTask(row)" >指标配置</el-button > <el-button type="text" size="mini" @click="delTask(row)" >删除</el-button > </div> </template> </CustomTable> </div> <SelectUser :dialogVisible="userSelectDialogVisible" :currentSelectedUser="currentSelectedUser" :currentSelectedUserName="currentSelectedUserName" :showSelection="true" :highligt="false" :selectable="selectable" ref="selectUserRef" @confirm="handleUserConfirm" @close="handleUserClose" /> <el-dialog :title="isEdit ? '编辑考核任务' : '新增考核任务'" :visible.sync="dialogVisible1" width="25%" > <div> <el-form :model="taskData" size="small" ref="taskFormRef" :rules="rules" label-width="100px" > <el-form-item label="任务名称" class="form-item" prop="taskName"> <el-input v-model="taskData.taskName" placeholder="0/20" style="width: 90%" ></el-input> </el-form-item> <el-form-item label="考核人员" class="form-item" prop="peopleNumberDetail" > <el-input v-model="taskData.peopleNumberDetail" placeholder="考核人员" readonly style="width: 90%" @click.native="openUserSelectDialog" ><el-button slot="append" icon="el-icon-s-custom"></el-button ></el-input> </el-form-item> <el-form-item label="截止时间" class="form-item" prop="endTime"> <el-date-picker v-model="taskData.endTime" type="date" style="width: 90%" placeholder="选择日期" value-format="yyyy-MM-dd 23:59:59" :picker-options="{ disabledDate: disabledDate, }" > </el-date-picker> </el-form-item> <el-form-item label="年份" class="form-item" prop="year"> <el-date-picker v-model="taskData.year" type="year" style="width: 90%" placeholder="选择年份" value-format="yyyy" :picker-options="{ disabledDate: disabledDate, }" > </el-date-picker> </el-form-item> </el-form> </div> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible1 = false">取 消</el-button> <el-button type="primary" @click="saveTask">确 定</el-button> </span> </el-dialog> <el-dialog title="配置指标和权重" :visible.sync="dialogVisible2"> <div class="modal"> <div class="left"> <div class="setText" style="font-weight: 600">累计权重</div> <el-collapse v-model="letfValue" :accordion="true" style="height: 300px; overflow: auto" > <el-collapse-item v-for="(item, index) in scoreList" :key="index" :name="item.title" > <template #title> <div class="jcsb flex-row contentTitle"> <span class="setTitle">{{ item.title }}</span> <span class="statusText">{{ item.weight }}%</span> </div> </template> <div> <div v-for="(ele, index) in item.list" :key="index" class="flex-row jcsb leftSub" :class="{ selectClass: selectLeftRow == ele.title + ele.type, }" @click="selectLeft(ele)" > <div>{{ ele.title }}</div> <div class="statusText">{{ ele.weight }}%</div> </div> </div> </el-collapse-item> </el-collapse> <div class="flex-row jcsb" style="margin-top: 20px; padding-right: 15px" > <div class="setTitle" style="font-weight: 600">总计</div> <div class="statusText"> {{ scoreList.reduce((total, ele) => total + (ele.weight || 0), 0) }}% </div> </div> </div> <div class="right"> <div class="flex-row jcsb" style="margin-bottom: 10px"> <div style="width: 50%;font-weight: 600">指标</div> <div class="center" style="width: 50%;font-weight: 600">权重占比</div> </div> <div> <div v-for="(item, index) in ( ( (scoreList.find((ele) => ele.title == letfValue) || {}) .list || [] ).find((ele) => ele.title + ele.type == selectLeftRow) || {} ).rightArr || []" :key="index" style="margin-bottom: 10px" class="flex-row jcsb aic" > <div style="width: 50%; font-weight: 600"> {{ item.reviewItem }} </div> <div class="center" style="width: 50%"> <div class="flex-row jcsb" style=" margin-left: 10px; width: 100%; color: #999; font-weight: 500; font-size: 14px; font-weight: 600; " > <div>0</div> <div v-show="item.weight != 20">20</div> </div> <div class="scoreText aic" v-show="item.weight != 0" :style="{ left: item.weight * 4.5 - 2.5 + '%', }" > <img src="@/assets/task/score.png" :style="{ height: '28px', width: '34px', zIndex: 0, position: 'absolute', top: '-2px', }" alt="" /> <div :style="{ zIndex: 10, textIndent: item.weight < 10 ? '13px' : '10px', fontSize: '12px', }" > {{ item.weight }} </div> </div> <!-- <div class="flex-row jcsb scoreBox" style="margin-left: 15px"> <div>0%</div> <div class="scoreText">{{ item.weight }}%</div> <div>20%</div> </div> --> <div v-for="item in item.weight == 0 ? 0 : item.weight - 1" :key="item" class="stepBox" :style="{ left: item * 5 + 4 + '%', }" ></div> <el-slider v-model="item.weight" :max="20" @change="changeTotal" ></el-slider> </div> </div> </div> </div> </div> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible2 = false">取消</el-button> <el-button type="primary" @click="saveSet">保存</el-button> </span> </el-dialog> </div> </template> <script> import CustomTable from "@/components/CustomTable.vue"; import SelectUser from "@/components/SelectUser.vue"; import { taskApi } from "@/utils/api"; export default { components: { CustomTable, SelectUser, }, data() { return { searchForm: { taskName: "", taskStatus: "", year: "", }, selectLeftRow: "", columns: [ { prop: "taskName", label: "任务名称" }, { prop: "peopleNumber", label: "考核人数" }, { prop: "taskStatus", label: "任务状态", type: "status", callback: (value) => { if (value == 2) var color = "#999"; else var color = "#4096FF"; return `<span style="color: ${color}">${ value ? "已过期" : "进行中" }</span>`; }, }, { prop: "year", label: "年份", }, { prop: "createTime", label: "创建时间", type: "status", callback: (value) => { return value.split(" ")[0]; }, }, { prop: "endTime", label: "截至时间", type: "status", callback: (value) => { return value.split(" ")[0]; }, }, { prop: "operation", label: "操作", width: "250", className: "operation-column", }, ], tableData: [], total: 0, userSelectDialogVisible: false, currentSelectedUser: [], currentSelectedUserName: [], pageNum: 1, // 当前页码 pageSize: 10, // 每页条数 statusList: [ { label: "全部", value: "" }, { label: "进行中", value: "0" }, { label: "已过期", value: "2" }, ], dialogVisible1: false, isEdit: false, taskData: { id: "", taskName: "", peopleNumberDetail: "", userIdList: [], endTime: "", peopleNumber: 0, year: "", }, rules: { taskName: [ { required: true, message: "请输入活动名称", trigger: "blur" }, { min: 1, max: 20, message: "长度限制20个字符", trigger: "blur" }, ], peopleNumberDetail: [ { required: true, message: "请选择考核人员", trigger: "change" }, ], endTime: [ { required: true, message: "请选择截止时间", trigger: "blur" }, ], year: [{ required: true, message: "请选择年份", trigger: "blur" }], }, dialogVisible2: false, letfValue: "", scoreList: [], selectRow: {}, }; }, watch: { dialogVisible1(newVal) { if (newVal) { this.$nextTick(() => { this.$refs.selectUserRef?.$refs.customTableRef?.handleCurrentChange( 1 ); }); } }, }, methods: { onSearch() { this.getTaskList(); }, onReset() { Object.keys(this.searchForm).forEach((key) => { this.searchForm[key] = ""; }); this.getTaskList(); }, disabledDate(date) { const startDate = new Date().getTime(); // return new Date(date).getTime() + 60 * 60 * 24 * 1000 < startDate; // 禁用范围外的日期 }, getTaskList() { taskApi .getTaskList({ ...this.searchForm, pageNum: this.pageNum, pageSize: this.pageSize, }) .then((res) => { this.tableData = res.rows; this.total = res.total; }); }, handleSizeChange(size) { this.pageSize = size; this.pageNum = 1; // 重置为第一页 this.getTaskList(); }, handleCurrentChange(page) { this.pageNum = page; this.getTaskList(); }, openUserSelectDialog() { this.userSelectDialogVisible = true; this.$nextTick(() => { this.$refs.selectUserRef?.$refs.customTableRef?.clearSelection(); this.currentSelectedUser = []; this.currentSelectedUserName = []; (this.selectRow.userIdList || []).forEach((ele) => { this.currentSelectedUser.push(ele); }); (this.selectRow.peopleNumberDetail || "").split(",").forEach((ele) => { if (ele) this.currentSelectedUserName.push(ele); }); }); }, handleUserConfirm(data) { this.taskData.peopleNumberDetail = data .map((ele) => ele.nickName) .join(","); this.taskData.userIdList = data.map((ele) => ele.userId); this.taskData.peopleNumber = data.length; this.selectRow.userIdList = data.map((ele) => ele.userId); this.selectRow.peopleNumberDetail = data .map((ele) => ele.nickName) .join(","); }, handleUserClose() { this.userSelectDialogVisible = false; }, addTask() { this.isEdit = false; this.dialogVisible1 = true; this.taskData = { id: "", taskName: "", peopleNumberDetail: "", userIdList: [], endTime: "", peopleNumber: 0, }; this.selectRow = {}; }, editTask(row) { this.isEdit = true; this.dialogVisible1 = true; this.taskData.id = row.id; this.taskData.taskName = row.taskName; this.taskData.peopleNumber = row.peopleNumber; this.taskData.peopleNumberDetail = row.peopleNumberDetail; this.taskData.userIdList = row.userIdList; this.taskData.endTime = row.endTime; this.taskData.year = String(row.year); this.selectRow = row; }, setTask(row) { taskApi.getTaskSet(row.id).then((res) => { let objData = {}; res.data.forEach((ele) => { if (!objData[ele.reviewType]) objData[ele.reviewType] = []; objData[ele.reviewType].push(ele); }); let arrList = {}; Object.keys(objData).forEach((ele) => { let arr = []; objData[ele].forEach((item) => { if (!arr[item.reviewCategory]) arr[item.reviewCategory] = []; arr[item.reviewCategory].push(item); }); arrList[ele] = arr; }); this.scoreList = [ { title: "组长评估绩效指标", list: Object.keys(arrList["0"]).map((ele) => ({ title: ele, weight: arrList["0"][ele].reduce( (total, item) => (item.weight || 0) + total, 0 ), rightArr: arrList["0"][ele], type: 0, })), weight: Object.values(arrList["0"]).reduce( (total, item) => (item.reduce((total, item) => (item.weight || 0) + total, 0) || 0) + total, 0 ), }, { title: "个人自评绩效指标", list: Object.keys(arrList["1"]).map((ele) => ({ title: ele, weight: arrList["1"][ele].reduce( (total, item) => (item.weight || 0) + total, 0 ), rightArr: arrList["1"][ele], type: 1, })), weight: Object.values(arrList["1"]).reduce( (total, item) => (item.reduce((total, item) => (item.weight || 0) + total, 0) || 0) + total, 0 ), }, { title: "系统核算绩效指标", list: Object.keys(arrList["2"]).map((ele) => ({ title: ele, weight: arrList["2"][ele].reduce( (total, item) => (item.weight || 0) + total, 0 ), rightArr: arrList["2"][ele], type: 2, })), weight: Object.values(arrList["2"]).reduce( (total, item) => (item.reduce((total, item) => (item.weight || 0) + total, 0) || 0) + total, 0 ), }, ]; this.dialogVisible2 = true; }); }, saveSet() { if ( this.scoreList.reduce((total, ele) => total + (ele.weight || 0), 0) !== 100 ) { this.$message({ type: "warning", message: "累计权重值必须为100%,请调整后再试", }); } else { let examineConfigList = []; this.scoreList .map((ele) => ele.list) .flat() .map((ele) => ele.rightArr) .flat() .forEach((ele) => { examineConfigList.push({ id: ele.id, weight: ele.weight }); }); taskApi .setTaskSet({ examineConfigList, taskId: this.id }) .then((res) => { this.$message({ type: "success", message: "操作成功", }); this.dialogVisible2 = false; }); } }, delTask(row) { this.$confirm( "删除任务及绩效数据,该操作不可逆,请谨慎操作", "确认删除任务", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", } ).then(() => { taskApi.delTask(row.id).then((res) => { this.$message({ type: "success", message: "删除成功!", }); this.getTaskList(); }); }); }, selectLeft(row) { this.selectLeftRow = row.title + row.type; }, saveTask() { this.$refs.taskFormRef.validate((valid) => { if (valid) { if (this.taskData.id) { taskApi.upDateTask(this.taskData).then((res) => { this.$message({ type: "success", message: "修改成功!", }); this.dialogVisible1 = false; this.getTaskList(); }); } else { taskApi.addTask(this.taskData).then((res) => { this.$message({ type: "success", message: "新增成功!", }); this.dialogVisible1 = false; this.getTaskList(); }); } } else { console.log("error submit!!"); return false; } }); }, changeTotal(data) { this.$nextTick(() => { this.scoreList.forEach((ele) => { ele.list.forEach((item) => { item.weight = item.rightArr.reduce( (total, ele) => total + (ele.weight || 0), 0 ); }); ele.weight = ele.list.reduce( (total, ele) => total + (ele.weight || 0), 0 ); }); }); }, selectable(row,index){ if(row.roles.find((ele)=>ele.roleName=='普通员工')){ return true }else{ return false } } }, mounted() { this.getTaskList(); }, }; </script> <style lang="scss" scoped> .project-list { padding: 20px; background-color: white; height: 88vh; box-sizing: border-box; overflow: hidden; display: flex; flex-direction: column; } .search-bar { margin-bottom: 20px; } .demo-form-inline { // display: flex; // flex-wrap: nowrap; // align-items: flex-start; } .demo-form-inline .el-form-item { // margin-right: 50px; /* 将间距设置为 30px */ // margin-bottom: 0; } .demo-form-inline .el-form-item:last-child { margin-right: 0; /* 移除最后一个元素的右边距 */ } .form-item { flex: 1; } .form-item ::v-deep .el-form-item__content { // width: 100%; } .form-item ::v-deep .el-input, .form-item ::v-deep .el-select { // width: 100%; } .search-buttons { white-space: nowrap; } ::v-deep .operation-buttons .el-button { padding: 4px 8px; margin: 0 2px; } ::v-deep .operation-column { background-color: #ffffff; box-shadow: -2px 0 5px rgba(241, 112, 6, 0.1); } .el-button.is-text { min-width: 32px !important; } .dialog-footer { display: flex; justify-content: center; align-items: center; } .dialog-footer .el-button { margin: 0 10px; } .delete-content { display: flex; align-items: center; justify-content: center; text-align: center; } .warning-icon { font-size: 24px; color: #e6a23c; margin-right: 10px; } /* 添加以下样式来使对话框垂直居中 */ ::v-deep .delete-dialog.el-dialog { margin-top: 0 !important; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } ::v-deep .el-table th { text-align: center; } ::v-deep .el-table .cell { text-align: center; } ::v-deep .el-dialog { margin-top: 10% !important; } .modal { display: flex; border-bottom: 1px solid #ccc; border-top: 1px solid #ccc; } .left { width: 40%; padding: 20px; border-right: 1px solid #ccc; height: 450px; overflow: auto; } .right { width: 60%; padding: 20px 40px; height: 450px; overflow: auto; div.center { text-align: center; position: relative; } } .scoreBox { position: absolute; top: 45px; right: -20px; } ::v-deep .el-slider__runway { height: 14px; border-radius: 10px; margin: 10px !important; /* width: 95%; */ } ::v-deep .el-slider__runway.disabled .el-slider__bar { height: 14px; border-radius: 10px; background-color: #ff5722; } ::v-deep .el-slider__stop { height: 14px; border-radius: 0; z-index: 1000; } ::v-deep .el-slider__bar { height: 14px; border-radius: 10px; background: linear-gradient(to right, #ffb144, #ff7d00); } ::v-deep .el-slider__button { display: none; } .scoreText { color: #1686d8; } .setText { margin-bottom: 20px; text-align: right; } .leftSub { padding: 10px 20px 10px 10px; margin-bottom: 5px; cursor: pointer; } .setTitle { font-weight: bold; } ::v-deep .el-collapse-item__content { padding-bottom: 10px; } ::v-deep .el-collapse-item__header { } .contentTitle { } .statusText { color: #ff5722; } ::v-deep .el-collapse { border: none !important; } .search-buttons ::v-deep .el-button { width: 90px !important; height: 36px; } ::v-deep .operation-buttons .el-button { padding: 4px 8px; margin: 0 2px; font-weight: 600; font-size: 14px; } .selectClass { background-color: #4096ff; color: #fff; } .scoreText { color: #fff; position: absolute; top: 2px; font-weight: 500; display: flex; flex-direction: row; width: 40px; } .block { width: 4px; height: 24px; background-color: #4096ff; margin-right: 10px; } .stepBox { position: absolute; top: 29px; height: 14px; width: 2px; background-color: #fff; z-index: 100; } .totalBox { width: 150px; display: flex; flex-direction: row; font-size: 14px; color: #333333; } </style>