From 62294084c9c35d35594e9f2d6c41342ae84e9526 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期三, 29 七月 2026 16:39:20 +0800
Subject: [PATCH] 修改工资
---
src/views/personnelManagement/monthlyStatistics/components/formDia.vue | 343 +++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 283 insertions(+), 60 deletions(-)
diff --git a/src/views/personnelManagement/monthlyStatistics/components/formDia.vue b/src/views/personnelManagement/monthlyStatistics/components/formDia.vue
index 981fefe..709d316 100644
--- a/src/views/personnelManagement/monthlyStatistics/components/formDia.vue
+++ b/src/views/personnelManagement/monthlyStatistics/components/formDia.vue
@@ -48,6 +48,14 @@
</el-form-item>
</el-col>
<el-col :span="6">
+ <el-form-item label="宸ヤ綔鏃�">
+ <el-input v-model.number="monthWorkDays"
+ type="number"
+ placeholder="鑷姩璁$畻"
+ @input="handleMonthWorkDaysInput" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="6">
<el-form-item label="閫夋嫨閮ㄩ棬"
prop="deptIds">
<el-select v-model="form.deptIds"
@@ -59,12 +67,14 @@
<el-option v-for="item in deptOptions"
:key="item.deptId"
:label="item.deptName"
- :value="item.deptId"
- :disabled="isDeptDisabled(item.deptId)" />
+ :value="item.deptId"
+ :disabled="isDeptDisabled(item.deptId)" />
</el-select>
</el-form-item>
</el-col>
- <el-col :span="6">
+ </el-row>
+ <el-row :gutter="24">
+ <el-col :span="12">
<el-form-item label="澶囨敞"
prop="remark">
<el-input v-model="form.remark"
@@ -72,9 +82,7 @@
clearable />
</el-form-item>
</el-col>
- </el-row>
- <el-row :gutter="24">
- <el-col :span="6">
+ <el-col :span="4">
<el-form-item label="鏀粯閾惰"
prop="payBank">
<el-select v-model="form.payBank"
@@ -89,7 +97,7 @@
</el-select>
</el-form-item>
</el-col>
- <el-col :span="6">
+ <el-col :span="8">
<el-form-item label="瀹℃牳浜�"
prop="auditUserId">
<el-select v-model="form.auditUserId"
@@ -128,6 +136,9 @@
<el-table-column label="鍛樺伐濮撳悕"
prop="staffName"
minWidth="100" />
+ <el-table-column label="鍏ュ徃鏃ユ湡"
+ prop="entryDate"
+ minWidth="110" />
<el-table-column label="閮ㄩ棬"
prop="deptName"
minWidth="100" />
@@ -151,27 +162,28 @@
disabled />
</template>
</el-table-column>
- <el-table-column label="鏈堟椂闂村悎璁�"
+ <el-table-column label="鏃ヨ宸ヨ祫"
minWidth="110">
<template #default="{ row }">
- <el-input v-model.number="row.monthTimeTotal"
+ <el-input v-model.number="row.dailySalary"
type="number"
placeholder="0"
size="small"
disabled />
</template>
</el-table-column>
- <el-table-column label="鏈堟爣鍑嗗伐鏃�"
- minWidth="110">
+ <el-table-column label="鍑哄嫟澶╂暟"
+ minWidth="130">
<template #default="{ row }">
- <el-input v-model.number="row.standardHours"
+ <el-input v-model.number="row.workDays"
type="number"
placeholder="0"
size="small"
- disabled />
+ style="width: 100%"
+ @input="handleWorkDaysInput(row)" />
</template>
</el-table-column>
- <el-table-column label="鎶樺悎鍊嶆暟"
+ <el-table-column label="鏁堢泭"
minWidth="100">
<template #default="{ row }">
<el-input v-model.number="row.conversionRatio"
@@ -181,30 +193,70 @@
disabled />
</template>
</el-table-column>
- <el-table-column label="璁′欢宸ヨ祫"
+ <el-table-column label="鏈堟椂闂村悎璁�"
minWidth="110">
<template #default="{ row }">
- <el-input v-model.number="row.pieceSalary"
+ <el-input v-model.number="row.monthTimeTotal"
type="number"
placeholder="0"
size="small"
- @input="row.pieceSalary = parseNum(row.pieceSalary)" />
- </template>
- </el-table-column>
- <el-table-column label="璁℃椂宸ヨ祫"
- minWidth="110">
- <template #default="{ row }">
- <el-input v-model.number="row.hourlySalary"
- type="number"
- placeholder="0"
- size="small"
- @input="row.hourlySalary = parseNum(row.hourlySalary)" />
+ @input="handlePieceSalaryInput(row)" />
</template>
</el-table-column>
<el-table-column label="鍏朵粬鏀跺叆"
minWidth="110">
<template #default="{ row }">
<el-input v-model.number="row.otherIncome"
+ type="number"
+ placeholder="0"
+ size="small"
+ @input="handleSalaryInput(row)" />
+ </template>
+ </el-table-column>
+ <el-table-column label="浜嬪亣"
+ minWidth="90">
+ <template #default="{ row }">
+ <el-input v-model.number="row.personalLeave"
+ type="number"
+ placeholder="0"
+ size="small"
+ @input="handleLeaveInput(row)" />
+ </template>
+ </el-table-column>
+ <el-table-column label="鐥呭亣"
+ minWidth="90">
+ <template #default="{ row }">
+ <el-input v-model.number="row.sickLeave"
+ type="number"
+ placeholder="0"
+ size="small"
+ @input="handleLeaveInput(row)" />
+ </template>
+ </el-table-column>
+ <el-table-column label="涓у亣"
+ minWidth="90">
+ <template #default="{ row }">
+ <el-input v-model.number="row.bereavementLeave"
+ type="number"
+ placeholder="0"
+ size="small"
+ @input="handleLeaveInput(row)" />
+ </template>
+ </el-table-column>
+ <el-table-column label="濠氬亣"
+ minWidth="90">
+ <template #default="{ row }">
+ <el-input v-model.number="row.marriageLeave"
+ type="number"
+ placeholder="0"
+ size="small"
+ @input="handleLeaveInput(row)" />
+ </template>
+ </el-table-column>
+ <el-table-column label="娲ヨ创"
+ minWidth="110">
+ <template #default="{ row }">
+ <el-input v-model.number="row.allowance"
type="number"
placeholder="0"
size="small"
@@ -456,16 +508,20 @@
deptName: "",
basicSalary: 0,
attendanceBonus: 100,
- pieceSalary: 0,
+ entryDate: "",
+ workDays: 0,
+ dailySalary: 0,
hourlySalary: 0,
actualOutput: 0,
standardQuantity: 0,
- conversionFactor: 1,
- convertedWorkHours: 0,
monthTimeTotal: 0,
- standardHours: 0,
conversionRatio: 0,
otherIncome: 0,
+ personalLeave: 0,
+ sickLeave: 0,
+ bereavementLeave: 0,
+ marriageLeave: 0,
+ allowance: 0,
socialPersonal: 0,
fundPersonal: 0,
otherDeduct: 0,
@@ -488,16 +544,20 @@
deptName: source?.deptName ?? fallback.deptName ?? "",
basicSalary: parseNum(source?.basicSalary ?? fallback.basicSalary),
attendanceBonus: parseNum(source?.attendanceBonus ?? fallback.attendanceBonus ?? 100) || 100,
- pieceSalary: parseNum(source?.pieceSalary ?? fallback.pieceSalary),
+ entryDate: source?.entryDate ?? source?.contractStartTime ?? fallback.entryDate ?? "",
+ workDays: parseNum(source?.workDays ?? fallback.workDays),
+ dailySalary: parseNum(source?.dailySalary ?? fallback.dailySalary),
hourlySalary: parseNum(source?.hourlySalary ?? fallback.hourlySalary),
actualOutput: parseNum(source?.actualOutput ?? fallback.actualOutput),
standardQuantity: parseNum(source?.standardQuantity ?? fallback.standardQuantity),
- conversionFactor: parseNum(source?.conversionFactor ?? fallback.conversionFactor) || 1,
- convertedWorkHours: parseNum(source?.convertedWorkHours ?? fallback.convertedWorkHours),
monthTimeTotal: parseNum(source?.monthTimeTotal ?? fallback.monthTimeTotal),
- standardHours: parseNum(source?.standardHours ?? fallback.standardHours),
conversionRatio: parseNum(source?.conversionRatio ?? fallback.conversionRatio),
otherIncome: parseNum(source?.otherIncome ?? fallback.otherIncome),
+ personalLeave: parseNum(source?.personalLeave ?? fallback.personalLeave),
+ sickLeave: parseNum(source?.sickLeave ?? fallback.sickLeave),
+ bereavementLeave: parseNum(source?.bereavementLeave ?? fallback.bereavementLeave),
+ marriageLeave: parseNum(source?.marriageLeave ?? fallback.marriageLeave),
+ allowance: parseNum(source?.allowance ?? fallback.allowance),
socialPersonal: parseNum(source?.socialPersonal ?? fallback.socialPersonal),
fundPersonal: parseNum(source?.fundPersonal ?? fallback.fundPersonal),
otherDeduct: parseNum(source?.otherDeduct ?? fallback.otherDeduct),
@@ -510,8 +570,35 @@
remark: source?.remark ?? fallback.remark ?? "",
});
- const applyBackendRow = (targetRow, sourceRow) => {
+ const recalculateRowByWorkDays = row => {
+ const workDayCount = parseNum(monthWorkDays.value);
+ const monthTimeTotal = parseNum(row.monthTimeTotal);
+ const standardHours = workDayCount > 0 ? workDayCount * 8 : 0;
+ if (workDayCount > 0 && parseNum(row.workDays) > workDayCount) {
+ row.workDays = workDayCount;
+ }
+ if (standardHours > 0) {
+ row.conversionRatio = (monthTimeTotal / standardHours).toFixed(4);
+ } else {
+ row.conversionRatio = 0;
+ }
+ };
+
+ const applyBackendRow = (targetRow, sourceRow, options = {}) => {
+ const preservedConversionRatio = options.preserveConversionRatio
+ ? parseNum(targetRow.conversionRatio)
+ : null;
Object.assign(targetRow, normalizeEmployeeRow(sourceRow, targetRow));
+ if (options.preserveConversionRatio) {
+ targetRow.conversionRatio = preservedConversionRatio;
+ }
+ recalculateRowByWorkDays(targetRow);
+ };
+
+ const recalcRowsByCurrentWorkDays = () => {
+ employeeList.value.forEach(row => {
+ recalculateRowByWorkDays(row);
+ });
};
const clearRowRecalcState = row => {
@@ -528,30 +615,36 @@
recalcVersions.clear();
};
- const requestEmployeeRecalculate = row => {
+ const requestEmployeeRecalculate = (row, options = {}) => {
const key = String(row?.staffOnJobId ?? row?.id ?? "");
if (!key || !form.value.salaryMonth) return;
const version = (recalcVersions.get(key) || 0) + 1;
recalcVersions.set(key, version);
+ const manualWorkDays = parseNum(row.workDays);
clearTimeout(recalcTimers.get(key));
const payload = {
staffOnJobId: row.staffOnJobId ?? row.id,
salaryMonth: form.value.salaryMonth,
+ monthWorkDays: parseNum(monthWorkDays.value),
staffName: row.staffName ?? "",
postName: row.postName ?? "",
deptName: row.deptName ?? "",
+ entryDate: row.entryDate ?? row.contractStartTime ?? "",
basicSalary: parseNum(row.basicSalary),
attendanceBonus: parseNum(row.attendanceBonus),
- pieceSalary: parseNum(row.pieceSalary),
+ workDays: parseNum(row.workDays),
+ dailySalary: parseNum(row.dailySalary),
hourlySalary: parseNum(row.hourlySalary),
actualOutput: parseNum(row.actualOutput),
standardQuantity: parseNum(row.standardQuantity),
- conversionFactor: parseNum(row.conversionFactor) || 1,
- convertedWorkHours: parseNum(row.convertedWorkHours),
monthTimeTotal: parseNum(row.monthTimeTotal),
- standardHours: parseNum(row.standardHours),
conversionRatio: parseNum(row.conversionRatio),
otherIncome: parseNum(row.otherIncome),
+ personalLeave: parseNum(row.personalLeave),
+ sickLeave: parseNum(row.sickLeave),
+ bereavementLeave: parseNum(row.bereavementLeave),
+ marriageLeave: parseNum(row.marriageLeave),
+ allowance: parseNum(row.allowance),
otherDeduct: parseNum(row.otherDeduct),
socialSupplementAmount: parseNullableNum(row.socialSupplementAmount),
remark: row.remark ?? "",
@@ -561,7 +654,12 @@
setTimeout(() => {
staffSalaryMainCalculateByEmployeeId(payload).then(res => {
if (recalcVersions.get(key) !== version) return;
- applyBackendRow(row, res?.data || {});
+ applyBackendRow(row, res?.data || {}, options);
+ if (manualWorkDays > 0) {
+ row.workDays = manualWorkDays;
+ row.dailySalary = manualWorkDays > 0 ? parseNum(row.basicSalary) / manualWorkDays : 0;
+ recalculateRowByWorkDays(row);
+ }
});
}, 250)
);
@@ -572,8 +670,81 @@
row.otherIncome = parseNum(row.otherIncome);
row.actualOutput = parseNum(row.actualOutput);
row.standardQuantity = parseNum(row.standardQuantity);
- row.conversionFactor = parseNum(row.conversionFactor) || 1;
+ row.workDays = parseNum(row.workDays);
+ row.dailySalary = row.workDays > 0
+ ? (parseNum(row.basicSalary) / row.workDays).toFixed(3)
+ : 0;
+ requestEmployeeRecalculate(row, { preserveConversionRatio: true });
+ };
+
+ const handlePieceSalaryInput = row => {
+ row.monthTimeTotal = parseNum(row.monthTimeTotal);
requestEmployeeRecalculate(row);
+ };
+
+ const handleWorkDaysInput = row => {
+ row.workDays = parseNum(row.workDays);
+ const maxWorkDays = parseNum(monthWorkDays.value);
+ if (maxWorkDays > 0 && row.workDays > maxWorkDays) {
+ row.workDays = maxWorkDays;
+ proxy.$modal.msgWarning("鍑哄嫟澶╂暟涓嶈兘澶т簬宸ヤ綔鏃�");
+ }
+ enforceLeaveAndWorkDayLimit(row);
+ row.dailySalary = row.workDays > 0
+ ? (parseNum(row.basicSalary) / row.workDays).toFixed(3)
+ : 0;
+ requestEmployeeRecalculate(row);
+ };
+
+ const handleLeaveInput = row => {
+ row.personalLeave = parseNum(row.personalLeave);
+ row.sickLeave = parseNum(row.sickLeave);
+ row.bereavementLeave = parseNum(row.bereavementLeave);
+ row.marriageLeave = parseNum(row.marriageLeave);
+ enforceLeaveAndWorkDayLimit(row);
+ requestEmployeeRecalculate(row);
+ };
+
+ const enforceLeaveAndWorkDayLimit = row => {
+ const maxWorkDays = parseNum(monthWorkDays.value);
+ if (maxWorkDays <= 0) {
+ return true;
+ }
+ const leaveTotal = parseNum(row.personalLeave)
+ + parseNum(row.sickLeave)
+ + parseNum(row.bereavementLeave)
+ + parseNum(row.marriageLeave)
+ + parseNum(row.workDays);
+ if (leaveTotal > maxWorkDays) {
+ proxy.$modal.msgWarning("璇峰亣鍚堣鍔犲嚭鍕ゅぉ鏁颁笉鑳借秴杩囧伐浣滄棩");
+ const allowedWorkDays = Math.max(0, maxWorkDays
+ - parseNum(row.personalLeave)
+ - parseNum(row.sickLeave)
+ - parseNum(row.bereavementLeave)
+ - parseNum(row.marriageLeave));
+ row.workDays = allowedWorkDays;
+ return false;
+ }
+ return true;
+ };
+
+ const handleMonthWorkDaysInput = () => {
+ monthWorkDays.value = parseNum(monthWorkDays.value);
+ employeeList.value.forEach(row => {
+ const standardHours = monthWorkDays.value > 0 ? monthWorkDays.value * 8 : 0;
+ if (parseNum(row.workDays) > monthWorkDays.value) {
+ row.workDays = monthWorkDays.value;
+ row.dailySalary = monthWorkDays.value > 0
+ ? (parseNum(row.basicSalary) / monthWorkDays.value).toFixed(3)
+ : 0;
+ }
+ if (parseNum(row.monthTimeTotal) > 0 && standardHours > 0) {
+ row.conversionRatio = (parseNum(row.monthTimeTotal) / standardHours).toFixed(4);
+ } else {
+ row.conversionRatio = 0;
+ }
+ requestEmployeeRecalculate(row);
+ });
};
// 鍩虹璧勬枡琛ㄥ崟
@@ -606,6 +777,26 @@
const totalSalary = computed(() => {
return employeeList.value.reduce((sum, e) => sum + parseNum(e.netSalary), 0);
});
+
+ const monthWorkDays = ref(0);
+
+ const calculateMonthWorkDays = salaryMonth => {
+ if (!salaryMonth) return 0;
+ const [yearStr, monthStr] = String(salaryMonth).split("-");
+ const year = Number(yearStr);
+ const month = Number(monthStr);
+ if (!year || !month) return 0;
+ const start = new Date(year, month - 1, 1);
+ const end = new Date(year, month, 0);
+ let workDays = 0;
+ for (let day = new Date(start); day <= end; day.setDate(day.getDate() + 1)) {
+ const weekday = day.getDay();
+ if (weekday !== 0 && weekday !== 6) {
+ workDays += 1;
+ }
+ }
+ return workDays;
+ };
// 鏍规嵁瀹℃牳浜篒D鑾峰彇瀹℃牳浜哄悕绉�
const auditUserName = computed(() => {
@@ -712,6 +903,7 @@
loadUserList();
loadAllDeptOptions();
employeeList.value = [];
+ monthWorkDays.value = 0;
Object.assign(form.value, {
id: undefined,
salaryTitle: "",
@@ -736,10 +928,19 @@
form.value.remark = row.remark ?? "";
form.value.payBank = row.payBank ?? "";
form.value.auditUserId = row.auditUserId ?? undefined;
- // 濡傛灉鏈夊憳宸ユ槑缁嗘暟鎹紝鐩存帴鍙嶆樉
- if (row.staffSalaryDetailList && row.staffSalaryDetailList.length > 0) {
- employeeList.value = row.staffSalaryDetailList.map(e => normalizeEmployeeRow(e));
+ monthWorkDays.value = parseNum(row.monthWorkDays) || calculateMonthWorkDays(form.value.salaryMonth);
+ // 濡傛灉鏈夊憳宸ユ槑缁嗘暟鎹紝鐩存帴鍙嶆樉
+ if (row.staffSalaryDetailList && row.staffSalaryDetailList.length > 0) {
+ employeeList.value = row.staffSalaryDetailList.map(e => {
+ const normalized = normalizeEmployeeRow(e);
+ recalculateRowByWorkDays(normalized);
+ return normalized;
+ });
}
+ recalcRowsByCurrentWorkDays();
+ } else if (form.value.salaryMonth) {
+ monthWorkDays.value = calculateMonthWorkDays(form.value.salaryMonth);
+ recalcRowsByCurrentWorkDays();
}
// 缂栬緫鐘舵�佷笖鏈夋湀浠芥椂锛屽姞杞藉彲鐢ㄩ儴闂ㄥ垪琛�
if (type === "edit" && form.value.salaryMonth) {
@@ -753,6 +954,10 @@
// 鍒囨崲鏈堜唤鏃舵竻绌哄凡閫夋嫨鐨勯儴闂�
if (newMonth !== oldMonth) {
form.value.deptIds = [];
+ }
+ if (!monthWorkDays.value || newMonth !== oldMonth) {
+ monthWorkDays.value = calculateMonthWorkDays(newMonth);
+ recalcRowsByCurrentWorkDays();
}
loadAvailableDepts(newMonth);
});
@@ -786,6 +991,7 @@
staffName: node.label,
postName: node.postName ?? node.post ?? "",
deptName: node.deptName ?? "",
+ entryDate: node.contractStartTime ?? node.entryDate ?? "",
remark: "",
socialSupplementAmount: null,
attendanceBonus: node.attendanceBonus,
@@ -873,16 +1079,30 @@
};
const saveData = status => {
- const payload = {
- id: form.value.id,
- salaryTitle: form.value.salaryTitle,
- deptIds: form.value.deptIds?.length ? form.value.deptIds.join(",") : "",
- salaryMonth: form.value.salaryMonth,
- remark: form.value.remark,
- payBank: form.value.payBank,
- auditUserId: form.value.auditUserId,
- auditUserName: auditUserName.value,
- totalSalary: totalSalary.value,
+ const invalidRow = employeeList.value.find(row => {
+ const maxWorkDays = parseNum(monthWorkDays.value);
+ const leaveTotal = parseNum(row.personalLeave)
+ + parseNum(row.sickLeave)
+ + parseNum(row.bereavementLeave)
+ + parseNum(row.marriageLeave)
+ + parseNum(row.workDays);
+ return maxWorkDays > 0 && leaveTotal > maxWorkDays;
+ });
+ if (invalidRow) {
+ proxy.$modal.msgWarning("璇峰亣鍚堣鍔犲嚭鍕ゅぉ鏁颁笉鑳借秴杩囧伐浣滄棩");
+ return;
+ }
+ const payload = {
+ id: form.value.id,
+ salaryTitle: form.value.salaryTitle,
+ deptIds: form.value.deptIds?.length ? form.value.deptIds.join(",") : "",
+ salaryMonth: form.value.salaryMonth,
+ monthWorkDays: parseNum(monthWorkDays.value),
+ remark: form.value.remark,
+ payBank: form.value.payBank,
+ auditUserId: form.value.auditUserId,
+ auditUserName: auditUserName.value,
+ totalSalary: totalSalary.value,
staffSalaryDetailList: employeeList.value.map(e => ({
staffOnJobId: e.staffOnJobId ?? e.staffId ?? e.id,
staffName: e.staffName,
@@ -890,14 +1110,17 @@
deptName: e.deptName ?? "",
basicSalary: parseNum(e.basicSalary),
attendanceBonus: parseNum(e.attendanceBonus),
- pieceSalary: parseNum(e.pieceSalary),
+ dailySalary: parseNum(e.dailySalary),
+ workDays: parseNum(e.workDays),
+ personalLeave: parseNum(e.personalLeave),
+ sickLeave: parseNum(e.sickLeave),
+ bereavementLeave: parseNum(e.bereavementLeave),
+ marriageLeave: parseNum(e.marriageLeave),
+ allowance: parseNum(e.allowance),
hourlySalary: parseNum(e.hourlySalary),
actualOutput: parseNum(e.actualOutput),
standardQuantity: parseNum(e.standardQuantity),
- conversionFactor: parseNum(e.conversionFactor) || 1,
- convertedWorkHours: parseNum(e.convertedWorkHours),
monthTimeTotal: parseNum(e.monthTimeTotal),
- standardHours: parseNum(e.standardHours),
conversionRatio: parseNum(e.conversionRatio),
otherIncome: parseNum(e.otherIncome),
socialPersonal: parseNum(e.socialPersonal),
--
Gitblit v1.9.3