Compare commits
31 Commits
Author | SHA1 | Date |
---|---|---|
|
5de5b930bf | |
|
e63e98399a | |
|
c66e0e5f06 | |
|
5757b4d23c | |
|
b3bd2eff61 | |
|
b1251e3bc5 | |
|
bc1c74ce55 | |
|
659108a7ac | |
|
cf743c0dd6 | |
|
da50ed138f | |
|
f9a402e991 | |
|
6032c814e2 | |
|
338b1ef8bd | |
|
5327adc0ea | |
|
6160798087 | |
|
e7a69e87f8 | |
|
3a7e373394 | |
|
829acc5857 | |
|
74de4ef76c | |
|
8d02e76518 | |
|
7532d5cf47 | |
|
434a20f0a6 | |
|
43492eb5d2 | |
|
4d62c1baba | |
|
0392aab0e1 | |
|
35a32950a7 | |
|
bfa923b3ac | |
|
c8bb8ef089 | |
|
90867530d3 | |
|
5f860e2fed | |
|
ecacb4dc02 |
|
@ -21,3 +21,4 @@ selenium-debug.log
|
|||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
/npm
|
||||
|
|
After Width: | Height: | Size: 606 B |
After Width: | Height: | Size: 775 B |
After Width: | Height: | Size: 804 B |
After Width: | Height: | Size: 241 B |
After Width: | Height: | Size: 338 B |
After Width: | Height: | Size: 1000 B |
After Width: | Height: | Size: 352 B |
|
@ -1,5 +1,4 @@
|
|||
@use 'sass:math';
|
||||
|
||||
@use "sass:math";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
@ -9,7 +8,7 @@
|
|||
-webkit-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
font-family: AliPuHui;
|
||||
font-family: PingFang SC;
|
||||
}
|
||||
$lightThemColor: #3686ff;
|
||||
html {
|
||||
|
@ -105,11 +104,6 @@ body {
|
|||
align-items: flex-start;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//外边距
|
||||
.ml10 {
|
||||
margin-left: 0.1rem;
|
||||
|
@ -179,7 +173,6 @@ body {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
/* 竖向弹性盒子 */
|
||||
.flex-col {
|
||||
@include flex-row-vc;
|
||||
|
@ -256,7 +249,6 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// dialog弹出给body加了该类名,导致页面右侧偏移
|
||||
.el-popup-parent--hidden {
|
||||
width: 100% !important;
|
||||
|
@ -286,7 +278,6 @@ body {
|
|||
border-radius: 0.1rem;
|
||||
}
|
||||
|
||||
|
||||
:deep(.el-form-item__content) {
|
||||
align-items: flex-start !important;
|
||||
}
|
||||
|
@ -306,7 +297,6 @@ body {
|
|||
padding-right: 15px;
|
||||
}
|
||||
|
||||
|
||||
:deep(.el-button--primary:not(.is-text)) {
|
||||
background-color: #5584ff !important ;
|
||||
color: #fff !important;
|
||||
|
@ -322,3 +312,8 @@ body {
|
|||
:deep(.el-dialog--center .el-dialog__body) {
|
||||
padding: 32px !important;
|
||||
}
|
||||
|
||||
.search-buttons {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
}
|
||||
|
|
|
@ -289,3 +289,5 @@
|
|||
position: relative;
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
|
|
After Width: | Height: | Size: 223 B |
After Width: | Height: | Size: 1000 B |
After Width: | Height: | Size: 14 KiB |
|
@ -9,10 +9,19 @@
|
|||
:border="border"
|
||||
:highlight-current-row="highligt"
|
||||
@row-click="rowClick"
|
||||
@sort-change="sortChange"
|
||||
:row-key="rowKey"
|
||||
:height="tableHeight"
|
||||
@select="selected"
|
||||
@select-all="selectAll"
|
||||
>
|
||||
<el-table-column v-if="showSelection" type="selection" width="55" />
|
||||
<el-table-column
|
||||
reserve-selection
|
||||
v-if="showSelection"
|
||||
type="selection"
|
||||
width="55"
|
||||
:selectable="selectable"
|
||||
/>
|
||||
<el-table-column v-if="showIndex" type="index" width="50" label="序号" />
|
||||
<template>
|
||||
<el-table-column
|
||||
|
@ -21,6 +30,7 @@
|
|||
:label="column.label"
|
||||
v-for="(column, index) in columns"
|
||||
:key="index"
|
||||
:sortable="column.sortable"
|
||||
>
|
||||
<template #default="scope">
|
||||
<slot :name="column.prop" :row="scope.row">
|
||||
|
@ -146,6 +156,10 @@ export default {
|
|||
type: String,
|
||||
default: "100%",
|
||||
},
|
||||
selectable:{
|
||||
type: Function,
|
||||
default: ()=>true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -170,7 +184,7 @@ export default {
|
|||
this.$emit("current-change", val);
|
||||
},
|
||||
setCurrentRow(row) {
|
||||
this.$refs.elTableRef.setCurrentRow(row);
|
||||
this.$refs.elTableRef?.setCurrentRow(row);
|
||||
},
|
||||
clearSelection() {
|
||||
this.$refs.elTableRef?.clearSelection();
|
||||
|
@ -178,12 +192,22 @@ export default {
|
|||
toggleRowSelection(row, selected) {
|
||||
this.$refs.elTableRef?.toggleRowSelection(row, selected);
|
||||
},
|
||||
sortChange(data) {
|
||||
this.$emit("sortChange", data);
|
||||
},
|
||||
selected(arr, row) {
|
||||
this.$emit("selected", { arr, row });
|
||||
},
|
||||
selectAll(arr) {
|
||||
this.$emit("selectAll", arr);
|
||||
},
|
||||
},
|
||||
updated() {
|
||||
if (this.$refs.elTableRef && this.$refs.elTableRef.doLayout) {
|
||||
this.$refs.elTableRef.doLayout();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {},
|
||||
beforeDestroy() {},
|
||||
};
|
||||
|
@ -208,12 +232,17 @@ export default {
|
|||
--el-table-text-align: center;
|
||||
width: 100%;
|
||||
flex: none;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
::v-deep .el-table th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
::v-deep .operation-column .el-button-text {
|
||||
font-weight: 600 !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
::v-deep .el-table .cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -239,4 +268,7 @@ export default {
|
|||
::v-deep .el-table {
|
||||
max-height: 99%;
|
||||
}
|
||||
::v-deep .el-table .el-table__body .el-table__cell {
|
||||
padding: 15px 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,29 +1,71 @@
|
|||
<template>
|
||||
<el-dialog title="选择人员" :visible="dialogVisible" width="50%" @close="handleClose">
|
||||
<el-dialog
|
||||
title="选择人员"
|
||||
:visible="dialogVisible"
|
||||
width="50%"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="select-user-container">
|
||||
<div class="org-tree">
|
||||
<el-tree :data="treeData" :props="defaultProps" @node-click="handleNodeClick" default-expand-all />
|
||||
<el-tree
|
||||
:data="treeData"
|
||||
:props="defaultProps"
|
||||
@node-click="handleNodeClick"
|
||||
default-expand-all
|
||||
/>
|
||||
</div>
|
||||
<div class="user-list">
|
||||
<div class="flex-row aic jcfe mb10" style="gap: 10px">
|
||||
<el-input
|
||||
v-model="searchForm.nickName"
|
||||
style="width: 300px"
|
||||
placeholder="输入名称"
|
||||
/>
|
||||
<div>
|
||||
<el-button type="primary" size="medium" @click="fetchUserList"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button type="primary" size="medium" @click="resetTable"
|
||||
>重置</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CustomTable
|
||||
ref="customTableRef"
|
||||
:columns="columns"
|
||||
:tableData="userData"
|
||||
:total="total"
|
||||
:show-selection="true"
|
||||
:show-selection="showSelection"
|
||||
:show-index="true"
|
||||
:table-height="tableHeight"
|
||||
:multiSelect="multiSelect"
|
||||
:selectable="selectable"
|
||||
@selection-change="handleSelectionChange"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
@selected="selectRow"
|
||||
@selectAll="selectAll"
|
||||
rowKey="userId"
|
||||
:rowClick="
|
||||
(row) => {
|
||||
rowClick(row);
|
||||
}
|
||||
"
|
||||
maxHeight="380"
|
||||
:highligt="highligt"
|
||||
>
|
||||
<template slot="operation" slot-scope="{ row }">
|
||||
<div class="operation-buttons">
|
||||
<el-button text type="primary" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button text type="primary" @click="showTimesheet(row)">工作日志</el-button>
|
||||
<el-button text type="danger" @click="handleDelete(row)">删除</el-button>
|
||||
<el-button text type="primary" @click="handleEdit(row)"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button text type="primary" @click="showTimesheet(row)"
|
||||
>工作日志</el-button
|
||||
>
|
||||
<el-button text type="danger" @click="handleDelete(row)"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</CustomTable>
|
||||
|
@ -39,8 +81,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import CustomTable from '@/components/CustomTable.vue'
|
||||
import { systemApi } from '@/utils/api'
|
||||
import CustomTable from "@/components/CustomTable.vue";
|
||||
import { systemApi } from "@/utils/api";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -53,12 +95,37 @@ export default {
|
|||
},
|
||||
dialogVisible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: true,
|
||||
},
|
||||
currentSelectedUser: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
currentSelectedUserName: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
showSelection: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
highligt: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
selectable: {
|
||||
type: Function,
|
||||
default: () => true,
|
||||
},
|
||||
userIdList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
isFilter: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -66,106 +133,229 @@ export default {
|
|||
pageSize: 10,
|
||||
total: 0,
|
||||
selectedUsers: [],
|
||||
currentDepartment: '',
|
||||
tableHeight: 350, // 设置一个合适的高度
|
||||
currentDepartment: "",
|
||||
tableHeight: "350", // 设置一个合适的高度
|
||||
treeData: [],
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'label',
|
||||
children: "children",
|
||||
label: "label",
|
||||
},
|
||||
columns: [
|
||||
{ prop: 'nickName', label: '姓名' },
|
||||
{ prop: 'dept', label: '部门', type: 'status', callback: (data) => data?.deptName },
|
||||
{ prop: 'roles', label: '角色', type: 'status', callback: (data) => data.map(ele => ele.roleName).join(',') },
|
||||
{ prop: "nickName", label: "姓名" },
|
||||
{
|
||||
prop: "dept",
|
||||
label: "部门",
|
||||
type: "status",
|
||||
callback: (data) => data?.deptName,
|
||||
},
|
||||
{
|
||||
prop: "roles",
|
||||
label: "角色",
|
||||
type: "status",
|
||||
callback: (data) => data.map((ele) => ele.roleName).join(","),
|
||||
},
|
||||
],
|
||||
userData: [],
|
||||
customTableRef: null,
|
||||
isInternalChange: false,
|
||||
}
|
||||
selectAllData: [],
|
||||
searchForm: {
|
||||
nickName: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
emits: [ 'close', 'confirm'],
|
||||
emits: ["close", "confirm"],
|
||||
methods: {
|
||||
handleNodeClick(data) {
|
||||
this.currentDepartment = data.id
|
||||
this.currentPage = 1
|
||||
this.fetchUserList()
|
||||
this.currentDepartment = data.id;
|
||||
this.currentPage = 1;
|
||||
this.fetchUserList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.currentPage = val
|
||||
this.fetchUserList()
|
||||
this.currentPage = val;
|
||||
this.fetchUserList();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.pageSize = val
|
||||
this.fetchUserList()
|
||||
this.pageSize = val;
|
||||
this.fetchUserList();
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit('close')
|
||||
this.$emit("close");
|
||||
},
|
||||
handleConfirm() {
|
||||
this.$emit('confirm', this.selectedUsers)
|
||||
this.handleClose()
|
||||
if (!this.showSelection) this.$emit("confirm", this.selectedUsers);
|
||||
else
|
||||
this.$emit(
|
||||
"confirm",
|
||||
this.selectedUsers.map((ele, index) => ({
|
||||
userId: ele.userId,
|
||||
nickName: ele.nickName,
|
||||
}))
|
||||
);
|
||||
|
||||
this.handleClose();
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
if (this.isInternalChange) return
|
||||
|
||||
if (this.isInternalChange) return;
|
||||
if (!this.multiSelect) {
|
||||
this.isInternalChange = true
|
||||
this.isInternalChange = true;
|
||||
this.$nextTick(() => {
|
||||
if (val.length > 0) {
|
||||
const lastSelected = val[val.length - 1]
|
||||
this.selectedUsers = [lastSelected]
|
||||
this.$refs.customTableRef.clearSelection()
|
||||
this.$refs.customTableRef.toggleRowSelection(lastSelected, true)
|
||||
const lastSelected = val[val.length - 1];
|
||||
this.selectedUsers = [lastSelected];
|
||||
this.$refs.customTableRef.clearSelection();
|
||||
this.$refs.customTableRef.toggleRowSelection(lastSelected, true);
|
||||
} else {
|
||||
this.selectedUsers = []
|
||||
this.selectedUsers = [];
|
||||
}
|
||||
this.isInternalChange = false
|
||||
})
|
||||
this.isInternalChange = false;
|
||||
});
|
||||
} else {
|
||||
this.selectedUsers = val
|
||||
// this.selectedUsers = val;
|
||||
}
|
||||
},
|
||||
selectRow({ arr, row }) {
|
||||
if (!row) return;
|
||||
if (this.selectedUsers.filter((ele) => ele.userId == row.userId).length) {
|
||||
this.selectedUsers = this.selectedUsers.filter(
|
||||
(ele) => ele.userId != row.userId
|
||||
);
|
||||
} else {
|
||||
this.selectedUsers.push({ userId: row.userId, nickName: row.nickName });
|
||||
}
|
||||
},
|
||||
selectAll(arr) {
|
||||
let filterArr = this.selectAllData.filter(
|
||||
(ele) => !arr.some((item) => item.userId == ele.userId)
|
||||
);
|
||||
|
||||
arr.forEach((ele) => {
|
||||
if (
|
||||
!this.selectedUsers.filter((item) => item.userId == ele.userId).length
|
||||
)
|
||||
this.selectRow({ row: ele });
|
||||
});
|
||||
filterArr.forEach((ele) => {
|
||||
this.selectRow({ row: ele });
|
||||
});
|
||||
},
|
||||
fetchUserList: async function () {
|
||||
const response = await systemApi.getUserList({
|
||||
pageNum: this.currentPage,
|
||||
pageSize: this.pageSize,
|
||||
deptId: this.currentDepartment,
|
||||
})
|
||||
this.userData = response.rows
|
||||
this.total = response.total
|
||||
...this.searchForm,
|
||||
userIdList:this.userIdList,
|
||||
});
|
||||
this.userData = response.rows;
|
||||
this.total = response.total;
|
||||
if (!this.multiSelect) {
|
||||
this.userData.forEach((ele) => {
|
||||
if (ele.userId == this.selectedUsers[0]?.userId)
|
||||
this.$refs.customTableRef?.setCurrentRow(ele);
|
||||
});
|
||||
}
|
||||
},
|
||||
resetTable() {
|
||||
this.searchForm.nickName = "";
|
||||
this.fetchUserList();
|
||||
},
|
||||
fetchTreeData: async function () {
|
||||
const response = await systemApi.getDeptTree()
|
||||
this.treeData = response.data
|
||||
const response = await systemApi.getDeptTree();
|
||||
this.treeData = response.data;
|
||||
},
|
||||
currentRow(val) {
|
||||
this.selectedUsers = [val];
|
||||
},
|
||||
rowClick(row) {
|
||||
if (!this.showSelection) {
|
||||
if (row.userId == this.selectedUsers[0]?.userId)
|
||||
this.$refs.customTableRef.$refs.elTableRef.setCurrentRow();
|
||||
else this.selectedUsers = [row];
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentSelectedUser: {
|
||||
handler(newVal) {
|
||||
this.isInternalChange = true
|
||||
this.$nextTick(() => {
|
||||
this.selectedUsers = newVal
|
||||
if (this.$refs.customTableRef) {
|
||||
this.$refs.customTableRef.clearSelection()
|
||||
newVal.forEach(user => {
|
||||
const row = this.userData.find(item => item.userId === user.userId)
|
||||
if (row) {
|
||||
this.$refs.customTableRef.toggleRowSelection(row, true)
|
||||
}
|
||||
})
|
||||
if (!this.showSelection) {
|
||||
let row = this.userData.find(
|
||||
(ele) => ele.userId == newVal[0]?.userId
|
||||
);
|
||||
if (row) {
|
||||
this.$refs.customTableRef?.setCurrentRow(row);
|
||||
this.selectedUsers = [row];
|
||||
} else {
|
||||
this.selectedUsers = [];
|
||||
this.$refs.customTableRef?.setCurrentRow();
|
||||
}
|
||||
} else {
|
||||
if (!newVal.length) {
|
||||
this.selectedUsers = [];
|
||||
this.$refs.customTableRef?.clearSelection();
|
||||
} else {
|
||||
// this.$refs.customTableRef?.clearSelection();
|
||||
this.selectedUsers = [];
|
||||
newVal.forEach((item, index) => {
|
||||
this.selectedUsers.push({
|
||||
userId: item,
|
||||
nickName: this.currentSelectedUserName[index],
|
||||
});
|
||||
let row = this.userData.find((ele) => ele.userId == item);
|
||||
if (row) {
|
||||
this.selectAllData.push(row);
|
||||
this.$refs.customTableRef?.toggleRowSelection(row, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
this.isInternalChange = false
|
||||
})
|
||||
});
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
userData: {
|
||||
handler(newVal) {
|
||||
this.$nextTick(() => {
|
||||
if (!this.showSelection) {
|
||||
let row = this.userData.find(
|
||||
(ele) => ele.userId == this.currentSelectedUser[0]?.userId
|
||||
);
|
||||
if (row) {
|
||||
this.selectedUsers = [row];
|
||||
this.$refs.customTableRef?.setCurrentRow(row);
|
||||
}
|
||||
} else {
|
||||
this.selectAllData = [];
|
||||
this.currentSelectedUser.forEach((item) => {
|
||||
let row = newVal.find((ele) => ele.userId == item);
|
||||
if (row) {
|
||||
this.selectAllData.push(row);
|
||||
|
||||
this.$refs.customTableRef?.toggleRowSelection(row, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
userIdList: {
|
||||
handler(newVal) {
|
||||
this.fetchUserList();
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.fetchTreeData()
|
||||
this.fetchUserList()
|
||||
this.fetchTreeData();
|
||||
if(!this.isFilter){
|
||||
this.fetchUserList();
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -206,6 +396,10 @@ export default {
|
|||
margin-left: 0;
|
||||
}
|
||||
::v-deep .el-table {
|
||||
max-height:360px !important;
|
||||
max-height: 360px !important;
|
||||
}
|
||||
</style>
|
||||
::v-deep tr.el-table__row.current-row .el-table__cell {
|
||||
background-color: #409eff !important;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -63,8 +63,8 @@ export default {
|
|||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
|
@ -72,7 +72,7 @@ export default {
|
|||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #c0c0c0;
|
||||
background-color: #c8c8c8;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,36 +1,60 @@
|
|||
<template>
|
||||
<div id="tags-view-container" class="tags-view-container">
|
||||
<scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
|
||||
<scroll-pane
|
||||
ref="scrollPane"
|
||||
class="tags-view-wrapper"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<router-link
|
||||
v-for="tag in visitedViews"
|
||||
ref="tag"
|
||||
:key="tag.path"
|
||||
:class="isActive(tag)?'active':''"
|
||||
:class="isActive(tag) ? 'active' : ''"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
tag="span"
|
||||
class="tags-view-item"
|
||||
:style="activeStyle(tag)"
|
||||
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
|
||||
@contextmenu.prevent.native="openMenu(tag,$event)"
|
||||
@click.middle.native="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
||||
@contextmenu.prevent.native="openMenu(tag, $event)"
|
||||
>
|
||||
{{ tag.title }}
|
||||
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
|
||||
<span
|
||||
v-if="!isAffix(tag)"
|
||||
class="el-icon-close"
|
||||
@click.prevent.stop="closeSelectedTag(tag)"
|
||||
/>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
|
||||
<li @click="refreshSelectedTag(selectedTag)"><i class="el-icon-refresh-right"></i> 刷新页面</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> 关闭当前</li>
|
||||
<li @click="closeOthersTags"><i class="el-icon-circle-close"></i> 关闭其他</li>
|
||||
<li v-if="!isFirstView()" @click="closeLeftTags"><i class="el-icon-back"></i> 关闭左侧</li>
|
||||
<li v-if="!isLastView()" @click="closeRightTags"><i class="el-icon-right"></i> 关闭右侧</li>
|
||||
<li @click="closeAllTags(selectedTag)"><i class="el-icon-circle-close"></i> 全部关闭</li>
|
||||
<ul
|
||||
v-show="visible"
|
||||
:style="{ left: left + 'px', top: top + 'px' }"
|
||||
class="contextmenu"
|
||||
>
|
||||
<li @click="refreshSelectedTag(selectedTag)">
|
||||
<i class="el-icon-refresh-right"></i> 刷新页面
|
||||
</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
|
||||
<i class="el-icon-close"></i> 关闭当前
|
||||
</li>
|
||||
<li @click="closeOthersTags">
|
||||
<i class="el-icon-circle-close"></i> 关闭其他
|
||||
</li>
|
||||
<li v-if="!isFirstView()" @click="closeLeftTags">
|
||||
<i class="el-icon-back"></i> 关闭左侧
|
||||
</li>
|
||||
<li v-if="!isLastView()" @click="closeRightTags">
|
||||
<i class="el-icon-right"></i> 关闭右侧
|
||||
</li>
|
||||
<li @click="closeAllTags(selectedTag)">
|
||||
<i class="el-icon-circle-close"></i> 全部关闭
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ScrollPane from './ScrollPane'
|
||||
import path from 'path'
|
||||
import ScrollPane from "./ScrollPane";
|
||||
import path from "path";
|
||||
|
||||
export default {
|
||||
components: { ScrollPane },
|
||||
|
@ -40,201 +64,207 @@ export default {
|
|||
top: 0,
|
||||
left: 0,
|
||||
selectedTag: {},
|
||||
affixTags: []
|
||||
}
|
||||
affixTags: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
visitedViews() {
|
||||
return this.$store.state.tagsView.visitedViews
|
||||
return this.$store.state.tagsView.visitedViews;
|
||||
},
|
||||
routes() {
|
||||
return this.$store.state.permission.routes
|
||||
return this.$store.state.permission.routes;
|
||||
},
|
||||
theme() {
|
||||
return this.$store.state.settings.theme;
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.addTags()
|
||||
this.moveToCurrentTag()
|
||||
this.addTags();
|
||||
this.moveToCurrentTag();
|
||||
},
|
||||
visible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.closeMenu)
|
||||
document.body.addEventListener("click", this.closeMenu);
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.closeMenu)
|
||||
document.body.removeEventListener("click", this.closeMenu);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initTags()
|
||||
this.addTags()
|
||||
this.initTags();
|
||||
this.addTags();
|
||||
},
|
||||
methods: {
|
||||
isActive(route) {
|
||||
return route.path === this.$route.path
|
||||
return route.path === this.$route.path;
|
||||
},
|
||||
activeStyle(tag) {
|
||||
if (!this.isActive(tag)) return {};
|
||||
return {
|
||||
"background-color": this.theme,
|
||||
"border-color": this.theme
|
||||
"border-color": this.theme,
|
||||
};
|
||||
},
|
||||
isAffix(tag) {
|
||||
return tag.meta && tag.meta.affix
|
||||
return tag.meta && tag.meta.affix;
|
||||
},
|
||||
isFirstView() {
|
||||
try {
|
||||
return this.selectedTag.fullPath === '/index' || this.selectedTag.fullPath === this.visitedViews[1].fullPath
|
||||
return (
|
||||
this.selectedTag.fullPath === "/index" ||
|
||||
this.selectedTag.fullPath === this.visitedViews[1].fullPath
|
||||
);
|
||||
} catch (err) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
},
|
||||
isLastView() {
|
||||
try {
|
||||
return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath
|
||||
return (
|
||||
this.selectedTag.fullPath ===
|
||||
this.visitedViews[this.visitedViews.length - 1].fullPath
|
||||
);
|
||||
} catch (err) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
},
|
||||
filterAffixTags(routes, basePath = '/') {
|
||||
let tags = []
|
||||
routes.forEach(route => {
|
||||
filterAffixTags(routes, basePath = "/") {
|
||||
let tags = [];
|
||||
routes.forEach((route) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path)
|
||||
const tagPath = path.resolve(basePath, route.path);
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta }
|
||||
})
|
||||
meta: { ...route.meta },
|
||||
});
|
||||
}
|
||||
if (route.children) {
|
||||
const tempTags = this.filterAffixTags(route.children, route.path)
|
||||
const tempTags = this.filterAffixTags(route.children, route.path);
|
||||
if (tempTags.length >= 1) {
|
||||
tags = [...tags, ...tempTags]
|
||||
tags = [...tags, ...tempTags];
|
||||
}
|
||||
}
|
||||
})
|
||||
return tags
|
||||
});
|
||||
return tags;
|
||||
},
|
||||
initTags() {
|
||||
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
|
||||
const affixTags = (this.affixTags = this.filterAffixTags(this.routes));
|
||||
for (const tag of affixTags) {
|
||||
// Must have tag name
|
||||
if (tag.name) {
|
||||
this.$store.dispatch('tagsView/addVisitedView', tag)
|
||||
this.$store.dispatch("tagsView/addVisitedView", tag);
|
||||
}
|
||||
}
|
||||
},
|
||||
addTags() {
|
||||
const { name } = this.$route
|
||||
const { name } = this.$route;
|
||||
if (name) {
|
||||
this.$store.dispatch('tagsView/addView', this.$route)
|
||||
this.$store.dispatch("tagsView/addView", this.$route);
|
||||
if (this.$route.meta.link) {
|
||||
this.$store.dispatch('tagsView/addIframeView', this.$route)
|
||||
this.$store.dispatch("tagsView/addIframeView", this.$route);
|
||||
}
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
},
|
||||
moveToCurrentTag() {
|
||||
const tags = this.$refs.tag
|
||||
const tags = this.$refs.tag;
|
||||
this.$nextTick(() => {
|
||||
for (const tag of tags) {
|
||||
if (tag.to.path === this.$route.path) {
|
||||
this.$refs.scrollPane.moveToTarget(tag)
|
||||
this.$refs.scrollPane.moveToTarget(tag);
|
||||
// when query is different then update
|
||||
if (tag.to.fullPath !== this.$route.fullPath) {
|
||||
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
|
||||
this.$store.dispatch("tagsView/updateVisitedView", this.$route);
|
||||
}
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
refreshSelectedTag(view) {
|
||||
this.$tab.refreshPage(view);
|
||||
if (this.$route.meta.link) {
|
||||
this.$store.dispatch('tagsView/delIframeView', this.$route)
|
||||
this.$store.dispatch("tagsView/delIframeView", this.$route);
|
||||
}
|
||||
},
|
||||
closeSelectedTag(view) {
|
||||
this.$tab.closePage(view).then(({ visitedViews }) => {
|
||||
if (this.isActive(view)) {
|
||||
this.toLastView(visitedViews, view)
|
||||
this.toLastView(visitedViews, view);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
closeRightTags() {
|
||||
this.$tab.closeRightPage(this.selectedTag).then(visitedViews => {
|
||||
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
|
||||
this.toLastView(visitedViews)
|
||||
this.$tab.closeRightPage(this.selectedTag).then((visitedViews) => {
|
||||
if (!visitedViews.find((i) => i.fullPath === this.$route.fullPath)) {
|
||||
this.toLastView(visitedViews);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
closeLeftTags() {
|
||||
this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => {
|
||||
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
|
||||
this.toLastView(visitedViews)
|
||||
this.$tab.closeLeftPage(this.selectedTag).then((visitedViews) => {
|
||||
if (!visitedViews.find((i) => i.fullPath === this.$route.fullPath)) {
|
||||
this.toLastView(visitedViews);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
closeOthersTags() {
|
||||
this.$router.push(this.selectedTag.fullPath).catch(()=>{});
|
||||
this.$router.push(this.selectedTag.fullPath).catch(() => {});
|
||||
this.$tab.closeOtherPage(this.selectedTag).then(() => {
|
||||
this.moveToCurrentTag()
|
||||
})
|
||||
this.moveToCurrentTag();
|
||||
});
|
||||
},
|
||||
closeAllTags(view) {
|
||||
this.$tab.closeAllPage().then(({ visitedViews }) => {
|
||||
if (this.affixTags.some(tag => tag.path === this.$route.path)) {
|
||||
return
|
||||
if (this.affixTags.some((tag) => tag.path === this.$route.path)) {
|
||||
return;
|
||||
}
|
||||
this.toLastView(visitedViews, view)
|
||||
})
|
||||
this.toLastView(visitedViews, view);
|
||||
});
|
||||
},
|
||||
toLastView(visitedViews, view) {
|
||||
const latestView = visitedViews.slice(-1)[0]
|
||||
const latestView = visitedViews.slice(-1)[0];
|
||||
if (latestView) {
|
||||
this.$router.push(latestView.fullPath)
|
||||
this.$router.push(latestView.fullPath);
|
||||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view.name === 'Dashboard') {
|
||||
if (view.name === "Dashboard") {
|
||||
// to reload home page
|
||||
this.$router.replace({ path: '/redirect' + view.fullPath })
|
||||
this.$router.replace({ path: "/redirect" + view.fullPath });
|
||||
} else {
|
||||
this.$router.push('/')
|
||||
this.$router.push("/");
|
||||
}
|
||||
}
|
||||
},
|
||||
openMenu(tag, e) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||
const left = e.clientX - offsetLeft + 15 // 15: margin right
|
||||
const menuMinWidth = 105;
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left; // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth; // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth; // left boundary
|
||||
const left = e.clientX - offsetLeft + 15; // 15: margin right
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.left = maxLeft
|
||||
this.left = maxLeft;
|
||||
} else {
|
||||
this.left = left
|
||||
this.left = left;
|
||||
}
|
||||
|
||||
this.top = e.clientY
|
||||
this.visible = true
|
||||
this.selectedTag = tag
|
||||
this.top = e.clientY;
|
||||
this.visible = true;
|
||||
this.selectedTag = tag;
|
||||
},
|
||||
closeMenu() {
|
||||
this.visible = false
|
||||
this.visible = false;
|
||||
},
|
||||
handleScroll() {
|
||||
this.closeMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
this.closeMenu();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -243,7 +273,7 @@ export default {
|
|||
width: 100%;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
|
||||
.tags-view-wrapper {
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
|
@ -269,7 +299,7 @@ export default {
|
|||
color: #fff;
|
||||
border-color: #42b983;
|
||||
&::before {
|
||||
content: '';
|
||||
content: "";
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
|
@ -292,7 +322,7 @@ export default {
|
|||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
|
@ -315,10 +345,10 @@ export default {
|
|||
vertical-align: 2px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
transition: all .3s cubic-bezier(.645, .045, .355, 1);
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
transform-origin: 100% 50%;
|
||||
&:before {
|
||||
transform: scale(.6);
|
||||
transform: scale(0.6);
|
||||
display: inline-block;
|
||||
vertical-align: -3px;
|
||||
}
|
||||
|
|
|
@ -161,6 +161,48 @@ export const dynamicRoutes = [
|
|||
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/project/detail',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['project:list:detail'],
|
||||
children: [
|
||||
{
|
||||
path: '/project/detail',
|
||||
component: () => import('@/views/project/detail'),
|
||||
name: 'GenEdit',
|
||||
meta: { title: '项目详情', activeMenu: '/project/detail' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/project/detail',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['project:list:detail'],
|
||||
children: [
|
||||
{
|
||||
path: '/project/detail',
|
||||
component: () => import('@/views/project/detail'),
|
||||
name: 'GenEdit',
|
||||
meta: { title: '项目详情', activeMenu: '/project/detail' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/project/detail',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
permissions: ['project:list:demand'],
|
||||
children: [
|
||||
{
|
||||
path: '/project/demandManage',
|
||||
component: () => import('@/views/project/demandManage/demandManage'),
|
||||
name: 'GenEdit',
|
||||
meta: { title: '项目详情', activeMenu: '/project/demandManage' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
154
src/utils/api.js
|
@ -1,6 +1,6 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 登录板块api
|
||||
// 公用板块
|
||||
|
||||
// 项目板块
|
||||
export const projectApi = {
|
||||
|
@ -87,6 +87,11 @@ export const workLogApi = {
|
|||
method: 'put',
|
||||
data: data,
|
||||
}),
|
||||
delLog: (id) => request({
|
||||
url: `/business/work/hour/${id}`,
|
||||
method: 'delete',
|
||||
}),
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -129,4 +134,151 @@ export const systemApi = {
|
|||
url: '/system/user/deptTree',
|
||||
method: 'get',
|
||||
}),
|
||||
fileUpload: process.env.NODE_ENV == 'development' ? '/common/upload' : '/prod-api/common/upload',
|
||||
downFile: (data) => request({
|
||||
url: '/common/download',
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
delFile: (id) => request({
|
||||
url: `/business/project/file/${id}`,
|
||||
method: 'delete',
|
||||
|
||||
}),
|
||||
delFileBatch: (id) => request({
|
||||
url: `/business/project/file/batch/${id}`,
|
||||
method: 'delete',
|
||||
|
||||
}),
|
||||
|
||||
}
|
||||
|
||||
// 任务考核板块
|
||||
export const taskApi = {
|
||||
getTaskUserList: (data) => request({
|
||||
url: '/examine/user',
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
getTaskScoreDetail: (data) => request({
|
||||
url: '/examine/detail',
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
saveTaskUserScore: (data) => request({
|
||||
url: '/examine/detail/batch',
|
||||
method: 'post',
|
||||
data: data,
|
||||
}),
|
||||
getTaskListSelf: (data) => request({
|
||||
url: '/task/list',
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
getTaskListSelfNormal: (data) => request({
|
||||
url: '/task/listSelf',
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
|
||||
getTaskList: (data) => request({
|
||||
url: '/task/get',
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
addTask: (data) => request({
|
||||
url: '/task/add',
|
||||
method: 'post',
|
||||
data: data,
|
||||
}),
|
||||
upDateTask: (data) => request({
|
||||
url: '/task/update',
|
||||
method: 'put',
|
||||
data: data,
|
||||
}),
|
||||
delTask: (id) => request({
|
||||
url: `/task/${id}`,
|
||||
method: 'delete',
|
||||
}),
|
||||
delTaskModule: (id) => request({
|
||||
url: `/examine/template/${id}`,
|
||||
method: 'delete',
|
||||
}),
|
||||
getTaskSet: (id) => request({
|
||||
url: `/task/target/${id}`,
|
||||
method: 'get',
|
||||
}),
|
||||
setTaskSet: (data) => request({
|
||||
url: `/task/config/update`,
|
||||
method: 'put',
|
||||
data: data,
|
||||
|
||||
}),
|
||||
getTaskModel: (data) => request({
|
||||
url: `/examine/template/list`,
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
// 获取模板配置
|
||||
getTaskModelSet: (id) => request({
|
||||
url: `/examine/template/list/${id}`,
|
||||
method: 'get',
|
||||
}),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
export const demandApi = {
|
||||
getVersionTree: (data) => request({
|
||||
url: `/projectVersion/tree`,
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
addVersion: (data) => request({
|
||||
url: '/projectVersion/insert',
|
||||
method: 'post',
|
||||
data: data,
|
||||
}),
|
||||
// 删除版本号
|
||||
delVersion: (data) => request({
|
||||
url: `/projectVersion/${data}`,
|
||||
method: 'delete',
|
||||
}),
|
||||
editVersion: (data) => request({
|
||||
url: '/projectVersion/update',
|
||||
method: 'put',
|
||||
data: data,
|
||||
}),
|
||||
getDemandList: (data) => request({
|
||||
url: `/demand/list`,
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
addDemand: (data) => request({
|
||||
url: '/demand/insert',
|
||||
method: 'post',
|
||||
data: data,
|
||||
}),
|
||||
eidtDemand: (data) => request({
|
||||
url: '/demand/update',
|
||||
method: 'put',
|
||||
data: data,
|
||||
}),
|
||||
delDemand: (data) => request({
|
||||
url: `/demand/${data}`,
|
||||
method: 'delete',
|
||||
}),
|
||||
getDemandDetail: (id) => request({
|
||||
url: `/demand/${id}`,
|
||||
method: 'get',
|
||||
}),
|
||||
delDemandBatch: (data) => request({
|
||||
url: `/demand/remove/batch/${data}`,
|
||||
method: 'delete',
|
||||
}),
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-login-footer">
|
||||
<span>Copyright © 2018-2024 ruoyi.vip All Rights Reserved.</span>
|
||||
<span>unissense.tech</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,448 @@
|
|||
<template>
|
||||
<div
|
||||
class="sidebarTree"
|
||||
:class="{
|
||||
hidenLeft: !showFlag,
|
||||
}"
|
||||
>
|
||||
<i
|
||||
class="el-icon-arrow-right"
|
||||
style="font-size: 24px; margin-top: 20px; cursor: pointer"
|
||||
v-show="!showFlag"
|
||||
@click.stop="changeShow()"
|
||||
></i>
|
||||
|
||||
<!-- 树形菜单 -->
|
||||
<div class="treeTitle">版本列表</div>
|
||||
<div class="topBox" v-show="showFlag">
|
||||
<div class="topText" @click.stop="changeOpen">
|
||||
<i v-show="openFlag" class="el-icon-caret-bottom"></i>
|
||||
<i v-show="!openFlag" class="el-icon-caret-right"></i>
|
||||
<img src="@/assets/demand/list.png" />
|
||||
<span>全部版本</span>
|
||||
</div>
|
||||
<div class="topBtn">
|
||||
<i
|
||||
class="el-icon-plus"
|
||||
@click.stop="dialogVisible = true"
|
||||
style="font-size: 20px"
|
||||
></i>
|
||||
<i
|
||||
class="el-icon-arrow-left"
|
||||
style="font-size: 20px"
|
||||
@click.stop="changeShow()"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
<el-tree
|
||||
v-show="showFlag"
|
||||
:data="treeData"
|
||||
:props="defaultProps"
|
||||
:default-expanded-keys="defaultExpend"
|
||||
node-key="nodeId"
|
||||
class="tree"
|
||||
ref="treeRef"
|
||||
@node-click="handleNodeClick"
|
||||
:expand-on-click-node="false"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<div
|
||||
class="treeNode"
|
||||
@mousemove="data.hover = true"
|
||||
@mouseout="data.hover = false"
|
||||
>
|
||||
<!-- 展开/收起图标 -->
|
||||
|
||||
<!-- 节点图标 -->
|
||||
<img
|
||||
v-if="data.type == 0 && data.nodeId === selectedId"
|
||||
src="@/assets/demand/treeIcon.png"
|
||||
class="nodeIcon"
|
||||
/>
|
||||
<img
|
||||
v-if="data.type == 0 && data.nodeId !== selectedId"
|
||||
src="@/assets/demand/treeIcon1.png"
|
||||
class="nodeIcon"
|
||||
/>
|
||||
<!-- 节点文本 -->
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="light"
|
||||
:content="data.title"
|
||||
placement="top"
|
||||
:disabled="data.title.length > 8 ? false : true"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'nodeLabel',
|
||||
data.nodeId === selectedId ? 'selected' : '',
|
||||
]"
|
||||
>
|
||||
{{ data.title }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 右侧数字标记 -->
|
||||
<span
|
||||
v-if="data.type == 0 && !data.hover"
|
||||
:class="[
|
||||
'count',
|
||||
data.nodeId === selectedId ? 'selectedCount' : '',
|
||||
]"
|
||||
>
|
||||
{{ data.childrenList.length }}
|
||||
</span>
|
||||
<el-dropdown
|
||||
v-show="data.type == 0 && data.hover"
|
||||
trigger="click"
|
||||
placement="bottom"
|
||||
@command="
|
||||
(val) => {
|
||||
changeRow(val, data);
|
||||
}
|
||||
"
|
||||
>
|
||||
<i class="el-icon-more" style="font-size: 20px"></i>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="add">新建需求</el-dropdown-item>
|
||||
<el-dropdown-item command="edit">编辑</el-dropdown-item>
|
||||
<el-dropdown-item command="del" style="color: #dd242a"
|
||||
>删除</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<!-- 右侧操作图标 -->
|
||||
<img
|
||||
v-if="data.actionIcon"
|
||||
:src="data.actionIcon"
|
||||
class="actionIcon"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
<el-dialog title="添加版本号" :visible.sync="dialogVisible" width="30%">
|
||||
<el-form>
|
||||
<el-form-item label="版本号">
|
||||
<el-input v-model="demandData.name"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAddNode">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { demandApi } from "@/utils/api";
|
||||
export default {
|
||||
name: "SidebarTree",
|
||||
props: {
|
||||
projectId: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedId: "",
|
||||
treeData: [],
|
||||
defaultProps: {
|
||||
children: "childrenList",
|
||||
label: "title",
|
||||
},
|
||||
dialogVisible: false,
|
||||
demandData: {
|
||||
name: "",
|
||||
},
|
||||
openFlag: false,
|
||||
showFlag: true,
|
||||
defaultExpend: [],
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleNodeClick(data) {
|
||||
this.selectedId = data.nodeId;
|
||||
this.changeVersion(data);
|
||||
},
|
||||
getVersionTree(defaultId) {
|
||||
demandApi
|
||||
.getVersionTree({
|
||||
projectId: this.projectId,
|
||||
demandStatusList: [],
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.data.length) {
|
||||
this.treeData = [];
|
||||
return;
|
||||
}
|
||||
res.data = res.data.map((ele) => {
|
||||
ele.nodeId = ele.id + "_" + ele.type;
|
||||
ele.childrenList.map((item) => {
|
||||
item.nodeId = item.id + "_" + item.type;
|
||||
});
|
||||
ele.hover = false;
|
||||
return ele;
|
||||
});
|
||||
this.initTableData(defaultId, res.data);
|
||||
this.treeData = res.data;
|
||||
});
|
||||
},
|
||||
changeVersion(data) {
|
||||
if (!data) {
|
||||
data = {
|
||||
id: this.projectId,
|
||||
type: 2,
|
||||
};
|
||||
}
|
||||
this.$emit("changeVersion", data);
|
||||
},
|
||||
initTableData(defaultId, tableList) {
|
||||
if (defaultId && defaultId != "all") {
|
||||
let searchNode = {};
|
||||
this.defaultExpend = [defaultId];
|
||||
searchNode = tableList.find((ele) => ele.nodeId == defaultId);
|
||||
this.changeVersion(searchNode);
|
||||
this.selectedId = this.defaultExpend[0];
|
||||
} else if (defaultId == "all") {
|
||||
this.selectedId = "";
|
||||
this.changeVersion({
|
||||
id: this.projectId,
|
||||
type: 2,
|
||||
});
|
||||
} else {
|
||||
this.defaultExpend = [tableList[0].nodeId];
|
||||
this.selectedId = this.defaultExpend[0];
|
||||
this.setVersionList(tableList.filter((ele) => ele.type == 0));
|
||||
this.changeVersion(
|
||||
tableList.find((ele) => ele.nodeId == this.defaultExpend[0])
|
||||
);
|
||||
}
|
||||
},
|
||||
setVersionList(data) {
|
||||
this.$emit("setVersionList", data);
|
||||
},
|
||||
confirmAddNode() {
|
||||
if (!this.demandData.name) {
|
||||
this.$message({
|
||||
message: "请填写版本号",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.demandData.name.length > 10) {
|
||||
this.$message({
|
||||
message: "版本号限制10个字符",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
let param = {
|
||||
projectId: this.projectId,
|
||||
versionNumber: this.demandData.name,
|
||||
};
|
||||
if (this.demandData.id) {
|
||||
param.id = this.demandData.id;
|
||||
demandApi.editVersion(param).then((res) => {
|
||||
this.resetAdd(0);
|
||||
});
|
||||
} else {
|
||||
demandApi.addVersion(param).then((res) => {
|
||||
this.resetAdd(1);
|
||||
});
|
||||
}
|
||||
},
|
||||
resetAdd(add) {
|
||||
this.$message({
|
||||
message: add ? "添加成功" : "修改成功",
|
||||
type: "success",
|
||||
});
|
||||
this.dialogVisible = false;
|
||||
this.demandData.name = "";
|
||||
this.demandData.id = "";
|
||||
this.getVersionTree();
|
||||
},
|
||||
editDemand(data) {
|
||||
this.demandData = {
|
||||
name: data.title,
|
||||
id: data.id,
|
||||
};
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
delVersion(data) {
|
||||
this.$confirm("此操作将永久删除该版本号, 是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
demandApi.delVersion(data.id).then((res) => {
|
||||
this.$message({
|
||||
type: "success",
|
||||
message: "删除成功!",
|
||||
});
|
||||
this.getVersionTree();
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
changeOpen() {
|
||||
this.openFlag = !this.openFlag;
|
||||
if (this.openFlag) {
|
||||
this.defaultExpend = this.treeData.map((ele) => ele.nodeId);
|
||||
} else {
|
||||
this.defaultExpend = [];
|
||||
}
|
||||
this.getVersionTree("all");
|
||||
},
|
||||
changeShow() {
|
||||
this.showFlag = !this.showFlag;
|
||||
},
|
||||
changeRow(type, row) {
|
||||
if (type == "add") {
|
||||
this.$emit("addDemand");
|
||||
} else if (type == "edit") {
|
||||
this.editDemand(row);
|
||||
} else if (type == "del") {
|
||||
this.delVersion(row);
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
projectId(newVal) {
|
||||
this.$nextTick(() => {
|
||||
this.getVersionTree();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.sidebarTree {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
min-height: 100vh;
|
||||
background-color: inherit;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.hidenLeft {
|
||||
width: 40px;
|
||||
}
|
||||
.tree {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.treeNode {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
padding: 0 20px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
::v-deep .el-tree-node__children {
|
||||
padding-left: 15px !important;
|
||||
}
|
||||
.expandIcon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.nodeIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.nodeLabel {
|
||||
flex: 1;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
color: #333;
|
||||
max-width: 160px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.nodeLabel.selected {
|
||||
color: #4096ff;
|
||||
}
|
||||
|
||||
.count {
|
||||
font-family: "PingFang SC";
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 26px;
|
||||
color: #999;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.selectedCount {
|
||||
color: #4096ff;
|
||||
}
|
||||
|
||||
.actionIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
.treeTitle {
|
||||
padding: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
/* 选中状态背景色 */
|
||||
// .treeNode:has(.selected) {
|
||||
// background-color: #f6faff;
|
||||
// }
|
||||
|
||||
::v-deep .el-tree-node {
|
||||
min-height: 42px;
|
||||
.el-tree-node__content {
|
||||
height: 42px;
|
||||
}
|
||||
:not(.is-leaf.el-tree-node__expand-icon) {
|
||||
font-size: 16px;
|
||||
// color: #333;
|
||||
}
|
||||
}
|
||||
.topBox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
.topText {
|
||||
gap: 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
.topBtn {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
// /* <EFBFBD><EFBFBD>浮效果 */
|
||||
// .treeNode:hover {
|
||||
// background-color: #f5f5f5;
|
||||
// }
|
||||
</style>
|
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<div class="layout">
|
||||
<!-- 左侧树形菜单 -->
|
||||
<SidebarTree
|
||||
:projectId="projectId"
|
||||
class="sidebar"
|
||||
@changeVersion="changeVersion"
|
||||
@setVersionList="setVersionList"
|
||||
@addDemand="addDemand"
|
||||
ref="treeRef"
|
||||
/>
|
||||
<!-- 右侧表格和筛选 -->
|
||||
<MainContentTable
|
||||
:projectId="projectId"
|
||||
class="main-content"
|
||||
:version="version"
|
||||
:projectName="projectName"
|
||||
@refreshTree="refreshTree"
|
||||
:versionList="versionList"
|
||||
:minDate="minDate"
|
||||
:maxDate="maxDate"
|
||||
ref="rightRef"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 导入子组件
|
||||
import SidebarTree from "./components/SidebarTree.vue";
|
||||
import MainContentTable from "./components/MainContentTable.vue";
|
||||
|
||||
export default {
|
||||
name: "demandManage",
|
||||
components: {
|
||||
SidebarTree,
|
||||
MainContentTable,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
projectId: "",
|
||||
projectName: "",
|
||||
versionList: [],
|
||||
version: {},
|
||||
minDate: "",
|
||||
maxDate: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changeVersion(data) {
|
||||
this.version = data;
|
||||
},
|
||||
setVersionList(data) {
|
||||
this.versionList = data;
|
||||
},
|
||||
refreshTree(defaultId) {
|
||||
this.$refs.treeRef.getVersionTree(defaultId);
|
||||
},
|
||||
addDemand() {
|
||||
this.$refs.rightRef.handleAdd();
|
||||
},
|
||||
init() {
|
||||
this.projectId = this.$route.query.id;
|
||||
this.projectName = this.$route.query.projectName;
|
||||
this.minDate = this.$route.query.startDate;
|
||||
this.maxDate = this.$route.query.endDate;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex-grow: 1;
|
||||
max-width: 300px;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex-grow: 3;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
|
@ -1,219 +1,143 @@
|
|||
<template>
|
||||
<div class="project-management">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
class="custom-form"
|
||||
>
|
||||
<div class="project-management" v-loading="fileLoading">
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" class="custom-form">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<div>
|
||||
<el-input
|
||||
v-model="formData.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
:disabled="isEditing"
|
||||
class="full-width longInput"
|
||||
/>
|
||||
<el-input v-model="formData.projectName" placeholder="请输入项目名称" :disabled="isEditing"
|
||||
class="full-width longInput" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目编码" prop="projectCode">
|
||||
<el-input
|
||||
v-model="formData.projectCode"
|
||||
class="full-width"
|
||||
:disabled="isEditing"
|
||||
/>
|
||||
<el-input v-model="formData.projectCode" class="full-width" :disabled="isEditing" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目负责人" prop="projectLeader">
|
||||
<el-input
|
||||
v-model="formData.projectLeaderName"
|
||||
placeholder="选择项目负责人"
|
||||
readonly
|
||||
:disabled="isEditing"
|
||||
@click.native="openProjectManagerSelect"
|
||||
/>
|
||||
<el-input v-model="formData.projectLeaderName" placeholder="选择项目负责人" readonly :disabled="isEditing"
|
||||
@click.native="openProjectManagerSelect">
|
||||
<el-button size="mini" slot="append" icon="el-icon-s-custom"></el-button></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目状态" prop="projectState">
|
||||
<el-select
|
||||
v-model="formData.projectState"
|
||||
placeholder="根据时间自动生成"
|
||||
disabled
|
||||
class="full-width"
|
||||
>
|
||||
<el-form-item label="数据状态" prop="dataState" style="display: none">
|
||||
<el-select v-model="formData.dataState" placeholder="根据时间自动生成" disabled class="full-width">
|
||||
<el-option label="未启动" value="0" />
|
||||
<el-option label="进行中" value="1" />
|
||||
<el-option label="已完成" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目状态" prop="projectState">
|
||||
<el-select v-model="formData.projectState" placeholder="请选择项目状态" class="full-width">
|
||||
<el-option v-for="item in statusList" :key="item.dictValue" :label="item.dictLabel"
|
||||
:value="item.dictValue" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预计人天" prop="budgetDate">
|
||||
<el-input
|
||||
v-model.number="formData.budgetDate"
|
||||
:min="0"
|
||||
:disabled="isEditing"
|
||||
class="full-width"
|
||||
/>
|
||||
<el-input v-model.number="formData.budgetDate" :min="0" :disabled="isEditing" class="full-width" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker
|
||||
v-model="formData.startDate"
|
||||
type="date"
|
||||
placeholder="选择开始日期"
|
||||
format="yyyy-MM-dd"
|
||||
value-format="yyyy-MM-dd"
|
||||
class="full-width"
|
||||
:disabled="isEditing"
|
||||
/>
|
||||
<el-date-picker v-model="formData.startDate" type="date" placeholder="选择开始日期" format="yyyy-MM-dd"
|
||||
value-format="yyyy-MM-dd" class="full-width" :disabled="isEditing" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束日期" prop="endDate">
|
||||
<el-date-picker
|
||||
v-model="formData.endDate"
|
||||
type="date"
|
||||
placeholder="选择结束日期"
|
||||
format="yyyy-MM-dd"
|
||||
value-format="yyyy-MM-dd"
|
||||
class="full-width"
|
||||
:disabled="isEditing"
|
||||
/>
|
||||
<el-date-picker v-model="formData.endDate" type="date" placeholder="选择结束日期" format="yyyy-MM-dd"
|
||||
value-format="yyyy-MM-dd" class="full-width" :disabled="isEditing" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="附件" prop="fileList">
|
||||
<div>
|
||||
<el-upload class="upload-demo" ref="upload" :action="fileUpload" :show-file-list="false"
|
||||
:auto-upload="true" :multiple="true" :before-upload="beforeUpload" :on-success="successUpload" :headers="{
|
||||
Authorization: 'Bearer ' + token,
|
||||
}" :data="{}">
|
||||
<div class="flex-row aic" style="gap: 10px;margin-bottom: 10px;">
|
||||
<el-button slot="trigger" size="small" type="default"
|
||||
style="width: 200px;color: #333;font-weight: 500;">上传附件</el-button>
|
||||
<div slot="tip" style="color: #999999;font-size: 12px;">单个附件限制100M</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div class="fileBox">
|
||||
<div v-for="(item, index) in formData.fileList" class="fileRow flex-row jcsb aic" :key="index">
|
||||
<div class="flex-row aic fileItem">
|
||||
<img class="" :src="filePng" v-if="getFileType(item.fileName) == 'file'"> </img>
|
||||
<img class="" :src="imagePng" v-else-if="getFileType(item.fileName) == 'image'"> </img>
|
||||
<img class="" :src="zipPng" v-else-if="getFileType(item.fileName) == 'zip'"> </img>
|
||||
<div @click="downFile(item.fileUrl)">{{ item.fileName }}</div>
|
||||
</div>
|
||||
<div class="del" @click="delFile(item)">×</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="form-actions jcc" v-show="!isEditing">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="saveProject"
|
||||
v-hasPermi="['project:detail:save']"
|
||||
>保存</el-button
|
||||
>
|
||||
<el-button type="primary" @click="saveProject" v-hasPermi="['project:detail:save']">保存</el-button>
|
||||
<el-button @click="cancelEdit">取消</el-button>
|
||||
</div>
|
||||
<div class="userBox">
|
||||
<div class="table-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="addUser"
|
||||
v-hasPermi="['project:detail:addUser']"
|
||||
>新增成员</el-button
|
||||
>
|
||||
<el-button type="primary" @click="addUser" v-hasPermi="['project:detail:addUser']">新增成员</el-button>
|
||||
</div>
|
||||
<div class="f1">
|
||||
<CustomTable
|
||||
:columns="columns"
|
||||
:tableData="tableData"
|
||||
:show-selection="false"
|
||||
:show-index="true"
|
||||
:show-pagination="false"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
style="height: 100%;"
|
||||
>
|
||||
<CustomTable :columns="columns" :tableData="tableData" :show-selection="false" :show-index="true"
|
||||
:show-pagination="false" @size-change="handleSizeChange" @current-change="handleCurrentChange"
|
||||
style="height: 100%">
|
||||
<template slot="userName" slot-scope="{ row }">
|
||||
<el-input
|
||||
v-if="row.isNew"
|
||||
v-model="row.userName"
|
||||
placeholder="选择人员"
|
||||
readonly
|
||||
@click.native="openSelectUser(row)"
|
||||
/>
|
||||
<el-input v-if="row.isNew" v-model="row.userName" placeholder="选择人员" readonly
|
||||
@click.native="openSelectUser(row)" />
|
||||
<span v-else>{{ row.userName }}</span>
|
||||
</template>
|
||||
<template slot="post" slot-scope="{ row }">
|
||||
<el-select
|
||||
v-if="
|
||||
(row.isEditing === true && row.teamId == row.teamId) ||
|
||||
row.isNew
|
||||
"
|
||||
v-model="row.postId"
|
||||
placeholder="请选择职位"
|
||||
>
|
||||
<el-option
|
||||
v-for="post in postOptions"
|
||||
:key="post.dictValue"
|
||||
:label="post.dictLabel"
|
||||
:value="post.dictValue"
|
||||
/>
|
||||
<el-select v-if="
|
||||
(row.isEditing === true && row.teamId == row.teamId) ||
|
||||
row.isNew
|
||||
" v-model="row.postId" placeholder="请选择职位">
|
||||
<el-option v-for="post in postOptions" :key="post.dictValue" :label="post.dictLabel"
|
||||
:value="post.dictValue" />
|
||||
</el-select>
|
||||
<span v-else>{{
|
||||
postOptions.find((post) => post.dictValue === row.postId)
|
||||
? postOptions.find((post) => post.dictValue === row.postId)
|
||||
.dictLabel
|
||||
.dictLabel
|
||||
: ""
|
||||
}}</span>
|
||||
</template>
|
||||
<template slot="operation" slot-scope="{ row }">
|
||||
<div class="operation-buttons">
|
||||
<template v-if="row.isNew">
|
||||
<el-button
|
||||
text
|
||||
type="text"
|
||||
size="mini"
|
||||
@click="confirmAddUser(row)"
|
||||
>确认</el-button
|
||||
>
|
||||
<el-button
|
||||
text
|
||||
type="text"
|
||||
size="mini"
|
||||
@click="cancelAddUser(row)"
|
||||
>取消</el-button
|
||||
>
|
||||
<el-button text type="text" size="mini" @click="confirmAddUser(row)">确认</el-button>
|
||||
<el-button text type="text" size="mini" @click="cancelAddUser(row)">取消</el-button>
|
||||
</template>
|
||||
<template v-else-if="row.isEditing && row.teamId == row.teamId">
|
||||
<el-button
|
||||
text
|
||||
type="text"
|
||||
size="mini"
|
||||
@click="saveUserEdit(row)"
|
||||
>保存</el-button
|
||||
>
|
||||
<el-button
|
||||
text
|
||||
type="text"
|
||||
size="mini"
|
||||
@click="cancelUserEdit(row)"
|
||||
>取消</el-button
|
||||
>
|
||||
<el-button text type="text" size="mini" @click="saveUserEdit(row)">保存</el-button>
|
||||
<el-button text type="text" size="mini" @click="cancelUserEdit(row)">取消</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button
|
||||
type="text"
|
||||
size="mini"
|
||||
v-hasPermi="['project:detail:editUser']"
|
||||
@click="editUser(row)"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
type="text"
|
||||
size="mini"
|
||||
v-hasPermi="['project:detail:workLog']"
|
||||
@click="showTimesheet(row)"
|
||||
>工作日志</el-button
|
||||
>
|
||||
<el-button
|
||||
type="text"
|
||||
size="mini"
|
||||
v-hasPermi="['project:detail:deleteUser']"
|
||||
@click="confirmDelete(row)"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button type="text" size="mini" v-hasPermi="['project:detail:editUser']"
|
||||
@click="editUser(row)">编辑</el-button>
|
||||
<el-button type="text" size="mini" v-hasPermi="['project:detail:workLog']"
|
||||
@click="showTimesheet(row)">工作日志</el-button>
|
||||
<el-button type="text" size="mini" v-hasPermi="['project:detail:deleteUser']"
|
||||
@click="confirmDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -221,22 +145,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<SelectUser
|
||||
:dialogVisible="showSelectUser"
|
||||
:multi-select="false"
|
||||
:currentSelectedUser="currentSelectedUser"
|
||||
@confirm="handleUserConfirm"
|
||||
@close="closeSelectUser"
|
||||
/>
|
||||
<SelectUser :dialogVisible="showSelectUser" :multi-select="false" :currentSelectedUser="currentSelectedUser"
|
||||
@confirm="handleUserConfirm" @close="closeSelectUser" />
|
||||
|
||||
<SelectUser
|
||||
v-if="showProjectManagerSelect"
|
||||
:dialogVisible="showProjectManagerSelect"
|
||||
:multi-select="false"
|
||||
:selected-users="projectManagerSelectedUser"
|
||||
@confirm="handleProjectManagerConfirm"
|
||||
@close="closeProjectManagerSelect"
|
||||
/>
|
||||
<SelectUser :dialogVisible="showProjectManagerSelect" :multi-select="false"
|
||||
:currentSelectedUser="projectManagerSelectedUser" @confirm="handleProjectManagerConfirm"
|
||||
@close="closeProjectManagerSelect" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -244,6 +158,10 @@
|
|||
import CustomTable from "@/components/CustomTable.vue";
|
||||
import SelectUser from "@/components/SelectUser.vue";
|
||||
import { projectApi, systemApi } from "@/utils/api";
|
||||
import filePng from "@/assets/images/file.png";
|
||||
import zipPng from "@/assets/images/zip.png";
|
||||
import imagePng from "@/assets/images/image.png";
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -268,8 +186,13 @@ export default {
|
|||
endDate: "",
|
||||
budgetDate: 0,
|
||||
state: 0,
|
||||
dataState: 0,
|
||||
fileList: []
|
||||
},
|
||||
postOptions: [],
|
||||
statusList: [],
|
||||
fileUpload: systemApi.fileUpload,
|
||||
|
||||
columns: [
|
||||
{ prop: "userName", label: "姓名" },
|
||||
{ prop: "post", label: "项目职位" },
|
||||
|
@ -277,6 +200,8 @@ export default {
|
|||
{ prop: "operation", label: "操作", width: "250px" },
|
||||
],
|
||||
tableData: [],
|
||||
fileLoading: false,
|
||||
|
||||
currentEditingRow: {},
|
||||
currentSelectedUser: [],
|
||||
projectManagerSelectedUser: [],
|
||||
|
@ -287,8 +212,12 @@ export default {
|
|||
projectLeader: [
|
||||
{ required: true, message: "项目负责人为必填", trigger: "change" },
|
||||
],
|
||||
projectState: [
|
||||
{ required: true, message: "请选择项目状态", trigger: "change" },
|
||||
],
|
||||
startDate: [
|
||||
{ required: true, message: "开始日期为必填", trigger: "change" },
|
||||
{ validator: this.validateDates, trigger: "change" },
|
||||
],
|
||||
endDate: [
|
||||
{ required: true, message: "结束日期为必填", trigger: "change" },
|
||||
|
@ -296,9 +225,17 @@ export default {
|
|||
],
|
||||
budgetDate: [
|
||||
{ required: true, message: "预算天数为必填", trigger: "blur" },
|
||||
{ validator: this.validateDates, trigger: "change" },
|
||||
],
|
||||
// fileList: [
|
||||
// { required: true, message: "附件为必填", trigger: "blur" },
|
||||
// ],
|
||||
},
|
||||
filePng,
|
||||
zipPng,
|
||||
imagePng,
|
||||
token: getToken(),
|
||||
delFileArr: []
|
||||
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
@ -319,11 +256,11 @@ export default {
|
|||
|
||||
if (start && end) {
|
||||
if (now < start) {
|
||||
this.formData.projectState = "0"; // 未开始
|
||||
this.formData.dataState = "0"; // 未开始
|
||||
} else if (now >= start && now <= end) {
|
||||
this.formData.projectState = "1"; // 进行中
|
||||
this.formData.dataState = "1"; // 进行中
|
||||
} else {
|
||||
this.formData.projectState = "2"; // 已结束
|
||||
this.formData.dataState = "2"; // 已结束
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -351,10 +288,10 @@ export default {
|
|||
const projectDataToSave = {
|
||||
...this.formData,
|
||||
startDate: this.formData.startDate
|
||||
? new Date(this.formData.startDate+' 00:00:00').getTime()
|
||||
? new Date(this.formData.startDate + " 00:00:00").getTime()
|
||||
: null,
|
||||
endDate: this.formData.endDate
|
||||
? new Date(this.formData.endDate+' 23:59:59').getTime()
|
||||
? new Date(this.formData.endDate + " 23:59:59").getTime()
|
||||
: null,
|
||||
};
|
||||
|
||||
|
@ -363,6 +300,8 @@ export default {
|
|||
this.formData.projectId = res.data.projectId;
|
||||
this.formData.projectCode = res.data.projectCode;
|
||||
this.$modal.msgSuccess("操作成功");
|
||||
this.fetchProjectData()
|
||||
|
||||
} else {
|
||||
const hasLog = await projectApi.projectHasLogData({
|
||||
projectId: this.formData.projectId,
|
||||
|
@ -376,15 +315,28 @@ export default {
|
|||
)
|
||||
.then(async () => {
|
||||
await projectApi.updateProject(projectDataToSave);
|
||||
if (this.delFileArr.length) {
|
||||
await systemApi.delFileBatch(this.delFileArr.join(','))
|
||||
this.delFileArr = []
|
||||
|
||||
}
|
||||
this.fetchProjectData()
|
||||
|
||||
});
|
||||
} else {
|
||||
await projectApi.updateProject(projectDataToSave);
|
||||
if (this.delFileArr.length) {
|
||||
await systemApi.delFileBatch(this.delFileArr.join(','))
|
||||
this.delFileArr = []
|
||||
}
|
||||
this.fetchProjectData()
|
||||
|
||||
this.$modal.msgSuccess("操作成功");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.$modal.msgError("请检查表单填写是否正确");
|
||||
this.$modal.msgSuccess("操作成功");
|
||||
// this.$modal.msgSuccess("操作成功");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -405,10 +357,10 @@ export default {
|
|||
isNew: true,
|
||||
userId: "",
|
||||
isEditing: true,
|
||||
index: this.tableData.length,
|
||||
index: this.tableData.length - 1,
|
||||
};
|
||||
this.currentEditingRow = newUser;
|
||||
this.tableData.push(newUser);
|
||||
this.tableData.unshift(newUser);
|
||||
},
|
||||
confirmAddUser(row) {
|
||||
if (!this.formData.projectId) {
|
||||
|
@ -439,13 +391,15 @@ export default {
|
|||
});
|
||||
},
|
||||
cancelAddUser(row) {
|
||||
const index = this.tableData.findIndex(
|
||||
(item) => item.userId === row.userId
|
||||
);
|
||||
if (index !== -1) {
|
||||
if (row.index != undefined) {
|
||||
let index = this.tableData.findIndex((item) => item.index == row.index);
|
||||
|
||||
this.tableData.splice(index, 1);
|
||||
} else {
|
||||
this.tableData.splice(row.index, 1);
|
||||
var index = this.tableData.findIndex(
|
||||
(item) => item.userId === row.userId
|
||||
);
|
||||
this.tableData.splice(index, 1);
|
||||
}
|
||||
},
|
||||
cancelUserEdit(row) {
|
||||
|
@ -457,6 +411,8 @@ export default {
|
|||
this.currentSelectedUser = row.userId
|
||||
? [{ userId: row.userId, userName: row.userName }]
|
||||
: [];
|
||||
console.log(111, this.currentSelectedUser);
|
||||
|
||||
this.showSelectUser = true;
|
||||
},
|
||||
handleUserConfirm(selectedUsers) {
|
||||
|
@ -477,11 +433,11 @@ export default {
|
|||
openProjectManagerSelect() {
|
||||
this.projectManagerSelectedUser = this.formData.projectLeader
|
||||
? [
|
||||
{
|
||||
id: this.formData.projectLeaderId,
|
||||
name: this.formData.projectLeader,
|
||||
},
|
||||
]
|
||||
{
|
||||
userId: this.formData.projectLeader,
|
||||
nickName: this.formData.projectLeaderName,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
this.showProjectManagerSelect = true;
|
||||
},
|
||||
|
@ -532,7 +488,7 @@ export default {
|
|||
},
|
||||
async fetchProjectData(id) {
|
||||
try {
|
||||
const response = await projectApi.getProjectDetail(id);
|
||||
const response = await projectApi.getProjectDetail(id||this.formData.projectId);
|
||||
const projectData = response.data;
|
||||
let {
|
||||
projectId,
|
||||
|
@ -543,6 +499,7 @@ export default {
|
|||
projectState,
|
||||
budgetDate,
|
||||
state,
|
||||
fileList
|
||||
} = response.data;
|
||||
let startDate = projectData.startDate.split(" ")[0];
|
||||
let endDate = projectData.endDate.split(" ")[0];
|
||||
|
@ -557,6 +514,7 @@ export default {
|
|||
endDate,
|
||||
budgetDate,
|
||||
state,
|
||||
fileList
|
||||
};
|
||||
this.updateProjectState(); // 更新项目状态
|
||||
} catch (error) {
|
||||
|
@ -566,7 +524,9 @@ export default {
|
|||
},
|
||||
async getDictData() {
|
||||
const res = await systemApi.getDictData("business_positions");
|
||||
const res2 = await systemApi.getDictData("business_projectstate");
|
||||
this.postOptions = res.data;
|
||||
this.statusList = res2.data;
|
||||
},
|
||||
async getProjectUser() {
|
||||
const res = await projectApi.getProjectUser(this.formData.projectId);
|
||||
|
@ -597,6 +557,90 @@ export default {
|
|||
this.$modal.msgError("更新用户信息失败");
|
||||
}
|
||||
},
|
||||
beforeUpload(file) {
|
||||
|
||||
this.fileLoading = true;
|
||||
if (file.size > 1024 * 1024 * 100) {
|
||||
this.fileLoading = false;
|
||||
|
||||
this.$message({
|
||||
type: "warning",
|
||||
message: "单个文件不能大于100M!",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
},
|
||||
successUpload(res, file, fileList) {
|
||||
if (!fileList.filter((ele) => ele.percentage != 100).length) {
|
||||
this.fileLoading = false;
|
||||
}
|
||||
if (res.code == 200) {
|
||||
this.formData.fileList.push({
|
||||
fileName: res.originalFilename, //文件名称
|
||||
filePath: res.filePath, //文件路径
|
||||
fileNewName: res.newFileName, //文件新名称
|
||||
fileUrl: res.url,
|
||||
});
|
||||
} else {
|
||||
this.fileLoading = false;
|
||||
this.$message({
|
||||
type: "error",
|
||||
message: res.msg,
|
||||
});
|
||||
}
|
||||
},
|
||||
delFile(row) {
|
||||
if (row.id) {
|
||||
this.$confirm("此操作将永久删除文件, 是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
this.delFileArr.push(row.id)
|
||||
this.formData.fileList = this.formData.fileList.filter(
|
||||
(ele) => ele.fileNewName != row.fileNewName
|
||||
);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: "删除成功!"
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.formData.fileList = this.formData.fileList.filter(
|
||||
(ele) => ele.fileNewName != row.fileNewName
|
||||
);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: "删除成功!"
|
||||
})
|
||||
}
|
||||
},
|
||||
getFileType(name) {
|
||||
var data = {
|
||||
jpg: 'image',
|
||||
jpeg: 'image',
|
||||
png: 'image',
|
||||
gif: 'image',
|
||||
bmp: 'image',
|
||||
tiff: 'image',
|
||||
svg: 'image',
|
||||
pdf: 'file',
|
||||
doc: 'file',
|
||||
docx: 'file',
|
||||
xls: 'file',
|
||||
xlsx: 'file',
|
||||
txt: 'file',
|
||||
ppt: 'file',
|
||||
zip: 'zip',
|
||||
rar: 'zip',
|
||||
tar: 'zip',
|
||||
targz: 'zip',
|
||||
}
|
||||
return data[name.split('.')[1]] || 'file'
|
||||
},
|
||||
downFile(url) {
|
||||
window.open(url);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const projectId = this.$route.params.id || this.$route.query.id;
|
||||
|
@ -608,6 +652,7 @@ export default {
|
|||
} else {
|
||||
this.updateProjectState(); // 对于新项目,也要初始化状态
|
||||
}
|
||||
// 获取项目状态数据字典
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -638,7 +683,8 @@ export default {
|
|||
}
|
||||
|
||||
.custom-form ::v-deep .el-form-item {
|
||||
margin-bottom: 25px; /* 增加表单行间距 */
|
||||
margin-bottom: 25px;
|
||||
/* 增加表单行间距 */
|
||||
}
|
||||
|
||||
.custom-form ::v-deep .el-form-item__content {
|
||||
|
@ -647,22 +693,29 @@ export default {
|
|||
}
|
||||
|
||||
.custom-form ::v-deep .el-input {
|
||||
height: 42px; /* 调高输入框高度 */
|
||||
height: 42px;
|
||||
/* 调高输入框高度 */
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
::v-deep .el-form-item__label {
|
||||
height: 42px; /* 调高输入框高度 */
|
||||
height: 42px;
|
||||
/* 调高输入框高度 */
|
||||
line-height: 42px;
|
||||
}
|
||||
|
||||
.custom-form ::v-deep .el-input__wrapper,
|
||||
.custom-form ::v-deep .el-date-editor.el-input,
|
||||
.custom-form ::v-deep .el-input-number {
|
||||
height: 42px; /* 调高输入框高度 */
|
||||
height: 42px;
|
||||
/* 调高输入框高度 */
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.custom-form ::v-deep .el-input__inner {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.custom-form ::v-deep .el-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -674,12 +727,14 @@ export default {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.userBox {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.table-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
@ -746,4 +801,34 @@ export default {
|
|||
height: 64px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.fileRow {
|
||||
height: 38px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
img {
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.del {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.fileItem {
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fileBox {
|
||||
max-height: 136px;
|
||||
overflow: auto;
|
||||
padding-right: 10px;
|
||||
max-width: 90%;
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
<template>
|
||||
<div class="project-list">
|
||||
<div class="table-actions mb10" style="text-align: right">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
@click="addProject"
|
||||
v-hasPermi="['project:list:add']"
|
||||
style="height: 30px"
|
||||
>+ 新建项目</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="search-bar">
|
||||
<el-form
|
||||
:inline="true"
|
||||
|
@ -16,32 +26,32 @@
|
|||
placeholder="负责人"
|
||||
readonly
|
||||
@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">
|
||||
<el-select v-model="searchForm.projectState" placeholder="项目状态">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="未启动" value="0" />
|
||||
<el-option label="进行中" value="1" />
|
||||
<el-option label="已完成" value="2" />
|
||||
<el-select
|
||||
v-model="searchForm.projectState"
|
||||
placeholder="项目状态"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in statusList"
|
||||
:key="item.dictValue"
|
||||
:label="item.dictLabel"
|
||||
:value="item.dictValue"
|
||||
/>
|
||||
</el-select>
|
||||
</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 class="formBtn">
|
||||
<el-button type="primary" size="medium" @click="onSearch"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button @click="onReset" size="medium">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="table-actions mb10">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
@click="addProject"
|
||||
v-hasPermi="['project:list:add']"
|
||||
>+ 新建项目</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="f1 df">
|
||||
<CustomTable
|
||||
:columns="columns"
|
||||
|
@ -51,24 +61,31 @@
|
|||
:show-index="true"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
tableHeight="600"
|
||||
tableHeight="510px"
|
||||
ref="customTableRef"
|
||||
>
|
||||
<template slot="operation" slot-scope="{ row }">
|
||||
<div class="operation-buttons">
|
||||
<el-button
|
||||
@click="handleDemand(row)"
|
||||
type="text"
|
||||
size="mini"
|
||||
v-hasPermi="['project:list:demand']"
|
||||
>需求管理</el-button
|
||||
>
|
||||
<el-button
|
||||
@click="handleEdit(row)"
|
||||
type="text"
|
||||
size="mini"
|
||||
icon="el-icon-edit"
|
||||
v-hasPermi="['project:list:eidt']"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
type="text"
|
||||
size="mini"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(row)"
|
||||
v-hasPermi="['project:list:delete']"
|
||||
style="color: #666"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
|
@ -88,7 +105,7 @@
|
|||
<script>
|
||||
import CustomTable from "@/components/CustomTable.vue";
|
||||
import SelectUser from "@/components/SelectUser.vue";
|
||||
import { projectApi } from "@/utils/api";
|
||||
import { projectApi, systemApi } from "@/utils/api";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -104,52 +121,57 @@ export default {
|
|||
projectState: "",
|
||||
},
|
||||
columns: [
|
||||
{ prop: "projectName", label: "项目名称" },
|
||||
{ prop: "projectCode", label: "项目编号" },
|
||||
{ prop: "projectName", label: "项目名称", width: 300 },
|
||||
{ prop: "projectCode", label: "项目编号", width: 200 },
|
||||
{ prop: "projectLeaderName", label: "负责人" },
|
||||
{ prop: "budgetDate", label: "预计工时(天)" },
|
||||
{
|
||||
prop: "startDate",
|
||||
label: "开始时间",
|
||||
type: "status",
|
||||
callback: (data) => data.split(" ")[0],
|
||||
callback: (data) => data?.split(" ")[0],
|
||||
},
|
||||
{
|
||||
prop: "endDate",
|
||||
label: "结束时间",
|
||||
type: "status",
|
||||
callback: (data) => data.split(" ")[0],
|
||||
callback: (data) => data?.split(" ")[0],
|
||||
},
|
||||
{
|
||||
prop: "projectState",
|
||||
label: "项目状态",
|
||||
type: "status",
|
||||
callback: (value) => {
|
||||
let status = "未知";
|
||||
let color = "";
|
||||
switch (value) {
|
||||
case "0":
|
||||
status = "未启动";
|
||||
color = "#909399"; // 灰色
|
||||
let status =
|
||||
this.statusList.find((ele) => ele.dictValue == value)
|
||||
?.dictLabel || "";
|
||||
let color = "#333";
|
||||
switch (status) {
|
||||
case "待启动":
|
||||
color = "#999999"; // 橙色
|
||||
break;
|
||||
case "1":
|
||||
status = "进行中";
|
||||
color = "#409EFF"; // 蓝色
|
||||
case "进行中":
|
||||
color = "#FF7D00"; // 红色
|
||||
break;
|
||||
case "2":
|
||||
status = "已完成";
|
||||
color = "#67C23A"; // 绿色
|
||||
case "已完成":
|
||||
color = "#50B6AA"; // 绿色
|
||||
break;
|
||||
case "#50B6AA":
|
||||
color = "#999999"; // 红色
|
||||
break;
|
||||
default:
|
||||
color = "#333"; // 默认白色
|
||||
break;
|
||||
}
|
||||
return `<span style="color: ${color}">${status}</span>`;
|
||||
},
|
||||
},
|
||||
{ prop: "teamNum", label: "参与项目人数" },
|
||||
{ prop: "teamNum", label: "参与项目人数", width: 100 },
|
||||
{ prop: "createByName", label: "项目创建人" },
|
||||
{
|
||||
prop: "operation",
|
||||
label: "操作",
|
||||
width: "150",
|
||||
width: "250",
|
||||
fixed: "right",
|
||||
className: "operation-column",
|
||||
},
|
||||
|
@ -160,17 +182,19 @@ export default {
|
|||
currentSelectedUser: [],
|
||||
pageNum: 1, // 当前页码
|
||||
pageSize: 10, // 每页条数
|
||||
statusList: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onSearch() {
|
||||
console.log("Search with:", this.searchForm);
|
||||
this.fetchProjectList();
|
||||
},
|
||||
onReset() {
|
||||
Object.keys(this.searchForm).forEach((key) => {
|
||||
this.searchForm[key] = "";
|
||||
});
|
||||
this.currentSelectedUser = [];
|
||||
this.$refs.customTableRef.handleCurrentChange(1);
|
||||
this.fetchProjectList();
|
||||
},
|
||||
addProject() {
|
||||
|
@ -178,6 +202,17 @@ export default {
|
|||
path: "/project/detail",
|
||||
});
|
||||
},
|
||||
handleDemand(row) {
|
||||
this.$router.push({
|
||||
path: "/project/demandManage",
|
||||
query: {
|
||||
id: row.projectId,
|
||||
projectName: row.projectName,
|
||||
startDate: new Date(row.startDate).getTime(),
|
||||
endDate: new Date(row.endDate).getTime(),
|
||||
},
|
||||
});
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.$router.push({
|
||||
path: "/project/detail",
|
||||
|
@ -228,8 +263,13 @@ export default {
|
|||
handleUserClose() {
|
||||
this.userSelectDialogVisible = false;
|
||||
},
|
||||
async getDictData() {
|
||||
const res = await systemApi.getDictData("business_projectstate");
|
||||
this.statusList = res.data;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getDictData();
|
||||
this.fetchProjectList();
|
||||
},
|
||||
};
|
||||
|
@ -259,8 +299,16 @@ export default {
|
|||
.demo-form-inline .el-form-item {
|
||||
// margin-right: 50px; /* 将间距设置为 30px */
|
||||
margin-bottom: 0;
|
||||
border: 1px solid #ccc;
|
||||
padding-left: 10px;
|
||||
border-radius: 4px;
|
||||
::v-deep .el-form-item__label {
|
||||
color: #999 !important;
|
||||
}
|
||||
}
|
||||
.formBtn {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.demo-form-inline .el-form-item:last-child {
|
||||
margin-right: 0; /* 移除最后一个元素的右边距 */
|
||||
}
|
||||
|
@ -276,6 +324,10 @@ export default {
|
|||
.form-item ::v-deep .el-input,
|
||||
.form-item ::v-deep .el-select {
|
||||
// width: 100%;
|
||||
input,
|
||||
select {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.search-buttons {
|
||||
|
@ -285,11 +337,13 @@ export default {
|
|||
::v-deep .operation-buttons .el-button {
|
||||
padding: 4px 8px;
|
||||
margin: 0 2px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
::v-deep .operation-column {
|
||||
background-color: #fff;
|
||||
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
background-color: #ffffff;
|
||||
box-shadow: -2px 0 5px rgba(241, 112, 6, 0.1);
|
||||
}
|
||||
|
||||
.el-button.is-text {
|
||||
|
@ -327,4 +381,8 @@ export default {
|
|||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.search-buttons ::v-deep .el-button {
|
||||
width: 90px !important;
|
||||
height: 36px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -5,6 +5,22 @@
|
|||
<div class="shadowBox"></div>
|
||||
<div class="flex-row aic mb20">
|
||||
<h2 class="textC">项目执行表</h2>
|
||||
<div class="ml20">
|
||||
项目状态:
|
||||
<el-select
|
||||
v-model="projectState"
|
||||
placeholder="项目状态"
|
||||
@change="getProjectProgress"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in statusList"
|
||||
:key="item.dictValue"
|
||||
:label="item.dictLabel"
|
||||
:value="item.dictValue"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="date-range-container">
|
||||
<span class="date-range-label">统计时间:</span>
|
||||
<el-date-picker
|
||||
|
@ -20,7 +36,7 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="f1">
|
||||
<div class="f1" style="position: relative">
|
||||
<CustomTable
|
||||
:columns="fixedColumns"
|
||||
:tableData="executionData"
|
||||
|
@ -28,19 +44,22 @@
|
|||
:showSummary="true"
|
||||
:summaryMethod="getFixedColumnsSummaries"
|
||||
tableHeight="600"
|
||||
:border="true"
|
||||
></CustomTable>
|
||||
<div id="scrollBox">
|
||||
<div id="scrollBoxContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧滚动列表格 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { projectBank } from "@/utils/api";
|
||||
import { projectBank, systemApi } from "@/utils/api";
|
||||
import CustomTable from "@/components/CustomTable.vue";
|
||||
|
||||
export default {
|
||||
name: "ProjectProgress",
|
||||
components: {
|
||||
CustomTable,
|
||||
},
|
||||
|
@ -50,6 +69,8 @@ export default {
|
|||
dateRange: this.getDefaultDateRange(),
|
||||
fixedColumns: [],
|
||||
executionData: [],
|
||||
statusList: [],
|
||||
projectState: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -66,9 +87,25 @@ export default {
|
|||
const res = await projectBank.porjectProgress({
|
||||
startDate: this.dateRange[0] + " 00:00:00",
|
||||
endDate: this.dateRange[1] + " 00:00:00",
|
||||
projectState: this.projectState,
|
||||
});
|
||||
this.timeRange = [this.dateRange[0], this.dateRange[1]];
|
||||
this.executionData = res.data;
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
let table = document.querySelector(
|
||||
".el-table__body-wrapper .el-table__body"
|
||||
);
|
||||
let width = table.offsetWidth;
|
||||
let tableBox = document.querySelector(".el-table__body-wrapper");
|
||||
let box = document.getElementById("scrollBoxContent");
|
||||
let scroll = document.getElementById("scrollBox");
|
||||
box.style.width = width + "px";
|
||||
scroll.addEventListener("scroll", (event) => {
|
||||
tableBox.scrollLeft = scroll.scrollLeft;
|
||||
});
|
||||
},500);
|
||||
});
|
||||
},
|
||||
getFixedColumnsSummaries(param) {
|
||||
const { columns, data } = param;
|
||||
|
@ -80,7 +117,7 @@ export default {
|
|||
}
|
||||
let values;
|
||||
if (column.property == "detailList") {
|
||||
values = data.map((item) => Number(item[column.property][index - 4]));
|
||||
values = data.map((item) => Number(item[column.property][index - 5]));
|
||||
} else if (column.property == "projectState") {
|
||||
values = [];
|
||||
} else {
|
||||
|
@ -117,6 +154,10 @@ export default {
|
|||
},
|
||||
});
|
||||
},
|
||||
async getDictData() {
|
||||
const res = await systemApi.getDictData("business_projectstate");
|
||||
this.statusList = res.data;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
timeRange: {
|
||||
|
@ -171,24 +212,38 @@ export default {
|
|||
label: "项目状态",
|
||||
type: "button",
|
||||
fixed: "left",
|
||||
width: 100,
|
||||
width: 150,
|
||||
callback: (value) => {
|
||||
let status = "未知";
|
||||
let color = "";
|
||||
switch (value) {
|
||||
case "0":
|
||||
status = "未启动";
|
||||
color = "#909399"; // 灰色
|
||||
break;
|
||||
case "1":
|
||||
status = "进行中";
|
||||
color = "#409EFF"; // 蓝色
|
||||
break;
|
||||
case "2":
|
||||
status = "已完成";
|
||||
color = "#67C23A"; // 绿色
|
||||
break;
|
||||
}
|
||||
let status = this.statusList.find(
|
||||
(ele) => ele.dictValue == value
|
||||
)?.dictLabel;
|
||||
let color = "#333";
|
||||
// switch (status) {
|
||||
// case "待启动":
|
||||
// color = "#fa721d"; // 橙色
|
||||
// break;
|
||||
// case "已提需求-待开发":
|
||||
// color = "#dd242a"; // 红色
|
||||
// break;
|
||||
// case "已提需求-开发中":
|
||||
// color = "#1686d8"; // 绿色
|
||||
// break;
|
||||
// case "已提需求-已完成":
|
||||
// color = "#5cb85c"; // 红色
|
||||
// break;
|
||||
// case "开发完成-待验收":
|
||||
// color = "#f7c731"; // 黄色
|
||||
// break;
|
||||
// case "开发完成-已验收":
|
||||
// color = "#8C33FF"; // 蓝色
|
||||
// break;
|
||||
// case "开发完成-已计收":
|
||||
// color = "#08fb9e"; // 绿色
|
||||
// break;
|
||||
// default:
|
||||
// color = "#000"; // 默认白色
|
||||
// break;
|
||||
// }
|
||||
return `<span style="color: ${color}">${status}</span>`;
|
||||
},
|
||||
},
|
||||
|
@ -198,6 +253,12 @@ export default {
|
|||
width: 100,
|
||||
fixed: "left",
|
||||
},
|
||||
{
|
||||
prop: "allDateWorkTime",
|
||||
label: "总计工时\n(天)",
|
||||
width: 100,
|
||||
fixed: "left",
|
||||
},
|
||||
{
|
||||
prop: "allWorkTime",
|
||||
label: "统计工时\n(天)",
|
||||
|
@ -210,6 +271,7 @@ export default {
|
|||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getDictData();
|
||||
this.getProjectProgress();
|
||||
},
|
||||
beforeDestroy() {},
|
||||
|
@ -245,7 +307,7 @@ export default {
|
|||
text-align: center;
|
||||
}
|
||||
::v-deep .el-table__footer td {
|
||||
background-color: #c0c4cc !important;
|
||||
background-color: #e0e1e3 !important;
|
||||
font-weight: bold;
|
||||
text-align: center; /* 确保合计行内容居中 */
|
||||
}
|
||||
|
@ -298,7 +360,7 @@ export default {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
width: 500px;
|
||||
margin-left: 350px;
|
||||
margin-left: 180px;
|
||||
}
|
||||
.date-range-label {
|
||||
white-space: nowrap;
|
||||
|
@ -322,8 +384,6 @@ export default {
|
|||
}
|
||||
::v-deep .el-table__fixed {
|
||||
box-shadow: 5px 20px 20px rgba(7, 7, 7, 0.5) !important;
|
||||
|
||||
|
||||
}
|
||||
.shadowBox {
|
||||
// position: absolute;
|
||||
|
@ -333,4 +393,18 @@ export default {
|
|||
// left: 450px;
|
||||
// z-index: 100;
|
||||
}
|
||||
|
||||
::v-deep .el-table__body-wrapper::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#scrollBox {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
position: absolute;
|
||||
top: 590px;
|
||||
#scrollBoxContent {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -60,6 +60,8 @@
|
|||
:columns="scrollableColumns"
|
||||
:tableData="executionData"
|
||||
:showPagination="false"
|
||||
:border="true"
|
||||
tableHeight="600"
|
||||
></CustomTable>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -70,6 +72,7 @@ import CustomTable from "@/components/CustomTable.vue";
|
|||
import { projectBank, projectApi } from "@/utils/api";
|
||||
|
||||
export default {
|
||||
name: "ProjectUser",
|
||||
components: {
|
||||
CustomTable,
|
||||
},
|
||||
|
@ -146,7 +149,6 @@ export default {
|
|||
newEndDate.setMonth(startDate.getMonth() + 3);
|
||||
this.dateRange[1] = newEndDate.toISOString().split("T")[0]; // 格式化为 YYYY-MM-DD
|
||||
}
|
||||
|
||||
},
|
||||
async handleProjectChange(projectId, time) {
|
||||
const res = await projectApi.getProjectDetail(projectId);
|
||||
|
@ -165,8 +167,6 @@ export default {
|
|||
endDate: this.dateRange[1] + " 23:59:59",
|
||||
projectId: this.projectInfo.projectId,
|
||||
});
|
||||
console.log(123,this.dateRange);
|
||||
|
||||
const start = new Date(this.timeRange[0]);
|
||||
const end = new Date(this.timeRange[1]);
|
||||
let index = 0;
|
||||
|
@ -180,7 +180,11 @@ export default {
|
|||
goToDetail(row) {
|
||||
this.$router.push({
|
||||
path: "/",
|
||||
query: { userId: row.userId, projectId: this.projectInfo.projectId,nickName:row.userName },
|
||||
query: {
|
||||
userId: row.userId,
|
||||
projectId: this.projectInfo.projectId,
|
||||
nickName: row.userName,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@ -222,6 +226,15 @@ export default {
|
|||
this.scrollableColumns = days;
|
||||
},
|
||||
},
|
||||
'$route.query': {
|
||||
deep: true,
|
||||
handler(newVal) {
|
||||
if (newVal.projectId != this.projectInfo.projectId&&newVal.projectId) {
|
||||
this.selectedProject = Number(newVal.projectId);
|
||||
this.handleProjectChange(this.selectedProject)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getProjectList();
|
||||
|
@ -366,4 +379,15 @@ export default {
|
|||
width: 100px;
|
||||
}
|
||||
}
|
||||
::v-deep .el-table__body {
|
||||
height: 100%;
|
||||
}
|
||||
::v-deep td.el-table__cell {
|
||||
border-bottom: none;
|
||||
border-top: none;
|
||||
vertical-align: top;
|
||||
}
|
||||
::v-deep tr.el-table__row.current-row .el-table__cell {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
placeholder="请选择用户"
|
||||
readonly
|
||||
@click.native="openUserSelectDialog"
|
||||
suffix-icon="el-icon-s-custom"
|
||||
></el-input>
|
||||
</div>
|
||||
<div class="date-range-container">
|
||||
|
@ -34,7 +35,8 @@
|
|||
:showPagination="false"
|
||||
:showSummary="true"
|
||||
:summaryMethod="getFixedColumnsSummaries"
|
||||
:tableHeight="600"
|
||||
tableHeight="600"
|
||||
:border="true"
|
||||
></CustomTable>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -55,6 +57,7 @@ import SelectUser from "@/components/SelectUser.vue";
|
|||
import { projectBank } from "@/utils/api";
|
||||
|
||||
export default {
|
||||
name: "UserProject",
|
||||
components: {
|
||||
CustomTable,
|
||||
SelectUser,
|
||||
|
@ -64,11 +67,11 @@ export default {
|
|||
fixedColumns: [],
|
||||
executionData: [],
|
||||
dateRange: [],
|
||||
selectedUserName: "超级管理员",
|
||||
selectedUserId: 1,
|
||||
selectedUserName: this.$store.state.user.nickName,
|
||||
selectedUserId: this.$store.state.user.id,
|
||||
selectedUser: {
|
||||
userId: 1,
|
||||
selectedUserName: "超级管理员",
|
||||
userId: this.$store.state.user.id,
|
||||
selectedUserName: this.$store.state.user.nickName,
|
||||
},
|
||||
userSelectDialogVisible: false,
|
||||
};
|
||||
|
@ -229,11 +232,13 @@ export default {
|
|||
}
|
||||
.selectBox {
|
||||
width: 200px;
|
||||
margin-left: 35px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
.selectBox span {
|
||||
width: 120px;
|
||||
}
|
||||
.content{
|
||||
|
||||
::v-deep .el-table {
|
||||
height: 100% !important;
|
||||
}
|
||||
|
@ -286,7 +291,7 @@ export default {
|
|||
}
|
||||
/* 调整合计行的样式 */
|
||||
::v-deep .el-table__footer td {
|
||||
background-color: #c0c4cc !important;
|
||||
background-color: #e0e1e3 !important;
|
||||
|
||||
font-weight: bold;
|
||||
color: #606266;
|
||||
|
@ -312,4 +317,9 @@ export default {
|
|||
bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
::v-deep .el-table__footer td {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,392 @@
|
|||
<template>
|
||||
<div class="project-list flex-row jcsb">
|
||||
<div class="left">
|
||||
<div style="margin-bottom: 20px; font-weight: bold">组织架构</div>
|
||||
<el-tree
|
||||
:data="deptOptions"
|
||||
:expand-on-click-node="false"
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
@node-click="handleNodeClick"
|
||||
style="margin-left: 50px"
|
||||
/>
|
||||
</div>
|
||||
<div class="right f1">
|
||||
<div class="search-bar">
|
||||
<el-form
|
||||
:inline="true"
|
||||
:model="searchForm"
|
||||
class="demo-form-inline"
|
||||
size="small"
|
||||
>
|
||||
<el-form-item label="统计任务" class="form-item">
|
||||
<el-select
|
||||
v-model="taskId"
|
||||
placeholder="选择任务"
|
||||
@change="getTaskUserList"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in taskList"
|
||||
:key="item.id"
|
||||
:label="item.taskName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item class="search-buttons">
|
||||
<el-button @click="onReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</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"
|
||||
@sortChange="sortChange"
|
||||
:default-sort="{
|
||||
prop: 'score',
|
||||
order: 'descending',
|
||||
}"
|
||||
>
|
||||
<template slot="operation" slot-scope="{ row }">
|
||||
<div class="operation-buttons">
|
||||
<el-button type="text" size="mini" @click="handleEdit(row, 0)"
|
||||
>查看详情</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</CustomTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CustomTable from "@/components/CustomTable.vue";
|
||||
import SelectUser from "@/components/SelectUser.vue";
|
||||
import { taskApi } from "@/utils/api";
|
||||
import { deptTreeSelect } from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "UserScore",
|
||||
components: {
|
||||
CustomTable,
|
||||
SelectUser,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
deptOptions: undefined,
|
||||
taskId: "",
|
||||
searchForm: {
|
||||
userIdList: [],
|
||||
userName: "",
|
||||
isAsc: "desc",
|
||||
},
|
||||
columns: [
|
||||
{ prop: "userName", label: "考核人员" },
|
||||
{
|
||||
prop: "score",
|
||||
label: "考核评分",
|
||||
sortable: "custom",
|
||||
type: "status",
|
||||
callback: (value, row) => {
|
||||
if (row.score) {
|
||||
return row.score;
|
||||
} else if (row.examineStatus == 1) {
|
||||
return row.manageScore;
|
||||
} else if (row.examineStatusSelf == 1) {
|
||||
return row.selfScore;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "examineStatus",
|
||||
label: "状态",
|
||||
type: "status",
|
||||
callback: (value, row) => {
|
||||
if (row.examineStatusSelf != 0 && row.examineStatus != 0)
|
||||
var color = "#333";
|
||||
else var color = "#FF7D00";
|
||||
|
||||
if (row.examineStatusSelf == 0 && row.examineStatus == 0) {
|
||||
return `<span style="color: ${color}">待个人自评/组长评分</span>`;
|
||||
} else if (row.examineStatusSelf == 0) {
|
||||
return `<span style="color: ${color}">待个人自评</span>`;
|
||||
} else if (row.examineStatus == 0) {
|
||||
return `<span style="color: ${color}">待组长评分</span>`;
|
||||
} else {
|
||||
return `<span style="color: ${color}">已完成</span>`;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "operation",
|
||||
label: "操作",
|
||||
width: "250",
|
||||
className: "operation-column",
|
||||
},
|
||||
],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
currentSelectedUser: [],
|
||||
pageNum: 1, // 当前页码
|
||||
pageSize: 10, // 每页条数
|
||||
taskList: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onSearch() {
|
||||
this.getTaskUserList();
|
||||
},
|
||||
onReset() {
|
||||
Object.keys(this.searchForm).forEach((key) => {
|
||||
this.searchForm[key] = "";
|
||||
});
|
||||
this.$refs.treeRef.setCurrentKey(null);
|
||||
this.searchForm.isAsc = "desc"
|
||||
this.initSort()
|
||||
// this.searchForm.taskId = this.taskList[0]?.id;
|
||||
|
||||
this.getTaskUserList();
|
||||
},
|
||||
handleNodeClick(data) {
|
||||
this.searchForm.deptId = data.id;
|
||||
this.getTaskUserList();
|
||||
},
|
||||
handleEdit(row, edit) {
|
||||
let score = "";
|
||||
if (row.score) {
|
||||
score = row.score;
|
||||
} else if (row.manageScore) {
|
||||
score = row.manageScore;
|
||||
} else {
|
||||
score = row.selfScore;
|
||||
}
|
||||
this.$router.push({
|
||||
path: "/projectBank/userScoreDetail",
|
||||
query: { taskId: row.taskId, examineId: row.id, score },
|
||||
});
|
||||
},
|
||||
getTaskUserList(init) {
|
||||
if (!this.taskId) return;
|
||||
taskApi
|
||||
.getTaskUserList({
|
||||
...this.searchForm,
|
||||
taskId: this.taskId,
|
||||
sortFiled: "all",
|
||||
pageNum: this.pageNum,
|
||||
pageSize: this.pageSize,
|
||||
})
|
||||
.then((res) => {
|
||||
this.tableData = res.rows;
|
||||
this.total = res.total;
|
||||
if (init === 1) {
|
||||
this.$nextTick(() => {
|
||||
this.initSort();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
getTaskList(init) {
|
||||
taskApi
|
||||
.getTaskList({
|
||||
pageNum: 1,
|
||||
pageSize: 100000,
|
||||
})
|
||||
.then((res) => {
|
||||
this.taskList = res.rows;
|
||||
this.taskId = res.rows[0]?.id;
|
||||
this.getTaskUserList(init);
|
||||
});
|
||||
},
|
||||
handleSizeChange(size) {
|
||||
this.pageSize = size;
|
||||
this.pageNum = 1; // 重置为第一页
|
||||
this.getTaskUserList();
|
||||
},
|
||||
handleCurrentChange(page) {
|
||||
this.pageNum = page;
|
||||
this.getTaskUserList();
|
||||
},
|
||||
getDeptTree() {
|
||||
deptTreeSelect().then((response) => {
|
||||
this.deptOptions = response.data;
|
||||
});
|
||||
},
|
||||
sortChange({ order }) {
|
||||
let ele = document.getElementsByClassName("is-sortable")[0];
|
||||
|
||||
let className = ele.getAttribute("class");
|
||||
if (order == "descending") {
|
||||
ele.setAttribute(
|
||||
"class",
|
||||
className.replace("ascending", "") + " descending"
|
||||
);
|
||||
} else if (order == "ascending") {
|
||||
ele.setAttribute(
|
||||
"class",
|
||||
className.replace("descending", "") + " ascending"
|
||||
);
|
||||
} else {
|
||||
ele.setAttribute(
|
||||
"class",
|
||||
className.replace("ascending", "").replace("descending", "")
|
||||
);
|
||||
}
|
||||
|
||||
this.searchForm.isAsc =
|
||||
order == "descending" ? "desc" : order == "ascending" ? "asc" : "";
|
||||
this.getTaskUserList();
|
||||
},
|
||||
initSort() {
|
||||
let ele = document.getElementsByClassName("is-sortable")[0];
|
||||
if(!ele) return
|
||||
let className = ele.getAttribute("class");
|
||||
ele.setAttribute("class", className.replace("ascending", "") + " descending");
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getDeptTree();
|
||||
this.getTaskList(1);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.project-list {
|
||||
padding: 0 20px;
|
||||
background-color: white;
|
||||
height: 88vh;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
.left {
|
||||
padding-top: 20px;
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
// box-shadow: 5px 0 5px rgba(0, 0, 0, 0.5); /* 阴影效果 */
|
||||
border-right: 1px solid #eeeeee;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.right {
|
||||
padding-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
::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;
|
||||
}
|
||||
.search-buttons ::v-deep .el-button {
|
||||
width: 90px !important;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
::v-deep .el-table {
|
||||
border: 1px solid #eee;
|
||||
border-bottom: none;
|
||||
}
|
||||
::v-deep .el-tree-node__content {
|
||||
font-weight: bold;
|
||||
}
|
||||
::v-deep .is-current > .el-tree-node__content:first-child .el-tree-node__label {
|
||||
color: #4096ff !important;
|
||||
}
|
||||
::v-deep .el-tree-node__content {
|
||||
height: 36px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,510 @@
|
|||
<template>
|
||||
<div class="conetentBox">
|
||||
<div class="titleBox flex-row aic jcsb">
|
||||
<div class="flex-row aic">
|
||||
<div class="block"></div>
|
||||
评分详情
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row jcsb aic userBox">
|
||||
<div>
|
||||
<el-form :inline="true" class="demo-form-inline" size="small">
|
||||
<el-form-item label="人员姓名" class="form-item">
|
||||
<el-select v-model="examineId" placeholder="请选择" @change="userChange" style="width: 300px">
|
||||
<el-option v-for="item in userList" :key="item.id" :label="item.userName" :value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="任务名称" class="form-item">
|
||||
<el-select v-model="examineTaskId" placeholder="请选择" @change="getUserList" style="width: 300px">
|
||||
<el-option v-for="item in taskList" :key="item.id" :label="item.taskName" :value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row jcsb aic userBox headerBox">
|
||||
<div class="flex-row aic" style="width: 200px">
|
||||
<i class="el-icon-user-solid" style="color: #4096ff; font-size: 24px; margin-right: 5px"></i>{{
|
||||
(userList.find((ele) => ele.id == examineId) || {}).userName}}
|
||||
</div>
|
||||
<div class="totalBox aic">
|
||||
<div>考核评分:</div>
|
||||
<div class="scoreTotal">{{ score }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="组长评估" name="first">
|
||||
<div class="tableBox">
|
||||
<div class="tableRow" v-for="(table, index) in tableData1" :key="index" style="margin-bottom: 20px">
|
||||
<div class="userBox">{{ table[0].reviewCategory }}</div>
|
||||
<el-table :data="table" style="width: 100%">
|
||||
<el-table-column v-for="(header, hIndex) in headers" :key="hIndex" :label="header.label"
|
||||
:prop="header.prop" :width="header.width" :minWidth="header.minWidth">
|
||||
</el-table-column>
|
||||
<el-table-column class-name="editCell" label="员工自评" prop="score"
|
||||
v-if="examineTask.templateId && table[0].reviewCategory == '发展与协作' && examineTask.templateType != 0"
|
||||
min-width="220">
|
||||
<template slot-scope="scope">
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4 }" placeholder="0/300" v-model="scope.row.remark"
|
||||
readonly maxlength="300" show-word-limit>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="评分" prop="score" :minWidth="420" class-name="sorceTableCell">
|
||||
<template slot-scope="scope">
|
||||
<div style="width: 88%; position: relative">
|
||||
<div>
|
||||
<div class="flex-row jcsb" style="
|
||||
margin-left: 10px;
|
||||
width: 90%;
|
||||
color: #999;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
">
|
||||
<div>0</div>
|
||||
<div v-show="scope.row.score != 10">10</div>
|
||||
</div>
|
||||
<div class="scoreText aic" v-show="scope.row.score != 0" :style="{
|
||||
left: scope.row.score * 9 - 5 + '%',
|
||||
}">
|
||||
<img src="@/assets/task/score.png" :style="{
|
||||
height: '28px',
|
||||
width: '34px',
|
||||
zIndex: 0,
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
}" alt="" />
|
||||
<div :style="{
|
||||
zIndex: 10,
|
||||
paddingLeft: scope.row.score != 10 ? '13px' : '9px',
|
||||
}">
|
||||
{{ scope.row.score }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="item in scope.row.score == 0
|
||||
? 0
|
||||
: scope.row.score - 1" :key="item" class="stepBox" :style="{
|
||||
left: item * 9 + 1.5 + '%',
|
||||
}"></div>
|
||||
<el-slider v-model="scope.row.score" :min="0" :max="10" :disabled="true" style="width: 90%"
|
||||
show-stops show-tooltip></el-slider>
|
||||
</div>
|
||||
<div class="statusText" v-show="scope.row.score == 0">
|
||||
暂未打分
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<div class="userBox">总体评价</div>
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4 }" placeholder="0/300" v-model="judgeContent"
|
||||
:readonly="true" maxlength="300" show-word-limit>
|
||||
</el-input>
|
||||
</div>
|
||||
<div style="margin-top: 20px;font-weight: bold;">组长:{{ manageUserName }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="个人自评" name="second">
|
||||
<div class="tableBox">
|
||||
<div class="tableRow" v-for="(table, index) in tableData2" :key="index" style="margin-bottom: 20px">
|
||||
<div class="userBox">{{ table[0].reviewCategory }}</div>
|
||||
<el-table :data="table" style="width: 100%">
|
||||
<el-table-column v-for="(header, hIndex) in headers" :key="hIndex" :label="header.label"
|
||||
:prop="header.prop" :width="header.width" :minWidth="header.minWidth">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="评分" prop="score" :minWidth="320" class-name="sorceTableCell"
|
||||
v-if="table[0].reviewCategory != '发展与协作'">
|
||||
<template slot-scope="scope">
|
||||
<div style="width: 88%; position: relative">
|
||||
<div>
|
||||
<div class="flex-row jcsb" style="
|
||||
margin-left: 10px;
|
||||
width: 90%;
|
||||
color: #999;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
">
|
||||
<div>0</div>
|
||||
<div v-show="scope.row.score != 10">10</div>
|
||||
</div>
|
||||
<div class="scoreText aic" v-show="scope.row.score != 0" :style="{
|
||||
left: scope.row.score * 9 - 5 + '%',
|
||||
}">
|
||||
<img src="@/assets/task/score.png" :style="{
|
||||
height: '28px',
|
||||
width: '34px',
|
||||
zIndex: 0,
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
}" alt="" />
|
||||
<div :style="{
|
||||
zIndex: 10,
|
||||
paddingLeft: scope.row.score != 10 ? '13px' : '9px',
|
||||
}">
|
||||
{{ scope.row.score }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="item in scope.row.score == 0
|
||||
? 0
|
||||
: scope.row.score - 1" :key="item" class="stepBox" :style="{
|
||||
left: item * 9 + 1.5 + '%',
|
||||
}"></div>
|
||||
<el-slider v-model="scope.row.score" :min="0" :max="10" @change="updateScore(scope.row)"
|
||||
:disabled="true" style="width: 90%" show-stops show-tooltip></el-slider>
|
||||
</div>
|
||||
<div class="statusText" v-show="scope.row.score == 0">
|
||||
暂未打分
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="自评总结" prop="score" width="150"
|
||||
v-if="(examineTask.templateId && table[0].reviewCategory == '发展与协作') || !examineTask.templateId">
|
||||
<template slot-scope="scope">
|
||||
<div>
|
||||
<el-button @click="handleEdit(scope.row)" type="text" size="mini"
|
||||
:class="{ hasEdit: !scope.row.remark }" style="font-weight: 600">{{ scope.row.remark ? "查看" :
|
||||
"暂未评价" }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="examineTask.templateType != 0 && table[0].reviewCategory != '发展与协作'&&examineTask.templateType" style="margin-top: 10px;">
|
||||
<div class="userBox">评价</div>
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4 }" placeholder="0/300" v-model="table[0].remarkCate"
|
||||
readonly maxlength="300" show-word-limit>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="(examineTask.templateType && examineTask.templateType == 0)||!examineTask.templateType">
|
||||
<div class="userBox">总体评价</div>
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4 }" placeholder="0/300" v-model="selfJudgeContent"
|
||||
:readonly="true" maxlength="300" show-word-limit>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-dialog title="自评总结" :visible.sync="dialogVisible" width="30%">
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4 }" placeholder="0/200" v-model="remark" readonly>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">关闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { taskApi } from "@/utils/api";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
examineTaskId: "",
|
||||
examineId: "",
|
||||
userList: [],
|
||||
dialogVisible: false,
|
||||
remark: "",
|
||||
headers: [
|
||||
{ label: "考核项", prop: "reviewItem", minWidth: 200 },
|
||||
{
|
||||
label: "评分标准",
|
||||
prop: "remarks",
|
||||
minWidth: 300,
|
||||
},
|
||||
],
|
||||
// 二维数组,每个子数组代表一个表格的数据
|
||||
tableData1: [],
|
||||
tableData2: [],
|
||||
taskList: [],
|
||||
judgeContent: "",
|
||||
selfJudgeContent: "",
|
||||
score: "",
|
||||
activeName: "first",
|
||||
manageUserName: "",
|
||||
examineTask: {},
|
||||
examineRemark: {}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleEdit(row) {
|
||||
this.dialogVisible = true;
|
||||
this.remark = row.remark;
|
||||
},
|
||||
|
||||
getTaskList(first) {
|
||||
taskApi
|
||||
.getTaskList({
|
||||
pageNum: 1,
|
||||
pageSize: 100000,
|
||||
})
|
||||
.then((res) => {
|
||||
this.taskList = res.rows;
|
||||
this.$nextTick(() => {
|
||||
this.examineTaskId = Number(this.$route.query.taskId);
|
||||
this.getUserList(first);
|
||||
});
|
||||
});
|
||||
},
|
||||
getUserList(first) {
|
||||
if (!this.examineTaskId) return;
|
||||
taskApi
|
||||
.getTaskUserList({
|
||||
taskId: this.examineTaskId,
|
||||
pageNum: 1,
|
||||
pageSize: 100000,
|
||||
})
|
||||
.then((res) => {
|
||||
this.userList = res.rows;
|
||||
this.$nextTick(() => {
|
||||
if (first !== 1) this.examineId = this.userList[0].id;
|
||||
else this.examineId = Number(this.$route.query.examineId);
|
||||
|
||||
this.getSocreDetail(0);
|
||||
this.getSocreDetail(1);
|
||||
});
|
||||
});
|
||||
},
|
||||
userChange() {
|
||||
this.getSocreDetail(0);
|
||||
this.getSocreDetail(1);
|
||||
},
|
||||
// 获取评分详情
|
||||
getSocreDetail(type) {
|
||||
let param = {
|
||||
examineTaskId: this.examineTaskId,
|
||||
reviewType: type,
|
||||
examineId: this.examineId,
|
||||
};
|
||||
taskApi.getTaskScoreDetail(param).then((res) => {
|
||||
this.examineRemark = res.data.remark
|
||||
|
||||
let objData = {};
|
||||
res.data.examineConfigDetailVoList.forEach((ele) => {
|
||||
if (!objData[ele.reviewCategory]) objData[ele.reviewCategory] = [];
|
||||
objData[ele.reviewCategory].push(ele);
|
||||
});
|
||||
if (type == 0) this.tableData1 = Object.values(objData);
|
||||
else this.tableData2 = Object.values(objData).map((ele, index) => {
|
||||
ele[0].remarkCate = this.examineRemark.find((item)=>item.reviewCategory==ele[0].reviewCategory)?.remark||''
|
||||
return ele
|
||||
});;
|
||||
this.judgeContent = res.data.examineUser.judgeContent;
|
||||
this.selfJudgeContent = res.data.examineUser.judgeContent;
|
||||
|
||||
|
||||
this.manageUserName = res.data.examineUser.manageUserName;
|
||||
this.examineTask = res.data.examineTask
|
||||
if (res.data.examineUser.score) {
|
||||
this.score = res.data.examineUser.score;
|
||||
} else if (res.data.examineUser.manageScore) {
|
||||
this.score = res.data.examineUser.manageScore;
|
||||
} else {
|
||||
this.score = res.data.examineUser.selfScore;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getTaskList(1);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.conetentBox {
|
||||
padding: 40px 30px 30px;
|
||||
background-color: #fff;
|
||||
height: 90vh;
|
||||
/* 设置整体高度 */
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.userBox {
|
||||
margin: 0 0 20px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::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: #fff;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
::v-deep .el-table th {
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::v-deep .el-table .cell {
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.statusText {
|
||||
color: #ff7d00;
|
||||
position: absolute;
|
||||
right: -50px;
|
||||
top: 26px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.hasEdit {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
::v-deep .el-dialog {
|
||||
margin-top: 15% !important;
|
||||
}
|
||||
|
||||
.tableBox {
|
||||
height: 75%;
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.tableBox ::v-deep .el-table {
|
||||
border-left: 1px solid #eeeeee;
|
||||
border-right: 1px solid #eeeeee;
|
||||
}
|
||||
|
||||
.tableRow {
|
||||
padding: 20px;
|
||||
box-shadow: 0 0 20px #0000000f;
|
||||
}
|
||||
|
||||
.titleBox {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.headerBox {
|
||||
padding: 20px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 2px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.block {
|
||||
width: 4px;
|
||||
height: 24px;
|
||||
background-color: #4096ff;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.stepBox {
|
||||
position: absolute;
|
||||
top: 33px;
|
||||
height: 14px;
|
||||
width: 3px;
|
||||
background-color: #fff;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.totalBox {
|
||||
width: 180px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.scoreTotal {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #ff7d00;
|
||||
}
|
||||
|
||||
::v-deep .sorceTableCell .cell {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
::v-deep .el-table__body .el-table__cell {
|
||||
padding: 20px 40px;
|
||||
}
|
||||
|
||||
::v-deep .el-table__header .el-table__cell {
|
||||
padding: 14px 30px;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__item.is-active {
|
||||
color: #4096ff;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__item {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__active-bar {
|
||||
height: 3px;
|
||||
width: 32px !important;
|
||||
left: 20px;
|
||||
}
|
||||
</style>
|
|
@ -61,7 +61,7 @@
|
|||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-register-footer">
|
||||
<span>Copyright © 2018-2024 ruoyi.vip All Rights Reserved.</span>
|
||||
<span>unissense.tech</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="handleDataScope" icon="el-icon-circle-check"
|
||||
v-hasPermi="['system:role:edit']">数据权限</el-dropdown-item>
|
||||
<el-dropdown-item command="handleAuthUser" icon="el-icon-user"
|
||||
<el-dropdown-item command="handleAuthUser" icon="el-icon-s-custom"
|
||||
v-hasPermi="['system:role:edit']">分配用户</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
|
|
@ -0,0 +1,570 @@
|
|||
<template>
|
||||
<div class="conetentBox">
|
||||
<div class="titleBox flex-row aic jcsb">
|
||||
<div class="flex-row aic">
|
||||
<div class="block"></div>
|
||||
{{ examineTask.taskName }}
|
||||
</div>
|
||||
<div class="flex-row jcfs f1" style="gap: 20px" v-if="isEdit">
|
||||
<el-button type="default" style="width: 90px" @click="saveScoreCheck(0)">保存</el-button>
|
||||
<el-button type="primary" style="width: 90px" @click="saveScoreCheck(1)">提交</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row jcsb aic userBox headerBox">
|
||||
<div class="flex-row aic" style="width: 200px;">
|
||||
<i class="el-icon-user-solid" style="color: #4096ff; font-size: 24px; margin-right: 5px"></i>{{
|
||||
examineUser.userName }}
|
||||
</div>
|
||||
<div class="totalBox aic" v-if="!isNormal">
|
||||
<div>考核评分:</div>
|
||||
<div class="scoreTotal">{{ saveData.manageScore }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tableBox">
|
||||
<div class="tableRow" v-for="(table, index) in tableData" :key="index" style="margin-bottom: 20px">
|
||||
<div class="userBox">{{ table[0].reviewCategory }}</div>
|
||||
<el-table :data="table" style="width: 100%">
|
||||
<el-table-column v-for="(header, hIndex) in headers" :key="hIndex" :label="header.label" :prop="header.prop"
|
||||
:width="header.width" :minWidth="header.minWidth">
|
||||
</el-table-column>
|
||||
<el-table-column class-name="editCell" label="员工自评" prop="score"
|
||||
v-if="!isNormal && examineTask.templateId && table[0].reviewCategory == '发展与协作' && examineTask.templateType != 0"
|
||||
min-width="320">
|
||||
<template slot-scope="scope">
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4 }" placeholder="0/300" v-model="scope.row.remark"
|
||||
readonly maxlength="300" show-word-limit>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="评分" prop="score"
|
||||
:minWidth="400" class-name="sorceTableCell"
|
||||
v-if="(isNormal && table[0].reviewCategory != '发展与协作') || !isNormal">
|
||||
<template slot-scope="scope">
|
||||
<div style="width: 88%; position: relative">
|
||||
<div>
|
||||
<div class="flex-row jcsb" style="
|
||||
margin-left: 10px;
|
||||
width: 90%;
|
||||
color: #999;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
">
|
||||
<div>0</div>
|
||||
<div v-show="scope.row.score != 10">10</div>
|
||||
</div>
|
||||
<div class="scoreText aic" v-show="scope.row.score != 0" :style="{
|
||||
left: scope.row.score * 9 - 5 + '%',
|
||||
}">
|
||||
<img src="@/assets/task/score.png" :style="{
|
||||
height: '28px',
|
||||
width: '34px',
|
||||
zIndex: 0,
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
}" alt="" />
|
||||
<div :style="{
|
||||
zIndex: 10,
|
||||
paddingLeft: scope.row.score != 10 ? '13px' : '9px',
|
||||
}">
|
||||
{{ scope.row.score }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="item in scope.row.score == 0
|
||||
? 0
|
||||
: scope.row.score - 1" :key="item" class="stepBox" :style="{
|
||||
left: item * 9 + 1.5 + '%',
|
||||
}"></div>
|
||||
<el-slider v-model="scope.row.score" :min="0" :max="10" @change="updateScore(scope.row)"
|
||||
:disabled="!isEdit" style="width: 90%" show-stops show-tooltip></el-slider>
|
||||
</div>
|
||||
<div class="statusText" v-show="scope.row.score == 0">
|
||||
暂未打分
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column class-name="editCell" label="自评总结" prop="score"
|
||||
v-if="(isNormal && examineTask.templateId && table[0].reviewCategory == '发展与协作') || (isNormal && !examineTask.templateId)"
|
||||
minWidth="140">
|
||||
<template slot-scope="scope">
|
||||
<div>
|
||||
<el-button v-if="isEdit" @click="handleEdit(scope.row)" type="text" size="mini"
|
||||
:class="{ hasEdit: !scope.row.remark }" style="font-weight: 600">{{ scope.row.remark ? "查看" : "暂未评价"
|
||||
}}
|
||||
<i style="color: #4096ff; font-size: 14px" class="el-icon-edit el-icon--right"></i></el-button>
|
||||
<el-button v-if="!isEdit" @click="handleEdit(scope.row)" type="text" size="mini"
|
||||
:class="{ hasEdit: !scope.row.remark }" style="font-weight: 600">{{ scope.row.remark ? "查看" : "暂未评价"
|
||||
}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="examineTask.templateType != 0 && isNormal && table[0].reviewCategory != '发展与协作'"
|
||||
style="margin-top: 10px;">
|
||||
<div class="userBox">评价</div>
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4 }" placeholder="0/300" v-model="table[0].remarkCate"
|
||||
:readonly="!isEdit" maxlength="300" show-word-limit>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="(isNormal && examineTask.templateType == 0) || (!isNormal)">
|
||||
<div class="userBox">总体评价</div>
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4 }" placeholder="0/300" v-model="saveData.judgeContent"
|
||||
:readonly="!isEdit" maxlength="300" show-word-limit>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog title="自评总结" :visible.sync="dialogVisible" width="30%">
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4 }" placeholder="0/200" v-model="remark" :readonly="!isEdit"
|
||||
maxlength="200" show-word-limit>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="cancelEdit">{{
|
||||
isEdit ? "取 消" : "关闭"
|
||||
}}</el-button>
|
||||
<el-button type="primary" @click="saveEdit" v-if="isEdit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { taskApi } from "@/utils/api";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
userId: "",
|
||||
taskId: "",
|
||||
isNormal: false,
|
||||
dialogVisible: false,
|
||||
selectRow: false,
|
||||
saveStatus: "",
|
||||
remark: "",
|
||||
isEdit: "",
|
||||
headers: [
|
||||
{ label: "考核项", prop: "reviewItem", minWidth: 150 },
|
||||
{
|
||||
label: "评分标准",
|
||||
prop: "remarks",
|
||||
minWidth:300
|
||||
// width: '25%',
|
||||
},
|
||||
],
|
||||
// 二维数组,每个子数组代表一个表格的数据
|
||||
tableData: [
|
||||
// 可以根据需要添加更多表格数据
|
||||
],
|
||||
// 获取评分的参数
|
||||
examineTaskId: "",
|
||||
examineId: "",
|
||||
reviewType: "",
|
||||
reviewType: "",
|
||||
// 保存评分的参数
|
||||
saveData: {
|
||||
examineId: "",
|
||||
examineStatus: "",
|
||||
examineStatusSelf: "",
|
||||
manageScore: "",
|
||||
taskId: "",
|
||||
judgeContent: "",
|
||||
examineDetailList: [],
|
||||
},
|
||||
examineUser: {},
|
||||
examineTask: {},
|
||||
examineRemark: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateScore(row) {
|
||||
// 实时计算组长评分数
|
||||
if (!this.isNormal)
|
||||
this.saveData.manageScore =
|
||||
this.tableData
|
||||
.flat()
|
||||
.reduce((total, ele) => (ele.score || 0) * ele.weight + total, 0) /
|
||||
10;
|
||||
},
|
||||
// 获取评分详情
|
||||
getSocreDetail() {
|
||||
let param = {
|
||||
examineTaskId: this.examineTaskId,
|
||||
reviewType: this.reviewType,
|
||||
userId: this.userId,
|
||||
};
|
||||
|
||||
param.examineId = this.examineId;
|
||||
taskApi.getTaskScoreDetail(param).then((res) => {
|
||||
let objData = {};
|
||||
this.examineRemark = res.data.remark;
|
||||
res.data.examineConfigDetailVoList.forEach((ele, index) => {
|
||||
if (!objData[ele.reviewCategory]) objData[ele.reviewCategory] = [];
|
||||
objData[ele.reviewCategory].push(ele);
|
||||
});
|
||||
|
||||
this.tableData = Object.values(objData).map((ele, index) => {
|
||||
ele[0].remarkCate = this.examineRemark.find((item)=>item.reviewCategory==ele[0].reviewCategory)?.remark||''
|
||||
return ele
|
||||
});
|
||||
if (!this.examineId) {
|
||||
} else {
|
||||
this.saveData.judgeContent = res.data.examineUser.judgeContent || "";
|
||||
this.saveData.manageScore = res.data.examineUser.manageScore || "";
|
||||
}
|
||||
this.examineId = res.data.examineUser.id;
|
||||
this.examineTask = res.data.examineTask;
|
||||
this.examineUser = res.data.examineUser;
|
||||
if (this.examineTask.templateType == 0) {
|
||||
this.saveData.judgeContent = res.data.examineUser.selfJudgeContent
|
||||
}
|
||||
if (
|
||||
res.data.examineUser.examineStatusSelf == 1 &&
|
||||
res.data.examineUser.examineStatus == 1
|
||||
)
|
||||
this.isEdit = false;
|
||||
});
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.dialogVisible = true;
|
||||
this.selectRow = row;
|
||||
this.remark = this.selectRow.remark;
|
||||
},
|
||||
saveEdit() {
|
||||
if (this.remark.length > 200) {
|
||||
this.$message({
|
||||
message: "自评总结限制200个字符",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.dialogVisible = false;
|
||||
this.selectRow.remark = this.remark;
|
||||
},
|
||||
cancelEdit() {
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
saveDataSet(status) {
|
||||
if (!this.isNormal) {
|
||||
this.saveData.examineStatus = status;
|
||||
} else {
|
||||
this.saveData.examineStatusSelf = status;
|
||||
}
|
||||
this.saveData.examineDetailList = this.tableData.flat().map((ele) => ({
|
||||
score: ele.score,
|
||||
configId: ele.id,
|
||||
remark: ele.remark,
|
||||
reviewCategory: ele.reviewCategory,
|
||||
}));
|
||||
if (this.examineTask.templateType != 0 && this.isNormal) {
|
||||
this.saveData.examineRemarkList = this.tableData.filter((ele) => ele[0].reviewCategory != '发展与协作').map((ele) => ({ reviewCategory: ele[0].reviewCategory, remark: ele[0].remarkCate }))
|
||||
} else {
|
||||
this.saveData.examineRemarkList = []
|
||||
}
|
||||
|
||||
this.saveData.taskId = this.examineTaskId;
|
||||
this.saveData.examineId = this.examineId;
|
||||
},
|
||||
saveScoreCheck(status) {
|
||||
// 组装保存数据
|
||||
this.saveDataSet(status);
|
||||
|
||||
// 保存
|
||||
if (status == 0) {
|
||||
if (this.isNormal && this.examineTask.templateType == 0) {
|
||||
this.saveData.selfJudgeContent = this.saveData.judgeContent
|
||||
this.saveData.judgeContent = ''
|
||||
}
|
||||
this.saveScore();
|
||||
return;
|
||||
}
|
||||
// 提交
|
||||
if (this.saveData.examineDetailList.filter((ele) => !ele.score && ele.reviewCategory != '发展与协作' || (!ele.score && !this.isNormal)).length) {
|
||||
this.$alert("存在未评分绩效项,请完善后再试", "提交失败", {
|
||||
confirmButtonText: "确定",
|
||||
type: "warning",
|
||||
});
|
||||
} else if (
|
||||
this.saveData.examineDetailList.filter((ele) => !ele.remark && ele.reviewCategory == '发展与协作').length &&
|
||||
this.isNormal &&
|
||||
status
|
||||
) {
|
||||
this.$alert("发展与协作下的自评总结为必填,请完善后再试", "提交失败", {
|
||||
confirmButtonText: "确定",
|
||||
type: "warning",
|
||||
});
|
||||
} else if (
|
||||
this.saveData.examineDetailList.filter((ele) => ele.remark.length < 100 && ele.reviewCategory == '发展与协作').length &&
|
||||
this.isNormal &&
|
||||
status
|
||||
) {
|
||||
this.$alert("发展与协作下的自评总结最少100个字符,请完善后再试", "提交失败", {
|
||||
confirmButtonText: "确定",
|
||||
type: "warning",
|
||||
});
|
||||
} else if (this.saveData.judgeContent.length > 300 && !this.isNormal && this.examineTask.templateType == '0') {
|
||||
this.$message({
|
||||
message: "总体评价限制300个字符",
|
||||
type: "warning",
|
||||
});
|
||||
} else if (!this.saveData.judgeContent.length && !this.isNormal) {
|
||||
this.$message({
|
||||
message: "总体评价为必填",
|
||||
type: "warning",
|
||||
});
|
||||
} else if (!this.saveData.judgeContent.length && this.isNormal && this.examineTask.templateType == 0) {
|
||||
this.$message({
|
||||
message: "个人总体评价为必填",
|
||||
type: "warning",
|
||||
});
|
||||
} else if (this.saveData.examineRemarkList.filter((ele) => !ele.remark).length && this.isNormal && this.examineTask.templateType != 0 && this.isNormal) {
|
||||
this.$message({
|
||||
message: "存在未填写大类评价,请完善后再试",
|
||||
type: "warning",
|
||||
});
|
||||
} else if (this.saveData.examineRemarkList.filter((ele) => ele.remark.length < 100).length && this.isNormal && this.examineTask.templateType != 0 && this.isNormal) {
|
||||
this.$message({
|
||||
message: "大类评价最少100个字符,请完善后再试",
|
||||
type: "warning",
|
||||
});
|
||||
} else {
|
||||
if (status) {
|
||||
this.$confirm(
|
||||
"提交后将无法修改,该操作不可逆,请确认后再试",
|
||||
"确认提交绩效评分",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}
|
||||
).then(() => {
|
||||
if (this.isNormal && this.examineTask.templateType == 0) {
|
||||
this.saveData.selfJudgeContent = this.saveData.judgeContent
|
||||
this.saveData.judgeContent = ''
|
||||
}
|
||||
this.saveScore();
|
||||
});
|
||||
} else {
|
||||
if (this.isNormal && this.examineTask.templateType == 0) {
|
||||
this.saveData.selfJudgeContent = this.saveData.judgeContent
|
||||
this.saveData.judgeContent = ''
|
||||
}
|
||||
this.saveScore();
|
||||
}
|
||||
}
|
||||
},
|
||||
saveScore() {
|
||||
taskApi.saveTaskUserScore(this.saveData).then((res) => {
|
||||
this.$message({
|
||||
message: "操作成功",
|
||||
type: "success",
|
||||
});
|
||||
this.$router.go(-1);
|
||||
});
|
||||
},
|
||||
getRouteData() {
|
||||
this.isNormal = this.$route.query.isNormal || "";
|
||||
this.examineTaskId = this.$route.query.examineTaskId;
|
||||
this.examineId = this.$route.query.examineId;
|
||||
this.reviewType = this.$route.query.reviewType;
|
||||
this.saveData.reviewType = this.$route.query.reviewType;
|
||||
this.isEdit = this.$route.query.edit == 1 ? true : false;
|
||||
this.userId = this.$store.state.user.id;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getRouteData();
|
||||
},
|
||||
mounted() {
|
||||
this.getSocreDetail();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.conetentBox {
|
||||
padding: 40px 30px 30px;
|
||||
background-color: #fff;
|
||||
height: 90vh;
|
||||
/* 设置整体高度 */
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.userBox {
|
||||
margin: 0 0 20px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::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: #fff;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
::v-deep .el-table th {
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::v-deep .el-table .cell {
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.statusText {
|
||||
color: #ff7d00;
|
||||
position: absolute;
|
||||
right: -50px;
|
||||
top: 26px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.hasEdit {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
::v-deep .el-dialog {
|
||||
margin-top: 15% !important;
|
||||
}
|
||||
|
||||
.tableBox {
|
||||
height: 75%;
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.tableBox ::v-deep .el-table {
|
||||
border-left: 1px solid #eeeeee;
|
||||
border-right: 1px solid #eeeeee;
|
||||
}
|
||||
|
||||
.tableRow {
|
||||
padding: 20px;
|
||||
box-shadow: 0 0 20px #0000000f;
|
||||
}
|
||||
|
||||
.titleBox {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.headerBox {
|
||||
padding: 20px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 2px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.block {
|
||||
width: 4px;
|
||||
height: 24px;
|
||||
background-color: #4096ff;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.stepBox {
|
||||
position: absolute;
|
||||
top: 33px;
|
||||
height: 14px;
|
||||
width: 3px;
|
||||
background-color: #fff;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.totalBox {
|
||||
width: 180px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.scoreTotal {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: #ff7d00;
|
||||
}
|
||||
|
||||
::v-deep .el-table__body .sorceTableCell .cell {
|
||||
margin-bottom: 20px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
::v-deep .sorceTableCell.el-table__cell {
|
||||
padding-left: 10% !important;
|
||||
/* padding-right: 10px !important; */
|
||||
}
|
||||
|
||||
::v-deep .el-table__body .el-table__cell {
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
::v-deep .el-table__header .el-table__cell {
|
||||
padding: 14px 30px;
|
||||
|
||||
}
|
||||
|
||||
::v-deep .el-table__header .el-table__cell .cell {
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
|
||||
::v-deep .editCell {
|
||||
padding-right: 20px !important;
|
||||
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,227 @@
|
|||
<template>
|
||||
<div class="appraisal-manager">
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="进行中" name="进行中">
|
||||
<div class="assessment-container flex-row">
|
||||
<div v-for="item in taskList['0']" :key="item.id" class="taskBox">
|
||||
<div class="card-content">
|
||||
<div class="cardBox flex-col">
|
||||
<img
|
||||
src="@/assets/task/titleIcon.png"
|
||||
alt=""
|
||||
style="width: 32px; height: 34px"
|
||||
/>
|
||||
<div class="flex-row aic nameBox">
|
||||
<div>{{ item.taskName }}</div>
|
||||
<el-tag type="warning" size="mini">进行中</el-tag>
|
||||
</div>
|
||||
<div class="timeBox">
|
||||
截止时间:{{ item.endTime.split(" ")[0] }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-actions aic">
|
||||
<div class="peopleNumber">
|
||||
考核人数:{{ item.peopleNumber }}
|
||||
</div>
|
||||
<div
|
||||
class="action-buttons aic"
|
||||
@click="viewDetails(item, 1)"
|
||||
:class="{ waitBtn: !item.taskEditFlag }"
|
||||
>
|
||||
考核评分
|
||||
<img
|
||||
src="@/assets/task/right.png"
|
||||
alt=""
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="!taskList['0']" description="暂无数据"></el-empty>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="已过期" name="已过期">
|
||||
<div class="assessment-container flex-row">
|
||||
<div v-for="item in taskList['2']" :key="item.id" class="taskBox">
|
||||
<div class="card-content">
|
||||
<div class="cardBox flex-col">
|
||||
<img
|
||||
src="@/assets/task/titleIcon.png"
|
||||
alt=""
|
||||
style="width: 32px; height: 34px"
|
||||
/>
|
||||
<div class="flex-row aic nameBox">
|
||||
<div>{{ item.taskName }}</div>
|
||||
<el-tag type="info" size="mini">已过期</el-tag>
|
||||
</div>
|
||||
<div class="timeBox">
|
||||
截止时间:{{ item.endTime.split(" ")[0] }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-actions aic">
|
||||
<div class="peopleNumber">
|
||||
考核人数:{{ item.peopleNumber }}
|
||||
</div>
|
||||
<div
|
||||
class="action-buttons aic"
|
||||
@click="viewDetails(item, 0)"
|
||||
:class="{ waitBtn: !item.taskEditFlag }"
|
||||
>
|
||||
查看详情
|
||||
<img
|
||||
src="@/assets/task/right.png"
|
||||
alt=""
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="!taskList['2']" description="暂无数据"></el-empty>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { taskApi } from "@/utils/api";
|
||||
|
||||
export default {
|
||||
name: "AppraisalManager",
|
||||
data() {
|
||||
return {
|
||||
activeName: "进行中",
|
||||
taskList: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getTaks() {
|
||||
taskApi.getTaskListSelf().then((res) => {
|
||||
this.taskList = res.data;
|
||||
});
|
||||
},
|
||||
viewDetails(row, isEdit) {
|
||||
if (!row.taskEditFlag) {
|
||||
this.$message({
|
||||
message: "分数正在计算中,请等待计算完成",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 查看考核详情
|
||||
this.$router.push({
|
||||
path: "/workAppraisal/managerUser",
|
||||
query: { taskId: row.id, isEdit },
|
||||
});
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getTaks();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.appraisal-manager {
|
||||
padding: 30px;
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 100px); /* 设置整体高度 */
|
||||
}
|
||||
|
||||
.assessment-container {
|
||||
max-height: 650px; /* 减去标题高度 */
|
||||
overflow-y: auto; /* 允许垂直滚动 */
|
||||
gap: 2%;
|
||||
padding-top: 20px;
|
||||
padding-left: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.taskBox {
|
||||
width: 23%;
|
||||
height: 200px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 20px 0 0 0;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.cardBox {
|
||||
gap: 20px;
|
||||
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.status-actions {
|
||||
display: flex;
|
||||
justify-content: space-between; /* 两端对齐 */
|
||||
align-items: center; /* 垂直居中 */
|
||||
height: 44px;
|
||||
background-color: #f7faff;
|
||||
padding: 0 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.peopleNumber {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-weight: bold; /* 状态文本加粗 */
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 10px; /* 按钮之间的间距 */
|
||||
color: #4096ff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
.statusText {
|
||||
font-size: 26px;
|
||||
margin: 20px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
.timeBox {
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
font-weight: bold;
|
||||
}
|
||||
::v-deep .el-tabs__item.is-active {
|
||||
color: #4096ff;
|
||||
}
|
||||
::v-deep .el-tabs__item {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
}
|
||||
::v-deep .el-tabs__active-bar {
|
||||
height: 3px;
|
||||
width: 32px !important;
|
||||
left: 10px;
|
||||
}
|
||||
.nameBox {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
gap: 10px;
|
||||
}
|
||||
::v-deep .el-tag--warning {
|
||||
color: #ea741e;
|
||||
}
|
||||
.waitBtn {
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,320 @@
|
|||
<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.userName"
|
||||
placeholder="考核人员"
|
||||
readonly
|
||||
@click.native="openUserSelectDialog"
|
||||
style="width: 300px"
|
||||
><el-button slot="append" icon="el-icon-s-custom"></el-button
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" class="form-item">
|
||||
<el-select
|
||||
v-model="searchForm.examineStatus"
|
||||
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 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="f1 df">
|
||||
<CustomTable
|
||||
:columns="columns"
|
||||
:tableData="tableData"
|
||||
:total="total"
|
||||
:show-selection="false"
|
||||
:show-index="true"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
@sortChange="sortChange"
|
||||
tableHeight="495px"
|
||||
>
|
||||
<template slot="operation" slot-scope="{ row }">
|
||||
<div class="operation-buttons">
|
||||
<el-button
|
||||
v-if="isEdit == 1 && row.examineStatus == '0'"
|
||||
@click="handleEdit(row, 1)"
|
||||
type="text"
|
||||
size="mini"
|
||||
>去评分</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="row.examineStatus == '1' || isEdit == 0"
|
||||
type="text"
|
||||
size="mini"
|
||||
@click="handleEdit(row, 0)"
|
||||
>查看详情</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</CustomTable>
|
||||
</div>
|
||||
<SelectUser
|
||||
:dialogVisible="userSelectDialogVisible"
|
||||
:currentSelectedUser="currentSelectedUser"
|
||||
:showSelection="true"
|
||||
:highligt="false"
|
||||
@confirm="handleUserConfirm"
|
||||
@close="handleUserClose"
|
||||
/>
|
||||
</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: {
|
||||
userIdList: [],
|
||||
userName: "",
|
||||
examineStatus: "",
|
||||
},
|
||||
taskId: "",
|
||||
isEdit: "",
|
||||
columns: [
|
||||
{ prop: "userName", label: "考核人员" },
|
||||
{ prop: "manageScore", label: "考核评分", sortable: "custom" },
|
||||
{
|
||||
prop: "examineStatus",
|
||||
label: "状态",
|
||||
type: "status",
|
||||
callback: (value) => {
|
||||
if (value == 0) var color = "#EA741D";
|
||||
else var color = "#999";
|
||||
|
||||
return `<span style="color: ${color}">${
|
||||
value == 0 ? "待评分" : "已完成"
|
||||
}</span>`;
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "operation",
|
||||
label: "操作",
|
||||
width: "250",
|
||||
className: "operation-column",
|
||||
},
|
||||
],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
userSelectDialogVisible: false,
|
||||
currentSelectedUser: [],
|
||||
pageNum: 1, // 当前页码
|
||||
pageSize: 10, // 每页条数
|
||||
statusList: [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "待评分", value: "0" },
|
||||
{ label: "已完成", value: "1" },
|
||||
],
|
||||
isAsc: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onSearch() {
|
||||
this.taskUserList();
|
||||
},
|
||||
onReset() {
|
||||
Object.keys(this.searchForm).forEach((key) => {
|
||||
this.searchForm[key] = "";
|
||||
});
|
||||
this.currentSelectedUser = [];
|
||||
this.taskUserList();
|
||||
},
|
||||
handleEdit(row, edit) {
|
||||
this.$router.push({
|
||||
path: "/workAppraisal/detail",
|
||||
query: {
|
||||
edit,
|
||||
examineTaskId: this.taskId,
|
||||
examineId: row.id,
|
||||
reviewType: 0,
|
||||
},
|
||||
});
|
||||
},
|
||||
taskUserList() {
|
||||
taskApi
|
||||
.getTaskUserList({
|
||||
...this.searchForm,
|
||||
taskId: this.taskId,
|
||||
pageNum: this.pageNum,
|
||||
pageSize: this.pageSize,
|
||||
isAsc: this.isAsc,
|
||||
sortFiled: "manageScore",
|
||||
})
|
||||
.then((res) => {
|
||||
this.tableData = res.rows;
|
||||
this.total = res.total;
|
||||
});
|
||||
},
|
||||
handleSizeChange(size) {
|
||||
this.pageSize = size;
|
||||
this.pageNum = 1; // 重置为第一页
|
||||
this.taskUserList();
|
||||
},
|
||||
handleCurrentChange(page) {
|
||||
this.pageNum = page;
|
||||
this.taskUserList();
|
||||
},
|
||||
openUserSelectDialog() {
|
||||
this.userSelectDialogVisible = true;
|
||||
},
|
||||
handleUserConfirm(data) {
|
||||
this.searchForm.userName = data.map((ele) => ele.nickName).join(",");
|
||||
this.searchForm.userIdList = data.map((ele) => ele.userId);
|
||||
},
|
||||
handleUserClose() {
|
||||
this.userSelectDialogVisible = false;
|
||||
},
|
||||
sortChange({ order }) {
|
||||
this.isAsc =
|
||||
order == "descending" ? "desc" : order == "ascending" ? "asc" : "";
|
||||
this.taskUserList();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.taskId = this.$route.query.taskId;
|
||||
this.isEdit = this.$route.query.isEdit;
|
||||
},
|
||||
mounted() {
|
||||
this.taskUserList();
|
||||
},
|
||||
};
|
||||
</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-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 .operation-buttons .el-button {
|
||||
padding: 4px 8px;
|
||||
margin: 0 2px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
.search-buttons ::v-deep .el-button {
|
||||
width: 90px !important;
|
||||
height: 36px;
|
||||
}
|
||||
.waitBtn {
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,234 @@
|
|||
<template>
|
||||
<div class="appraisal-manager">
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="进行中" name="进行中">
|
||||
<div class="assessment-container flex-row">
|
||||
<div v-for="item in taskList['0']" :key="item.id" class="taskBox">
|
||||
<div class="card-content">
|
||||
<div class="cardBox flex-col">
|
||||
<img
|
||||
src="@/assets/task/titleIcon.png"
|
||||
alt=""
|
||||
style="width: 32px; height: 34px"
|
||||
/>
|
||||
<div class="flex-row aic nameBox">
|
||||
<div>{{ item.taskName }}</div>
|
||||
<el-tag type="warning" size="mini">进行中</el-tag>
|
||||
</div>
|
||||
<div class="timeBox">
|
||||
截止时间:{{ item.endTime.split(" ")[0] }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-actions aic">
|
||||
<div class="peopleNumber">
|
||||
<!-- 考核人数:{{ item.peopleNumber }} -->
|
||||
</div>
|
||||
<div
|
||||
class="action-buttons aic"
|
||||
@click="viewDetails(item, item.examineStatusSelf == 1 ?0: 1)"
|
||||
:class="{ waitBtn: !item.taskEditFlag }"
|
||||
>
|
||||
{{ item.examineStatusSelf == 1 ? "查看详情" : "考核评分" }}
|
||||
<img
|
||||
src="@/assets/task/right.png"
|
||||
alt=""
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="!taskList['0']" description="暂无数据"></el-empty
|
||||
></el-tab-pane>
|
||||
<el-tab-pane label="已过期" name="已过期">
|
||||
<div class="assessment-container flex-row">
|
||||
<div v-for="item in taskList['2']" :key="item.id" class="taskBox">
|
||||
<div class="card-content">
|
||||
<div class="cardBox flex-col">
|
||||
<img
|
||||
src="@/assets/task/titleIcon.png"
|
||||
alt=""
|
||||
style="width: 32px; height: 34px"
|
||||
/>
|
||||
<div class="flex-row aic nameBox">
|
||||
<div>{{ item.taskName }}</div>
|
||||
<el-tag type="info" size="mini">已过期</el-tag>
|
||||
</div>
|
||||
<div class="timeBox">
|
||||
截止时间:{{ item.endTime.split(" ")[0] }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-actions aic">
|
||||
<div class="peopleNumber">
|
||||
<!-- 考核人数:{{ item.peopleNumber }} -->
|
||||
</div>
|
||||
<div
|
||||
class="action-buttons aic"
|
||||
@click="viewDetails(item, 0)"
|
||||
:class="{ waitBtn: item.taskEditFlag }"
|
||||
>
|
||||
查看详情
|
||||
<img
|
||||
src="@/assets/task/right.png"
|
||||
alt=""
|
||||
style="width: 16px; height: 16px"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="!taskList['2']" description="暂无数据"></el-empty>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { taskApi } from "@/utils/api";
|
||||
|
||||
export default {
|
||||
name: "NormalWorker",
|
||||
data() {
|
||||
return {
|
||||
activeName: "进行中",
|
||||
taskList: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getTaks() {
|
||||
taskApi.getTaskListSelfNormal().then((res) => {
|
||||
this.taskList = res.data;
|
||||
});
|
||||
},
|
||||
viewDetails(item, edit) {
|
||||
if (!item.taskEditFlag) {
|
||||
this.$message({
|
||||
message: "分数正在计算中,请等待计算完成",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 查看考核详情
|
||||
this.$router.push({
|
||||
path: "/workAppraisal/detail",
|
||||
query: {
|
||||
edit,
|
||||
isNormal: true,
|
||||
examineTaskId: item.id,
|
||||
reviewType: 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
created() {
|
||||
console.log(123);
|
||||
|
||||
this.getTaks();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.appraisal-manager {
|
||||
padding: 30px;
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 100px); /* 设置整体高度 */
|
||||
}
|
||||
|
||||
.assessment-container {
|
||||
max-height: 650px; /* 减去标题高度 */
|
||||
overflow-y: auto; /* 允许垂直滚动 */
|
||||
gap: 2%;
|
||||
padding-top: 20px;
|
||||
padding-left: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.taskBox {
|
||||
width: 23%;
|
||||
height: 200px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 20px 0 0 0;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.cardBox {
|
||||
gap: 20px;
|
||||
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.status-actions {
|
||||
display: flex;
|
||||
justify-content: space-between; /* 两端对齐 */
|
||||
align-items: center; /* 垂直居中 */
|
||||
height: 44px;
|
||||
background-color: #f7faff;
|
||||
padding: 0 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.peopleNumber {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-weight: bold; /* 状态文本加粗 */
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 10px; /* 按钮之间的间距 */
|
||||
color: #4096ff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
.statusText {
|
||||
font-size: 26px;
|
||||
margin: 20px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
.timeBox {
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
font-weight: bold;
|
||||
}
|
||||
::v-deep .el-tabs__item.is-active {
|
||||
color: #4096ff;
|
||||
}
|
||||
::v-deep .el-tabs__item {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
}
|
||||
::v-deep .el-tabs__active-bar {
|
||||
height: 3px;
|
||||
width: 32px !important;
|
||||
left: 10px;
|
||||
}
|
||||
.nameBox {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
gap: 10px;
|
||||
}
|
||||
::v-deep .el-tag--warning {
|
||||
color: #ea741e;
|
||||
}
|
||||
.waitBtn {
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,493 @@
|
|||
<template>
|
||||
<div class="appraisal-manager ">
|
||||
|
||||
<div class="flex-row aic" style="gap: 20px;margin-bottom: 20px;">
|
||||
<div>看板名称</div>
|
||||
<div>
|
||||
<el-input v-model="moduleName" class="filter-input" readonly>
|
||||
</el-input>
|
||||
</div>
|
||||
<div>看板类型</div>
|
||||
|
||||
<el-select v-model="moduleType" placeholder="看板类型" clearable style="width: 200px" readonly disabled>
|
||||
<el-option label="年度考核" value="0" />
|
||||
<el-option label="季度考核" value="1" />
|
||||
<el-option label="年度考核" value="2" />
|
||||
</el-select>
|
||||
|
||||
</div>
|
||||
<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"
|
||||
@change="showAllScoure">
|
||||
<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 v-show="leftType !== '组长评估绩效指标' && leftType !== '个人自评绩效指标'">
|
||||
<div class="flex-row jcsb setHeader">
|
||||
<div style="width: 15%; font-weight: 600">考核项</div>
|
||||
<div style="width: 50%; font-weight: 600;text-align: center;">评分标准</div>
|
||||
<div class="center" style="width: 25%; font-weight: 600">
|
||||
权重占比
|
||||
</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" class="flex-row jcsb aic contentRow">
|
||||
<div style="width: 15%; font-weight: 500">
|
||||
{{ item.reviewItem }}
|
||||
</div>
|
||||
<div class="center" style="width: 50%;padding: 0 20px;">
|
||||
{{ item.remarks }}
|
||||
</div>
|
||||
<div class="center" style="width: 25%">
|
||||
<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 :disabled="isDisabled" v-model="item.weight" :max="20"
|
||||
@change="changeTotal"></el-slider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="leftType == '组长评估绩效指标' || leftType == '个人自评绩效指标'">
|
||||
<div v-for="(ele, index) in
|
||||
(scoreList.find((ele) => ele.title == leftType) || {})
|
||||
.list || []
|
||||
|
||||
" :key="index">
|
||||
<div style="margin: 10px;font-weight: 600;font-size: 18px;">{{ ele.title }}</div>
|
||||
<div class="flex-row jcsb setHeader">
|
||||
<div style="width: 15%; font-weight: 600">考核项</div>
|
||||
<div style="width: 50%; font-weight: 600;text-align: center;">评分标准</div>
|
||||
<div class="center" style="width: 25%; font-weight: 600">
|
||||
权重占比
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 50px;">
|
||||
<div v-for="(item, index) in (ele.rightArr || [])" class="flex-row jcsb aic contentRow">
|
||||
<div style="width: 15%; font-weight: 600">
|
||||
{{ item.reviewItem }}
|
||||
</div>
|
||||
<div class="center" style="width: 50%;padding: 0 20px;">
|
||||
{{ item.remarks }}
|
||||
</div>
|
||||
<div class="center" style="width: 25%">
|
||||
<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 v-for="item in item.weight == 0 ? 0 : item.weight - 1" :key="item"
|
||||
class="stepBox" :style="{
|
||||
left: item * 5 + 4 + '%',
|
||||
}"></div>
|
||||
<el-slider :disabled="isDisabled" v-model="item.weight" :max="20"
|
||||
@change="changeTotal"></el-slider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { taskApi } from "@/utils/api";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
moduleId: '',
|
||||
moduleName: '',
|
||||
moduleType: '',
|
||||
scoreList: [],
|
||||
letfValue: "",
|
||||
selectLeftRow: '',
|
||||
leftType: '',
|
||||
isDisabled: true
|
||||
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
getDetail() {
|
||||
taskApi.getTaskModelSet(this.moduleId).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
|
||||
),
|
||||
},
|
||||
];
|
||||
if (!arrList["2"]) {
|
||||
this.scoreList.length = 2
|
||||
}
|
||||
|
||||
this.dialogVisible2 = true;
|
||||
});
|
||||
},
|
||||
selectLeft(row) {
|
||||
this.leftType = ''
|
||||
this.selectLeftRow = row.title + row.type;
|
||||
},
|
||||
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
|
||||
);
|
||||
});
|
||||
});
|
||||
},
|
||||
showAllScoure(val) {
|
||||
if (val)
|
||||
this.leftType = val
|
||||
else {
|
||||
this.leftType = parseInt(this.selectLeftRow)==0?'组长评估绩效指标':'个人自评绩效指标'
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.moduleId = this.$route.query.id
|
||||
this.moduleName = this.$route.query.moduleName
|
||||
this.moduleType = this.$route.query.moduleType
|
||||
this.getDetail()
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.appraisal-manager {
|
||||
padding: 30px;
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 100px);
|
||||
/* 设置整体高度 */
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-top: 1px solid #ccc;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 20%;
|
||||
padding: 20px;
|
||||
border-right: 1px solid #ccc;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 80%;
|
||||
padding: 20px 40px;
|
||||
height: 100%;
|
||||
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;
|
||||
}
|
||||
|
||||
.contentRow {
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.setHeader {
|
||||
background-color: #f8f8f9;
|
||||
padding: 15px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,492 @@
|
|||
<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.templateName" placeholder="看板名称" style="width: 300px"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建人" class="form-item">
|
||||
<el-input v-model="searchForm.createByName" placeholder="创建人" readonly
|
||||
@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">
|
||||
<el-select v-model="searchForm.templateType" placeholder="状态" clearable style="width: 300px">
|
||||
<el-option label="年度考核" value="0" />
|
||||
<el-option label="季度考核" value="1" />
|
||||
<el-option label="年度考核" value="2" />
|
||||
</el-select>
|
||||
</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 type="text" size="mini" @click="viewDetail(row)">看板详情</el-button>
|
||||
<!-- <el-button @click="viewDetail(row)" type="text" size="mini">编辑</el-button> -->
|
||||
<el-button type="text" size="mini" @click="delTask(row)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</CustomTable>
|
||||
</div>
|
||||
<SelectUser :dialogVisible="userSelectDialogVisible" :currentSelectedUser="currentSelectedUser"
|
||||
:currentSelectedUserName="currentSelectedUserName" :highligt="true" ref="selectUserRef"
|
||||
@confirm="handleUserConfirm" @close="handleUserClose" />
|
||||
</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: {
|
||||
templateName: "",
|
||||
templateType: "",
|
||||
createBy: "",
|
||||
createByName: ''
|
||||
},
|
||||
columns: [
|
||||
{ prop: "templateName", label: "看板名称" },
|
||||
{
|
||||
prop: "templateType", label: "考核类型", type: "status",
|
||||
callback: (value) => {
|
||||
if (value == 0) var type = "年度考核";
|
||||
else if (value == 1) var type = "季度考核";
|
||||
else if (value == 2) var type = "月度考核";
|
||||
return type
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "createByName",
|
||||
label: "创建人",
|
||||
},
|
||||
{
|
||||
prop: "createTime",
|
||||
label: "创建日期",
|
||||
|
||||
},
|
||||
{
|
||||
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: [],
|
||||
modelList: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
dialogVisible1(newVal) {
|
||||
if (newVal) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.selectUserRef?.$refs.customTableRef?.handleCurrentChange(
|
||||
1
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onSearch() {
|
||||
this.getTaskModelList();
|
||||
},
|
||||
onReset() {
|
||||
Object.keys(this.searchForm).forEach((key) => {
|
||||
this.searchForm[key] = "";
|
||||
});
|
||||
this.getTaskModelList();
|
||||
},
|
||||
getTaskModelList() {
|
||||
taskApi
|
||||
.getTaskModel({
|
||||
...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.getTaskModelList();
|
||||
},
|
||||
handleCurrentChange(page) {
|
||||
this.pageNum = page;
|
||||
this.getTaskModelList();
|
||||
},
|
||||
openUserSelectDialog() {
|
||||
this.userSelectDialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.selectUserRef?.$refs.customTableRef?.clearSelection();
|
||||
this.currentSelectedUser = [this.searchForm.createBy];
|
||||
this.currentSelectedUserName = [this.searchForm.createName];
|
||||
|
||||
});
|
||||
},
|
||||
handleUserConfirm(data) {
|
||||
this.searchForm.createByName = data[0].nickName;
|
||||
this.searchForm.createBy = data[0].userId;
|
||||
},
|
||||
handleUserClose() {
|
||||
this.userSelectDialogVisible = false;
|
||||
},
|
||||
addTask() {
|
||||
this.isEdit = false;
|
||||
this.dialogVisible1 = true;
|
||||
this.taskData = {
|
||||
id: "",
|
||||
taskName: "",
|
||||
peopleNumberDetail: "",
|
||||
userIdList: [],
|
||||
endTime: "",
|
||||
peopleNumber: 0,
|
||||
};
|
||||
},
|
||||
viewDetail(row) {
|
||||
this.$router.push({
|
||||
path: `/workAppraisal/moduleDetail?id=${row.id}&moduleName=${row.templateName}&moduleType=${row.templateType}`
|
||||
})
|
||||
},
|
||||
delTask(row) {
|
||||
this.$confirm(
|
||||
"是否确认删除该条看板",
|
||||
"确认删除",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}
|
||||
).then(() => {
|
||||
taskApi.delTaskModule(row.id).then((res) => {
|
||||
this.$message({
|
||||
type: "success",
|
||||
message: "删除成功!",
|
||||
});
|
||||
this.getTaskModelList();
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getTaskModelList();
|
||||
},
|
||||
};
|
||||
</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>
|
|
@ -0,0 +1,876 @@
|
|||
<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="templateId">
|
||||
<div>
|
||||
<el-radio-group v-model="taskData.templateType" @change="() => {
|
||||
taskData.templateId = ''
|
||||
}" :disabled="taskData.taskStatus == 2">
|
||||
<el-radio label="0">年度看板</el-radio>
|
||||
<el-radio label="1">季度看板</el-radio>
|
||||
<el-radio label="2">月度看板</el-radio>
|
||||
</el-radio-group>
|
||||
<el-select v-model="taskData.templateId" placeholder="选择看板" clearable style="width: 300px"
|
||||
:disabled="taskData.taskStatus == 2">
|
||||
<el-option v-for="item in modelList.filter((ele) => ele.templateType == taskData.templateType)"
|
||||
:key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
</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 flex-row jcsb" style="font-weight: 600">
|
||||
<div>评估维度</div>
|
||||
<div>累计权重</div>
|
||||
</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 setText" style="margin-bottom: 20px">
|
||||
<div style="width: 50%; font-weight: 600;text-align: left;">指标</div>
|
||||
<div class="center" style="width: 50%; font-weight: 600;text-align: left;padding-left: 5px;">
|
||||
权重占比
|
||||
</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;color: #333;">
|
||||
{{ 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 style="font-weight: 500;">0</div>
|
||||
<div v-show="item.weight != 20" style="font-weight: 500;">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 ? '7px' : '5px',
|
||||
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,
|
||||
templateType: '0',
|
||||
templateId: '',
|
||||
year: "",
|
||||
taskStatus: 0
|
||||
},
|
||||
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" }],
|
||||
templateId: [{ required: true, message: "请选择任务看板", trigger: "blur" }],
|
||||
},
|
||||
dialogVisible2: false,
|
||||
letfValue: "",
|
||||
scoreList: [],
|
||||
selectRow: {},
|
||||
modelList: [],
|
||||
};
|
||||
},
|
||||
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,
|
||||
templateType: '0',
|
||||
templateId: '',
|
||||
taskStatus: 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.templateId = row.templateId;
|
||||
this.taskData.templateType = row.templateType;
|
||||
this.taskData.taskStatus = row.taskStatus;
|
||||
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
|
||||
),
|
||||
},
|
||||
];
|
||||
if (!arrList["2"]) {
|
||||
this.scoreList.length = 2
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
},
|
||||
async getTaskModel() {
|
||||
const res = await taskApi.getTaskModel({ pageNum: 1, pageSize: 1000 });
|
||||
this.modelList = res.rows.map((ele) => ({
|
||||
value: ele.id,
|
||||
label: ele.templateName,
|
||||
templateType: ele.templateType
|
||||
}));
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getTaskList();
|
||||
this.getTaskModel();
|
||||
},
|
||||
};
|
||||
</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 #E6E6E6;
|
||||
// border-top: 1px solid #E6E6E6;
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 40%;
|
||||
padding:0;
|
||||
// border-right: 1px solid #E6E6E6;
|
||||
height: 450px;
|
||||
overflow: auto;
|
||||
box-shadow: 5px 0 20px 5px #eeeeee;
|
||||
z-index: 100;
|
||||
>div{
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 60%;
|
||||
// padding: 0 40px;
|
||||
height: 450px;
|
||||
overflow: auto;
|
||||
|
||||
div.center {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
>div{
|
||||
padding-left: 3cap;
|
||||
padding-right: 30px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
background-color: #FAFAFA;
|
||||
text-align: right;
|
||||
padding:15px 5px;
|
||||
}
|
||||
|
||||
.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: transparent;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.totalBox {
|
||||
width: 150px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,258 @@
|
|||
<template>
|
||||
<div class="leftBox">
|
||||
<div class="topBox">
|
||||
<el-date-picker
|
||||
v-model="selectMonth"
|
||||
type="month"
|
||||
format="yyyy年MM月"
|
||||
:clearable="false"
|
||||
style="margin-bottom: 10px; width: 150px"
|
||||
@change="changeMonth"
|
||||
prefix-icon="false"
|
||||
value-format="yyyy-MM-dd"
|
||||
/>
|
||||
<div>
|
||||
<i
|
||||
class="el-icon-arrow-left"
|
||||
style="font-size: 18px; font-weight: bold"
|
||||
@click="preMonth"
|
||||
></i>
|
||||
<i
|
||||
class="el-icon-arrow-right"
|
||||
style="font-size: 18px; font-weight: bold"
|
||||
@click="nextMonth"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-calendar v-model="selectMonthValue">
|
||||
<template #dateCell="{ data }">
|
||||
<div
|
||||
:key="data.day"
|
||||
:id="data.day"
|
||||
@click="searchLog(data, isOverDay(data.day))"
|
||||
:class="{
|
||||
dayBox: true,
|
||||
disabled: isOverDay(data.day),
|
||||
hasLog: calendarList.filter(
|
||||
(ele) => ele.date == data.day + ' 00:00:00' && ele.state != -1
|
||||
).length,
|
||||
timeout: isTimeOut(
|
||||
data.day,
|
||||
calendarList.find(
|
||||
(ele) => ele.date == data.day + ' 00:00:00' && ele.state == -1
|
||||
)
|
||||
),
|
||||
}"
|
||||
>
|
||||
{{ data.day.split("-")[2] }}
|
||||
</div>
|
||||
</template>
|
||||
</el-calendar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { workLogApi, projectApi } from "@/utils/api";
|
||||
|
||||
export default {
|
||||
name: "LeftMonth",
|
||||
data() {
|
||||
return {
|
||||
selectMonth: this.moment().format("YYYY-MM"),
|
||||
nowDay: this.moment().format("YYYY-MM-DD 23:59:59"),
|
||||
userId: this.$store.state.user.id,
|
||||
calendarList: [],
|
||||
selectDay:
|
||||
this.moment().date() > 10
|
||||
? "0" + this.moment().date()
|
||||
: this.moment().date(),
|
||||
selectMonthValue: this.moment().format("YYYY-MM-DD"),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
preMonth() {
|
||||
this.selectDay = "01";
|
||||
this.selectMonth = this.moment(this.selectMonth)
|
||||
.subtract(1, "months")
|
||||
.format(`YYYY-MM-${this.selectDay}`);
|
||||
this.changeMonth();
|
||||
},
|
||||
nextMonth() {
|
||||
this.selectDay = "01";
|
||||
this.selectMonth = this.moment(this.selectMonth)
|
||||
.add(1, "months")
|
||||
.format(`YYYY-MM-${this.selectDay}`);
|
||||
this.changeMonth();
|
||||
},
|
||||
searchLog(data, overDay) {
|
||||
let checkMonth = this.moment(data.day).format("YYYY-MM");
|
||||
let selectMonth = this.moment(this.selectMonth).format("YYYY-MM");
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (overDay) {
|
||||
this.$message({
|
||||
message: "不能超过当前时间",
|
||||
type: "warning",
|
||||
});
|
||||
this.$emit("setDisableTable", true);
|
||||
} else {
|
||||
this.selectDay = data.day.split("-")[2];
|
||||
if (selectMonth !== checkMonth) {
|
||||
this.selectMonth = data.day;
|
||||
this.getLogMonth();
|
||||
}
|
||||
this.$emit("searchDay", data.day + " 00:00:00");
|
||||
this.$emit("setDisableTable", false);
|
||||
}
|
||||
});
|
||||
},
|
||||
isOverDay(data) {
|
||||
return new Date(data).getTime() > new Date(this.nowDay).getTime();
|
||||
},
|
||||
isTimeOut(data, hasDay) {
|
||||
if (
|
||||
hasDay &&
|
||||
new Date(data).getTime() <
|
||||
new Date(this.moment().format(`YYYY-MM-DD 00:00:00`)).getTime()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
getLogMonth() {
|
||||
if (this.$route.query.userId) {
|
||||
this.userId = this.$route.query.userId;
|
||||
}
|
||||
let start = this.moment(this.selectMonth)
|
||||
.startOf("month")
|
||||
.format(`YYYY-MM-DD 00:00:00`);
|
||||
let end = this.moment(this.selectMonth)
|
||||
.endOf("month")
|
||||
.format("YYYY-MM-DD 23:59:59");
|
||||
|
||||
workLogApi
|
||||
.getLogData({
|
||||
startDate: start,
|
||||
endDate: end,
|
||||
userId: this.userId,
|
||||
})
|
||||
.then((res) => {
|
||||
this.calendarList = res.data;
|
||||
});
|
||||
},
|
||||
changeMonth() {
|
||||
this.$nextTick(() => {
|
||||
if (this.selectMonth == this.selectMonthValue) return;
|
||||
this.selectDay = "01";
|
||||
this.selectMonth = this.moment(this.selectMonth).format(
|
||||
`YYYY-MM-${this.selectDay}`
|
||||
);
|
||||
this.selectMonthValue = this.selectMonth;
|
||||
this.searchLog(
|
||||
{ day: this.selectMonth },
|
||||
this.isOverDay(this.selectMonth)
|
||||
);
|
||||
this.searchLog(
|
||||
{ day: this.selectMonth },
|
||||
this.isOverDay(this.selectMonth)
|
||||
);
|
||||
this.getLogMonth();
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {},
|
||||
mounted() {
|
||||
this.getLogMonth();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.leftBox ::v-deep .el-calendar__header {
|
||||
display: none;
|
||||
}
|
||||
.leftBox ::v-deep .el-date-editor {
|
||||
.el-input__inner {
|
||||
border: none;
|
||||
padding-left: 0;
|
||||
font-size: 18px;
|
||||
color: #333333;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.leftBox ::v-deep .el-calendar__body {
|
||||
padding: 0 !important;
|
||||
thead {
|
||||
th {
|
||||
font-family: PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
.hasLog {
|
||||
background: #71afff;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
}
|
||||
.timeout {
|
||||
background: #f7c940;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
}
|
||||
td {
|
||||
border: none;
|
||||
padding: 10px 0;
|
||||
background-color: #fff;
|
||||
|
||||
.el-calendar-day {
|
||||
text-align: center;
|
||||
padding: 0 !important;
|
||||
font-size: 16px;
|
||||
background-color: #fff;
|
||||
|
||||
span {
|
||||
font-family: cursive !important;
|
||||
}
|
||||
}
|
||||
&.next,
|
||||
&.prev {
|
||||
color: #333 !important;
|
||||
}
|
||||
&.is-selected .dayBox:not(.disabled) {
|
||||
border-radius: 50% !important;
|
||||
background-color: #4096ff;
|
||||
color: #fff;
|
||||
}
|
||||
.el-calendar-day {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
line-height: 40px;
|
||||
border-radius: 50% !important;
|
||||
&:hover {
|
||||
background-color: #88bdfd;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topBox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
i {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.leftBox ::v-deep .disabled {
|
||||
background-color: #fff !important;
|
||||
color: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
border-radius: 50% !important;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,659 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<div class="topBox" v-show="!disableTable">
|
||||
<div>
|
||||
<div class="topTitle">当日日志</div>
|
||||
<div class="topText">
|
||||
总计填报工时:<span> {{ totalWorkTime }} </span>天
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-button type="primary" @click="handleAdd">
|
||||
<el-icon class="el-icon-plus" />
|
||||
添加日志
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<el-table :data="tableData" style="width: 100%" class="tableBox">
|
||||
<el-table-column label="序号" width="70" type="index" />
|
||||
<el-table-column label="项目名称" prop="projectName">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<div v-if="scope.row.edit">
|
||||
<el-select v-model="scope.row.projectId" placeholder="时间匹配的项目" class="filter-select" @change="
|
||||
(val) => {
|
||||
getVersionList(val, scope.row);
|
||||
}
|
||||
">
|
||||
<el-option v-for="item in projectListFilter" :key="item.projectId" :label="item.projectName"
|
||||
:value="item.projectId" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div v-show="!scope.row.edit">{{ scope.row.projectName }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="版本" prop="versionNumber">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<div v-if="scope.row.edit">
|
||||
<el-select v-model="scope.row.versionId" placeholder="含有可选需求的版本" class="filter-select"
|
||||
ref="versionSelectRef" @change="
|
||||
(val) => {
|
||||
getDemandList(val, scope.row);
|
||||
}
|
||||
">
|
||||
<el-option v-for="item in versionList.filter((ele) => ele.type == 0)" :key="item.id" :label="item.title"
|
||||
:value="item.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div v-show="!scope.row.edit">{{ scope.row.versionNumber }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目需求">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<div v-if="scope.row.edit">
|
||||
<el-select v-model="scope.row.demandId" placeholder="时间匹配且非已结束的需求" class="filter-select"
|
||||
@change="openContent(scope.row)" ref="demandSelectRef">
|
||||
<el-option v-for="item in demandList" :key="item.id" :label="item.title" :value="item.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div v-show="!scope.row.edit">{{ scope.row.title }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工作内容" prop="workContent">
|
||||
<template #default="scope">
|
||||
<div @click="openContent(scope.row)">
|
||||
<el-popover placement="bottom" width="400" trigger="click" v-model="scope.row.showContent"
|
||||
:key="scope.row.loggerId">
|
||||
<el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="scope.row.workContent"
|
||||
:disabled="!scope.row.edit">
|
||||
</el-input>
|
||||
<div style="margin-top: 10px; text-align: right" v-show="!disableTable && scope.row.edit">
|
||||
<el-button type="primary" @click="saveContent(scope.row, 1)">确认</el-button>
|
||||
<el-button @click="saveContent(scope.row, 0)">取消</el-button>
|
||||
</div>
|
||||
<div slot="reference">
|
||||
<div style="cursor: pointer" :class="{
|
||||
contentText: true,
|
||||
noneText: !scope.row.workContent,
|
||||
}">
|
||||
{{ scope.row.workContent || "请输入" }}
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="工时分配" prop="estimatedWorkHours">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<div v-if="scope.row.edit">
|
||||
<el-select v-model="scope.row.workTime" placeholder="请选择工时" class="filter-select" filterable allow-create>
|
||||
<el-option v-for="item in workTimeList" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div v-show="!scope.row.edit">{{ scope.row.workTime }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<el-button type="text" @click="handleFile(scope.row)">
|
||||
附件详情
|
||||
</el-button>
|
||||
<el-button type="text" @click="handleEdit(scope.row)" v-show="!disableTable">
|
||||
{{ scope.row.loggerId && !scope.row.edit ? "编辑" : "确认" }}
|
||||
</el-button>
|
||||
|
||||
<el-button type="text" @click="handleDelete(scope.row, scope.$index)" style="color: #666"
|
||||
v-show="!disableTable">
|
||||
{{ scope.row.loggerId && !scope.row.edit ? "删除" : "取消" }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-dialog title="附件" :visible.sync="dialogVisibleFile" width="780px" :close-on-click-modal="false">
|
||||
<div v-loading="fileLoading">
|
||||
<div v-show="!disableTable">
|
||||
<el-upload class="upload-demo" ref="upload" :action="fileUpload" :show-file-list="false" :auto-upload="true"
|
||||
:multiple="true" :before-upload="beforeUpload" :on-success="successUpload" :headers="{
|
||||
Authorization: 'Bearer ' + token,
|
||||
}" :data="{}">
|
||||
<div class="flex-row aic" style="gap: 10px; margin-bottom: 10px">
|
||||
<el-button slot="trigger" size="small" type="default"
|
||||
style="width: 80px; color: #333; font-weight: 500">上传附件</el-button>
|
||||
<div slot="tip" style="color: #999999; font-size: 12px">
|
||||
单个附件限制100M
|
||||
</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div style="height: 500px;overflow: auto;">
|
||||
<el-table :data="fileList" style="width: 100%;height: 100%;" class="tableBox">
|
||||
<el-table-column label="名称" prop="fileNewName">
|
||||
<template #default="scope">
|
||||
<div class="flex-row aic fileBox" style="gap: 10px;">
|
||||
<img class="" :src="filePng" v-if="getFileType(scope.row.fileNewName) == 'file'"> </img>
|
||||
<img class="" :src="imagePng" v-else-if="getFileType(scope.row.fileNewName) == 'image'"> </img>
|
||||
<img class="" :src="zipPng" v-else-if="getFileType(scope.row.fileNewName) == 'zip'"> </img>
|
||||
<div class="downFileBox" @click="downFile(scope.row.fileUrl)">
|
||||
{{ scope.row.fileNewName }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" prop="fileName">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{ scope.row.fileNewName.split(".")[1] }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" prop="createTime">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
{{
|
||||
scope.row.createTime || moment().format("YYYY-MM-DD HH:mm:ss")
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<el-button type="text" @click="delFile(scope.row)" v-show="!disableTable">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer" v-show="!disableTable">
|
||||
<el-button @click="cancalFile">取消</el-button>
|
||||
<el-button type="primary" @click="saveFile">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { demandApi, workLogApi, projectApi, systemApi } from "@/utils/api";
|
||||
import { getToken } from "@/utils/auth";
|
||||
import filePng from "@/assets/images/file.png";
|
||||
import zipPng from "@/assets/images/zip.png";
|
||||
import imagePng from "@/assets/images/image.png";
|
||||
export default {
|
||||
name: "RightTable",
|
||||
props: {
|
||||
selectDay: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
disableTable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statusList: [],
|
||||
userId: this.$store.state.user.id,
|
||||
projectId: "",
|
||||
tableData: [],
|
||||
projectList: [],
|
||||
versionList: [],
|
||||
demandList: [],
|
||||
hasTimeLong: 0,
|
||||
workTimeList: [],
|
||||
oldContent: "",
|
||||
dialogVisibleFile: false,
|
||||
fileList: [],
|
||||
checkRow: {},
|
||||
fileUpload: systemApi.fileUpload,
|
||||
token: getToken(),
|
||||
fileLoading: false,
|
||||
filePng,
|
||||
zipPng,
|
||||
imagePng,
|
||||
delFileArr: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
if (this.$route.query.userId) {
|
||||
this.userId = this.$route.query.userId;
|
||||
this.userName = this.$route.query.nickName;
|
||||
this.projectId = this.$route.query.projectId;
|
||||
}
|
||||
//获取项目列表
|
||||
},
|
||||
getLogList() {
|
||||
workLogApi
|
||||
.getLogDataDetail({
|
||||
createBy: this.userId,
|
||||
loggerDate: this.selectDay,
|
||||
projectId: this.projectId,
|
||||
})
|
||||
.then((res) => {
|
||||
this.tableData = res.data.map((ele) => {
|
||||
ele.edit = false;
|
||||
ele.showContent = false;
|
||||
return ele;
|
||||
});
|
||||
if (this.tableData.length == 0 && !this.disableTable) {
|
||||
this.handleAdd();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDelete(row, index) {
|
||||
if (row.loggerId && !row.edit) {
|
||||
this.$confirm("此操作将永久删除该版本号, 是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
workLogApi.delLog(row.loggerId).then((res) => {
|
||||
this.$message({
|
||||
type: "success",
|
||||
message: "删除成功!",
|
||||
});
|
||||
this.getLogList();
|
||||
this.$emit("changeCaleder");
|
||||
});
|
||||
})
|
||||
.catch(() => { });
|
||||
} else if (!row.loggerId) {
|
||||
this.tableData = this.tableData.filter((ele, ind) => index != ind);
|
||||
} else {
|
||||
row.edit = false;
|
||||
}
|
||||
// 实现删除逻辑
|
||||
},
|
||||
handleAdd() {
|
||||
if (this.tableData.filter((ele) => ele.edit).length) {
|
||||
this.$message({
|
||||
message: "请先保存未完成数据",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.versionList = [];
|
||||
this.demandList = [];
|
||||
this.oldContent = "";
|
||||
this.getDayTime("add");
|
||||
},
|
||||
async handleEdit(row) {
|
||||
if (
|
||||
this.tableData.filter((ele) => ele.edit && ele.loggerId != row.loggerId)
|
||||
.length
|
||||
) {
|
||||
this.$message({
|
||||
message: "请先保存未完成数据",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (row.edit) {
|
||||
if (row.workTime > this.workTimeList[this.workTimeList.length - 1]) {
|
||||
this.$modal.msgWarning("工时超过最大值");
|
||||
return;
|
||||
} else if (!row.workContent) {
|
||||
this.$modal.msgWarning("工作日志不能为空");
|
||||
return;
|
||||
} else if ((String(row.workTime).split(".")[1] || "").length > 1) {
|
||||
this.$modal.msgWarning("工时只允许一位小数");
|
||||
return;
|
||||
} else if (row.workTime <= 0) {
|
||||
this.$modal.msgWarning("工时不能小于等于0");
|
||||
return;
|
||||
}
|
||||
|
||||
let param = {
|
||||
...row,
|
||||
};
|
||||
if (row.loggerId) {
|
||||
await workLogApi.editLog(param);
|
||||
} else {
|
||||
await workLogApi.addLog(param);
|
||||
this.$emit("changeCaleder");
|
||||
}
|
||||
if (this.delFileArr.length) {
|
||||
systemApi.delFile({ ids: this.delFileArr }).then((res) => {
|
||||
this.fileList = []
|
||||
this.delFileArr = []
|
||||
});
|
||||
}
|
||||
this.$modal.msgSuccess("操作成功");
|
||||
row.edit = false;
|
||||
this.getLogList();
|
||||
} else {
|
||||
row.edit = true;
|
||||
this.oldContent = row.workContent;
|
||||
this.getDayTime("edit", row);
|
||||
this.getVersionList(row.projectId, row, true);
|
||||
}
|
||||
},
|
||||
handleFile(row) {
|
||||
this.dialogVisibleFile = true;
|
||||
this.checkRow = row;
|
||||
this.fileList = [...(row.fileList || []).map((ele) => ({ ...ele }))];
|
||||
},
|
||||
beforeUpload(file) {
|
||||
this.fileLoading = true;
|
||||
if (file.size > 1024 * 1024 * 100) {
|
||||
this.fileLoading = false;
|
||||
|
||||
this.$message({
|
||||
type: "warning",
|
||||
message: "单个文件不能大于100M!",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
},
|
||||
successUpload(res, file, fileList) {
|
||||
if (!fileList.filter((ele) => ele.percentage != 100).length) {
|
||||
this.fileLoading = false;
|
||||
}
|
||||
if (res.code == 200) {
|
||||
this.fileList.push({
|
||||
fileName: res.originalFilename, //文件名称
|
||||
filePath: res.filePath, //文件路径
|
||||
fileNewName: res.newFileName, //文件新名称
|
||||
fileUrl: res.url,
|
||||
});
|
||||
} else {
|
||||
this.fileLoading = false;
|
||||
|
||||
this.$message({
|
||||
type: "error",
|
||||
message: res.msg,
|
||||
});
|
||||
}
|
||||
},
|
||||
cancalFile() {
|
||||
this.dialogVisibleFile = false;
|
||||
this.fileList = [];
|
||||
},
|
||||
async saveFile() {
|
||||
this.dialogVisibleFile = false;
|
||||
this.checkRow.fileList = this.fileList;
|
||||
if (this.checkRow.loggerId) {
|
||||
await workLogApi.editLog(this.checkRow);
|
||||
if (this.delFileArr.length) {
|
||||
systemApi.delFileBatch(this.delFileArr.join(',')).then((res) => {
|
||||
this.fileList = []
|
||||
this.delFileArr = []
|
||||
this.getLogList();
|
||||
|
||||
});
|
||||
} else {
|
||||
this.getLogList();
|
||||
|
||||
}
|
||||
}
|
||||
this.$message({
|
||||
type: "success",
|
||||
message: "保存成功!",
|
||||
});
|
||||
},
|
||||
async getAllProject() {
|
||||
const response = await projectApi.listProject({
|
||||
pageSize: 10000,
|
||||
pageNum: 1,
|
||||
});
|
||||
this.projectList = response.rows;
|
||||
},
|
||||
// 获取剩余工时
|
||||
async getDayTime(type, row) {
|
||||
const res = await workLogApi.getDayTime({
|
||||
loggerDate: this.selectDay,
|
||||
});
|
||||
this.hasTimeLong = Number(res.data);
|
||||
if (type == "add") {
|
||||
let now = new Date(
|
||||
this.moment().format("YYYY-MM-DD 00:00:00")
|
||||
).getTime();
|
||||
let select = new Date(this.selectDay).getTime();
|
||||
let row = {
|
||||
loggerDate: this.selectDay,
|
||||
projectId: "",
|
||||
workTime: "",
|
||||
workContent: "",
|
||||
state: now == select ? 0 : 1,
|
||||
userId: this.userId,
|
||||
demandId: "",
|
||||
edit: true,
|
||||
fileList: [],
|
||||
};
|
||||
this.computedTime(0);
|
||||
if (this.workTimeList.length) this.tableData.push(row);
|
||||
} else {
|
||||
this.computedTime(row.workTime);
|
||||
}
|
||||
},
|
||||
computedTime(time) {
|
||||
let length = (this.hasTimeLong + (Number(time) || 0)).toFixed(1);
|
||||
if (length <= 0) {
|
||||
this.$modal.msgWarning("当日剩余工时为0");
|
||||
this.workTimeList = [];
|
||||
return;
|
||||
}
|
||||
this.workTimeList = new Array((length * 10) / 1)
|
||||
.fill(0)
|
||||
.map((ele, index) => {
|
||||
return (index + 1) / 10;
|
||||
});
|
||||
},
|
||||
getVersionList(val, row, isOpen) {
|
||||
if (!isOpen) {
|
||||
row.versionId = "";
|
||||
row.demandId = "";
|
||||
}
|
||||
this.$nextTick(async () => {
|
||||
const res = await demandApi.getVersionTree({
|
||||
projectId: val,
|
||||
userId: this.userId,
|
||||
demandStatusList: [0, 1, 2, 3],
|
||||
queryDate: this.selectDay,
|
||||
});
|
||||
this.versionList = res.data.filter((ele) => ele.type == 0);
|
||||
this.demandList = res.data.filter((ele) => ele.type == 1);
|
||||
if (!isOpen) this.$refs.versionSelectRef.toggleMenu();
|
||||
if (isOpen) this.getDemandList(row.versionId, row, true);
|
||||
});
|
||||
},
|
||||
async getDemandList(val, row, isOpen) {
|
||||
if (!isOpen) row.demandId = "";
|
||||
if (this.versionList.find((ele) => ele.id == val)) {
|
||||
this.demandList = this.versionList.find(
|
||||
(ele) => ele.id == val
|
||||
).childrenList;
|
||||
} else {
|
||||
this.demandList = [];
|
||||
}
|
||||
if (!this.demandList.find((ele) => ele.id == row.demandId)) {
|
||||
row.demandId = "";
|
||||
}
|
||||
if (!isOpen) this.$refs.demandSelectRef.toggleMenu();
|
||||
},
|
||||
openContent(row) {
|
||||
row.showContent = true;
|
||||
},
|
||||
saveContent(row, type) {
|
||||
this.$nextTick(() => {
|
||||
if (type == 0) {
|
||||
row.workContent = this.oldContent;
|
||||
} else {
|
||||
this.oldContent = row.workContent;
|
||||
}
|
||||
row.showContent = false;
|
||||
});
|
||||
},
|
||||
downFile(url) {
|
||||
window.open(url);
|
||||
},
|
||||
delFile(row) {
|
||||
if (row.id) {
|
||||
this.$confirm("此操作将永久删除文件, 是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
this.delFileArr.push(row.id)
|
||||
this.fileList = this.fileList.filter(
|
||||
(ele) => ele.fileNewName != row.fileNewName
|
||||
);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: "删除成功!"
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.fileList = this.fileList.filter(
|
||||
(ele) => ele.fileNewName != row.fileNewName
|
||||
);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: "删除成功!"
|
||||
})
|
||||
}
|
||||
},
|
||||
getFileType(name) {
|
||||
console.log(name);
|
||||
var data = {
|
||||
jpg: "image",
|
||||
jpeg: "image",
|
||||
png: "image",
|
||||
gif: "image",
|
||||
bmp: "image",
|
||||
tiff: "image",
|
||||
svg: "image",
|
||||
pdf: "file",
|
||||
doc: "file",
|
||||
docx: "file",
|
||||
xls: "file",
|
||||
xlsx: "file",
|
||||
txt: "file",
|
||||
ppt: "file",
|
||||
zip: "zip",
|
||||
rar: "zip",
|
||||
tar: "zip",
|
||||
targz: "zip",
|
||||
};
|
||||
return data[name.split(".")[1]] || "file";
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
if (this.$route.query.userId) {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
selectDay(newVal) {
|
||||
this.getLogList();
|
||||
},
|
||||
disableTable(newVal) {
|
||||
if (newVal) {
|
||||
this.tableData = [];
|
||||
}
|
||||
},
|
||||
versionList(newVal) { },
|
||||
},
|
||||
computed: {
|
||||
projectListFilter() {
|
||||
let now = new Date(this.selectDay).getTime();
|
||||
return this.projectList.filter(
|
||||
(ele) =>
|
||||
new Date(ele.startDate).getTime() <= now &&
|
||||
new Date(ele.endDate).getTime() >= now
|
||||
);
|
||||
},
|
||||
totalWorkTime() {
|
||||
return (
|
||||
this.tableData
|
||||
.map((ele) => Number(ele.workTime))
|
||||
.reduce((total, now) => {
|
||||
return now * 10 + total;
|
||||
}, 0) / 10
|
||||
).toFixed(1);
|
||||
},
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
this.getAllProject();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.topBox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.topTitle {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.topText {
|
||||
color: #999999;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
|
||||
span {
|
||||
margin: 0 5px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contentText {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.noneText {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.downFileBox {
|
||||
color: #1686d8;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fileBox {
|
||||
img {
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
|
@ -34,6 +34,7 @@
|
|||
:highligt="true"
|
||||
ref="projectRef"
|
||||
style="height: 100%"
|
||||
rowKey="projectId"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -90,6 +91,7 @@
|
|||
type="month"
|
||||
format="yyyy年MM月"
|
||||
:clearable="false"
|
||||
@change="changeMonth"
|
||||
/>
|
||||
</div>
|
||||
<div class="calendar-view" v-if="projectInfo">
|
||||
|
@ -106,7 +108,7 @@
|
|||
disabled: isFutureDate(data.date) && isInProjectRange(data),
|
||||
}"
|
||||
>
|
||||
{{ data.day.split("-")[2] }}
|
||||
{{ data.day.split("-")[1] + "/" + data.day.split("-")[2] }}
|
||||
</div>
|
||||
</template>
|
||||
</el-calendar>
|
||||
|
@ -150,6 +152,7 @@
|
|||
<el-button
|
||||
type="text"
|
||||
v-if="projectInfo.userId == $store.state.user.id"
|
||||
@click="logForm.workTime = logForm.max"
|
||||
>{{ `当天剩余可分配工时:${logForm.max}人天` }}</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
|
@ -168,6 +171,9 @@
|
|||
v-if="projectInfo.userId == $store.state.user.id"
|
||||
>
|
||||
<el-button @click="logDialogVisible = false">取消</el-button>
|
||||
<el-button v-if="logForm.loggerId" type="danger" @click="delWorkLog"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button type="primary" @click="saveWorkLog">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
|
@ -220,7 +226,11 @@ export default {
|
|||
res.data.startDate.split(" ")[0] + " 00:00:00";
|
||||
this.projectInfo.endDate = res.data.endDate.split(" ")[0] + " 23:59:59";
|
||||
|
||||
this.selectedDate = new Date(this.projectInfo.startDate); // 设置为项目开始时间
|
||||
if (this.projectInfo.userId == this.$store.state.user.id) {
|
||||
this.selectedDate = new Date();
|
||||
} else {
|
||||
this.selectedDate = new Date(this.projectInfo.startDate); // 设置为项目开始时间
|
||||
}
|
||||
|
||||
const res2 = await workLogApi.getLogData({
|
||||
projectId: this.projectInfo.projectId,
|
||||
|
@ -232,13 +242,23 @@ export default {
|
|||
this.initDateColor(first);
|
||||
},
|
||||
initDateColor(first) {
|
||||
let boxs = document.getElementsByClassName("date-cell");
|
||||
for (var i = 0; i < boxs.length; i++) {
|
||||
boxs[i].style = "";
|
||||
}
|
||||
|
||||
this.logData.map((item) => {
|
||||
var ele = document.getElementById(item.date.split(" ")[0]);
|
||||
|
||||
if (ele) {
|
||||
if (item.state == -1) {
|
||||
ele.style = "background:#ecf5ff";
|
||||
} else {
|
||||
ele.style = "background:#409eff;color:#fff";
|
||||
} else if (item.state == 0||item.state == 1) {
|
||||
ele.style = `background:linear-gradient(to right, #409eff ${
|
||||
(item.workTime || 1) * 100
|
||||
}% , #ecf5ff ${(item.workTime || 1) * 100}%);color:${
|
||||
item.workTime > 0.65 ? "#fff" : "#333"
|
||||
};`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -254,7 +274,6 @@ export default {
|
|||
this.$modal.msgWarning("请先选择一个项目");
|
||||
return;
|
||||
}
|
||||
this.handleProjectClick(this.projectInfo);
|
||||
|
||||
const clickedDate = new Date(data.day);
|
||||
const currentDate = new Date();
|
||||
|
@ -289,9 +308,15 @@ export default {
|
|||
projectId: this.projectInfo.projectId,
|
||||
loggerDate: this.logForm.loggerDate,
|
||||
});
|
||||
this.logForm.workTime = res.data.workTime;
|
||||
this.logForm.workContent = res.data.workContent;
|
||||
this.logForm.loggerId = res.data.loggerId;
|
||||
if (res.data) {
|
||||
this.logForm.workTime = res.data.workTime;
|
||||
this.logForm.workContent = res.data.workContent;
|
||||
this.logForm.loggerId = res.data.loggerId;
|
||||
} else {
|
||||
this.logForm.workTime = "";
|
||||
this.logForm.workContent = "";
|
||||
this.logForm.loggerId = "";
|
||||
}
|
||||
} else {
|
||||
this.logForm.workTime = "";
|
||||
this.logForm.workContent = "";
|
||||
|
@ -300,9 +325,33 @@ export default {
|
|||
const res = await workLogApi.getDayTime({
|
||||
loggerDate: this.logForm.loggerDate,
|
||||
});
|
||||
this.logForm.max =
|
||||
Number(res.data) + (Number(this.logForm.workTime) || 0);
|
||||
|
||||
this.logForm.max = (
|
||||
Number(res.data) + (Number(this.logForm.workTime) || 0)
|
||||
).toFixed(1);
|
||||
this.logDialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.changeMonth();
|
||||
});
|
||||
},
|
||||
changeMonth() {
|
||||
let that = this;
|
||||
this.$nextTick(async () => {
|
||||
const res2 = await workLogApi.getLogData({
|
||||
projectId: that.projectInfo.projectId,
|
||||
startDate:
|
||||
that
|
||||
.moment(that.selectedDate)
|
||||
.startOf("month")
|
||||
.format("YYYY-MM-DD") + " 00:00:00",
|
||||
endDate:
|
||||
that.moment(that.selectedDate).endOf("month").format("YYYY-MM-DD") +
|
||||
" 23:59:59",
|
||||
userId: that.projectInfo.userId,
|
||||
});
|
||||
that.logData = res2.data;
|
||||
that.initDateColor(1);
|
||||
});
|
||||
},
|
||||
isInProjectRange(data) {
|
||||
if (!this.projectInfo) return false;
|
||||
|
@ -377,14 +426,13 @@ export default {
|
|||
}
|
||||
this.logDialogVisible = false;
|
||||
this.$modal.msgSuccess("操作成功");
|
||||
this.handleProjectClick(this.projectInfo);
|
||||
const response = await workLogApi.userProject(this.projectInfo.userId);
|
||||
this.projectList = response.data;
|
||||
this.handleProjectClick(this.projectInfo);
|
||||
},
|
||||
async fetchUserProjects() {
|
||||
try {
|
||||
const response = await workLogApi.userProject(this.projectInfo.userId);
|
||||
console.log(12333, this.projectList);
|
||||
this.projectList = response.data;
|
||||
if (this.projectList.length) {
|
||||
let arr = [];
|
||||
|
@ -415,6 +463,17 @@ export default {
|
|||
//获取项目列表
|
||||
this.fetchUserProjects();
|
||||
},
|
||||
// 删除日志
|
||||
delWorkLog() {
|
||||
this.$modal.confirm(`是否确认删日志`).then(async () => {
|
||||
await workLogApi.delLog(this.logForm.loggerId);
|
||||
this.$modal.msgSuccess("操作成功");
|
||||
this.logDialogVisible = false;
|
||||
this.changeMonth();
|
||||
const response = await workLogApi.userProject(this.projectInfo.userId);
|
||||
this.projectList = response.data;
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
|
@ -511,22 +570,30 @@ export default {
|
|||
color: white; /* 白色文字 */
|
||||
padding: 5px 0; /* 增加一些内边距 */
|
||||
}
|
||||
|
||||
.project-info-calendar ::v-deep .el-calendar__body th {
|
||||
height: 60px !important;
|
||||
font-weight: bold !important;
|
||||
font-size: 20px;
|
||||
}
|
||||
.project-info-calendar .calendar-view ::v-deep .el-calendar-day {
|
||||
height: 65px; /* 增加日期单元格高度(原高度 + 5px) */
|
||||
height: 70px; /* 增加日期单元格高度(原高度 + 5px) */
|
||||
// padding-top: 5px;
|
||||
padding: 3px;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.project-info-calendar .calendar-view ::v-deep .el-calendar-day.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.project-info-calendar .calendar-view ::v-deep table * {
|
||||
border: none;
|
||||
}
|
||||
.project-info-calendar .calendar-view ::v-deep .date-cell {
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
line-height: 55px;
|
||||
line-height: 50px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.project-info-calendar .calendar-view ::v-deep .in-range {
|
||||
|
@ -534,7 +601,7 @@ export default {
|
|||
}
|
||||
|
||||
.project-info-calendar .calendar-view ::v-deep .out-range {
|
||||
background-color: rgba(0, 0, 0, 0.05) !important;
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.project-info-calendar .calendar-view ::v-deep .disabled {
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<div class="layout">
|
||||
<!-- 左侧树形菜单 -->
|
||||
<LeftMonth
|
||||
class="sidebar"
|
||||
ref="leftRef"
|
||||
@searchDay="searchDay"
|
||||
@setDisableTable="setDisableTable"
|
||||
/>
|
||||
<!-- 右侧表格和筛选 -->
|
||||
<RightTable
|
||||
class="main-content"
|
||||
ref="rightRef"
|
||||
:selectDay="selectDay"
|
||||
:disableTable="disableTable"
|
||||
@changeCaleder="changeCaleder"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 导入子组件
|
||||
import LeftMonth from "./components/leftMonth.vue";
|
||||
import RightTable from "./components/rightTable.vue";
|
||||
|
||||
export default {
|
||||
name: "worklog",
|
||||
components: {
|
||||
LeftMonth,
|
||||
RightTable,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectDay: "",
|
||||
disableTable: false,
|
||||
userId: this.$store.state.user.id,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.selectDay = this.moment().format("YYYY-MM-DD 00:00:00");
|
||||
if (this.$route.query.userId && this.userId != this.$route.query.userId) {
|
||||
this.setDisableTable(false);
|
||||
}
|
||||
},
|
||||
searchDay(data) {
|
||||
this.selectDay = data;
|
||||
},
|
||||
setDisableTable(data) {
|
||||
if (this.$route.query.userId && this.userId != this.$route.query.userId) {
|
||||
data = true;
|
||||
}
|
||||
this.disableTable = data;
|
||||
},
|
||||
changeCaleder() {
|
||||
this.$refs.leftRef.getLogMonth();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex-grow: 1;
|
||||
max-width: 320px;
|
||||
width: 360px;
|
||||
height: 100vh;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|