微搭低代码MBA 培训管理系统实战 39——工资核算看板
目录
前情回顾与本节目标
在上一节中,我们完成了薪酬与提成引擎功能,实现了提成自动计算、薪资结构配置和自动算薪。
本节我们将继续完善OA管理模块,实现工资核算看板功能。这是企业内部HR和财务人员最核心的工作台,需要支持月度工资核算、批量发放、工资条查看、社保个税申报以及异常处理等完整流程。
本节核心目标:
- 数据模型设计:创建工资记录表、发放批次表、社保配置表、异议反馈表
- 工资核算看板:月度工资总额、发放进度、异常项数等核心指标
- 工资核算表:员工工资明细列表,支持批量核算、批量发放、导出工资条
- 发放记录:按批次查看历史发放记录
- 我的工资条:员工查看个人工资明细,支持异议反馈
- 社保个税:参保规则配置和本月申报明细
- 一键核算:自动核算本月工资,预览结果并确认

第一步:数据模型准备
1.1 工资记录表(MBA_PayrollRecords)
用于记录每位员工每月的工资明细:
| 字段名称 | 字段标识 | 字段类型 | 说明 |
|---|---|---|---|
| 记录ID | _id | 文本 | 主键,系统自动生成 |
| 关联员工 | rel_employee_id | 关联关系 | 关联 MBA_Users 表 |
| 工号 | employee_no | 文本 | 员工工号 |
| 部门 | department | 文本 | 所属部门 |
| 职位 | post | 文本 | 职位名称 |
| 核算月份 | calc_month | 文本 | 如:2026-02 |
| 基本工资 | base_salary | 数字 | 基本工资金额 |
| 绩效奖金 | performance_bonus | 数字 | 绩效奖金金额 |
| 业务提成 | commission | 数字 | 销售提成金额 |
| 补贴加班 | allowance | 数字 | 补贴和加班费 |
| 考勤扣款 | deduction | 数字 | 考勤扣款金额 |
| 社保代扣 | social_security | 数字 | 社保个人缴纳部分 |
| 公积金代扣 | housing_fund | 数字 | 公积金个人缴纳部分 |
| 个人所得税 | tax | 数字 | 个人所得税 |
| 应发工资 | gross_pay | 数字 | 应发工资合计 |
| 实发工资 | net_pay | 数字 | 实发工资合计 |
| 核算状态 | status | 枚举 | 1-待核算、2-已核算、3-已发放、4-异常 |
| 异常原因 | abnormal_reason | 文本 | 异常原因说明 |
| 发放日期 | payment_date | 日期 | 实际发放日期 |
| 创建时间 | created_at | 日期时间 | 自动生成 |
| 更新时间 | updated_at | 日期时间 | 自动更新 |
1.2 发放批次表(MBA_PayrollBatches)
用于记录每次工资发放的批次信息:
| 字段名称 | 字段标识 | 字段类型 | 说明 |
|---|---|---|---|
| 批次ID | _id | 文本 | 主键,系统自动生成 |
| 批次编号 | batch_no | 文本 | 如:BATCH001 |
| 发放月份 | pay_month | 文本 | 如:2026-02 |
| 发放日期 | payment_date | 日期 | 实际发放日期 |
| 发放人数 | employee_count | 数字 | 本次发放人数 |
| 发放总金额 | total_amount | 数字 | 本次发放总金额 |
| 发放方式 | pay_method | 枚举 | 1-银行代发、2-微信支付、3-现金发放 |
| 发放状态 | status | 枚举 | 1-已发放、2-部分发放、3-发放失败 |
| 创建时间 | created_at | 日期时间 | 自动生成 |
| 更新时间 | updated_at | 日期时间 | 自动更新 |
1.3 社保配置表(MBA_TaxInsuranceConfig)
用于配置社保和公积金的计算参数:
| 字段名称 | 字段标识 | 字段类型 | 说明 |
|---|---|---|---|
| 配置ID | _id | 文本 | 主键,系统自动生成 |
| 参保城市 | city | 文本 | 参保城市名称 |
| 社保基数下限 | social_base_min | 数字 | 社保最低缴费基数 |
| 公积金基数下限 | fund_base_min | 数字 | 公积金最低缴费基数 |
| 社保个人比例 | social_rate | 数字 | 社保个人缴纳比例(%) |
| 公积金个人比例 | fund_rate | 数字 | 公积金个人缴纳比例(%) |
| 生效日期 | effective_date | 日期 | 配置生效日期 |
| 创建时间 | created_at | 日期时间 | 自动生成 |
| 更新时间 | updated_at | 日期时间 | 自动更新 |
1.4 异议反馈表(MBA_PayrollDisputes)
用于记录员工对工资的异议反馈:
| 字段名称 | 字段标识 | 字段类型 | 说明 |
|---|---|---|---|
| 反馈ID | _id | 文本 | 主键,系统自动生成 |
| 关联工资记录 | rel_payroll_id | 关联关系 | 关联 MBA_PayrollRecords 表 |
| 反馈人 | rel_employee_id | 关联关系 | 关联 MBA_Users 表 |
| 反馈内容 | content | 多行文本 | 异议反馈内容 |
| 处理状态 | status | 枚举 | 1-待处理、2-处理中、3-已处理 |
| 处理结果 | result | 多行文本 | 财务复核结果 |
| 处理人 | rel_handler_id | 关联关系 | 关联 MBA_Users 表(财务人员) |
| 处理时间 | handled_at | 日期时间 | 处理时间 |
| 创建时间 | created_at | 日期时间 | 自动生成 |
| 更新时间 | updated_at | 日期时间 | 自动更新 |
第二步:工资核算看板页面搭建
2.1 创建工资核算页面
点击创建页面图标,选择表格与表单模板,数据模型选择工资记录表,选择财务布局
切换到布局管理,选择财务布局,添加平级菜单,添加"工资核算"菜单
2.2 页面顶部区域
切换回页面设计,在财务布局的内容插槽下添加布局组件,修改标题为"工资核算看板"
第二步:统计卡片区域
2.1 统计卡片配置
在顶部区域下方添加网格布局。
选中行组件,设置列的数量为5
在容器内添加5个卡片组件,分别展示核心指标:
| 卡片 | 指标 | 数据来源 |
|---|---|---|
| 本月工资总额 | ¥254,000 | 汇总当月工资记录应发工资 |
| 已发放金额 | ¥0 | 汇总当月已发放状态的实发工资 |
| 待发放金额 | ¥254,000 | 汇总当月非已发放状态的实发工资 |
| 异常项数 | 1 | 统计当月异常状态的记录数 |
| 社保个税代扣 | ¥12,000 | 汇总当月社保+公积金+个税 |

3.2 加载仪表盘数据
创建自定义变量,用来存储统计卡片的数据
创建自定义方法 loadDashboardData:
export default async function loadDashboardData({ event, data }) {
try {
// 获取当前月份的第一天毫秒值
const today = new Date();
const currentMonth = new Date(today.getFullYear(), today.getMonth(), 1).getTime();
const payrollRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_PayrollRecords',
methodName: 'wedaGetRecordsV2',
params: {
filter: { where: { calc_month: { $eq: currentMonth } } },
select: { $master: true }
}
});
const records = payrollRes.records || [];
const totalGross = records.reduce((sum, r) => sum + (r.gross_pay || 0), 0);
const paidAmount = records
.filter(r => r.status === '3')
.reduce((sum, r) => sum + (r.net_pay || 0), 0);
const pendingAmount = records
.filter(r => r.status !== '3')
.reduce((sum, r) => sum + (r.net_pay || 0), 0);
const abnormalCount = records.filter(r => r.status === '4').length;
const taxTotal = records.reduce((sum, r) =>
sum + (r.social_security || 0) + (r.housing_fund || 0) + (r.tax || 0), 0
);
const payProgress = totalGross > 0 ? Math.round((paidAmount / (paidAmount + pendingAmount)) * 100) : 0;
$w.page.dataset.state.dashboardData = {
totalGross,
paidAmount,
pendingAmount,
abnormalCount,
taxTotal,
payProgress
};
} catch (error) {
console.error('加载仪表盘数据失败:', error);
}
}
在页面加载时调用 loadDashboardData 方法。
给卡片的统计结果文本绑定对应的字段
第三步:标签页配置
3.1 标签页组件
在统计卡片下方添加标签页组件,配置五个标签页:
| 标签页值 | 名称 | 内容组件 |
|---|---|---|
| payroll-table | 工资核算表 | 工资明细表格 |
| history | 发放记录 | 发放批次列表 |
| my-payslip | 我的工资条 | 个人工资条 |
| tax-insurance | 社保&个税 | 参保配置和申报明细 |
| abnormal | 异常处理 | 异常项处理队列 |

第四步:工资核算表
4.1 批量操作工具栏
在全局按钮里添加批量操作按钮:
| 按钮 | 功能 |
|---|---|
| 批量核算 | 对选中记录执行核算 |
| 批量发放 | 对选中记录执行发放 |
| 导出工资条 | 导出选中记录的工资条 |

4.2 工资明细表格
添加数据表格组件,数据模型选择工资记录表(MBA_PayrollRecords)
配置表格列:
| 列名 | 绑定字段 | 说明 |
|---|---|---|
| 复选框 | - | 多选操作 |
| 员工姓名 | rel_employee_id.name | 关联查询员工姓名 |
| 部门/职位 | department / post | 部门和职位 |
| 底薪 | base_salary | 基本工资 |
| 绩效奖金 | performance_bonus | 绩效奖金 |
| 提成金额 | commission | 业务提成 |
| 补贴/扣款 | allowance / deduction | 补贴和扣款 |
| 应发工资 | gross_pay | 应发合计 |
| 实发工资 | net_pay | 实发合计 |
| 状态 | status | 核算状态 |
| 操作 | - | 操作菜单 |
4.3 批量核算功能
创建自定义方法 batchCalculate:
export default async function batchCalculate({ event, data }) {
try {
$w.utils.showLoading({ title: '核算中...' });
// 获取当前月份
const today = new Date();
const currentMonth = new Date(today.getFullYear(), today.getMonth(), 1).getTime();
// 查询社保配置
const configRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_TaxInsuranceConfig',
methodName: 'wedaGetRecordsV2',
params: {
filter: { where: {} },
select: { $master: true }
}
});
const config = configRes.records?.[0] || {};
const socialRate = (config.social_rate || 10.5) / 100;
const fundRate = (config.fund_rate || 7) / 100;
const socialBaseMin = config.social_base_min || 7310;
const fundBaseMin = config.fund_base_min || 2800;
// 1. 从薪资计算记录表获取当月所有记录
const salaryRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_SalaryRecords',
methodName: 'wedaGetRecordsV2',
params: {
filter: {
where: {
calc_month: { $eq: currentMonth }
}
},
select: { $master: true }
}
});
const salaryRecords = salaryRes.records || [];
if (salaryRecords.length === 0) {
$w.utils.hideLoading();
return $w.utils.showToast({ title: '当月无薪资计算记录', icon: 'warning' });
}
// 2. 查询当月已有的工资记录(用于判断是更新还是创建)
const existingRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_PayrollRecords',
methodName: 'wedaGetRecordsV2',
params: {
filter: {
where: {
calc_month: { $eq: currentMonth }
}
},
select: { $master: true }
}
});
const existingMap = new Map();
(existingRes.records || []).forEach(r => {
existingMap.set(r.rel_employee_id?._id || r.rel_employee_id, r);
});
// 3. 查询上月工资记录(用于异常检测)
const prevMonth = new Date(currentMonth);
prevMonth.setMonth(prevMonth.getMonth() - 1);
const prevMonthTime = new Date(prevMonth.getFullYear(), prevMonth.getMonth(), 1).getTime();
const prevRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_PayrollRecords',
methodName: 'wedaGetRecordsV2',
params: {
filter: {
where: {
calc_month: { $eq: prevMonthTime }
}
},
select: { $master: true }
}
});
const prevMap = new Map();
(prevRes.records || []).forEach(r => {
prevMap.set(r.rel_employee_id?._id || r.rel_employee_id, r);
});
// 4. 逐条核算
for (const salaryRecord of salaryRecords) {
const employeeId = salaryRecord.rel_salesperson_id?._id || salaryRecord.rel_salesperson_id;
// 从薪资表获取基础数据
const baseSalary = salaryRecord.base_salary || 0;
const performanceBonus = salaryRecord.performance_salary || 0;
const commission = salaryRecord.commission_total || 0;
// 获取员工信息
const empRes = await $w.cloud.callDataSource({
dataSourceName: 'MBA_Employees',
methodName: 'wedaGetItemV2',
params: {
filter: { where: { _id: { $eq: employeeId } } },
select: { $master: true }
}
});
const employee = empRes || {};
// 计算社保公积金
const socialBase = Math.max(baseSalary, socialBaseMin);
const fundBase = Math.max(baseSalary, fundBaseMin);
const socialSecurity = Math.round(socialBase * socialRate);
const housingFund = Math.round(fundBase * fundRate);
// 计算应发和实发
const grossPay = baseSalary + performanceBonus + commission;
const tax = Math.round(grossPay * 0.03);
const netPay = grossPay - socialSecurity - housingFund - tax;
// 异常检测:实发工资涨幅超过30%
let status = '2';
let abnormalReason = '';
const prevRecord = prevMap.get(employeeId);
if (prevRecord && prevRecord.net_pay > 0 && netPay > prevRecord.net_pay * 1.3) {
status = '4';
abnormalReason = '实发工资涨幅超过30%';
}
// 判断是更新还是创建
const existingRecord = existingMap.get(employeeId);
if (existingRecord) {
// 更新已有记录
await $w.cloud.callDataSource({
dataSourceName: 'MBA_PayrollRecords',
methodName: 'wedaUpdateV2',
params: {
filter: { where: { _id: { $eq: existingRecord._id } } },
data: {
base_salary: baseSalary,
performance_bonus: performanceBonus,
commission: commission,
social_security: socialSecurity,
housing_fund: housingFund,
tax: tax,
gross_pay: grossPay,
net_pay: netPay,
status: status,
abnormal_reason: abnormalReason,
updated_at: Date.now()
}
}
});
} else {
// 创建新记录
await $w.cloud.callDataSource({
dataSourceName: 'MBA_PayrollRecords',
methodName: 'wedaCreateV2',
params: {
data: {
rel_employee_id: { _id: employeeId },
employee_no: employee.employee_no || '',
department: employee.department || '',
post: employee.position || '',
calc_month: currentMonth,
base_salary: baseSalary,
performance_bonus: performanceBonus,
commission: commission,
allowance: 0,
deduction: 0,
social_security: socialSecurity,
housing_fund: housingFund,
tax: tax,
gross_pay: grossPay,
net_pay: netPay,
status: status,
abnormal_reason: abnormalReason,
created_at: Date.now(),
updated_at: Date.now()
}
}
});
}
}
$w.utils.hideLoading();
$w.utils.showToast({
title: `核算完成,共处理 ${salaryRecords.length} 条记录`,
icon: 'success'
});
$w.table2.refresh();
} catch (error) {
console.error('批量核算失败:', error);
$w.utils.hideLoading();
$w.utils.showToast({ title: '核算失败,请重试', icon: 'error' });
}
}
给按钮配置点击事件,调用方法
最终效果
HR或财务人员进入工资核算看板,查看5个核心指标卡片。
通过标签页切换,可以在工资核算表、发放记录、我的工资条、社保个税和异常处理五个模块之间切换。
在工资核算表中,可以勾选多条记录进行批量核算、批量发放或导出工资条。每条记录展示完整的工资构成:底薪、绩效奖金、提成、补贴扣款、应发工资、实发工资和核算状态。

总结
本节完成了工资核算看板功能的实现:
- 数据模型设计:创建工资记录表(MBA_PayrollRecords)、发放批次表(MBA_PayrollBatches)、社保配置表(MBA_TaxInsuranceConfig)、异议反馈表(MBA_PayrollDisputes)
- 工资核算看板:月份切换、发放进度、5个核心指标卡片
- 工资核算表:员工工资明细列表,支持批量核算、批量发放、导出工资条
- 发放记录:按批次查看历史发放记录
- 我的工资条:个人工资明细查看,支持异议反馈
- 社保个税:参保规则配置和本月申报明细
- 一键核算:自动核算本月工资,异常检测,结果预览确认
通过本节的学习,我们完善了财务管理中的薪资核算流程,实现了从工资数据准备到自动核算再到批量发放的完整闭环。下一节我们将继续完善OA模块的其他功能。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)