gaoluyang
5 天以前 d2008d93877c0ff1628b5d90f034410da3834f07
九鸿:薪酬核算预览所有数值列均可手工修改

- 标准工资、岗位工资、加班工资(标准)、安全环保奖(标准)、绩效(标准)、出勤天数/时长、
各项计算结果、合计、税后工资全部改为可编辑,自动计算只作为初值
- 修改某个字段时只重算其影响的合计,不覆盖正在编辑的列
- 出勤天数按整数、时长按 1 位小数、金额按 2 位小数录入

Co-Authored-By: Claude Code <noreply@anthropic.com>
已修改2个文件
133 ■■■■■ 文件已修改
src/views/hrm/salary/calculation/data.ts 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/salary/calculation/index.vue 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/salary/calculation/data.ts
@@ -131,9 +131,28 @@
  ];
}
/** 预览表格里允许手工调整的字段(其余均为自动计算) */
/**
 * 预览表格里可手工调整的字段:所有薪酬数值字段都可在自动计算结果上修改。
 * 自动计算只负责生成初值,保存时以页面上看到的值为准。
 */
export const PREVIEW_EDITABLE_FIELDS = [
  'workDays',
  'paidAttendanceDays',
  'standardSalary',
  'baseSalary',
  'postSalary',
  'overtimeHourlyRate',
  'safetyAwardStandard',
  'performanceStandard',
  'standardWorkHours',
  'overtimeHours',
  'legalHolidayHours',
  'standardAttendancePay',
  'overtimePay',
  'legalHolidayPay',
  'safetyAward',
  'floatingPerformancePay',
  'payableSalary',
  'seniorityPay',
  'nightShiftAllowance',
  'educationAllowance',
@@ -141,11 +160,35 @@
  'excellentEmployeeAward',
  'otherSubsidy',
  'deptPerformance',
  'additionTotal',
  'missedPunchDeduction',
  'lateEarlyDeduction',
  'otherDeduction',
  'deductionTotal',
  'socialSecurityDeduction',
  'housingFundDeduction',
  'taxDeduction',
  'actualSalary',
] as const;
/** 应付工资的构成字段(任一变化都重算应付工资) */
export const PREVIEW_PAYABLE_COMPONENTS = [
  'standardAttendancePay',
  'overtimePay',
  'legalHolidayPay',
  'safetyAward',
  'floatingPerformancePay',
] as const;
/** 各字段的录入精度(未列出的按金额 2 位小数) */
export const PREVIEW_FIELD_PRECISION: Record<string, number> = {
  workDays: 0,
  paidAttendanceDays: 1,
  standardWorkHours: 1,
  overtimeHours: 1,
  legalHolidayHours: 1,
};
/** 加项字段(汇总为「加项合计」) */
export const PREVIEW_ADDITION_FIELDS = [
  'seniorityPay',
src/views/hrm/salary/calculation/index.vue
@@ -30,6 +30,8 @@
  PREVIEW_ADDITION_FIELDS,
  PREVIEW_DEDUCTION_FIELDS,
  PREVIEW_EDITABLE_FIELDS,
  PREVIEW_FIELD_PRECISION,
  PREVIEW_PAYABLE_COMPONENTS,
  useGridColumns,
  useGridFormSchema,
  usePreviewColumns,
@@ -92,18 +94,10 @@
  }
}
/** 预览表中按原样展示的字段(非金额) */
const PLAIN_FIELDS = [
  'userName',
  'hireDate',
  'postName',
  'deptName',
  'workDays',
  'paidAttendanceDays',
  'standardWorkHours',
  'overtimeHours',
  'legalHolidayHours',
];
/** 各字段的录入精度 */
function fieldPrecision(field: string) {
  return PREVIEW_FIELD_PRECISION[field] ?? 2;
}
/** 是否为可手工调整的列 */
function isEditableField(field: string) {
@@ -118,14 +112,6 @@
  return (record as unknown as Record<string, number | string | undefined>)[
    field
  ];
}
/** 金额展示:保留两位小数 */
function formatAmount(value?: number | string) {
  if (value === null || value === undefined || value === '' || Number.isNaN(Number(value))) {
    return '-';
  }
  return Number(value).toFixed(2);
}
/** 求和(空值按 0 计) */
@@ -158,23 +144,51 @@
    Number(record.floatingPerformancePay ?? 0);
  record.additionTotal = sumFields(record, PREVIEW_ADDITION_FIELDS);
  record.deductionTotal = sumFields(record, PREVIEW_DEDUCTION_FIELDS);
  record.actualSalary =
    record.payableSalary +
    record.additionTotal -
    record.deductionTotal -
    Number(record.socialSecurityDeduction ?? 0) -
    Number(record.housingFundDeduction ?? 0) -
    Number(record.taxDeduction ?? 0);
  record.actualSalary = calculateActualSalary(record);
}
/** 更新行数据并重算合计 */
/** 按当前行数据重算税后工资 */
function calculateActualSalary(
  record: HrmSalaryCalculationApi.SalaryCalculation,
) {
  return (
    Number(record.payableSalary ?? 0) +
    Number(record.additionTotal ?? 0) -
    Number(record.deductionTotal ?? 0) -
    Number(record.socialSecurityDeduction ?? 0) -
    Number(record.housingFundDeduction ?? 0) -
    Number(record.taxDeduction ?? 0)
  );
}
/**
 * 手工修改某个字段后,只重算它影响的合计,不覆盖刚改的那一列:
 * - 改应付工资构成项 → 重算应付工资
 * - 改加项 → 重算加项合计
 * - 改扣款 → 重算扣款合计
 * - 改其余任何金额 → 重算税后工资(改税后工资本身时不再回写)
 */
function updateFieldValue(
  record: HrmSalaryCalculationApi.SalaryCalculation,
  field: string,
  value: number,
) {
  (record as Record<string, unknown>)[field] = value;
  recalculateRecord(record);
  const payableComponents = PREVIEW_PAYABLE_COMPONENTS as readonly string[];
  const additionFields = PREVIEW_ADDITION_FIELDS as readonly string[];
  const deductionFields = PREVIEW_DEDUCTION_FIELDS as readonly string[];
  if (payableComponents.includes(field)) {
    record.payableSalary = sumFields(record, PREVIEW_PAYABLE_COMPONENTS);
  }
  if (additionFields.includes(field)) {
    record.additionTotal = sumFields(record, PREVIEW_ADDITION_FIELDS);
  }
  if (deductionFields.includes(field)) {
    record.deductionTotal = sumFields(record, PREVIEW_DEDUCTION_FIELDS);
  }
  if (field !== 'actualSalary') {
    record.actualSalary = calculateActualSalary(record);
  }
}
/** 提交薪酬核算(手动调整后保存) */
@@ -333,7 +347,7 @@
      :maskClosable="false"
    >
      <Alert
        message="核算流程:选好核算周期与部门后点「预览数据」→ 核对自动计算结果,可手工调整浮动绩效、加项与扣款 → 点「保存核算」落库。标准工资/岗位工资/加班工资(标准)/安全环保奖(标准)/绩效(标准) 在员工档案中维护。"
        message="核算流程:选好核算周期与部门后点「预览数据」→ 系统按员工档案自动算出各项金额,所有数值都可在此基础上手工修改(改某一项会自动重算它影响到的合计)→ 点「保存核算」按页面上的值落库。标准工资/岗位工资/加班工资(标准)/安全环保奖(标准)/绩效(标准) 默认取员工档案。"
        type="info"
        show-icon
        style="margin-bottom: 16px"
@@ -384,9 +398,9 @@
            <InputNumber
              :value="fieldValue(record, String(column.dataIndex)) as number"
              :min="0"
              :precision="2"
              :precision="fieldPrecision(String(column.dataIndex))"
              size="small"
              style="width: 90px"
              style="width: 96px"
              @change="
                (val: number | string | null) =>
                  updateFieldValue(
@@ -397,16 +411,8 @@
              "
            />
          </template>
          <template v-else-if="column.dataIndex === 'actualSalary'">
            <span class="font-bold text-green-600">{{
              formatAmount(record.actualSalary)
            }}</span>
          </template>
          <template v-else-if="PLAIN_FIELDS.includes(String(column.dataIndex))">
            {{ fieldValue(record, String(column.dataIndex)) ?? '-' }}
          </template>
          <template v-else>
            {{ formatAmount(fieldValue(record, String(column.dataIndex))) }}
            {{ fieldValue(record, String(column.dataIndex)) ?? '-' }}
          </template>
        </template>
      </Table>