feat(wms/bonus): 新增奖金模板管理功能,优化奖金池页面
1. 新增奖金模板CRUD API和管理组件 2. 重构奖金池页面:替换产线输入为下拉选择,新增时间范围查询和钢卷重量自动计算 3. 优化岗位系数配置页面,仅查询当前用户创建的配置 4. 重构员工选择器组件,改用穿梭框实现多选逻辑 5. 为奖金分配页面新增模板导入导出功能
This commit is contained in:
44
klp-ui/src/api/wms/bonusTemplate.js
Normal file
44
klp-ui/src/api/wms/bonusTemplate.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询奖金模板列表(当前登录用户创建的)
|
||||
export function listBonusTemplate(query) {
|
||||
return request({
|
||||
url: '/wms/bonusTemplate/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询奖金模板详细
|
||||
export function getBonusTemplate(templateId) {
|
||||
return request({
|
||||
url: '/wms/bonusTemplate/' + templateId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增奖金模板
|
||||
export function addBonusTemplate(data) {
|
||||
return request({
|
||||
url: '/wms/bonusTemplate',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改奖金模板
|
||||
export function updateBonusTemplate(data) {
|
||||
return request({
|
||||
url: '/wms/bonusTemplate',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除奖金模板
|
||||
export function delBonusTemplate(templateId) {
|
||||
return request({
|
||||
url: '/wms/bonusTemplate/' + templateId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -10,34 +10,56 @@
|
||||
</div>
|
||||
|
||||
<!-- 选择对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
||||
<el-input v-model="searchQuery" placeholder="请输入员工姓名或部门" clearable @keyup.enter.native="handleSearch">
|
||||
<el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
|
||||
<!-- 单选模式:搜索框 -->
|
||||
<el-input v-if="!multiple" v-model="searchQuery" placeholder="请输入员工姓名或部门" clearable @keyup.enter.native="handleSearch">
|
||||
<el-button slot="append" icon="el-icon-search" @click="handleSearch" />
|
||||
</el-input>
|
||||
|
||||
<!-- 已选员工列表(多选模式) -->
|
||||
<div v-if="multiple && selectedEmployees.length > 0" class="selected-list">
|
||||
<div class="selected-list-title">已选员工 ({{ selectedEmployees.length }})</div>
|
||||
<el-tag v-for="employee in selectedEmployees" :key="employee[keyField]" closable
|
||||
@close="removeSelectedEmployee(employee)" class="selected-tag">
|
||||
{{ employee.name }} ({{ employee.dept }})
|
||||
</el-tag>
|
||||
</div>
|
||||
<!-- 多选模式:穿梭框 -->
|
||||
<el-transfer
|
||||
v-loading="loading"
|
||||
v-if="multiple"
|
||||
v-model="selectedIds"
|
||||
:data="transferData"
|
||||
:titles="['可选员工', '已选员工']"
|
||||
:button-texts="['移除', '添加']"
|
||||
:props="{
|
||||
key: keyField,
|
||||
label: 'label',
|
||||
disabled: 'disabled'
|
||||
}"
|
||||
filterable
|
||||
:filter-method="transferFilterMethod"
|
||||
filter-placeholder="根据员工部门搜索"
|
||||
/>
|
||||
|
||||
<el-table v-loading="loading" :data="employeeList" style="width: 100%" height="400px" @row-click="handleRowClick"
|
||||
:row-class-name="rowClassName">
|
||||
<!-- 单选模式:表格 -->
|
||||
<div v-if="!multiple" class="table-container">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="employeeList"
|
||||
style="width: 100%"
|
||||
height="400px"
|
||||
@row-click="handleRowClick"
|
||||
:row-class-name="rowClassName"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column label="部门" align="center" prop="dept" />
|
||||
<el-table-column label="姓名" align="center" prop="name" />
|
||||
<el-table-column label="岗位工种" align="center" prop="jobType" />
|
||||
<el-table-column label="联系电话" align="center" prop="phone" />
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<div slot="footer" class="dialog-footer" v-if="multiple">
|
||||
<el-button @click="cancelSelection">取消</el-button>
|
||||
<el-button v-if="multiple" type="primary" @click="confirmSelection" :disabled="selectedEmployees.length === 0">
|
||||
确认选择 ({{ selectedEmployees.length }})
|
||||
<el-button type="primary" @click="confirmSelection">
|
||||
确认选择
|
||||
</el-button>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer" v-else>
|
||||
<el-button @click="cancelSelection">取消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
@@ -75,20 +97,20 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedEmployee: {},
|
||||
selectedEmployees: [],
|
||||
rawEmployeeList: [],
|
||||
open: false,
|
||||
loading: false,
|
||||
searchQuery: ''
|
||||
searchQuery: '',
|
||||
selectedIds: [],
|
||||
selectedId: '',
|
||||
selectedEmployee: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 显示在触发器上的文本
|
||||
displayText() {
|
||||
if (this.multiple) {
|
||||
if (this.selectedEmployees.length > 0) {
|
||||
return `${this.selectedEmployees.length} 人已选择`
|
||||
if (this.selectedIds.length > 0) {
|
||||
return `${this.selectedIds.length} 人已选择`
|
||||
} else {
|
||||
return this.placeholder
|
||||
}
|
||||
@@ -96,20 +118,25 @@ export default {
|
||||
return this.selectedEmployee.name || this.placeholder
|
||||
}
|
||||
},
|
||||
// 禁用的员工ID列表
|
||||
disabledIdList() {
|
||||
if (!this.disabledNames) {
|
||||
return []
|
||||
}
|
||||
return this.disabledNames.split(',').map(id => id.trim()).filter(id => id)
|
||||
},
|
||||
// 处理后的员工列表,包含禁用状态
|
||||
employeeList() {
|
||||
return this.rawEmployeeList.filter(employee => {
|
||||
return employee.name?.includes(this.searchQuery) || employee.dept?.includes(this.searchQuery)
|
||||
}).map(employee => ({
|
||||
...employee,
|
||||
disabled: this.disabledIdList.includes(employee.infoId.toString())
|
||||
disabled: this.disabledIdList.includes(employee[this.keyField].toString())
|
||||
}))
|
||||
},
|
||||
transferData() {
|
||||
return this.employeeList.map(employee => ({
|
||||
[this.keyField]: employee[this.keyField],
|
||||
label: `${employee.dept} - ${employee.name} (${employee.jobType})`,
|
||||
disabled: employee.disabled
|
||||
}))
|
||||
}
|
||||
},
|
||||
@@ -118,16 +145,16 @@ export default {
|
||||
handler(newVal) {
|
||||
if (this.multiple) {
|
||||
if (newVal) {
|
||||
// 多选模式:根据逗号分隔的字符串查找已选员工
|
||||
this.findSelectedEmployees(newVal.split(','))
|
||||
this.selectedIds = newVal.split(',').map(id => id.trim()).filter(id => id).map(id => isNaN(Number(id)) ? id : Number(id))
|
||||
} else {
|
||||
this.selectedEmployees = []
|
||||
this.selectedIds = []
|
||||
}
|
||||
} else {
|
||||
if (newVal) {
|
||||
this.findSelectedEmployee(newVal)
|
||||
} else {
|
||||
this.selectedEmployee = {}
|
||||
this.selectedId = ''
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -135,7 +162,6 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 切换对话框显示
|
||||
toggleDialog() {
|
||||
if (!this.open) {
|
||||
this.getEmployeeList()
|
||||
@@ -143,18 +169,14 @@ export default {
|
||||
this.open = !this.open
|
||||
},
|
||||
|
||||
// 获取员工列表
|
||||
getEmployeeList() {
|
||||
this.loading = true
|
||||
const params = {
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
// name: this.searchQuery || undefined,
|
||||
// dept: this.searchQuery || undefined
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
listEmployeeInfo(params).then(response => {
|
||||
// 前端筛选员工列表,根据姓名或部门
|
||||
this.rawEmployeeList = response.rows;
|
||||
this.loading = false
|
||||
resolve()
|
||||
@@ -165,36 +187,19 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
// 搜索员工
|
||||
handleSearch() {
|
||||
this.getEmployeeList()
|
||||
},
|
||||
|
||||
// 选择员工
|
||||
handleRowClick(row) {
|
||||
// 检查员工是否被禁用
|
||||
if (this.isDisabled(row)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.multiple) {
|
||||
// 多选模式:添加或移除员工
|
||||
const index = this.selectedEmployees.findIndex(item => item[this.keyField] === row[this.keyField])
|
||||
if (index > -1) {
|
||||
this.selectedEmployees.splice(index, 1)
|
||||
} else {
|
||||
this.selectedEmployees.push(row)
|
||||
}
|
||||
} else {
|
||||
// 单选模式:选择员工并关闭弹窗
|
||||
this.selectedEmployee = row
|
||||
this.$emit('input', row[this.keyField])
|
||||
this.$emit('change', row)
|
||||
this.open = false
|
||||
}
|
||||
},
|
||||
|
||||
// 检查员工是否被禁用
|
||||
isDisabled(row) {
|
||||
const isDisabled = this.disabledIdList.includes(row[this.keyField].toString())
|
||||
if (isDisabled) {
|
||||
@@ -203,61 +208,17 @@ export default {
|
||||
return isDisabled
|
||||
},
|
||||
|
||||
// 确认选择(多选模式)
|
||||
confirmSelection() {
|
||||
const selectedIds = this.selectedEmployees.map(item => item[this.keyField])
|
||||
this.$emit('input', selectedIds.join(','))
|
||||
this.$emit('change', this.selectedEmployees)
|
||||
this.open = false
|
||||
},
|
||||
|
||||
cancelSelection() {
|
||||
if (this.multiple) {
|
||||
if (this.value) {
|
||||
// 多选模式:根据逗号分隔的字符串查找已选员工
|
||||
this.findSelectedEmployees(this.value.split(','))
|
||||
} else {
|
||||
this.selectedEmployees = []
|
||||
}
|
||||
} else {
|
||||
if (this.value) {
|
||||
this.findSelectedEmployee(this.value)
|
||||
} else {
|
||||
this.selectedEmployee = {}
|
||||
}
|
||||
}
|
||||
this.open = false
|
||||
},
|
||||
|
||||
// 移除已选员工
|
||||
removeSelectedEmployee(employee) {
|
||||
const index = this.selectedEmployees.findIndex(item => item[this.keyField] === employee[this.keyField])
|
||||
if (index > -1) {
|
||||
this.selectedEmployees.splice(index, 1)
|
||||
}
|
||||
},
|
||||
|
||||
// 表格行样式
|
||||
rowClassName({ row }) {
|
||||
// if (this.isDisabled(row)) {
|
||||
// return 'disabled-row'
|
||||
// }
|
||||
if (this.isSelected(row)) {
|
||||
return 'selected-row'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
|
||||
// 检查员工是否已选
|
||||
isSelected(row) {
|
||||
if (this.multiple) {
|
||||
return this.selectedEmployees.some(item => item[this.keyField] === row[this.keyField])
|
||||
} else {
|
||||
return this.selectedEmployee[this.keyField] === row[this.keyField]
|
||||
}
|
||||
},
|
||||
|
||||
// 根据value查找选中的员工(单选)
|
||||
findSelectedEmployee(value) {
|
||||
if (this.employeeList.length > 0) {
|
||||
const employee = this.employeeList.find(item => item[this.keyField] === value)
|
||||
@@ -265,7 +226,6 @@ export default {
|
||||
this.selectedEmployee = employee
|
||||
}
|
||||
} else {
|
||||
// 如果员工列表为空,先获取列表再查找
|
||||
this.getEmployeeList().then(() => {
|
||||
const employee = this.employeeList.find(item => item[this.keyField] === value)
|
||||
if (employee) {
|
||||
@@ -275,19 +235,35 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 根据value查找选中的员工(多选)
|
||||
findSelectedEmployees(values) {
|
||||
if (this.employeeList.length > 0) {
|
||||
this.selectedEmployees = this.employeeList.filter(item => values.includes(item[this.keyField]))
|
||||
// 因为员工可能出现重名,所以需要去重,保证selectedEmployees[i][this.keyField]是唯一的
|
||||
this.selectedEmployees = this.selectedEmployees.filter((item, index, arr) => arr.findIndex(t => t[this.keyField] === item[this.keyField]) === index)
|
||||
} else {
|
||||
// 如果员工列表为空,先获取列表再查找
|
||||
this.getEmployeeList().then(() => {
|
||||
this.selectedEmployees = this.employeeList.filter(item => values.includes(item[this.keyField]))
|
||||
this.selectedEmployees = this.selectedEmployees.filter((item, index, arr) => arr.findIndex(t => t[this.keyField] === item[this.keyField]) === index)
|
||||
})
|
||||
transferFilterMethod(query, item) {
|
||||
return item.label.toLowerCase().includes(query.toLowerCase())
|
||||
},
|
||||
|
||||
confirmSelection() {
|
||||
if (this.multiple) {
|
||||
this.$emit('input', this.selectedIds.join(','))
|
||||
const selectedEmployees = this.rawEmployeeList.filter(item => this.selectedIds.includes(item[this.keyField]))
|
||||
this.$emit('change', selectedEmployees)
|
||||
}
|
||||
this.open = false
|
||||
},
|
||||
|
||||
cancelSelection() {
|
||||
if (this.multiple) {
|
||||
if (this.value) {
|
||||
this.selectedIds = this.value.split(',').map(id => id.trim()).filter(id => id).map(id => isNaN(Number(id)) ? id : Number(id))
|
||||
} else {
|
||||
this.selectedIds = []
|
||||
}
|
||||
} else {
|
||||
if (this.value) {
|
||||
this.findSelectedEmployee(this.value)
|
||||
} else {
|
||||
this.selectedEmployee = {}
|
||||
this.selectedId = ''
|
||||
}
|
||||
}
|
||||
this.open = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,39 +291,44 @@ export default {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.selected-list {
|
||||
.table-container {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
::v-deep .el-transfer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
::v-deep .el-transfer-panel {
|
||||
width: 380px;
|
||||
}
|
||||
|
||||
::v-deep .el-transfer-panel__body {
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
::v-deep .el-transfer__buttons {
|
||||
padding: 0 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
::v-deep .el-transfer__button {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.selected-list-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.selected-tag {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.el-table .selected-row {
|
||||
::v-deep .el-table .selected-row {
|
||||
background-color: #ecf5ff !important;
|
||||
}
|
||||
|
||||
.el-table .selected-row:hover {
|
||||
::v-deep .el-table .selected-row:hover {
|
||||
background-color: #ecf5ff !important;
|
||||
}
|
||||
|
||||
.el-table .disabled-row {
|
||||
background-color: #f5f7fa !important;
|
||||
color: #c0c4cc !important;
|
||||
}
|
||||
|
||||
.el-table .disabled-row:hover {
|
||||
background-color: #f5f7fa !important;
|
||||
color: #c0c4cc !important;
|
||||
::v-deep .el-transfer-panel__list.is-filterable {
|
||||
height: calc(100% - 60px)
|
||||
}
|
||||
</style>
|
||||
@@ -1,40 +1,89 @@
|
||||
<template>
|
||||
<div class="bonus-config">
|
||||
<!-- 警告提示 -->
|
||||
<el-alert
|
||||
v-if="adjustCoeffSum !== 0"
|
||||
title="警告"
|
||||
type="warning"
|
||||
:description="`主任加减系数总和为 ${adjustCoeffSum.toFixed(2)},必须为 0`"
|
||||
show-icon
|
||||
style="margin-bottom: 16px"
|
||||
:closable="false"
|
||||
/>
|
||||
<el-alert v-if="adjustCoeffSum !== 0" title="警告" type="warning"
|
||||
:description="`主任加减系数必须为 0,当前总和为 ${adjustCoeffSum.toFixed(2)}`" show-icon style="margin-bottom: 16px"
|
||||
:closable="false" />
|
||||
|
||||
<el-alert
|
||||
title="提示"
|
||||
type="info"
|
||||
:description="`主任加减系数的最大值为 ${maxAdjustCoeff},最小值为 ${-maxAdjustCoeff}`"
|
||||
show-icon
|
||||
style="margin-bottom: 16px"
|
||||
/>
|
||||
<el-alert title="提示" type="info" :description="`主任加减系数的最大值为 ${maxAdjustCoeff},最小值为 ${-maxAdjustCoeff}`" show-icon
|
||||
style="margin-bottom: 16px" />
|
||||
|
||||
<el-row style="margin-bottom: 16px;" :gutter="20">
|
||||
<el-col :span="8">
|
||||
<!-- 新增按钮 -->
|
||||
<EmployeeSelector
|
||||
ref="employeeSelector"
|
||||
:multiple="true"
|
||||
@change="handleBatchAdd"
|
||||
title="选择员工"
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-plus"
|
||||
size="small"
|
||||
slot="trigger"
|
||||
style="margin-bottom: 16px; width: 100%"
|
||||
>
|
||||
<EmployeeSelector ref="employeeSelector" :multiple="true" @change="handleBatchAdd" title="选择员工">
|
||||
<el-button icon="el-icon-plus" size="small" slot="trigger" style="width: 100%">
|
||||
点击此处新增更多员工参与奖金分配
|
||||
</el-button>
|
||||
</EmployeeSelector>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<!-- 选择模板按钮 -->
|
||||
<el-button type="primary" size="small" plain @click="handleSelectTemplate" style="width: 100%">
|
||||
从模板快速创建
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<!-- 保存按钮 -->
|
||||
<el-button type="warning" size="small" plain @click="handleSaveTemplate" style="width: 100%">
|
||||
将当前分配方案保存为模板
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 保存模板对话框 -->
|
||||
<el-dialog title="保存为模板" :visible.sync="saveTemplateDialogVisible" width="600px" append-to-body>
|
||||
<el-form :model="templateForm" label-width="100px">
|
||||
<el-form-item label="模板名称">
|
||||
<el-input v-model="templateForm.templateName" placeholder="请输入模板名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="选择保存列">
|
||||
<el-checkbox-group v-model="templateForm.selectedColumns">
|
||||
<el-checkbox v-for="col in availableColumns" :key="col.value" :label="col.value">
|
||||
{{ col.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="saveTemplateDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="confirmSaveTemplate">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 选择模板对话框 -->
|
||||
<el-dialog title="选择模板快速创建" :visible.sync="selectTemplateDialogVisible" width="800px" append-to-body>
|
||||
<el-table :data="templateList" @current-change="handleTemplateSelect" highlight-current-row style="margin-bottom: 16px;">
|
||||
<el-table-column label="模板名称" prop="templateName" show-overflow-tooltip />
|
||||
<el-table-column label="创建时间" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 模板预览 -->
|
||||
<div v-if="selectedTemplateContent.length > 0" style="margin-top: 16px;">
|
||||
<el-divider>模板预览</el-divider>
|
||||
<el-table :data="selectedTemplateContent" border size="small" max-height="300">
|
||||
<el-table-column label="姓名" prop="empName" width="100" />
|
||||
<el-table-column label="岗位" prop="postName" width="120" />
|
||||
<el-table-column label="职务系数" prop="dutyCoeff" width="100" />
|
||||
<el-table-column label="基本系数" prop="baseCoeff" width="100" />
|
||||
<el-table-column label="主任加减系数" prop="adjustCoeff" width="120" />
|
||||
<el-table-column label="备注" prop="remark" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="selectTemplateDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="confirmApplyTemplate" :disabled="!selectedTemplate">应 用</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
|
||||
<!-- 可编辑表格 -->
|
||||
<el-table :data="bonusConfigList" v-loading="loading" border style="width: 100%" height="300px">
|
||||
@@ -48,19 +97,10 @@
|
||||
<!-- 岗位 -->
|
||||
<el-table-column label="岗位" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-select
|
||||
v-model="scope.row.postName"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
@change="handlePostChange(scope.row)"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="post in postCoeffList"
|
||||
:key="post.configId"
|
||||
:label="post.postName"
|
||||
:value="post.postName"
|
||||
/>
|
||||
<el-select v-model="scope.row.postName" size="small" style="width: 100%" @change="handlePostChange(scope.row)"
|
||||
clearable>
|
||||
<el-option v-for="post in postCoeffList" :key="post.configId" :label="post.postName"
|
||||
:value="post.postName" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -68,46 +108,30 @@
|
||||
<!-- 职务系数 -->
|
||||
<el-table-column label="职务系数">
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.dutyCoeff"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
@blur="handleCellBlur(scope.row, 'dutyCoeff')"
|
||||
@keyup.enter="handleCellBlur(scope.row, 'dutyCoeff')"
|
||||
/>
|
||||
<el-input v-model="scope.row.dutyCoeff" size="small" style="width: 100%"
|
||||
@blur="handleCellBlur(scope.row, 'dutyCoeff')" @keyup.enter="handleCellBlur(scope.row, 'dutyCoeff')" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 基本系数 -->
|
||||
<el-table-column label="基本系数">
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.baseCoeff"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
@blur="handleCellBlur(scope.row, 'baseCoeff')"
|
||||
@keyup.enter="handleCellBlur(scope.row, 'baseCoeff')"
|
||||
/>
|
||||
<el-input v-model="scope.row.baseCoeff" size="small" style="width: 100%"
|
||||
@blur="handleCellBlur(scope.row, 'baseCoeff')" @keyup.enter="handleCellBlur(scope.row, 'baseCoeff')" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 主任加减系数 -->
|
||||
<el-table-column label="主任加减系数">
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.adjustCoeff"
|
||||
:max="maxAdjustCoeff"
|
||||
:min="-maxAdjustCoeff"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
@blur="handleAdjustCoeffBlur(scope.row)"
|
||||
@keyup.enter="handleAdjustCoeffBlur(scope.row)"
|
||||
/>
|
||||
<el-input v-model="scope.row.adjustCoeff" :max="maxAdjustCoeff" :min="-maxAdjustCoeff" size="small"
|
||||
style="width: 100%" @blur="handleAdjustCoeffBlur(scope.row)"
|
||||
@keyup.enter="handleAdjustCoeffBlur(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 分配基数 -->
|
||||
<el-table-column label="分配基数">
|
||||
<!-- <el-table-column label="分配基数">
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.allocBase"
|
||||
@@ -117,7 +141,7 @@
|
||||
@keyup.enter="handleCellBlur(scope.row, 'allocBase')"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
|
||||
<!-- 个人总系数 -->
|
||||
<el-table-column label="个人总系数" width="100">
|
||||
@@ -129,31 +153,28 @@
|
||||
<!-- 分配金额 -->
|
||||
<el-table-column label="分配金额(元)">
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
<span style="color: red;">¥{{ scope.row.bonusAmount }}</span>
|
||||
<!-- <el-input
|
||||
v-model="scope.row.bonusAmount"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
disabled
|
||||
/>
|
||||
/> -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 备注 -->
|
||||
<el-table-column label="备注" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.remark || '-' }}</span>
|
||||
<el-input v-model="scope.row.remark" size="small" style="width: 100%"
|
||||
@blur="handleCellBlur(scope.row, 'remark')" @keyup.enter="handleCellBlur(scope.row, 'remark')" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
size="small"
|
||||
@click="handleDelete(scope.row)"
|
||||
>
|
||||
<el-button type="text" icon="el-icon-delete" size="small" @click="handleDelete(scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -165,6 +186,7 @@
|
||||
<script>
|
||||
import { listBonusConfig, addBonusConfig, updateBonusConfig, delBonusConfig } from "@/api/wms/bonusConfig";
|
||||
import { listPostCoeffConfig } from "@/api/wms/postCoeffConfig";
|
||||
import { addBonusTemplate, listBonusTemplate } from "@/api/wms/bonusTemplate";
|
||||
|
||||
import EmployeeSelector from "@/components/EmployeeSelector";
|
||||
|
||||
@@ -185,7 +207,7 @@ export default {
|
||||
totalBonus: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -193,6 +215,23 @@ export default {
|
||||
bonusConfigList: [],
|
||||
originalData: new Map(),
|
||||
postCoeffList: [],
|
||||
saveTemplateDialogVisible: false,
|
||||
selectTemplateDialogVisible: false,
|
||||
templateForm: {
|
||||
templateName: '',
|
||||
selectedColumns: ['empName', 'postName', 'dutyCoeff', 'baseCoeff', 'adjustCoeff', 'remark']
|
||||
},
|
||||
availableColumns: [
|
||||
{ value: 'empName', label: '姓名' },
|
||||
{ value: 'postName', label: '岗位' },
|
||||
{ value: 'dutyCoeff', label: '职务系数' },
|
||||
{ value: 'baseCoeff', label: '基本系数' },
|
||||
{ value: 'adjustCoeff', label: '主任加减系数' },
|
||||
{ value: 'remark', label: '备注' }
|
||||
],
|
||||
templateList: [],
|
||||
selectedTemplate: null,
|
||||
selectedTemplateContent: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -396,7 +435,132 @@ export default {
|
||||
}).catch(() => {
|
||||
this.$message.error('删除失败');
|
||||
});
|
||||
}).catch(() => { });
|
||||
},
|
||||
// 保存为模板
|
||||
handleSaveTemplate() {
|
||||
if (this.bonusConfigList.length === 0) {
|
||||
this.$message.warning('当前没有数据可保存为模板');
|
||||
return;
|
||||
}
|
||||
this.saveTemplateDialogVisible = true;
|
||||
},
|
||||
// 确认保存模板
|
||||
confirmSaveTemplate() {
|
||||
if (!this.templateForm.templateName) {
|
||||
this.$message.warning('请输入模板名称');
|
||||
return;
|
||||
}
|
||||
if (this.templateForm.selectedColumns.length === 0) {
|
||||
this.$message.warning('请至少选择一列');
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据选择的列过滤数据,始终包含empId
|
||||
const templateContent = this.bonusConfigList.map(row => {
|
||||
const filteredRow = {};
|
||||
// 始终包含empId
|
||||
if (row.empId !== undefined && row.empId !== null) {
|
||||
filteredRow.empId = row.empId;
|
||||
}
|
||||
// 添加用户选择的列
|
||||
this.templateForm.selectedColumns.forEach(col => {
|
||||
filteredRow[col] = row[col];
|
||||
});
|
||||
return filteredRow;
|
||||
});
|
||||
|
||||
const saveData = {
|
||||
templateName: this.templateForm.templateName,
|
||||
templateContent: JSON.stringify(templateContent),
|
||||
createBy: this.$store.getters.name
|
||||
};
|
||||
|
||||
addBonusTemplate(saveData).then(response => {
|
||||
this.$modal.msgSuccess("保存模板成功");
|
||||
this.saveTemplateDialogVisible = false;
|
||||
this.templateForm = {
|
||||
templateName: '',
|
||||
selectedColumns: ['empName', 'postName', 'dutyCoeff', 'baseCoeff', 'adjustCoeff', 'remark']
|
||||
};
|
||||
});
|
||||
},
|
||||
// 选择模板
|
||||
handleSelectTemplate() {
|
||||
this.selectedTemplate = null;
|
||||
this.selectedTemplateContent = [];
|
||||
this.selectTemplateDialogVisible = true;
|
||||
// 获取模板列表
|
||||
listBonusTemplate({}).then(response => {
|
||||
this.templateList = response.rows || [];
|
||||
});
|
||||
},
|
||||
// 选择模板行
|
||||
handleTemplateSelect(row) {
|
||||
if (!row) {
|
||||
this.selectedTemplate = null;
|
||||
this.selectedTemplateContent = [];
|
||||
return;
|
||||
}
|
||||
this.selectedTemplate = row;
|
||||
try {
|
||||
this.selectedTemplateContent = JSON.parse(row.templateContent || '[]');
|
||||
} catch (e) {
|
||||
this.selectedTemplateContent = [];
|
||||
}
|
||||
},
|
||||
// 确认应用模板
|
||||
confirmApplyTemplate() {
|
||||
if (!this.selectedTemplate) {
|
||||
this.$message.warning('请选择一个模板');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.bonusConfigList.length > 0) {
|
||||
this.$modal.confirm('当前已有数据,应用模板会清空现有数据,是否继续?').then(() => {
|
||||
this.applyTemplate();
|
||||
}).catch(() => {});
|
||||
} else {
|
||||
this.applyTemplate();
|
||||
}
|
||||
},
|
||||
// 应用模板
|
||||
async applyTemplate() {
|
||||
try {
|
||||
this.loading = true;
|
||||
|
||||
// 先删除现有数据
|
||||
const deletePromises = this.bonusConfigList.map(row => delBonusConfig(row.configId));
|
||||
await Promise.all(deletePromises);
|
||||
|
||||
// 从模板添加新数据
|
||||
const addPromises = this.selectedTemplateContent.map(templateRow => {
|
||||
const newRow = {
|
||||
poolId: this.poolId,
|
||||
empId: templateRow.empId || null,
|
||||
empName: templateRow.empName || '',
|
||||
postName: templateRow.postName || '',
|
||||
dutyCoeff: templateRow.dutyCoeff || 0,
|
||||
baseCoeff: templateRow.baseCoeff || 0,
|
||||
adjustCoeff: templateRow.adjustCoeff || 0,
|
||||
allocBase: 0,
|
||||
remark: templateRow.remark || ''
|
||||
};
|
||||
return addBonusConfig(newRow);
|
||||
});
|
||||
|
||||
await Promise.all(addPromises);
|
||||
this.$message.success(`成功应用模板,添加 ${this.selectedTemplateContent.length} 条记录`);
|
||||
this.selectTemplateDialogVisible = false;
|
||||
|
||||
// 刷新数据
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error('应用模板失败:', error);
|
||||
this.$message.error('应用模板失败');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,581 @@
|
||||
<template>
|
||||
<div class="bonus-template-management">
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧:模板列表 -->
|
||||
<el-col :span="6">
|
||||
<div class="template-list-panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">我的模板</span>
|
||||
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAddTemplate">新增模板</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-box">
|
||||
<el-input v-model="queryParams.templateName" placeholder="搜索模板名称" prefix-icon="el-icon-search" size="small" clearable
|
||||
@clear="handleSearchClear" @change="getTemplateList" />
|
||||
</div>
|
||||
|
||||
<!-- Loading状态 -->
|
||||
<div v-if="loading" class="template-list-loading">
|
||||
<i class="el-icon-loading"></i>
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-else-if="templateList.length === 0" class="template-list-empty">
|
||||
<i class="el-icon-document"></i>
|
||||
<span>暂无模板</span>
|
||||
</div>
|
||||
|
||||
<!-- 自定义列表 -->
|
||||
<div v-else class="template-list-container">
|
||||
<!-- 搜索无结果 -->
|
||||
<div v-if="templateList.length === 0" class="search-no-result">
|
||||
<i class="el-icon-search"></i>
|
||||
<span>未找到 "{{ searchKeyword }}" 相关的模板</span>
|
||||
</div>
|
||||
<!-- 搜索结果 -->
|
||||
<div v-else>
|
||||
<div v-for="(template, index) in templateList" :key="template.templateId" class="template-item"
|
||||
:class="{ 'template-item-active': currentTemplateId === template.templateId }"
|
||||
@click="handleTemplateSelect(template)">
|
||||
<div class="template-item-content">
|
||||
<div class="template-item-name">{{ template.templateName }}</div>
|
||||
<div class="template-item-time">
|
||||
<i class="el-icon-time"></i>
|
||||
<span>{{ parseTime(template.createTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="template-item-arrow">
|
||||
<i class="el-icon-arrow-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:模板编辑 -->
|
||||
<el-col :span="18">
|
||||
<div v-if="!isEditing" class="template-edit-panel template-edit-empty">
|
||||
<div class="empty-state">
|
||||
<i class="el-icon-document-copy"></i>
|
||||
<div class="empty-state-text">请选择一个模板或点击"新增模板"开始编辑</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="template-edit-panel">
|
||||
<div class="panel-header">
|
||||
<el-form :model="templateForm" ref="templateForm" :inline="true" size="small">
|
||||
<el-form-item label="模板名称" prop="templateName">
|
||||
<el-input v-model="templateForm.templateName" placeholder="请输入模板名称" clearable style="width: 300px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-document" @click="handleSaveTemplate"
|
||||
:loading="saving">保存</el-button>
|
||||
<el-button type="danger" icon="el-icon-delete" @click="handleDeleteTemplate" v-if="currentTemplateId"
|
||||
:loading="deleting">删除</el-button>
|
||||
<el-button icon="el-icon-close" @click="handleCancelEdit" v-if="isEditing">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 员工选择器 -->
|
||||
<div style="margin-bottom: 16px;">
|
||||
<EmployeeSelector ref="employeeSelector" :multiple="true" @change="handleBatchAddEmployees" title="选择员工">
|
||||
<el-button icon="el-icon-plus" size="small" slot="trigger" style="width: 100%">
|
||||
选择员工添加
|
||||
</el-button>
|
||||
</EmployeeSelector>
|
||||
</div>
|
||||
|
||||
<!-- 可编辑表格 -->
|
||||
<div style="flex: 1; overflow: hidden; display: flex; flex-direction: column;">
|
||||
<el-table :data="templateContent" border style="width: 100%" height="100%">
|
||||
<!-- 员工姓名 -->
|
||||
<el-table-column label="姓名" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.empName" size="small" style="width: 100%" clearable />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 岗位 -->
|
||||
<el-table-column label="岗位" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.postName" size="small" style="width: 100%" clearable
|
||||
@change="handlePostChange(scope.row)">
|
||||
<el-option v-for="post in postCoeffList" :key="post.configId" :label="post.postName"
|
||||
:value="post.postName" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 职务系数 -->
|
||||
<el-table-column label="职务系数">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.dutyCoeff" size="small" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 基本系数 -->
|
||||
<el-table-column label="基本系数">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.baseCoeff" size="small" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 主任加减系数 -->
|
||||
<el-table-column label="主任加减系数">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.adjustCoeff" size="small" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 备注 -->
|
||||
<el-table-column label="备注" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.remark" size="small" style="width: 100%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" icon="el-icon-delete" size="small" @click="handleDeleteRow(scope.$index)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listBonusTemplate, addBonusTemplate, updateBonusTemplate, delBonusTemplate } from "@/api/wms/bonusTemplate";
|
||||
import { listPostCoeffConfig } from "@/api/wms/postCoeffConfig";
|
||||
import EmployeeSelector from "@/components/EmployeeSelector";
|
||||
|
||||
export default {
|
||||
name: "BonusTemplateManagement",
|
||||
components: {
|
||||
EmployeeSelector
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
saving: false,
|
||||
deleting: false,
|
||||
templateList: [],
|
||||
currentTemplateId: null,
|
||||
searchKeyword: '',
|
||||
isEditing: false,
|
||||
templateForm: {
|
||||
templateId: null,
|
||||
templateName: '',
|
||||
createBy: null
|
||||
},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 1000,
|
||||
templateName: '',
|
||||
createBy: this.$store.getters.name
|
||||
},
|
||||
templateContent: [],
|
||||
postCoeffList: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getTemplateList();
|
||||
this.getPostCoeffList();
|
||||
},
|
||||
methods: {
|
||||
// 根据岗位名称查找岗位系数配置
|
||||
findPostCoeff(postName) {
|
||||
return this.postCoeffList.find(post => post.postName === postName);
|
||||
},
|
||||
// 处理岗位变化,自动填充系数
|
||||
handlePostChange(row) {
|
||||
if (!row.postName) {
|
||||
return;
|
||||
}
|
||||
const postCoeff = this.findPostCoeff(row.postName);
|
||||
if (postCoeff) {
|
||||
row.dutyCoeff = postCoeff.dutyCoeff;
|
||||
row.baseCoeff = postCoeff.baseCoeff;
|
||||
}
|
||||
},
|
||||
// 清空搜索
|
||||
handleSearchClear() {
|
||||
this.queryParams.templateName = '';
|
||||
this.getTemplateList();
|
||||
},
|
||||
// 批量添加员工
|
||||
handleBatchAddEmployees(employees) {
|
||||
if (!employees || employees.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
employees.forEach(employee => {
|
||||
const postName = employee.jobType || '';
|
||||
// 查找匹配的岗位系数
|
||||
const postCoeff = this.findPostCoeff(postName);
|
||||
|
||||
const newRow = {
|
||||
empId: employee.infoId,
|
||||
empName: employee.name,
|
||||
postName: postName,
|
||||
dutyCoeff: postCoeff ? postCoeff.dutyCoeff : 0,
|
||||
baseCoeff: postCoeff ? postCoeff.baseCoeff : 0,
|
||||
adjustCoeff: 0,
|
||||
remark: ''
|
||||
};
|
||||
this.templateContent.push(newRow);
|
||||
});
|
||||
|
||||
this.$message.success(`成功添加 ${employees.length} 名员工`);
|
||||
},
|
||||
// 获取模板列表(当前登录用户创建的)
|
||||
async getTemplateList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await listBonusTemplate(this.queryParams);
|
||||
this.templateList = res.rows || [];
|
||||
} catch (error) {
|
||||
console.error('获取模板列表失败:', error);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 获取岗位系数列表
|
||||
async getPostCoeffList() {
|
||||
try {
|
||||
const res = await listPostCoeffConfig({ pageNum: 1, pageSize: 1000, createBy: this.$store.getters.name });
|
||||
this.postCoeffList = res.rows || [];
|
||||
} catch (error) {
|
||||
console.error('获取岗位系数失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 选择模板
|
||||
handleTemplateSelect(row) {
|
||||
if (!row) return;
|
||||
this.isEditing = true;
|
||||
this.currentTemplateId = row.templateId;
|
||||
this.templateForm = {
|
||||
templateId: row.templateId,
|
||||
templateName: row.templateName,
|
||||
createBy: row.createBy
|
||||
};
|
||||
try {
|
||||
this.templateContent = JSON.parse(row.templateContent || '[]');
|
||||
} catch (error) {
|
||||
this.templateContent = [];
|
||||
console.error('解析模板内容失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 新增模板
|
||||
handleAddTemplate() {
|
||||
this.isEditing = true;
|
||||
this.currentTemplateId = null;
|
||||
this.templateForm = {
|
||||
templateId: null,
|
||||
templateName: '',
|
||||
};
|
||||
this.templateContent = [];
|
||||
},
|
||||
|
||||
// 取消编辑
|
||||
handleCancelEdit() {
|
||||
this.isEditing = false;
|
||||
this.currentTemplateId = null;
|
||||
this.templateForm = {
|
||||
templateId: null,
|
||||
templateName: '',
|
||||
createBy: null
|
||||
};
|
||||
this.templateContent = [];
|
||||
},
|
||||
|
||||
// 添加行
|
||||
handleAddRow() {
|
||||
this.templateContent.push({
|
||||
empId: null,
|
||||
empName: '',
|
||||
postName: '',
|
||||
dutyCoeff: 0,
|
||||
baseCoeff: 0,
|
||||
adjustCoeff: 0,
|
||||
remark: ''
|
||||
});
|
||||
},
|
||||
|
||||
// 删除行
|
||||
handleDeleteRow(index) {
|
||||
this.templateContent.splice(index, 1);
|
||||
},
|
||||
|
||||
// 保存模板
|
||||
async handleSaveTemplate() {
|
||||
if (!this.templateForm.templateName) {
|
||||
this.$message.warning('请输入模板名称');
|
||||
return;
|
||||
}
|
||||
|
||||
this.saving = true;
|
||||
const saveData = {
|
||||
templateId: this.templateForm.templateId,
|
||||
templateName: this.templateForm.templateName,
|
||||
templateContent: JSON.stringify(this.templateContent),
|
||||
createBy: this.$store.getters.name
|
||||
};
|
||||
const isNew = !this.currentTemplateId;
|
||||
|
||||
try {
|
||||
if (this.currentTemplateId) {
|
||||
await updateBonusTemplate(saveData);
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
} else {
|
||||
await addBonusTemplate(saveData);
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
}
|
||||
await this.getTemplateList();
|
||||
if (isNew) {
|
||||
// 新增保存后回到空白状态
|
||||
this.handleCancelEdit();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存模板失败:', error);
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 删除模板
|
||||
async handleDeleteTemplate() {
|
||||
this.$modal.confirm('是否确认删除该模板?').then(async () => {
|
||||
this.deleting = true;
|
||||
try {
|
||||
await delBonusTemplate(this.currentTemplateId);
|
||||
await this.getTemplateList();
|
||||
this.handleCancelEdit();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
} catch (error) {
|
||||
console.error('删除模板失败:', error);
|
||||
} finally {
|
||||
this.deleting = false;
|
||||
}
|
||||
}).catch(() => { });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.template-list-panel,
|
||||
.template-edit-panel {
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
height: calc(100vh - 300px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #EBEEF5;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
/* 搜索框 */
|
||||
.search-box {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Loading状态 */
|
||||
.template-list-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.template-list-loading i {
|
||||
font-size: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.template-list-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.template-list-empty i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 12px;
|
||||
color: #C0C4CC;
|
||||
}
|
||||
|
||||
/* 搜索无结果 */
|
||||
.search-no-result {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.search-no-result i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 12px;
|
||||
color: #C0C4CC;
|
||||
}
|
||||
|
||||
/* 模板列表容器 */
|
||||
.template-list-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.template-list-container::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.template-list-container::-webkit-scrollbar-thumb {
|
||||
background: #DCDFE6;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* 模板项 */
|
||||
.template-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.template-item:hover {
|
||||
background: #F5F7FA;
|
||||
border-color: #E4E7ED;
|
||||
}
|
||||
|
||||
.template-item-active {
|
||||
background: #ECF5FF;
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
/* 模板项图标 */
|
||||
.template-item-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.template-item-icon i {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
/* 模板项内容 */
|
||||
.template-item-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.template-item-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-bottom: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.template-item-time {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.template-item-time i {
|
||||
margin-right: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 模板项箭头 */
|
||||
.template-item-arrow {
|
||||
color: #C0C4CC;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.template-item:hover .template-item-arrow,
|
||||
.template-item-active .template-item-arrow {
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
/* 编辑面板表格 */
|
||||
.template-edit-panel .el-table {
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 空白状态 */
|
||||
.template-edit-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 64px;
|
||||
margin-bottom: 16px;
|
||||
color: #C0C4CC;
|
||||
}
|
||||
|
||||
.empty-state-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -99,6 +99,11 @@ export default {
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentUserName() {
|
||||
return this.$store.getters.name;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
handler(newVal) {
|
||||
@@ -112,7 +117,7 @@ export default {
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listPostCoeffConfig({}).then(response => {
|
||||
listPostCoeffConfig({ createBy: this.currentUserName, pageNum: 1, pageSize: 100 }).then(response => {
|
||||
this.postCoeffConfigList = response.rows || [];
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
<!-- 搜索表单 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="60px">
|
||||
<el-form-item label="产线" prop="productionLine">
|
||||
<el-input v-model="queryParams.productionLine" placeholder="请输入产线" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="奖金时间" prop="bonusTime">
|
||||
<el-date-picker v-model="queryParams.bonusTime" type="date" placeholder="选择奖金时间" value-format="yyyy-MM-dd"
|
||||
clearable style="width: 100%" />
|
||||
<el-select v-model="queryParams.productionLine" placeholder="请选择产线" clearable>
|
||||
<el-option v-for="item in productionLineList" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
@@ -25,6 +22,10 @@
|
||||
<el-button type="warning" plain icon="el-icon-setting" size="mini"
|
||||
@click="handlePostCoeffConfig">岗位系数配置</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-document" size="mini"
|
||||
@click="handleTemplateManagement">模板管理</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
@@ -32,10 +33,20 @@
|
||||
<el-table v-loading="loading" :data="bonusPoolList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="产线" align="center" prop="productionLine" width="150" />
|
||||
<el-table-column label="奖金时间" align="center" prop="bonusTime" width="120">
|
||||
<!-- <el-table-column label="奖金时间" align="center" prop="bonusTime" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.bonusTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="开始时间" align="center" prop="bonusStartTime" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.bonusStartTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" align="center" prop="bonusEndTime" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.bonusEndTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总金额(元)" align="center" prop="totalBonus" width="120">
|
||||
<template slot-scope="scope">
|
||||
@@ -65,14 +76,39 @@
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="产线" prop="productionLine">
|
||||
<el-input v-model="form.productionLine" placeholder="请输入产线" />
|
||||
<muti-select v-model="form.productionLine" placeholder="请选择产线" clearable filterable
|
||||
:options="productionLineList">
|
||||
<el-option v-for="item in productionLineList" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</muti-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="奖金时间" prop="bonusTime">
|
||||
<el-date-picker v-model="form.bonusTime" type="date" placeholder="选择奖金时间" value-format="yyyy-MM-dd"
|
||||
|
||||
<el-form-item label="开始时间" prop="bonusStartTime">
|
||||
<el-date-picker v-model="form.bonusStartTime" type="date" placeholder="选择开始时间" value-format="yyyy-MM-dd"
|
||||
style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="结束时间" prop="bonusEndTime">
|
||||
<el-date-picker v-model="form.bonusEndTime" type="date" placeholder="选择结束时间" value-format="yyyy-MM-dd"
|
||||
style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="查询重量">
|
||||
<el-button type="primary" @click="queryCoilWeight"
|
||||
:disabled="!form.bonusStartTime || !form.bonusEndTime || !form.productionLine"
|
||||
:loading="queryLoading">查询钢卷总重量</el-button>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="总重量(吨)">
|
||||
<el-input v-model="totalWeight" disabled placeholder="请先查询钢卷重量" style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="系数" prop="coeff">
|
||||
<el-input-number v-model="form.coeff" :precision="4" :step="0.0001" :min="0" placeholder="请输入系数"
|
||||
style="width: 100%" @change="calculateTotalBonus" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="总金额" prop="totalBonus">
|
||||
<el-input-number v-model="form.totalBonus" :precision="2" :step="0.01" :min="0" placeholder="请输入总金额"
|
||||
<el-input-number v-model="form.totalBonus" :precision="2" :step="0.01" :min="0" placeholder="自动计算或手动输入"
|
||||
style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
@@ -89,16 +125,17 @@
|
||||
<el-dialog title="奖金池详情" :visible.sync="detailOpen" width="1000px" append-to-body>
|
||||
<el-descriptions :column="2" border style="margin-bottom: 20px">
|
||||
<el-descriptions-item label="产线">{{ bonusPoolDetail.productionLine }}</el-descriptions-item>
|
||||
<el-descriptions-item label="奖金时间">{{ parseTime(bonusPoolDetail.bonusTime, '{y}-{m}-{d}')
|
||||
<el-descriptions-item label="开始时间">{{ parseTime(bonusPoolDetail.bonusStartTime, '{y}-{m}-{d}')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="结束时间">{{ parseTime(bonusPoolDetail.bonusEndTime, '{y}-{m}-{d}')
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ parseTime(bonusPoolDetail.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总金额(元)">{{ Number(bonusPoolDetail.totalBonus).toFixed(2) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ bonusPoolDetail.remark || '无' }}</el-descriptions-item>
|
||||
|
||||
</el-descriptions>
|
||||
|
||||
<el-divider>奖金分配明细</el-divider>
|
||||
<BonusConfig ref="bonusConfigRef" :pool-id="currentPoolId" :total-bonus="bonusPoolDetail.totalBonus" :max-adjust-coeff="maxAdjustCoeff" />
|
||||
<BonusConfig ref="bonusConfigRef" :pool-id="currentPoolId" :total-bonus="bonusPoolDetail.totalBonus"
|
||||
:max-adjust-coeff="maxAdjustCoeff" />
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailOpen = false">关 闭</el-button>
|
||||
@@ -112,6 +149,14 @@
|
||||
<el-button @click="postCoeffConfigOpen = false">关 闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 模板管理对话框 -->
|
||||
<el-dialog title="模板管理" :visible.sync="templateManagementOpen" width="1200px" append-to-body>
|
||||
<BonusTemplateManagement :show="templateManagementOpen" />
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="templateManagementOpen = false">关 闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -119,13 +164,17 @@
|
||||
import { listBonusPool, getBonusPool, delBonusPool, addBonusPool, updateBonusPool } from "@/api/wms/bonusPool";
|
||||
import BonusConfig from "./components/BonusConfig.vue";
|
||||
import PostCoeffConfig from "./components/PostCoeffConfig.vue";
|
||||
import BonusTemplateManagement from "./components/BonusTemplateManagement.vue";
|
||||
import { getConfigKey } from '@/api/system/config'
|
||||
import { listPendingAction } from '@/api/wms/pendingAction'
|
||||
import { getCoilStatisticsList } from "@/api/wms/coil";
|
||||
|
||||
export default {
|
||||
name: "Bonus",
|
||||
components: {
|
||||
BonusConfig,
|
||||
PostCoeffConfig
|
||||
PostCoeffConfig,
|
||||
BonusTemplateManagement
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -140,11 +189,14 @@ export default {
|
||||
open: false,
|
||||
detailOpen: false,
|
||||
currentPoolId: null,
|
||||
totalWeight: null,
|
||||
queryLoading: false,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
productionLine: null,
|
||||
bonusTime: null,
|
||||
createBy: this.$store.getters.name,
|
||||
},
|
||||
maxAdjustCoeff: 10.0,
|
||||
form: {},
|
||||
@@ -156,13 +208,58 @@ export default {
|
||||
bonusTime: [
|
||||
{ required: true, message: "奖金时间不能为空", trigger: "change" }
|
||||
],
|
||||
bonusStartTime: [
|
||||
{ required: true, message: "开始时间不能为空", trigger: "change" }
|
||||
],
|
||||
bonusEndTime: [
|
||||
{ required: true, message: "结束时间不能为空", trigger: "change" }
|
||||
],
|
||||
totalBonus: [
|
||||
{ required: true, message: "总金额不能为空", trigger: "blur" }
|
||||
]
|
||||
},
|
||||
postCoeffConfigOpen: false,
|
||||
productionLineList: [
|
||||
{
|
||||
label: '酸轧线',
|
||||
value: '酸轧线',
|
||||
actions: '11,200,520'
|
||||
},
|
||||
{
|
||||
label: '镀锌线',
|
||||
value: '镀锌线',
|
||||
actions: '206,501,521'
|
||||
},
|
||||
{
|
||||
label: '拉矫线',
|
||||
value: '拉矫线',
|
||||
actions: '204,503,523'
|
||||
},
|
||||
{
|
||||
label: '双机架',
|
||||
value: '双机架',
|
||||
actions: '205,504,524'
|
||||
},
|
||||
{
|
||||
label: '镀铬线',
|
||||
value: '镀铬线',
|
||||
actions: '206,505,525'
|
||||
},
|
||||
{
|
||||
label: '脱脂线',
|
||||
value: '脱脂线',
|
||||
actions: '203,502,522'
|
||||
},
|
||||
],
|
||||
templateManagementOpen: false,
|
||||
};
|
||||
},
|
||||
// computed: {
|
||||
// currentUserName() {
|
||||
// console.log(this.$store.getters.name, '计算属性');
|
||||
// return this.$store.getters.name;
|
||||
// }
|
||||
// },
|
||||
created() {
|
||||
this.getList();
|
||||
this.getMaxAdjustCoeff();
|
||||
@@ -188,20 +285,52 @@ export default {
|
||||
this.reset();
|
||||
},
|
||||
reset() {
|
||||
this.totalWeight = null;
|
||||
this.form = {
|
||||
poolId: null,
|
||||
productionLine: null,
|
||||
bonusTime: null,
|
||||
totalBonus: 0,
|
||||
remark: null,
|
||||
createBy: null,
|
||||
createBy: this.$store.getters.name,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
delFlag: null
|
||||
delFlag: null,
|
||||
coeff: null
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
async queryCoilWeight() {
|
||||
this.queryLoading = true;
|
||||
try {
|
||||
const productionLines = this.form.productionLine.split(',');
|
||||
|
||||
// 从productionLineList中获取productionLines对应的actions
|
||||
const productionLineActions = productionLines.map(line => this.productionLineList.find(item => item.value === line).actions);
|
||||
const pendingActions = await listPendingAction({
|
||||
startTime: this.form.bonusStartTime + ' 00:00:00',
|
||||
endTime: this.form.bonusEndTime + ' 23:59:59',
|
||||
actionTypes: productionLineActions.join(','),
|
||||
pageNum: 1,
|
||||
pageSize: 1000000,
|
||||
})
|
||||
const coilIds = pendingActions.rows.map(item => item.processedCoilIds);
|
||||
const coilStatistics = await getCoilStatisticsList({
|
||||
coilIds: coilIds.join(','),
|
||||
})
|
||||
this.totalWeight = coilStatistics.data.total_net_weight;
|
||||
this.calculateTotalBonus();
|
||||
this.queryLoading = false;
|
||||
} catch (error) {
|
||||
this.$modal.msgError(error.message);
|
||||
this.queryLoading = false;
|
||||
}
|
||||
},
|
||||
calculateTotalBonus() {
|
||||
if (this.totalWeight && this.form.coeff) {
|
||||
this.form.totalBonus = (this.totalWeight * this.form.coeff).toFixed(2);
|
||||
}
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
@@ -272,6 +401,9 @@ export default {
|
||||
},
|
||||
handlePostCoeffConfig() {
|
||||
this.postCoeffConfigOpen = true;
|
||||
},
|
||||
handleTemplateManagement() {
|
||||
this.templateManagementOpen = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user