ux: 新建账户按钮移到社保/公积金Tab内部,按类型显示

按钮从页面顶部移到Tab内容区顶部,文案改为"新建社保账户"/"新建公积金账户",
明确表示新建的是当前Tab对应类型的账户。

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-08-16 15:10:38 +08:00
parent de4f8c90fb
commit 54d138cc73
2 changed files with 109 additions and 83 deletions
+102 -77
View File
@@ -3,44 +3,33 @@
> 目标:将社保公积金从"按城市直接关联版本"改为"账户 + 年度标准"两层实体。
> 账户代表用户开设的社保/公积金账户(对应不同子公司/分公司/地区),年度标准是账户下每个年度的缴费比例和基数标准。
> **状态:已完成实施**2026-08-16
> 账户+年度标准模型已上线,前端已重构为账户卡片列表+展开年度标准管理。
---
## 一、现状分析
### 当前数据模型
### 当前数据模型(已重构)
| 表 | 说明 | 唯一约束 |
|----|------|---------|
| `SocialInsuranceConfig` | 社保版本(比例+基数+生效月份),按 orgId+city+effectiveFrom | `@@unique([orgId, city, effectiveFrom])` |
| `HousingFundConfig` | 公积金版本,按 orgId+city+accountType+effectiveFrom | `@@unique([orgId, city, accountType, effectiveFrom])` |
| `EmployeeSocialInsRecord` | 员工社保参保记录,字段含 city | `@@index([orgId, city])` |
| `EmployeeHousingFundRecord` | 员工公积金参保记录,字段含 city | 无 city 索引 |
| `SocialAccount` | 社保/公积金账户实体(type=SOCIAL/HOUSING | `@@unique([orgId, type, name])` |
| `SocialYearStandard` | 年度标准(比例+基数+最低工资+生效月份),关联到账户 | `@@unique([accountId, effectiveFrom])` |
| `SocialInsuranceConfig` | 旧社保版本表(保留兼容,回退用) | `@@unique([orgId, city, effectiveFrom])` |
| `HousingFundConfig` | 公积金版本表(保留兼容) | — |
| `EmployeeSocialInsRecord` | 员工社保参保记录,已加 accountId | — |
| `EmployeeHousingFundRecord` | 员工公积金参保记录,已加 accountId | — |
| `SocialMonthlyProcess` | 月度办理记录 | `@@unique([orgId, month, type])` |
### 当前问题
### 已解决的问题
1. **"城市"只是字符串**:无实体化管理,无法记录账户编号、开户行、缴费主体等
2. **多子公司/分公司场景缺失**同一城市可有多个社保账户(不同主体分别开户),当前按 city 唯一无法支持
3. **版本直接挂在 orgId+city 上**:缺少"账户"这一层抽象,无法区分同一城市不同主体的账户
4. **员工参保记录只有 city**:无法精确关联到具体哪个社保账户
5. **薪资计算中社保配置查询**:按 orgId+city 查 SocialInsuranceConfig,无法按账户区分
### 涉及"关联城市"的代码位置
| 文件 | 位置 | 说明 |
|------|------|------|
| `backend/prisma/schema.prisma` | `SocialInsuranceConfig.city` | 社保配置按城市区分 |
| `backend/prisma/schema.prisma` | `HousingFundConfig.city` | 公积金配置按城市区分 |
| `backend/prisma/schema.prisma` | `EmployeeSocialInsRecord.city` | 员工社保参保城市 |
| `backend/prisma/schema.prisma` | `EmployeeHousingFundRecord.city` | 员工公积金参保城市 |
| `backend/src/routes/social.routes.ts` | 全文 | 社保配置 CRUD/版本/城市列表/月度办理 均按 city |
| `backend/src/services/payroll.service.ts` | 社保计算逻辑 | 按 city 查 SocialInsuranceConfig |
| `backend/src/routes/payroll2.routes.ts` | 批次计算 | 社保配置查询 |
| `backend/src/routes/roster.routes.ts` | socialInsuranceStatus 派生 | 按 city 判定 |
| `backend/src/services/contract.service.ts` | createEmployee | 社保记录创建时关联 city |
| `frontend/src/pages/SocialInsurance.tsx` | 全文 | 城市选择器、版本管理、月度办理 |
| `frontend/src/pages/roster/modals.tsx` | 社保公积金区 | 员工参保城市 |
| `frontend/src/pages/Settings.tsx` | 社保配置 | 需新增账户管理入口 |
1. **"城市"只是字符串** → 已改为 SocialAccount 实体化管理
2. **多子公司/分公司场景缺失**同一城市可有多个账户,按根部门关联
3. **版本直接挂在 orgId+city 上** → 改为 SocialYearStandard 关联到 accountId
4. **员工参保记录只有 city** → 已加 accountId 字段
5. **薪资计算中社保配置查询** → 改为按 accountId 查 SocialYearStandard,旧表回退
6.**最低工资标准** → SocialYearStandard 新增 minWage 字段,支持最低工资保护和递延扣款
---
@@ -73,6 +62,10 @@ model SocialAccount {
socialRecords EmployeeSocialInsRecord[]
housingRecords EmployeeHousingFundRecord[]
// 部门关联(根部门 level=0 代表分公司/子公司)
deptSocialAccounts Department[] @relation("SocialAccountDepartments")
deptHousingAccounts Department[] @relation("HousingAccountDepartments")
@@unique([orgId, type, name])
@@index([orgId, type, city])
@@index([orgId, type, isDefault])
@@ -104,6 +97,9 @@ model SocialYearStandard {
medicalBaseMax Float @default(0)
extraInsurances Json?
// 最低工资标准(仅社保,20260816新增)
minWage Float @default(0) // 当地月最低工资标准,0=不检查
// 公积金比例(type=HOUSING 时使用)
housingOrg Float @default(12)
housingEmp Float @default(12)
@@ -248,7 +244,7 @@ JOIN "SocialAccount" a ON a."orgId" = c."orgId" AND a.city = c.city AND a.type =
## 四、后端 API 变更
### 新增:账户管理 API
### 新增:账户管理 API(已实施)
| 方法 | 路径 | 说明 |
|------|------|------|
@@ -257,15 +253,24 @@ JOIN "SocialAccount" a ON a."orgId" = c."orgId" AND a.city = c.city AND a.type =
| PUT | `/social/accounts/:id` | 编辑账户 |
| DELETE | `/social/accounts/:id` | 删除账户(无关联记录时可删) |
| PUT | `/social/accounts/:id/default` | 设为默认账户 |
| PUT | `/social/accounts/:id/departments` | 账户关联根部门(批量) |
| GET | `/social/accounts/:id/departments` | 获取账户已关联的根部门 |
### 改造:年度标准 API原版本 API
### 新增:年度标准 API已实施
| 方法 | 路径 | 说明 | 变更 |
|------|------|------|------|
| GET | `/social/config` | 获取当前标准 | 改为按 accountId 查询 |
| GET | `/social/config/versions` | 版本列表 | 改为按 accountId 查询 |
| POST | `/social/config/versions` | 新建版本 | 改为按 accountId 创建 |
| GET | `/social/config/by-month/:month` | 按月查标准 | 改为按 accountId+month 查询 |
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/social/accounts/:accountId/standards` | 账户的年度标准列表 |
| GET | `/social/accounts/:accountId/current-standard` | 账户的当前生效标准 |
| GET | `/social/accounts/:accountId/standard-by-month/:month` | 按账户+月份获取适用标准 |
| POST | `/social/accounts/:accountId/standards` | 新建年度标准(旧版本自动归档) |
| PUT | `/social/accounts/:accountId/min-wage` | 快速更新当前标准的最低工资 |
### 改造:薪资计算(已实施)
- `payroll.service.ts` 中社保配置查询:从 `orgId + city` 改为通过员工→根部门→账户→年度标准
- 员工参保记录查询:从 `employeeId + city` 改为 `employeeId + accountId`
- 新增最低工资保护和递延扣款逻辑(详见 20260816-薪税管理逻辑.md 第十五章)
### 废弃:城市相关 API
@@ -285,30 +290,36 @@ JOIN "SocialAccount" a ON a."orgId" = c."orgId" AND a.city = c.city AND a.type =
---
## 五、前端 UI 变更
## 五、前端 UI 变更(已实施)
### 1. 设置页新增"社保公积金账户管理"
### 1. 社保公积金菜单(SocialInsurance.tsx)— 已重构
- 账户列表(按 type 分社保/公积金 Tab)
- 每个账户卡片:名称、城市、账户编号、缴费主体、关联范围(公司/分公司/子公司)、是否默认
- 新增/编辑/停用账户弹窗
- **关联级别**:公司(Organization)或根部门(Department level=0,代表分公司/子公司),不向下到普通部门
- 员工通过所属根部门自动继承账户,未关联根部门的员工使用公司默认账户
**社保/公积金 Tab**20260816 重构):
- ~~原城市选择器 → 改为账户选择器~~ → 已改为**账户卡片列表 + 展开年度标准管理**
- 每个账户卡片:名称、城市、账号、关联部门数、参保记录数
- 卡片可展开/折叠(ChevronDown/ChevronRight 图标)
- 展开后显示:
- 当前年度标准(比例/基数/最低工资)
- 最低工资快速编辑(仅社保,Input + 保存按钮)
- 操作按钮:新建年度标准 | 批量调基 | 查看历史
- 新建年度标准弹窗(原"新建版本"改名,保留 AI 建议、附加险种配置)
- 版本历史展示
- 调基预览(保留原有展示和编辑逻辑)
- 试算工具(使用该账户的城市配置)
- 顶部"新建账户"按钮(账户新建/编辑/删除/设默认已从设置页面迁移到此)
- 新增组件:`AccountCard.tsx`(账户卡片+展开内容)、`AccountFormModal.tsx`(账户新建/编辑弹窗)
### 2. SocialInsurance.tsx 改造
**月度办理 Tab**:保留不变
**社保/公积金 Tab**
- 原城市选择器 → 改为**账户选择器**(下拉,显示"账户名称 - 城市"
- 选中账户后展示该账户下的年度标准(当前版本 + 历史版本)
- 年度标准展示比例和基数(与现在版本展示一致)
**员工参保 Tab**保留不变
**月度办理 Tab**
- 按账户分别办理
- 选择账户 → 获取该账户的增减员名单 → 确认办理
**专项附加扣除 Tab**保留不变
**员工参保 Tab**
- 员工参保记录展示关联的账户名称
- 新增参保时选择账户(而非仅选城市)
### 2. 设置页面(Settings.tsx)— 已简化
- ~~新增"社保公积金账户管理"Tab~~ → 已去掉该 Tab
- 账户管理全部移到社保公积金菜单中完成
- SocialAccountSettings 和 AccountFormModal 组件定义保留在文件中(不再显示)
### 3. 员工表单(roster/modals.tsx
@@ -354,34 +365,48 @@ JOIN "SocialAccount" a ON a."orgId" = c."orgId" AND a.city = c.city AND a.type =
---
## 七、实施计划
## 七、实施计划与完成状态
### 第一步:DB schema + 迁移脚本
- 新增 SocialAccount、SocialYearStandard 表
- 员工记录表加 accountId 字段(可空)
- 编写并执行迁移脚本
- 验证数据完整性
### 第一步:DB schema + 迁移脚本 ✅ 已完成
- 新增 SocialAccount、SocialYearStandard 表
- 员工记录表加 accountId 字段(可空)
- ✅ SocialYearStandard 新增 minWage 字段(最低工资标准)
- ✅ Department 表新增 socialAccountId / housingAccountId(根部门关联账户)
- ✅ 编写并执行迁移脚本
- ✅ 验证数据完整性
### 第二步:后端 API 重构
- 新增账户 CRUD API
- 年度标准 API 改为按 accountId
- 薪资计算适配
- 员工参保记录适配
- 保留旧 API 兼容(过渡期
### 第二步:后端 API 重构 ✅ 已完成
- 新增账户 CRUD API`/social/accounts`
- 年度标准 API 改为按 accountId`/social/accounts/:accountId/standards`
- ✅ 新增按账户获取当前标准 API`/social/accounts/:accountId/current-standard`
- ✅ 新增快速更新最低工资 API`/social/accounts/:accountId/min-wage`
- ✅ 薪资计算适配(payroll.service.ts 按 accountId 查标准,旧表回退
- ✅ 员工参保记录适配
- ✅ 保留旧 API 兼容(过渡期)
### 第三步:前端账户管理
- 设置页新增账户管理 UI
- SocialInsurance.tsx 改为账户选择器 + 年度标准
### 第三步:前端账户管理 ✅ 已完成
- ~~设置页新增账户管理 UI~~ → 已移到社保公积金菜单
- SocialInsurance.tsx 改为账户卡片列表 + 展开年度标准管理
- ✅ 新建版本改名为"新建年度标准"
- ✅ 最低工资快速编辑(当前配置区域可直接编辑)
- ✅ 新增 AccountCard.tsx 组件(账户卡片+展开内容)
- ✅ 新增 AccountFormModal.tsx 组件(账户新建/编辑弹窗)
### 第四步:前端参保流程适配
- 员工表单社保公积金区改为账户选择
- 月度办理按账户分别办理
- 员工参保记录展示账户名称
### 第四步:前端参保流程适配 ✅ 已完成
- 员工表单社保公积金区改为账户选择
- 月度办理保留不变
- 员工参保记录展示账户名称
### 第五步:清理
- 删除旧 SocialInsuranceConfig / HousingFundConfig 表
- 删除旧 API
- 删除前端城市选择器残留代码
### 第五步:清理 ⏳ 待执行
- 删除旧 SocialInsuranceConfig / HousingFundConfig 表
- 删除旧 API
- 删除前端城市选择器残留代码
### 额外完成的功能(20260816
- ✅ 最低工资保护与递延扣款机制(详见 20260816-薪税管理逻辑.md 第十五章)
- ✅ 最低工资保护支持当月累计实发判断(多批次场景)
- ✅ 预入职状态(PRE_ONBOARD),预入职员工不进入薪资批次
- ✅ 薪资批次新增 payMonth(发薪年月)字段,支持提前发薪场景
---
+7 -6
View File
@@ -175,11 +175,6 @@ export default function SocialInsurance() {
<p className="mt-1 text-sm text-gray-500"></p>
</div>
</div>
{(tab === 'social' || tab === 'housing') && (
<Button size="sm" onClick={() => { setEditAccount(null); setShowAccountForm(true) }}>
<Plus className="w-4 h-4 mr-1" />
</Button>
)}
</div>
{/* Tab 切换 */}
@@ -200,10 +195,16 @@ export default function SocialInsurance() {
{/* ========== 社保 / 公积金 Tab:账户卡片列表 ========== */}
{(tab === 'social' || tab === 'housing') && (
<div className="space-y-3">
{/* 新建对应类型账户按钮 */}
<div className="flex justify-end">
<Button size="sm" onClick={() => { setEditAccount(null); setShowAccountForm(true) }}>
<Plus className="w-4 h-4 mr-1" />{tab === 'housing' ? '公积金' : '社保'}
</Button>
</div>
{accounts.length === 0 ? (
<Card>
<div className="text-center py-8 text-gray-500">
{tab === 'housing' ? '公积金' : '社保'}
{tab === 'housing' ? '公积金' : '社保'}{tab === 'housing' ? '公积金' : '社保'}
</div>
</Card>
) : (