feat(hrm): 社保公积金方案表单加「适用部门」与默认缴费基数,修比例输入精度
- 修 8.5% 被四舍五入成 9%:比例格式化按 decimal(5,4) 精度保留 2 位百分比小数
(原 toFixed(step === 0.001 ? 1 : 0) 对 step=0.01 的字段只留整数百分比)
- 表单新增「适用部门」多选树(不选=全局默认)、默认社保/公积金基数
- 列表新增「适用部门」列,未绑定显示「全局默认」标签
- 个税税率配置页同款 toFixed(0) 顺带修正(表单回显 + 列表列)
Co-Authored-By: Claude Code <noreply@anthropic.com>
| | |
| | | maternityRatio?: number; |
| | | housingFundCompanyRatio?: number; |
| | | housingFundPersonalRatio?: number; |
| | | /** 默认社保缴费基数(不填则用员工基本工资) */ |
| | | defaultSocialSecurityBase?: number; |
| | | /** 默认公积金缴费基数(不填或 0 则用社保基数) */ |
| | | defaultHousingFundBase?: number; |
| | | /** 适用部门ID列表(为空表示全局默认方案) */ |
| | | deptIds?: number[]; |
| | | /** 适用部门名称,多个用、分隔(为空表示全局默认) */ |
| | | deptNames?: string; |
| | | status?: number; |
| | | remark?: string; |
| | | createTime?: string; |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmSocialSecuritySchemeApi } from '#/api/hrm/salary/social-security-scheme'; |
| | | |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | |
| | | /** 表单类型 */ |
| | | export type FormType = 'create' | 'update'; |
| | | |
| | | /** 格式化比例输入 */ |
| | | function createRatioFormatter(step: number = 0.01) { |
| | | /** |
| | | * 格式化比例输入:显示值(8.5%)与存储值(0.085)互转 |
| | | * |
| | | * 保留 2 位百分比小数,正好对应数据库 decimal(5,4) 的 4 位小数精度, |
| | | * 避免 8.5% 被四舍五入成 9% 后写回 0.09。 |
| | | */ |
| | | function createRatioFormatter() { |
| | | return { |
| | | formatter: (value: number) => value !== undefined && value !== null ? `${(value * 100).toFixed(step === 0.001 ? 1 : 0)}%` : '', |
| | | formatter: (value: number) => |
| | | value !== undefined && value !== null |
| | | ? `${Number((value * 100).toFixed(2))}%` |
| | | : '', |
| | | parser: (value: string) => { |
| | | const num = parseFloat(value.replace('%', '')); |
| | | const num = parseFloat(String(value).replace('%', '')); |
| | | return isNaN(num) ? 0 : num / 100; |
| | | }, |
| | | }; |
| | |
| | | ], |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'deptIds', |
| | | label: '适用部门', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const data = await getDeptList(); |
| | | return handleTree(data); |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | childrenField: 'children', |
| | | multiple: true, |
| | | treeCheckable: true, |
| | | showCheckedStrategy: 'SHOW_ALL', |
| | | allowClear: true, |
| | | treeDefaultExpandAll: true, |
| | | placeholder: '不选表示全局默认方案;只对所选部门本级的员工生效', |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | // 养老保险 |
| | | { |
| | |
| | | step: 0.001, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(0.001), |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | { |
| | |
| | | step: 0.001, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(0.001), |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | // 工伤/生育保险 |
| | |
| | | step: 0.001, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(0.001), |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | { |
| | |
| | | step: 0.001, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(0.001), |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | // 公积金 |
| | |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | // 缴费基数 |
| | | { |
| | | fieldName: 'divider_base', |
| | | label: '缴费基数(未单独维护员工社保档案的员工按此计算)', |
| | | component: 'Divider', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'defaultSocialSecurityBase', |
| | | label: '默认社保基数', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '留空则用员工基本工资', |
| | | min: 0, |
| | | precision: 2, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'defaultHousingFundBase', |
| | | label: '默认公积金基数', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '留空或 0 则用社保基数', |
| | | min: 0, |
| | | precision: 2, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | // 备注 |
| | |
| | | export function useGridColumns(): VxeTableGridOptions<HrmSocialSecuritySchemeApi.SocialSecurityScheme>['columns'] { |
| | | return [ |
| | | { field: 'name', title: '方案名称', minWidth: 180 }, |
| | | { |
| | | field: 'deptNames', |
| | | title: '适用部门', |
| | | minWidth: 160, |
| | | slots: { default: 'deptNames' }, |
| | | }, |
| | | { field: 'effectiveDate', title: '生效日期', width: 110 }, |
| | | { |
| | | field: 'pensionCompanyRatio', |
| | |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #deptNames="{ row }"> |
| | | <span v-if="row.deptNames">{{ row.deptNames }}</span> |
| | | <Tag v-else color="blue">全局默认</Tag> |
| | | </template> |
| | | <template #pensionCompanyRatio="{ row }"> |
| | | {{ formatRatio(row.pensionCompanyRatio) }} |
| | | </template> |
| | |
| | | maternityRatio: 0.005, |
| | | housingFundCompanyRatio: 0.12, |
| | | housingFundPersonalRatio: 0.12, |
| | | deptIds: [], |
| | | status: 0, |
| | | }); |
| | | return; |
| | |
| | | max: 1, |
| | | step: 0.01, |
| | | precision: 4, |
| | | formatter: (value: number) => `${(value * 100).toFixed(0)}%`, |
| | | parser: (value: string) => parseFloat(value.replace('%', '')) / 100, |
| | | // 保留 2 位百分比小数(对应 decimal(5,4) 的 4 位小数),避免 3.5% 被四舍五入成 4% |
| | | formatter: (value: number) => |
| | | value === undefined || value === null |
| | | ? '' |
| | | : `${Number((value * 100).toFixed(2))}%`, |
| | | parser: (value: string) => { |
| | | const num = parseFloat(String(value).replace('%', '')); |
| | | return isNaN(num) ? 0 : num / 100; |
| | | }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | |
| | | {{ row.minAmount }} ~ {{ row.maxAmount === 0 ? '∞' : row.maxAmount }} |
| | | </template> |
| | | <template #taxRate="{ row }"> |
| | | {{ (row.taxRate * 100).toFixed(0) }}% |
| | | {{ |
| | | row.taxRate === undefined || row.taxRate === null |
| | | ? '-' |
| | | : `${Number((row.taxRate * 100).toFixed(2))}%` |
| | | }} |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |