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/productionManagement/productionProcess/index.vue              |  107 +++++++---
 src/views/basicData/product/index.vue                                   |   33 +++
 src/views/productionManagement/productionCosting/index.vue              |   10 
 src/views/personnelManagement/monthlyStatistics/components/formDia.vue  |  343 ++++++++++++++++++++++++++++------
 src/views/productionManagement/productionProcess/Edit.vue               |   14 
 src/views/personnelManagement/monthlyStatistics/components/auditDia.vue |   74 ++++++
 6 files changed, 467 insertions(+), 114 deletions(-)

diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index b05b215..65f891d 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -112,6 +112,19 @@
             </el-form-item>
           </el-col>
         </el-row>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="鏍囧噯鏁伴噺锛�"
+                          prop="standardQuantity">
+              <el-input v-model="form.standardQuantity"
+                        type="number"
+                        :step="0.001"
+                        placeholder="璇疯緭鍏ヤ骇鍝佹爣鍑嗘暟閲�"
+                        clearable
+                        @keydown.enter.prevent />
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
@@ -331,11 +344,29 @@
   const data = reactive({
     form: {
       productName: "",
+      standardQuantity: null,
     },
     rules: {
       productName: [
         { required: true, message: "璇疯緭鍏�", trigger: "blur" },
         { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
+      ],
+      standardQuantity: [
+        {
+          required: false,
+          trigger: "blur",
+          validator: (rule, value, callback) => {
+            if (value == null || value === "") {
+              callback();
+              return;
+            }
+            if (isNaN(value) || value < 0) {
+              callback(new Error("鏍囧噯鏁伴噺蹇呴』鏄潪璐熸暟瀛�"));
+            } else {
+              callback();
+            }
+          },
+        },
       ],
     },
     modelForm: {
@@ -412,6 +443,8 @@
     productDia.value = true;
     form.value.productName =
       type === "edit" && data ? data.productName : "";
+    form.value.standardQuantity =
+      type === "edit" && data ? data.standardQuantity : null;
   };
   // 鎵撳紑瑙勬牸鍨嬪彿寮规
   const openModelDia = (type, data) => {
diff --git a/src/views/personnelManagement/monthlyStatistics/components/auditDia.vue b/src/views/personnelManagement/monthlyStatistics/components/auditDia.vue
index e615402..f8f5e18 100644
--- a/src/views/personnelManagement/monthlyStatistics/components/auditDia.vue
+++ b/src/views/personnelManagement/monthlyStatistics/components/auditDia.vue
@@ -13,6 +13,7 @@
       <el-descriptions :column="3" border>
         <el-descriptions-item label="宸ヨ祫涓婚">{{ auditData?.salaryTitle || '-' }}</el-descriptions-item>
         <el-descriptions-item label="宸ヨ祫鏈堜唤">{{ auditData?.salaryMonth || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="宸ヤ綔鏃ュぉ鏁�">{{ auditData?.monthWorkDays ?? '-' }}</el-descriptions-item>
         <el-descriptions-item label="宸ヨ祫鎬婚">楼 {{ formatMoney(auditData?.totalSalary) }}</el-descriptions-item>
         <el-descriptions-item label="鏀粯閾惰">{{ auditData?.payBank || '-' }}</el-descriptions-item>
         <el-descriptions-item label="瀹℃牳浜�">{{ auditData?.auditUserName || '-' }}</el-descriptions-item>
@@ -32,18 +33,43 @@
         <div v-else>
           <el-table :data="employeeList" border max-height="300" style="width: 100%">
             <el-table-column prop="staffName" label="鍛樺伐濮撳悕" width="100" />
+            <el-table-column prop="entryDate" label="鍏ュ徃鏃ユ湡" width="110" />
             <el-table-column prop="deptName" label="閮ㄩ棬" width="120" />
             <el-table-column prop="basicSalary" label="鍩烘湰宸ヨ祫" width="100" align="right">
               <template #default="{ row }">楼 {{ formatMoney(row.basicSalary) }}</template>
             </el-table-column>
-            <el-table-column prop="pieceSalary" label="璁′欢宸ヨ祫" width="100" align="right">
-              <template #default="{ row }">楼 {{ formatMoney(row.pieceSalary) }}</template>
+            <el-table-column prop="attendanceBonus" label="鍏ㄥ嫟濂�" width="100" align="right">
+              <template #default="{ row }">楼 {{ formatMoney(row.attendanceBonus) }}</template>
             </el-table-column>
-            <el-table-column prop="hourlySalary" label="璁℃椂宸ヨ祫" width="100" align="right">
-              <template #default="{ row }">楼 {{ formatMoney(row.hourlySalary) }}</template>
+            <el-table-column prop="dailySalary" label="鏃ヨ宸ヨ祫" width="100" align="right">
+              <template #default="{ row }">楼 {{ formatMoney(row.dailySalary) }}</template>
+            </el-table-column>
+            <el-table-column prop="workDays" label="鍑哄嫟澶╂暟" width="90" align="right">
+              <template #default="{ row }">{{ formatNumber(row.workDays, 0) }}</template>
+            </el-table-column>
+            <el-table-column prop="conversionRatio" label="鏁堢泭" width="90" align="right">
+              <template #default="{ row }">{{ formatRate(row.conversionRatio) }}</template>
+            </el-table-column>
+            <el-table-column prop="monthTimeTotal" label="鏈堟椂闂村悎璁�" width="110" align="right">
+              <template #default="{ row }">{{ formatNumber(row.monthTimeTotal, 2) }}</template>
             </el-table-column>
             <el-table-column prop="otherIncome" label="鍏朵粬鏀跺叆" width="100" align="right">
               <template #default="{ row }">楼 {{ formatMoney(row.otherIncome) }}</template>
+            </el-table-column>
+            <el-table-column prop="personalLeave" label="浜嬪亣" width="80" align="right">
+              <template #default="{ row }">{{ formatNumber(row.personalLeave, 0) }}</template>
+            </el-table-column>
+            <el-table-column prop="sickLeave" label="鐥呭亣" width="80" align="right">
+              <template #default="{ row }">{{ formatNumber(row.sickLeave, 0) }}</template>
+            </el-table-column>
+            <el-table-column prop="bereavementLeave" label="涓у亣" width="80" align="right">
+              <template #default="{ row }">{{ formatNumber(row.bereavementLeave, 0) }}</template>
+            </el-table-column>
+            <el-table-column prop="marriageLeave" label="濠氬亣" width="80" align="right">
+              <template #default="{ row }">{{ formatNumber(row.marriageLeave, 0) }}</template>
+            </el-table-column>
+            <el-table-column prop="allowance" label="娲ヨ创" width="100" align="right">
+              <template #default="{ row }">楼 {{ formatMoney(row.allowance) }}</template>
             </el-table-column>
             <el-table-column prop="socialPersonal" label="绀句繚涓汉" width="100" align="right">
               <template #default="{ row }">楼 {{ formatMoney(row.socialPersonal) }}</template>
@@ -51,8 +77,20 @@
             <el-table-column prop="fundPersonal" label="鍏Н閲戜釜浜�" width="120" align="right">
               <template #default="{ row }">楼 {{ formatMoney(row.fundPersonal) }}</template>
             </el-table-column>
+            <el-table-column prop="otherDeduct" label="鍏朵粬鏀嚭" width="100" align="right">
+              <template #default="{ row }">楼 {{ formatMoney(row.otherDeduct) }}</template>
+            </el-table-column>
+            <el-table-column prop="socialSupplementAmount" label="绀句繚琛ョ即" width="110" align="right">
+              <template #default="{ row }">楼 {{ formatMoney(row.socialSupplementAmount) }}</template>
+            </el-table-column>
             <el-table-column prop="salaryTax" label="宸ヨ祫涓◣" width="100" align="right">
               <template #default="{ row }">楼 {{ formatMoney(row.salaryTax) }}</template>
+            </el-table-column>
+            <el-table-column prop="grossSalary" label="搴斿彂宸ヨ祫" width="100" align="right">
+              <template #default="{ row }">楼 {{ formatMoney(row.grossSalary) }}</template>
+            </el-table-column>
+            <el-table-column prop="deductSalary" label="搴旀墸宸ヨ祫" width="100" align="right">
+              <template #default="{ row }">楼 {{ formatMoney(row.deductSalary) }}</template>
             </el-table-column>
             <el-table-column prop="netSalary" label="瀹炲彂宸ヨ祫" width="100" align="right" fixed="right">
               <template #default="{ row }">楼 {{ formatMoney(row.netSalary) }}</template>
@@ -123,6 +161,16 @@
   return num.toFixed(2);
 };
 
+const formatNumber = (value, digits = 2) => {
+  const num = Number(value) || 0;
+  return num.toFixed(digits);
+};
+
+const formatRate = (value) => {
+  const num = Number(value) || 0;
+  return num.toFixed(4);
+};
+
 // 璁$畻宸ヨ祫鎬婚
 const totalSalary = computed(() => {
   return employeeList.value.reduce((sum, e) => {
@@ -140,15 +188,29 @@
   if (row?.staffSalaryDetailList && Array.isArray(row.staffSalaryDetailList)) {
     employeeList.value = row.staffSalaryDetailList.map((e) => ({
       staffName: e.staffName ?? "",
+      entryDate: e.entryDate ?? "",
       deptName: e.deptName ?? "",
       basicSalary: Number(e.basicSalary) || 0,
+      attendanceBonus: Number(e.attendanceBonus) || 0,
+      dailySalary: Number(e.dailySalary) || (Number(e.workDays) > 0 ? (Number(e.basicSalary) / Number(e.workDays)) : 0),
+      workDays: Number(e.workDays) || 0,
       pieceSalary: Number(e.pieceSalary) || 0,
-      hourlySalary: Number(e.hourlySalary) || 0,
       otherIncome: Number(e.otherIncome) || 0,
+      personalLeave: Number(e.personalLeave) || 0,
+      sickLeave: Number(e.sickLeave) || 0,
+      bereavementLeave: Number(e.bereavementLeave) || 0,
+      marriageLeave: Number(e.marriageLeave) || 0,
+      allowance: Number(e.allowance) || 0,
       socialPersonal: Number(e.socialPersonal) || 0,
       fundPersonal: Number(e.fundPersonal) || 0,
+      otherDeduct: Number(e.otherDeduct) || 0,
+      socialSupplementAmount: Number(e.socialSupplementAmount) || 0,
       salaryTax: Number(e.salaryTax) || 0,
+      grossSalary: Number(e.grossSalary) || 0,
+      deductSalary: Number(e.deductSalary) || 0,
       netSalary: Number(e.netSalary) || 0,
+      monthTimeTotal: Number(e.monthTimeTotal) || 0,
+      conversionRatio: Number(e.conversionRatio) || 0,
     }));
   } else {
     console.log('娌℃湁鎵惧埌鍛樺伐鏄庣粏鏁版嵁');
@@ -220,4 +282,4 @@
 :deep(.el-descriptions__label) {
   width: 100px;
 }
-</style>
\ No newline at end of file
+</style>
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),
diff --git a/src/views/productionManagement/productionCosting/index.vue b/src/views/productionManagement/productionCosting/index.vue
index 53dfcf7..3653368 100644
--- a/src/views/productionManagement/productionCosting/index.vue
+++ b/src/views/productionManagement/productionCosting/index.vue
@@ -216,6 +216,11 @@
       minWidth: 100,
     },
     {
+      label: "鏍囧噯鏁伴噺",
+      prop: "standardQuantity",
+      minWidth: 100,
+    },
+    {
       label: "宸ュ崟缂栧彿",
       prop: "workOrderNo",
       minWidth: 140,
@@ -233,11 +238,6 @@
     {
       label: "鐢熶骇鏁伴噺",
       prop: "quantity",
-      minWidth: 100,
-    },
-    {
-      label: "宸ユ椂瀹氶",
-      prop: "workHours",
       minWidth: 100,
     },
     {
diff --git a/src/views/productionManagement/productionProcess/Edit.vue b/src/views/productionManagement/productionProcess/Edit.vue
index 28077b6..55be24e 100644
--- a/src/views/productionManagement/productionProcess/Edit.vue
+++ b/src/views/productionManagement/productionProcess/Edit.vue
@@ -31,14 +31,16 @@
             :rules="[
                 {
                 required: true,
-                message: '璇烽�夋嫨宸ュ簭绫诲瀷',
+                message: '宸ュ簭浠呮敮鎸佽浠�',
               }
             ]"
         >
-          <el-select v-model="formState.type" placeholder="璇烽�夋嫨宸ュ簭绫诲瀷">
-            <el-option label="璁℃椂" :value="0" />
+          <el-select v-model="formState.type" placeholder="宸ュ簭浠呮敮鎸佽浠�" disabled>
             <el-option label="璁′欢" :value="1" />
           </el-select>
+          <div style="margin-top: 6px; color: #909399; font-size: 12px;">
+            宸ュ簭浠呮敮鎸佽浠�
+          </div>
         </el-form-item>
         <el-form-item label="宸ヨ祫瀹氶" prop="salaryQuota">
           <el-input v-model="formState.salaryQuota" type="number" :step="0.001" />
@@ -88,7 +90,7 @@
 const formState = ref({
   id: props.record.id,
   name: props.record.name,
-  type: props.record.type,
+  type: 1,
   no: props.record.no,
   remark: props.record.remark,
   salaryQuota: props.record.salaryQuota,
@@ -113,7 +115,7 @@
       id: newRecord.id,
       name: newRecord.name || '',
       no: newRecord.no || '',
-      type: newRecord.type,
+      type: 1,
       remark: newRecord.remark || '',
       salaryQuota: newRecord.salaryQuota || '',
       isQuality: props.record.isQuality,
@@ -130,7 +132,7 @@
       id: props.record.id,
       name: props.record.name || '',
       no: props.record.no || '',
-      type: props.record.type,
+      type: 1,
       remark: props.record.remark || '',
       salaryQuota: props.record.salaryQuota || '',
       isQuality: props.record.isQuality,
diff --git a/src/views/productionManagement/productionProcess/index.vue b/src/views/productionManagement/productionProcess/index.vue
index d4c7f9e..9b1ae12 100644
--- a/src/views/productionManagement/productionProcess/index.vue
+++ b/src/views/productionManagement/productionProcess/index.vue
@@ -46,7 +46,7 @@
               <div class="process-desc">{{ process.remark || '鏆傛棤鎻忚堪' }}</div>
               <div class="process-device">鍏宠仈璁惧: {{ (deviceOptions.find(item => item.id === Number(process.deviceLedgerId))?.deviceName) || '鏈叧鑱�' }}</div>
               <div class="process-device">鏍囧噯鏁伴噺: {{ process.standardQuantity || 0 }}</div>
-              <div class="process-device">鎶樼畻绯绘暟: {{ process.conversionFactor ?? 1 }}</div>
+              <div class="process-device"></div>
             </div>
             <div class="card-footer">
               <div class="status-tag">
@@ -66,7 +66,7 @@
                   璁′欢
                 </el-tag>
               </div>
-              <span class="param-count">宸ヨ祫瀹氶: 楼{{ process.salaryQuota || 0 }}</span>
+              <span class="param-count"></span>
             </div>
           </div>
         </div>
@@ -120,12 +120,6 @@
           <el-input v-model="processForm.name"
                     placeholder="璇疯緭鍏ュ伐搴忓悕绉�" />
         </el-form-item>
-        <el-form-item label="宸ヨ祫瀹氶"
-                      prop="salaryQuota">
-          <el-input v-model="processForm.salaryQuota"
-                    type="number"
-                    :step="0.001" />
-        </el-form-item>
         <el-form-item label="鏍囧噯鏁伴噺"
                       prop="standardQuantity">
           <el-input v-model="processForm.standardQuantity"
@@ -133,12 +127,26 @@
                     :step="0.001"
                     placeholder="璇疯緭鍏ュ伐搴忔爣鍑嗘暟閲�" />
         </el-form-item>
-        <el-form-item label="鎶樼畻绯绘暟"
-                      prop="conversionFactor">
-          <el-input v-model="processForm.conversionFactor"
+        <el-form-item label="灏忔彁鐞寸郴鏁�"
+                      prop="violinFactor">
+          <el-input v-model="processForm.violinFactor"
                     type="number"
-                    :step="0.01"
-                    placeholder="榛樿 1" />
+                    :step="0.001"
+                    placeholder="璇疯緭鍏ュ皬鎻愮惔绯绘暟" />
+        </el-form-item>
+        <el-form-item label="澶ф彁鐞寸郴鏁�"
+                      prop="violaFactor">
+          <el-input v-model="processForm.violaFactor"
+                    type="number"
+                    :step="0.001"
+                    placeholder="璇疯緭鍏ュぇ鎻愮惔绯绘暟" />
+        </el-form-item>
+        <el-form-item label="璐濇柉绯绘暟"
+                      prop="bassFactor">
+          <el-input v-model="processForm.bassFactor"
+                    type="number"
+                    :step="0.001"
+                    placeholder="璇疯緭鍏ヨ礉鏂郴鏁�" />
         </el-form-item>
         <el-form-item label="鏄惁璐ㄦ"
                       prop="isQuality">
@@ -359,9 +367,10 @@
     id: null,
     no: "",
     name: "",
-    salaryQuota: null,
     standardQuantity: null,
-    conversionFactor: 1,
+    violinFactor: null,
+    violaFactor: null,
+    bassFactor: null,
     isQuality: false,
     isProduction: false,
     remark: "",
@@ -371,20 +380,6 @@
   const processRules = {
     no: [{ required: true, message: "璇疯緭鍏ュ伐搴忕紪鐮�", trigger: "blur" }],
     name: [{ required: true, message: "璇疯緭鍏ュ伐搴忓悕绉�", trigger: "blur" }],
-    salaryQuota: [
-      {
-        required: false,
-        message: "璇疯緭鍏ュ伐璧勫畾棰�",
-        trigger: "blur",
-        validator: (rule, value, callback) => {
-          if (isNaN(value) || value < 0) {
-            callback(new Error("宸ヨ祫瀹氶蹇呴』鏄潪璐熸暟瀛�"));
-          } else {
-            callback();
-          }
-        },
-      },
-    ],
     standardQuantity: [
       {
         required: false,
@@ -403,18 +398,54 @@
         },
       },
     ],
-    conversionFactor: [
+    violinFactor: [
       {
         required: false,
-        message: "璇疯緭鍏ユ姌绠楃郴鏁�",
+        message: "璇疯緭鍏ュ皬鎻愮惔绯绘暟",
         trigger: "blur",
         validator: (rule, value, callback) => {
           if (value == null || value === "") {
             callback();
             return;
           }
-          if (isNaN(value) || value <= 0) {
-            callback(new Error("鎶樼畻绯绘暟蹇呴』鏄ぇ浜� 0 鐨勬暟瀛�"));
+          if (isNaN(value) || value < 0) {
+            callback(new Error("灏忔彁鐞寸郴鏁板繀椤绘槸闈炶礋鏁板瓧"));
+          } else {
+            callback();
+          }
+        },
+      },
+    ],
+    violaFactor: [
+      {
+        required: false,
+        message: "璇疯緭鍏ュぇ鎻愮惔绯绘暟",
+        trigger: "blur",
+        validator: (rule, value, callback) => {
+          if (value == null || value === "") {
+            callback();
+            return;
+          }
+          if (isNaN(value) || value < 0) {
+            callback(new Error("澶ф彁鐞寸郴鏁板繀椤绘槸闈炶礋鏁板瓧"));
+          } else {
+            callback();
+          }
+        },
+      },
+    ],
+    bassFactor: [
+      {
+        required: false,
+        message: "璇疯緭鍏ヨ礉鏂郴鏁�",
+        trigger: "blur",
+        validator: (rule, value, callback) => {
+          if (value == null || value === "") {
+            callback();
+            return;
+          }
+          if (isNaN(value) || value < 0) {
+            callback(new Error("璐濇柉绯绘暟蹇呴』鏄潪璐熸暟瀛�"));
           } else {
             callback();
           }
@@ -621,9 +652,10 @@
     processForm.id = null;
     processForm.no = "";
     processForm.name = "";
-    processForm.salaryQuota = null;
     processForm.standardQuantity = null;
-    processForm.conversionFactor = 1;
+    processForm.violinFactor = null;
+    processForm.violaFactor = null;
+    processForm.bassFactor = null;
     processForm.isQuality = false;
     processForm.isProduction = false;
     processForm.remark = "";
@@ -637,9 +669,10 @@
     processForm.id = process.id;
     processForm.no = process.no;
     processForm.name = process.name;
-    processForm.salaryQuota = process.salaryQuota;
     processForm.standardQuantity = process.standardQuantity;
-    processForm.conversionFactor = process.conversionFactor ?? 1;
+    processForm.violinFactor = process.violinFactor;
+    processForm.violaFactor = process.violaFactor;
+    processForm.bassFactor = process.bassFactor;
     processForm.isQuality = !!process.isQuality;
     processForm.isProduction = !!process.isProduction;
     processForm.remark = process.remark || "";

--
Gitblit v1.9.3