feat(hrm/meal&employee): 新增员工吃辣偏好字段与就餐统计矩阵

1.  在员工入职/信息页面新增是否吃辣单选表单字段
2.  重构就餐统计模块,替换原有简单统计为吃辣/不吃辣 × 堂食/打包 × 有效/无效的三维交叉统计矩阵
3.  新增员工吃辣偏好映射接口,自动获取员工饮食偏好进行分类统计
This commit is contained in:
2026-06-18 13:50:46 +08:00
parent ee49fbdcc0
commit 5828dbd133
5 changed files with 196 additions and 50 deletions

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div style="display: flex; align-items: center; margin-bottom: 12px;">
<el-select v-model="selectedValue" placeholder="请选择合同" style="width: 100%">
<div style="display: flex; align-items: center; margin-bottom: 0px;">
<el-select v-model="selectedValue" placeholder="请选择合同" style="width: 100%" clearable>
<el-option v-for="item in contractList" :key="item.orderId" :value="item.orderId"
:label="item.contractCode" />
</el-select>

View File

@@ -158,11 +158,11 @@
</el-col>
</el-row>
<el-dialog title="完成退火" :visible.sync="completeOpen" width="720px" append-to-body>
<el-dialog title="完成退火" :visible.sync="completeOpen" width="800px" append-to-body>
<div class="complete-tip">请为每条钢卷分配逻辑库区去向和关联合同未分配将无法完成</div>
<el-table :data="completeCoils" v-loading="completeLoading" height="360px">
<el-table-column label="入场钢卷号" prop="enterCoilNo" align="center" />
<el-table-column label="钢卷去向" align="center" width="240">
<el-table-column label="钢卷去向" align="center" width="270">
<template slot-scope="scope">
<el-select v-model="scope.row.warehouseId" @change="handlePLanCoilChange(scope.row)">
<el-option v-for="item in warehouseList" :key="item.warehouseId" :label="item.warehouseName"
@@ -170,9 +170,11 @@
</el-select>
</template>
</el-table-column>
<el-table-column label="关联合同" align="center" width="240">
<el-table-column label="关联合同" align="center" width="270">
<template slot-scope="scope">
<ContractSelect v-model="scope.row.contractId" placeholder="请选择合同" />
<div style="width: 100%; display: flex; align-items: center;">
<ContractSelect v-model="scope.row.contractId" placeholder="请选择合同" />
</div>
</template>
</el-table-column>
</el-table>

View File

@@ -160,6 +160,14 @@
<el-input v-model="form.phone" placeholder="请输入联系电话" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否吃辣" prop="isSpicyEater">
<el-radio-group v-model="form.isSpicyEater" size="small">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="家庭住址" prop="homeAddress">
<el-input v-model="form.homeAddress" placeholder="请输入家庭住址" />
@@ -404,6 +412,7 @@ export default {
homeAddress: undefined,
phone: undefined,
entryTime: undefined,
isSpicyEater: undefined,
emergencyContact: undefined,
relationship: undefined,
emergencyContactPhone: undefined,

View File

@@ -216,6 +216,14 @@
<el-input v-model="form.phone" placeholder="请输入联系电话" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否吃辣" prop="isSpicyEater">
<el-radio-group v-model="form.isSpicyEater" size="small">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="家庭住址" prop="homeAddress">
<el-input v-model="form.homeAddress" placeholder="请输入家庭住址" />
@@ -432,6 +440,7 @@ export default {
homeAddress: undefined,
phone: undefined,
entryTime: undefined,
isSpicyEater: undefined,
emergencyContact: undefined,
relationship: undefined,
emergencyContactPhone: undefined,

View File

@@ -65,18 +65,44 @@
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 修改统计组件扩展为6列展示有效/无效的6项统计 -->
<el-descriptions size="small" border class="mb8" :column="3">
<el-descriptions-item label="堂食人数">{{ validDineIn + invalidDineIn }}</el-descriptions-item>
<el-descriptions-item label="打包人数">{{ validTakeout + invalidTakeout }}</el-descriptions-item>
<el-descriptions-item label="总人数">{{ validTotal + invalidTotal }}</el-descriptions-item>
<el-descriptions-item label="有效堂食人数">{{ validDineIn }}</el-descriptions-item>
<el-descriptions-item label="有效打包人数">{{ validTakeout }}</el-descriptions-item>
<el-descriptions-item label="有效总人数">{{ validTotal }}</el-descriptions-item>
<el-descriptions-item label="无效堂食人数">{{ invalidDineIn }}</el-descriptions-item>
<el-descriptions-item label="无效打包人数">{{ invalidTakeout }}</el-descriptions-item>
<el-descriptions-item label="无效总人数">{{ invalidTotal }}</el-descriptions-item>
</el-descriptions>
<!-- 三维交叉统计矩阵表 -->
<div class="stats-matrix mb8">
<table class="matrix-table">
<thead>
<tr>
<th rowspan="2" class="label-col"></th>
<th colspan="3" class="group-header group-valid">有效</th>
<th colspan="3" class="group-header group-invalid">无效</th>
<th colspan="3" class="group-header group-all">合计</th>
</tr>
<tr>
<th>堂食</th><th>打包</th><th>小计</th>
<th>堂食</th><th>打包</th><th>小计</th>
<th>堂食</th><th>打包</th><th>小计</th>
</tr>
</thead>
<tbody>
<tr>
<td class="label-col">吃辣</td>
<td>{{ matrix.spicy.validDine }}</td><td>{{ matrix.spicy.validTake }}</td><td class="subtotal">{{ matrix.spicy.validTotal }}</td>
<td>{{ matrix.spicy.invalidDine }}</td><td>{{ matrix.spicy.invalidTake }}</td><td class="subtotal">{{ matrix.spicy.invalidTotal }}</td>
<td>{{ matrix.spicy.dine }}</td><td>{{ matrix.spicy.take }}</td><td class="subtotal">{{ matrix.spicy.total }}</td>
</tr>
<tr>
<td class="label-col">不吃辣</td>
<td>{{ matrix.nonSpicy.validDine }}</td><td>{{ matrix.nonSpicy.validTake }}</td><td class="subtotal">{{ matrix.nonSpicy.validTotal }}</td>
<td>{{ matrix.nonSpicy.invalidDine }}</td><td>{{ matrix.nonSpicy.invalidTake }}</td><td class="subtotal">{{ matrix.nonSpicy.invalidTotal }}</td>
<td>{{ matrix.nonSpicy.dine }}</td><td>{{ matrix.nonSpicy.take }}</td><td class="subtotal">{{ matrix.nonSpicy.total }}</td>
</tr>
<tr class="total-row">
<td class="label-col">合计</td>
<td>{{ matrix.all.validDine }}</td><td>{{ matrix.all.validTake }}</td><td class="subtotal">{{ matrix.all.validTotal }}</td>
<td>{{ matrix.all.invalidDine }}</td><td>{{ matrix.all.invalidTake }}</td><td class="subtotal">{{ matrix.all.invalidTotal }}</td>
<td>{{ matrix.all.dine }}</td><td>{{ matrix.all.take }}</td><td class="subtotal">{{ matrix.all.total }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 修改添加行样式判断为无效报餐行加背景色 -->
<el-table
@@ -196,6 +222,7 @@
<script>
import { listMealReport, getMealReport, delMealReport, addMealReport, updateMealReport } from "@/api/wms/mealReport";
import { listEmployeeInfo } from "@/api/wms/employeeInfo";
import DictSelect from "@/components/DictSelect";
import EmployeeSelector from "@/components/EmployeeSelector";
import { listDept } from "@/api/wms/dept"
@@ -254,13 +281,13 @@ export default {
updateTime: undefined,
remark: undefined
},
// 替换原有统计变量新增6个有效/无效统计项
validDineIn: 0, // 有效堂食人数
validTakeout: 0, // 有效打包人数
validTotal: 0, // 有效总人数
invalidDineIn: 0, // 无效堂食人数
invalidTakeout: 0, // 无效打包人数
invalidTotal: 0, // 无效总人数
// 三维交叉统计矩阵
matrix: {
spicy: { validDine: 0, validTake: 0, validTotal: 0, invalidDine: 0, invalidTake: 0, invalidTotal: 0, dine: 0, take: 0, total: 0 },
nonSpicy: { validDine: 0, validTake: 0, validTotal: 0, invalidDine: 0, invalidTake: 0, invalidTotal: 0, dine: 0, take: 0, total: 0 },
all: { validDine: 0, validTake: 0, validTotal: 0, invalidDine: 0, invalidTake: 0, invalidTotal: 0, dine: 0, take: 0, total: 0 }
},
employeeSpicyMap: {},
// 表单校验规则
rules: {
reportDate: [{ required: true, message: '用餐日期不能为空', trigger: 'change' }],
@@ -278,6 +305,7 @@ export default {
this.getList();
this.getDeptList();
this.getDeadlineConfig();
this.buildEmployeeMap();
},
watch: {
'form.dineInPeople': {
@@ -324,37 +352,78 @@ export default {
this.form.takeoutPeople = val?.length || 0;
this.calcTotalPeople();
},
/** 核心修改:区分有效/无效统计 */
/** 构建员工吃辣偏好映射name -> isSpicyEater */
buildEmployeeMap() {
listEmployeeInfo({ pageNum: 1, pageSize: 9999 }).then(response => {
const map = {};
(response.rows || []).forEach(emp => {
if (emp.name && emp.isLeave !== 1 && emp.isSpicyEater !== undefined && emp.isSpicyEater !== null) {
map[emp.name] = emp.isSpicyEater == 1 ? 1 : 0;
}
});
this.employeeSpicyMap = map;
this.calcTableSum();
});
},
/** 根据人员名单统计不吃辣人数 */
countNonSpicy(nameList) {
if (!nameList) return 0;
const names = nameList.split(',').map(n => n.trim()).filter(n => n);
let count = 0;
const counted = {};
names.forEach(name => {
if (!counted[name] && this.employeeSpicyMap[name] === 0) {
count++;
counted[name] = true;
}
});
return count;
},
/** 三维交叉统计:吃辣/不吃辣 × 堂食/打包 × 有效/无效 */
calcTableSum(){
// 初始化统计变量
let validDine = 0, validTake = 0, validAll = 0;
let invalidDine = 0, invalidTake = 0, invalidAll = 0;
const m = {
spicy: { validDine: 0, validTake: 0, validTotal: 0, invalidDine: 0, invalidTake: 0, invalidTotal: 0, dine: 0, take: 0, total: 0 },
nonSpicy: { validDine: 0, validTake: 0, validTotal: 0, invalidDine: 0, invalidTake: 0, invalidTotal: 0, dine: 0, take: 0, total: 0 },
all: { validDine: 0, validTake: 0, validTotal: 0, invalidDine: 0, invalidTake: 0, invalidTotal: 0, dine: 0, take: 0, total: 0 }
};
this.mealReportList.forEach(item => {
// 处理空值,转为数字
const dine = item.dineInPeople ? Number(item.dineInPeople) : 0;
const take = item.takeoutPeople ? Number(item.takeoutPeople) : 0;
const total = item.totalPeople ? Number(item.totalPeople) : 0;
// 判断当前报餐是否有效
if (this.isValidMealReport(item.createTime)) {
validDine += dine;
validTake += take;
validAll += total;
} else {
invalidDine += dine;
invalidTake += take;
invalidAll += total;
}
const nonSpicyDine = this.countNonSpicy(item.dineInPeopleList);
const nonSpicyTake = this.countNonSpicy(item.takeoutPeopleList);
const spicyDine = dine - nonSpicyDine;
const spicyTake = take - nonSpicyTake;
const nonSpicyTotal = nonSpicyDine + nonSpicyTake;
const spicyTotal = spicyDine + spicyTake;
const v = this.isValidMealReport(item.createTime) ? 'valid' : 'invalid';
m.spicy[v + 'Dine'] += spicyDine;
m.spicy[v + 'Take'] += spicyTake;
m.spicy[v + 'Total'] += spicyTotal;
m.spicy.dine += spicyDine;
m.spicy.take += spicyTake;
m.spicy.total += spicyTotal;
m.nonSpicy[v + 'Dine'] += nonSpicyDine;
m.nonSpicy[v + 'Take'] += nonSpicyTake;
m.nonSpicy[v + 'Total'] += nonSpicyTotal;
m.nonSpicy.dine += nonSpicyDine;
m.nonSpicy.take += nonSpicyTake;
m.nonSpicy.total += nonSpicyTotal;
m.all[v + 'Dine'] += dine;
m.all[v + 'Take'] += take;
m.all[v + 'Total'] += total;
m.all.dine += dine;
m.all.take += take;
m.all.total += total;
});
// 赋值到统计变量
this.validDineIn = validDine;
this.validTakeout = validTake;
this.validTotal = validAll;
this.invalidDineIn = invalidDine;
this.invalidTakeout = invalidTake;
this.invalidTotal = invalidAll;
this.matrix = m;
},
/** 新增:判断报餐是否有效(仅比较时分秒) */
isValidMealReport(createTime) {
@@ -504,12 +573,69 @@ export default {
</script>
<style scoped>
/* 新增:无效报餐行样式 */
/* 无效报餐行样式 */
.invalid-meal-row {
background-color: #fff0f0 !important; /* 浅红色背景,可根据需求调整 */
background-color: #fff0f0 !important;
}
/* 优化表格行hover样式 */
::v-deep .el-table .invalid-meal-row:hover>td {
background-color: #ffe0e0 !important;
}
/* 三维交叉统计矩阵表 */
.matrix-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
border: 1px solid #EBEEF5;
}
.matrix-table th,
.matrix-table td {
padding: 8px 6px;
text-align: center;
border: 1px solid #EBEEF5;
min-width: 52px;
}
.matrix-table th {
background-color: #F5F7FA;
color: #909399;
font-weight: 500;
}
.matrix-table .label-col {
background-color: #F5F7FA;
color: #909399;
font-weight: 500;
text-align: center;
min-width: 60px;
}
.matrix-table .group-header {
font-weight: 600;
color: #303133;
}
.matrix-table .group-valid {
background-color: #F0F9EB;
color: #67C23A;
}
.matrix-table .group-invalid {
background-color: #FEF0F0;
color: #F56C6C;
}
.matrix-table .group-all {
background-color: #ECF5FF;
color: #409EFF;
}
.matrix-table .subtotal {
font-weight: 600;
color: #303133;
background-color: #FAFAFA;
}
.matrix-table .total-row td {
font-weight: 700;
border-top: 2px solid #DCDFE6;
}
.matrix-table .total-row .label-col {
font-weight: 700;
}
.matrix-table .total-row .subtotal {
font-weight: 700;
}
</style>