From 5cd5a5baa076377f3136338db7a9336d0d570d33 Mon Sep 17 00:00:00 2001
From: tuqin <tuqin@tuqin.cn>
Date: 星期六, 24 十月 2026 19:47:33 +0800
Subject: [PATCH] feat(hrm): 前端同步新疆中亿装配的人力资源模块

---
 src/views/hrm/salary/calculation/index.vue |   99 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 96 insertions(+), 3 deletions(-)

diff --git a/src/views/hrm/salary/calculation/index.vue b/src/views/hrm/salary/calculation/index.vue
index 33a1f36..1636793 100644
--- a/src/views/hrm/salary/calculation/index.vue
+++ b/src/views/hrm/salary/calculation/index.vue
@@ -7,7 +7,7 @@
 import { Page } from '#/packages/effects/common-ui/src';
 import { downloadFileFromBlobPart } from '#/packages/utils/src';
 
-import { message, Modal, Form, FormItem, DatePicker, TreeSelect, Table, InputNumber, Alert, Tag } from 'ant-design-vue';
+import { message, Modal, Form, FormItem, DatePicker, TreeSelect, Table, Input, InputNumber, Alert, Tag } from 'ant-design-vue';
 
 import { DICT_TYPE } from '#/packages/constants/src';
 import { getDictLabel } from '#/packages/effects/hooks/src';
@@ -22,14 +22,18 @@
   previewSalaryCalculation,
   revokeSalaryCalculation,
   saveSalaryCalculation,
+  updateSalaryCalculation,
 } from '#/api/hrm/salary/calculation';
 import { getDeptList } from '#/api/system/dept';
 import { handleTree } from '#/packages/utils/src';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema, usePreviewColumns } from './data';
+import FormModal from './modules/form.vue';
 
 // 鏍哥畻寮圭獥鐩稿叧
+const formModalRef = ref();
+const saving = ref(false);
 const calculateVisible = ref(false);
 const calculatePeriod = ref<string>();
 const calculateDeptId = ref<number>();
@@ -148,19 +152,35 @@
   previewData.value = [];
 }
 
-/** 纭钖叕鏍哥畻 */
+/** 鎶婅鍐呯紪杈戠殑鏂囨湰鍊煎綊涓�涓烘暟瀛楀悗鎻愪氦淇濆瓨 */
+async function saveRowEdits(row: any) {
+  const payload: any = { ...row };
+  EDITABLE_NUMBER_FIELDS.forEach((f) => {
+    payload[f] = toNumberOrZero(payload[f]);
+  });
+  await updateSalaryCalculation(payload);
+}
+
+/** 纭钖叕鏍哥畻锛堟柟妗圓锛氱‘璁ゅ墠鍏堜繚瀛樺綋鍓嶈鐨勪慨鏀癸紝閬垮厤銆屾敼浜嗘病淇濆瓨灏辩‘璁ゃ�嶅鑷撮噾棰濅涪澶憋級 */
 async function handleConfirm(row: HrmSalaryCalculationApi.SalaryCalculation) {
+  if (row.status === 0) {
+    await saveRowEdits(row);
+  }
   await confirmSalary([row.id!]);
   message.success('纭鎴愬姛');
   handleRefresh();
 }
 
-/** 鎵归噺纭閫変腑鐨勮褰� */
+/** 鎵归噺纭閫変腑鐨勮褰曪紙鍚屾牱鍏堜繚瀛樺嬀閫夌殑寰呯‘璁よ褰曪紝鍐嶇‘璁わ級 */
 async function handleBatchConfirm() {
   const records = gridApi.grid.getCheckboxRecords() as HrmSalaryCalculationApi.SalaryCalculation[];
   if (records.length === 0) {
     message.warning('璇烽�夋嫨瑕佺‘璁ょ殑璁板綍');
     return;
+  }
+  const pending = records.filter((r) => r.status === 0);
+  for (const row of pending) {
+    await saveRowEdits(row);
   }
   const ids = records.map((r) => r.id!);
   await confirmSalary(ids);
@@ -224,6 +244,55 @@
 async function handleExport() {
   const data = await exportSalaryCalculation(await gridApi.formApi.getValues());
   downloadFileFromBlobPart({ fileName: '钖叕鏍哥畻琛�.xls', source: data });
+}
+
+/** 鍒楄〃鍙紪杈戠殑鏁板�煎瓧娈� */
+const EDITABLE_NUMBER_FIELDS = [
+  'baseSalary',
+  'performanceSalary',
+  'overtimePay',
+  'mealAllowance',
+  'transportAllowance',
+  'otherAllowance',
+  'socialSecurityDeduction',
+  'housingFundDeduction',
+  'taxDeduction',
+  'otherDeduction',
+  'leaveDeduction',
+  'workDays',
+  'overtimeHours',
+];
+
+/** 鏂囨湰杈撳叆杞暟瀛楋紙绌哄�兼寜 0锛� */
+function toNumberOrZero(val: any) {
+  if (val === '' || val === null || val === undefined) {
+    return 0;
+  }
+  const num = Number(String(val).replace(/,/g, ''));
+  return Number.isNaN(num) ? 0 : num;
+}
+
+/** 淇濆瓨鍒楄〃鍐呰仈淇敼锛堜粎寰呯‘璁ょ姸鎬侊級 */
+async function handleSaveEdits() {
+  const rows = (gridApi.grid.getData() as any[]).filter((r) => r.status === 0);
+  if (rows.length === 0) {
+    message.warning('褰撳墠椤垫病鏈夊緟纭鐨勮褰曞彲淇敼');
+    return;
+  }
+  saving.value = true;
+  try {
+    for (const row of rows) {
+      const payload: any = { ...row };
+      EDITABLE_NUMBER_FIELDS.forEach((f) => {
+        payload[f] = toNumberOrZero(payload[f]);
+      });
+      await updateSalaryCalculation(payload);
+    }
+    message.success(`宸蹭繚瀛� ${rows.length} 鏉℃牳绠楄褰昤);
+    handleRefresh();
+  } finally {
+    saving.value = false;
+  }
 }
 
 const [Grid, gridApi] = useVbenVxeGrid({
@@ -469,6 +538,7 @@
       </div>
     </Modal>
 
+    <FormModal ref="formModalRef" @success="handleRefresh" />
     <Grid table-title="钖叕鏍哥畻鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -493,6 +563,13 @@
               onClick: handleBatchSave,
             },
             {
+              label: '淇濆瓨淇敼',
+              type: 'primary',
+              auth: ['hrm:salary-calculation:calculate'],
+              loading: saving,
+              onClick: handleSaveEdits,
+            },
+            {
               label: $t('ui.actionTitle.export'),
               type: 'primary',
               icon: ACTION_ICON.DOWNLOAD,
@@ -502,6 +579,15 @@
           ]"
         />
       </template>
+      <template #editableCell="{ row, column }">
+        <Input
+          v-if="row.status === 0"
+          v-model:value="row[column.field]"
+          size="small"
+          :style="{ width: '100%' }"
+        />
+        <span v-else>{{ row[column.field] ?? '' }}</span>
+      </template>
       <template #status="{ row }">
         <Tag :color="row.status === 0 ? 'warning' : row.status === 10 ? 'success' : 'processing'">
           {{ getDictLabel(DICT_TYPE.HRM_SALARY_STATUS, row.status) }}
@@ -511,6 +597,13 @@
         <TableAction
           :actions="[
             {
+              label: '淇敼',
+              type: 'link',
+              auth: ['hrm:salary-calculation:calculate'],
+              ifShow: row.status === 0,
+              onClick: () => formModalRef.value?.open(row),
+            },
+            {
               label: '纭',
               type: 'link',
               auth: ['hrm:salary-calculation:confirm'],

--
Gitblit v1.9.3