/** * 本地演示数据(对齐操作流程图线上节点) * 街镇覆盖:叶榭、佘山、车墩、石湖荡、泖港、新浜、小昆山 */ import type { AccountItem, AlertItem, Approval, DeathRecord, DiskFailPerson, DiskRecord, MonthlyBatch, OpLog, Person, SpecialBiz, AdjustReissuePerson, SubsidyStandard, VillageBatch, } from '@/types/biz' export const mockPeople: Person[] = [ { id: 1, batchNo: '202608', name: '张建国', idCard: '310117195803121234', town: '叶榭镇', village: '同建村', bankCard: '6222021001123456789', bankName: '工商银行叶榭支行', standard: 1280, startMonth: '2026-08', status: '正常在库', source: '村合作社录入', insuranceStatus: '匹配通过', }, { id: 2, batchNo: '202608', name: '李秀英', idCard: '310117196205081234', town: '叶榭镇', village: '同建村', bankCard: '6228480030123456789', bankName: '农业银行叶榭支行', standard: 1280, startMonth: '2026-08', status: '正常在库', source: 'Excel导入', insuranceStatus: '匹配通过', }, { id: 3, batchNo: '202608', name: '王德福', idCard: '310117195912151234', town: '叶榭镇', village: '同建村', bankCard: '6217001210123456789', bankName: '建设银行叶榭支行', standard: 1280, startMonth: '2026-08', status: '待人工确认', source: '村合作社录入', insuranceStatus: '匹配失败', remark: '居保姓名不一致', }, { id: 4, batchNo: '202608', name: '赵桂兰', idCard: '310118196108201234', town: '泖港镇', village: '泖港村', bankCard: '6225880210123456789', bankName: '招商银行松江支行', standard: 1260, startMonth: '2026-08', status: '暂停', source: '村合作社录入', insuranceStatus: '匹配通过', remark: '外出务工申请暂停', }, { id: 5, batchNo: '202607', name: '陈国强', idCard: '310117195701011234', town: '新浜镇', village: '新浜村', bankCard: '6222620110123456789', bankName: '交通银行松江支行', standard: 1250, startMonth: '2026-07', status: '正常在库', source: 'Excel导入', insuranceStatus: '匹配通过', }, { id: 6, batchNo: '202608', name: '孙美华', idCard: '310117196003151111', town: '叶榭镇', village: '东勤村', bankCard: '6222021001987654321', bankName: '工商银行叶榭支行', standard: 1280, startMonth: '2026-08', status: '正常在库', source: '村合作社录入', insuranceStatus: '匹配通过', }, { id: 7, batchNo: '202608', name: '周福生', idCard: '310117195508081122', town: '佘山镇', village: '岳阳村', bankCard: '6217001210556677889', bankName: '建设银行佘山支行', standard: 1280, startMonth: '2026-08', status: '正常在库', source: '历史台账', insuranceStatus: '匹配通过', }, { id: 8, batchNo: '202608', name: '吴根宝', idCard: '310118195611223344', town: '车墩镇', village: '车墩村', bankCard: '6225880210667788990', bankName: '招商银行松江支行', standard: 1260, startMonth: '2026-08', status: '正常在库', source: '村合作社录入', insuranceStatus: '匹配通过', }, { id: 9, batchNo: '202608', name: '郑阿婆', idCard: '310117196202021234', town: '石湖荡镇', village: '张庄村', bankCard: '6222620110334455667', bankName: '交通银行松江支行', standard: 1250, startMonth: '2026-08', status: '正常在库', source: 'Excel导入', insuranceStatus: '匹配通过', }, { id: 10, batchNo: '202607', name: '顾阿宝', idCard: '310117195812120031', town: '叶榭镇', village: '同建村', bankCard: '6228480099991111222', bankName: '农业银行松江支行', standard: 1280, startMonth: '2026-07', status: '正常在库', source: '7月历史发放', insuranceStatus: '匹配通过', }, { id: 11, batchNo: '202606', name: '朱美娟', idCard: '310117196001010047', town: '小昆山镇', village: '小昆山村', bankCard: '6217002812345678999', bankName: '建设银行松江支行', standard: 1260, startMonth: '2026-06', status: '正常在库', source: '6月历史发放', insuranceStatus: '匹配通过', }, { id: 12, batchNo: '202608', name: '钱建平', idCard: '310117195611110071', town: '新浜镇', village: '新浜村', bankCard: '6228480055556666777', bankName: '农业银行新浜支行', standard: 1250, startMonth: '2026-08', status: '草稿', source: '村合作社录入', insuranceStatus: '未匹配', }, ] /** 村级批次:stage 对齐流程图线上五段 */ export const mockVillageBatches: VillageBatch[] = [ { id: 'VB202608YX001', batchNo: '202608', month: '2026-08', town: '叶榭镇', village: '同建村', peopleCount: 3, totalAmount: 3840, stage: '人员新增录入', submitter: '村合作社经办', }, { id: 'VB202608YX002', batchNo: '202608', month: '2026-08', town: '叶榭镇', village: '东勤村', peopleCount: 12, totalAmount: 15360, stage: '镇经发中心初审', submitter: '村合作社经办', submittedAt: '2026-08-02 09:20', }, { id: 'VB202608MH001', batchNo: '202608', month: '2026-08', town: '泖港镇', village: '泖港村', peopleCount: 8, totalAmount: 10080, stage: '居保状态匹配', submitter: '村合作社经办', submittedAt: '2026-08-01 14:10', }, { id: 'VB202608XB001', batchNo: '202608', month: '2026-08', town: '新浜镇', village: '新浜村', peopleCount: 7, totalAmount: 8750, stage: '居保状态匹配', submitter: '村合作社经办', submittedAt: '2026-08-02 10:30', }, { id: 'VB202608YX003', batchNo: '202608', month: '2026-08', town: '叶榭镇', village: '同建村', peopleCount: 3, totalAmount: 3840, stage: '比对结果已生成', submitter: '村合作社经办', submittedAt: '2026-08-01 16:20', }, { id: 'VB202608YX004', batchNo: '202608', month: '2026-08', town: '叶榭镇', village: '叶兴村', peopleCount: 9, totalAmount: 11520, stage: '分管领导审批', submitter: '村合作社经办', submittedAt: '2026-08-03 09:40', }, { id: 'VB202607YX003', batchNo: '202607', month: '2026-07', town: '叶榭镇', village: '东勤村', peopleCount: 11, totalAmount: 14080, stage: '待线下发放', submitter: '村合作社经办', submittedAt: '2026-07-06 14:00', }, { id: 'VB202608SS001', batchNo: '202608', month: '2026-08', town: '佘山镇', village: '岳阳村', peopleCount: 6, totalAmount: 7680, stage: '分管领导审批', submitter: '村合作社经办', submittedAt: '2026-07-31 11:00', }, { id: 'VB202607YX001', batchNo: '202607', month: '2026-07', town: '叶榭镇', village: '同建村', peopleCount: 10, totalAmount: 12800, stage: '回盘信息确认', submitter: '村合作社经办', submittedAt: '2026-07-05 10:00', }, { id: 'VB202607YX002', batchNo: '202607', month: '2026-07', town: '叶榭镇', village: '叶兴村', peopleCount: 8, totalAmount: 10240, stage: '已回盘确认', submitter: '村合作社经办', submittedAt: '2026-07-04 11:20', }, { id: 'VB202608CD001', batchNo: '202608', month: '2026-08', town: '车墩镇', village: '车墩村', peopleCount: 5, totalAmount: 6300, stage: '待线下发放', submitter: '村合作社经办', submittedAt: '2026-07-30 09:40', }, ] export const mockApprovals: Approval[] = [ { id: 'AP202608001', bizType: '月度批次人员申请', batchNo: '202608', town: '叶榭镇', village: '东勤村', title: '东勤村2026年8月批次人员申请', applicant: '东勤村合作社', node: '镇经发中心初审', status: '待审批', createdAt: '2026-08-02 09:25', }, { id: 'AP202608002', bizType: '月补贴清单确认', batchNo: '202608', town: '叶榭镇', village: '同建村', title: '叶榭镇同建村月补贴清单提交分管领导', applicant: '叶榭镇社区事务受理服务中心', node: '分管领导线上审批', status: '待审批', createdAt: '2026-08-03 11:00', }, { id: 'AP202608002B', bizType: '月补贴清单确认', batchNo: '202608', town: '佘山镇', village: '岳阳村', title: '佘山镇岳阳村月补贴清单提交分管领导', applicant: '佘山镇社区事务受理服务中心', node: '分管领导线上审批', status: '待审批', createdAt: '2026-08-03 11:10', }, { id: 'AP202608003', bizType: '丧葬补贴', batchNo: '202608', town: '叶榭镇', village: '同建村', title: '张桂兰亲属丧葬补贴申请', applicant: '叶榭镇社区事务受理服务中心', node: '分管领导线上审批', status: '待审批', createdAt: '2026-08-03 14:20', }, { id: 'AP202608003B', bizType: '丧葬补贴', batchNo: '202608', town: '泖港镇', village: '泖港村', title: '赵桂兰亲属丧葬补贴申请', applicant: '泖港镇社区事务受理服务中心', node: '分管领导线上审批', status: '已通过', opinion: '同意', createdAt: '2026-07-30 16:40', }, ] /** 调标补发名单(触发申请后按镇生成,对接接口后由后端返回) */ export const mockAdjustReissueRoster: AdjustReissuePerson[] = [] export const mockSpecials: SpecialBiz[] = [ { id: 'SP202608001', batchNo: '202608', town: '泖港镇', village: '泖港村', type: '暂停/停止', personName: '赵桂兰', reason: '外出务工申请暂停', amount: 0, status: '已通过', createdAt: '2026-07-31 10:00', }, { id: 'SP202608002', batchNo: '202608', town: '叶榭镇', village: '同建村', type: '调标差额补发', personName: '张建国', reason: '2026年度调标差额', amount: 60, status: '待分管领导审批', createdAt: '2026-08-03 09:30', }, { id: 'SP202608003', batchNo: '202608', town: '泖港镇', village: '泖港村', type: '丧葬补贴', personName: '赵桂兰', reason: '未纳入居保体系丧葬补贴', amount: 6000, status: '已通过', heirName: '赵某某', createdAt: '2026-07-30 15:00', }, ] /** 死亡人员录入(镇经发中心) */ export const mockDeaths: DeathRecord[] = [ { id: 'DT202608001', personName: '刘福根', idCard: '310117195203031111', town: '叶榭镇', village: '同建村', deathDate: '2026-07-28', source: '镇经发中心手工录入', status: '待死亡状态更新', createdAt: '2026-08-02 14:20', }, ] export const mockMonthlyBatches: MonthlyBatch[] = [ { id: 'MB20260801', batchNo: '202608', month: '2026-08', town: '叶榭镇', peopleCount: 156, normalAmount: 199680, extraAmount: 6000, deductAmount: 1280, totalAmount: 204400, status: '待复核', }, { id: 'MB20260802', batchNo: '202608', month: '2026-08', town: '车墩镇', peopleCount: 5, normalAmount: 6300, extraAmount: 0, deductAmount: 0, totalAmount: 6300, status: '已生成拟发放文件', }, { id: 'MB20260701', batchNo: '202607', month: '2026-07', town: '叶榭镇', peopleCount: 154, normalAmount: 197120, extraAmount: 0, deductAmount: 0, totalAmount: 197120, status: '已归档', }, ] export const mockDisks: DiskRecord[] = [ { id: 'DP202607YX', batchNo: '202607', town: '叶榭镇', village: '井凌桥村', fileName: '202607-叶榭镇月补贴清单.xlsx', peopleCount: 154, amount: 197120, status: '已回盘', returnStatus: '成功', failCount: 0, returnFileName: '202607-叶榭镇回盘文件.xlsx', }, { id: 'DP202608YX', batchNo: '202608', town: '叶榭镇', village: '叶榭村', fileName: '202608-叶榭镇月补贴清单.xlsx', peopleCount: 156, amount: 204400, status: '待回盘', returnStatus: '待确认', failCount: 0, }, { id: 'DP202608CD', batchNo: '202608', town: '车墩镇', village: '车墩村', fileName: '202608-车墩镇月补贴发放文件.xlsx', peopleCount: 5, amount: 6300, status: '待回盘', returnStatus: '待确认', failCount: 0, }, ] /** 回盘发放失败人员明细(随上传回盘文件写入) */ export const mockDiskFailPeople: DiskFailPerson[] = [] export const mockAlerts: AlertItem[] = [ { id: 'AL202608001', batchNo: '202608', personName: '王德福', town: '叶榭镇', village: '同建村', type: '居保匹配失败', level: '高', content: '居保系统姓名与档案不一致,待人工确认', status: '待处理', createdAt: '2026-08-03 10:12', }, { id: 'AL202608002', batchNo: '202608', personName: '李秀英', town: '叶榭镇', village: '同建村', type: '银行卡异常', level: '中', content: '银行卡号校验未通过,请核实开户信息', status: '处理中', createdAt: '2026-08-02 15:40', }, { id: 'AL202608003', batchNo: '202608', personName: '刘福根', town: '叶榭镇', village: '同建村', type: '民政死亡比对', level: '高', content: '民政死亡数据比对命中,请业务部门完成死亡状态更新并停止发放', status: '待处理', createdAt: '2026-08-02 14:25', }, { id: 'AL202608004', batchNo: '202608', personName: '张桂兰', town: '叶榭镇', village: '同建村', type: '回盘发放失败', level: '高', failReason: '003冻结', content: '回盘发放失败:003冻结,需纳入下月补发(同建村)', status: '待处理', createdAt: '2026-08-01 16:08', }, { id: 'AL202608005', batchNo: '202607', personName: '赵建国', town: '叶榭镇', village: '同建村', type: '居保匹配失败', level: '中', content: '居保缴费状态异常,待村社核实后由镇社区事务中心复核', status: '处理中', createdAt: '2026-07-30 09:35', }, { id: 'AL202608006', batchNo: '202608', personName: '孙美华', town: '叶榭镇', village: '东勤村', type: '居保匹配失败', level: '高', content: '居保系统姓名与档案不一致,待人工确认', status: '待处理', createdAt: '2026-08-03 11:05', }, { id: 'AL202608007', batchNo: '202608', personName: '周福生', town: '叶榭镇', village: '叶兴村', type: '银行卡异常', level: '中', content: '银行卡号校验未通过,请核实开户信息', status: '待处理', createdAt: '2026-08-02 17:20', }, { id: 'AL202607008', batchNo: '202607', personName: '陈国强', town: '新浜镇', village: '新浜村', type: '回盘发放失败', level: '高', failReason: '002清户', content: '回盘发放失败:002清户,需下月补发', status: '已关闭', result: '已更换银行卡并纳入下月补发', createdAt: '2026-07-28 11:20', }, ] /** 各镇分年度补助标准(演示:当前镇含多年度,其余镇各一条最新) */ export const mockStandards: SubsidyStandard[] = [ { id: 1, town: '叶榭镇', year: 2026, monthly: 1280, funeral: 6000, date: '2026-01-01', reason: '2026年度标准' }, { id: 2, town: '叶榭镇', year: 2025, monthly: 1220, funeral: 5800, date: '2025-01-01', reason: '2025年度标准' }, { id: 3, town: '叶榭镇', year: 2024, monthly: 1180, funeral: 5500, date: '2024-01-01', reason: '2024年度标准' }, { id: 4, town: '佘山镇', year: 2026, monthly: 1280, funeral: 6000, date: '2026-01-01', reason: '2026年度标准' }, { id: 5, town: '车墩镇', year: 2026, monthly: 1260, funeral: 6000, date: '2026-01-01', reason: '2026年度标准' }, { id: 6, town: '石湖荡镇', year: 2026, monthly: 1250, funeral: 6000, date: '2026-01-01', reason: '2026年度标准' }, { id: 7, town: '泖港镇', year: 2026, monthly: 1260, funeral: 6000, date: '2026-01-01', reason: '2026年度标准' }, { id: 8, town: '新浜镇', year: 2026, monthly: 1250, funeral: 6000, date: '2026-01-01', reason: '2026年度标准' }, { id: 9, town: '小昆山镇', year: 2026, monthly: 1260, funeral: 6000, date: '2026-01-01', reason: '2026年度标准' }, ] export const mockAccounts: AccountItem[] = [ { id: 1, username: 'village_yx_tj', name: '村合作社经办', roleKey: 'village', org: '叶榭镇同建村合作社', status: '启用', lastLogin: '2026-08-04 08:50', }, { id: 2, username: 'town_yx', name: '镇经发审核', roleKey: 'town', org: '叶榭镇经济发展中心', status: '启用', lastLogin: '2026-08-04 09:10', }, { id: 3, username: 'social_yx', name: '业务经办', roleKey: 'social', org: '叶榭镇社区事务受理服务中心', status: '启用', lastLogin: '2026-08-03 16:22', }, { id: 4, username: 'admin', name: '系统管理员', roleKey: 'admin', org: '区数据局运维账号', status: '启用', lastLogin: '2026-08-04 09:00', }, ] export const mockLogs: OpLog[] = [ { id: 1, time: '2026-08-04 09:05', user: '系统管理员', role: '系统管理员', module: '系统管理', action: '查看操作日志', result: '成功', }, { id: 2, time: '2026-08-03 11:20', user: '业务经办', role: '镇社区事务受理服务中心', module: '居保状态匹配', action: '批量触发待匹配批次', result: '成功', }, { id: 3, time: '2026-08-02 14:20', user: '镇经发审核', role: '镇经发中心', module: '特殊业务处理', action: '死亡人员信息录入', result: '成功(已触发系统告警)', }, ] /* ========== 扩充演示数据(保证各列表可分页演示) ========== */ const TOWN_META = [ { town: '叶榭镇', villages: ['同建村', '东勤村', '叶兴村'], standard: 1280, code: 'YX' }, { town: '佘山镇', villages: ['岳阳村', '辰山村'], standard: 1280, code: 'SS' }, { town: '车墩镇', villages: ['车墩村', '俞塘村'], standard: 1260, code: 'CD' }, { town: '石湖荡镇', villages: ['张庄村', '新姚村'], standard: 1250, code: 'SH' }, { town: '泖港镇', villages: ['泖港村', '五库村'], standard: 1260, code: 'MH' }, { town: '新浜镇', villages: ['新浜村', '南杨村'], standard: 1250, code: 'XB' }, { town: '小昆山镇', villages: ['小昆山村', '周泾村'], standard: 1260, code: 'XK' }, ] /** 七镇街镇列表(管理员全区筛选用) */ export function getTownList(): string[] { return TOWN_META.map((t) => t.town) } /** 按街镇取下属村居列表(工作台镇级图表兜底用) */ export function getVillagesByTown(town?: string): string[] { if (!town) return [] return TOWN_META.find((t) => t.town === town)?.villages ?? [] } /** 按街镇取补助标准 */ export function getStandardByTown(town?: string): number { if (!town) return 1280 return TOWN_META.find((t) => t.town === town)?.standard ?? 1280 } const SURNAMES = '张李王赵陈孙周吴郑顾朱钱刘沈徐' const GIVEN = '建国秀英德福桂兰国强美华福生根宝阿婆阿宝美娟建平福根阿根金花根娣阿明' const PERSON_STATUSES = ['正常在库', '正常在库', '正常在库', '暂停', '草稿', '待人工确认'] const INSURANCE_STATUSES = ['匹配通过', '匹配通过', '匹配通过', '匹配失败', '未匹配'] const SOURCES = ['村合作社录入', 'Excel导入', '历史台账'] const STAGES = [ '人员新增录入', '镇经发中心初审', '居保状态匹配', '比对结果已生成', '分管领导审批', '待线下发放', '回盘信息确认', '已回盘确认', '已归档', '退回修改', ] const ALERT_TYPES = ['居保匹配失败', '银行卡异常', '民政死亡比对', '回盘发放失败'] const ALERT_LEVELS = ['高', '中', '低'] const ALERT_STATUSES = ['待处理', '处理中', '已关闭'] const SPECIAL_TYPES = ['暂停/停止', '恢复发放', '多发退款', '丧葬补贴', '调标差额补发'] const SPECIAL_STATUSES = ['待分管领导审批', '已通过', '已驳回'] const APPROVAL_NODES = ['镇经发中心初审', '分管领导线上审批'] const APPROVAL_STATUSES = ['待审批', '待审批', '已通过', '已驳回'] const MONTHLY_STATUSES = ['待复核', '已生成拟发放文件', '已归档'] const DISK_STATUSES = ['待回盘', '已回盘'] const DEATH_STATUSES = ['待死亡状态更新', '已停止发放'] const ROLE_KEYS = ['village', 'town', 'social', 'leader', 'agri', 'hr', 'admin'] as const const LOG_MODULES = [ '人员新增录入', '居保状态匹配', '审批管理', '特殊业务处理', '月补贴清单', '回盘信息确认', '监控告警', '系统管理', ] function pad2(n: number) { return String(n).padStart(2, '0') } /** 生成近期时间:基准 2026-08-04 往前推 */ function mockDateTime(daysAgo: number, hour = 9, minute = 0) { const d = new Date(2026, 7, 4) d.setDate(d.getDate() - daysAgo) return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(hour)}:${pad2(minute)}` } function mockDate(daysAgo: number) { return mockDateTime(daysAgo).slice(0, 10) } function pickName(i: number) { const sn = SURNAMES[i % SURNAMES.length] const g1 = GIVEN[i % GIVEN.length] const g2 = GIVEN[(i * 3) % GIVEN.length] return `${sn}${g1}${g2}` } function pickTown(i: number) { return TOWN_META[i % TOWN_META.length] } function idCardOf(i: number) { return `310117195${pad2((i % 12) + 1)}${pad2((i % 28) + 1)}${String(1000 + i).slice(-4)}` } function bankCardOf(i: number) { return `6222021001${String(100000000 + i).slice(-9)}` } /** 社会保险登记码演示编码 */ function socialInsuranceCodeOf(i: number) { return `SH310117${String(100000 + i).slice(-6)}` } /** 扩充人员:优先补足同建村近半年各批次,便于村合作社工作台柱状图演示 */ ;(function expandPeople() { const startId = Math.max(0, ...mockPeople.map((p) => Number(p.id) || 0)) + 1 const extra: Person[] = [] /** 近半年批次:202603–202608 */ const halfYearBatches = ['202603', '202604', '202605', '202606', '202607', '202608'] let nextId = startId for (let b = 0; b < halfYearBatches.length; b++) { const batchNo = halfYearBatches[b] const count = 4 + (b % 4) // 每批次约 4–7 人 for (let j = 0; j < count; j++) { const id = nextId++ const month = `${batchNo.slice(0, 4)}-${batchNo.slice(4)}` extra.push({ id, batchNo, name: pickName(id + b * 11), idCard: idCardOf(id), town: '叶榭镇', village: '同建村', bankCard: bankCardOf(id), bankName: '工商银行叶榭支行', socialInsuranceCode: socialInsuranceCodeOf(id), standard: 1280, startMonth: month, status: PERSON_STATUSES[j % PERSON_STATUSES.length], source: SOURCES[j % SOURCES.length], insuranceStatus: INSURANCE_STATUSES[j % INSURANCE_STATUSES.length], remark: j === 0 ? `${month}批次演示数据` : undefined, }) } } for (let i = 0; i < 40; i++) { const id = nextId++ const meta = pickTown(i + 3) const village = meta.villages[i % meta.villages.length] extra.push({ id, batchNo: i % 5 === 0 ? '202606' : i % 3 === 0 ? '202607' : '202608', name: pickName(id + 17), idCard: idCardOf(id + 50), town: meta.town, village, bankCard: bankCardOf(id + 50), bankName: `农业银行${meta.town.replace('镇', '')}支行`, socialInsuranceCode: socialInsuranceCodeOf(id + 50), standard: meta.standard, startMonth: i % 5 === 0 ? '2026-06' : i % 3 === 0 ? '2026-07' : '2026-08', status: PERSON_STATUSES[i % PERSON_STATUSES.length], source: SOURCES[i % SOURCES.length], insuranceStatus: INSURANCE_STATUSES[i % INSURANCE_STATUSES.length], }) } mockPeople.push(...extra) })() /** 补足各镇下属村本批次人员,便于镇经发中心「各村发放金额/人数」柱状图演示 */ ;(function expandTownBatchPeople() { const batchNo = '202608' const month = '2026-08' let nextId = Math.max(0, ...mockPeople.map((p) => Number(p.id) || 0)) + 1 const extra: Person[] = [] for (const meta of TOWN_META) { meta.villages.forEach((village, vIdx) => { const existing = mockPeople.filter( (p) => p.town === meta.town && p.village === village && p.batchNo === batchNo, ).length /** 每村至少 5~9 人,保证柱状图有差异 */ const target = 5 + ((vIdx + meta.code.charCodeAt(0)) % 5) for (let j = existing; j < target; j++) { const id = nextId++ extra.push({ id, batchNo, name: pickName(id + vIdx * 13), idCard: idCardOf(id + 200), town: meta.town, village, bankCard: bankCardOf(id + 200), bankName: `农业银行${meta.town.replace('镇', '')}支行`, socialInsuranceCode: socialInsuranceCodeOf(id + 200), standard: meta.standard, startMonth: month, status: PERSON_STATUSES[j % PERSON_STATUSES.length], source: SOURCES[j % SOURCES.length], insuranceStatus: INSURANCE_STATUSES[j % INSURANCE_STATUSES.length], remark: `${meta.town}${village}本批次演示数据`, }) } }) } mockPeople.push(...extra) })() /** 为人员补齐退养资格类型(镇经发初审核验演示) */ ;(function fillRetireQualifyTypes() { mockPeople.forEach((p, idx) => { if (p.retireQualifyType) return // 约 1/5 为「无土地有资格」,其余为「有土地退养」 p.retireQualifyType = idx % 5 === 0 ? '无土地有资格' : '有土地退养' if (p.retireQualifyType === '无土地有资格' && !p.remark) { p.remark = '无承包地,按政策认定具备退养补助资格' } }) // 保证本镇本批次有一定数量待初审人员,便于工作台清单演示 const townBatchPending = mockPeople.filter( (p) => p.town === '叶榭镇' && p.batchNo === '202608', ) townBatchPending.forEach((p, idx) => { if (idx % 4 === 0) { p.status = idx % 8 === 0 ? '待人工确认' : '草稿' } }) })() /** 为种子人员补齐社会保险登记码 */ ;(function fillSocialInsuranceCodes() { mockPeople.forEach((p, idx) => { if (!p.socialInsuranceCode) { p.socialInsuranceCode = socialInsuranceCodeOf(Number(p.id) || idx + 1) } }) })() ;(function expandVillageBatches() { let seq = mockVillageBatches.length for (let i = 0; i < 24; i++) { const meta = pickTown(i) const village = meta.villages[i % meta.villages.length] const month = i % 4 === 0 ? '2026-07' : '2026-08' const batchNo = month.replace('-', '') seq += 1 mockVillageBatches.push({ id: `VB${batchNo}${meta.code}${pad2(seq)}`, batchNo, month, town: meta.town, village, peopleCount: 5 + (i % 12), totalAmount: (5 + (i % 12)) * meta.standard, stage: STAGES[i % STAGES.length], submitter: '村合作社经办', submittedAt: mockDateTime(i + 1, 9 + (i % 6), 10 + (i % 40)), }) } })() /** 补足本批次各镇下属村批次记录,与人员扩充对齐 */ ;(function fillTownCurrentBatchRecords() { const batchNo = '202608' const month = '2026-08' let seq = mockVillageBatches.length for (const meta of TOWN_META) { for (const village of meta.villages) { const exists = mockVillageBatches.some( (b) => b.town === meta.town && b.village === village && b.batchNo === batchNo, ) if (exists) continue const peopleCount = mockPeople.filter( (p) => p.town === meta.town && p.village === village && p.batchNo === batchNo, ).length seq += 1 mockVillageBatches.push({ id: `VB${batchNo}${meta.code}${pad2(seq)}`, batchNo, month, town: meta.town, village, peopleCount: peopleCount || 5, totalAmount: (peopleCount || 5) * meta.standard, stage: '镇经发中心初审', submitter: '村合作社经办', submittedAt: '2026-08-03 10:00', }) } } })() ;(function expandApprovals() { for (let i = 0; i < 22; i++) { const meta = pickTown(i) const village = meta.villages[i % meta.villages.length] const node = APPROVAL_NODES[i % APPROVAL_NODES.length] const batchNo = i % 3 === 0 ? '202607' : '202608' mockApprovals.push({ id: `AP${batchNo}${pad2(i + 10)}`, bizType: i % 4 === 0 ? '丧葬补贴' : i % 3 === 0 ? '月补贴清单确认' : '月度批次人员申请', batchNo, town: meta.town, village, title: `${village}${batchNo.slice(0, 4)}年${Number(batchNo.slice(4))}月批次申请-${i + 1}`, applicant: i % 2 === 0 ? `${village}合作社` : `${meta.town}社区事务受理服务中心`, node, status: APPROVAL_STATUSES[i % APPROVAL_STATUSES.length], opinion: APPROVAL_STATUSES[i % APPROVAL_STATUSES.length] === '已通过' ? '同意' : undefined, createdAt: mockDateTime(i, 10, 15 + (i % 30)), }) } })() /** 补足本镇本批次村合作社已提交、待镇经发初审的名单 */ ;(function fillTownPendingApprovals() { const batchNo = '202608' const monthLabel = '2026年8月' for (const meta of TOWN_META) { for (const village of meta.villages) { const exists = mockApprovals.some( (a) => a.town === meta.town && a.village === village && a.batchNo === batchNo && a.node === '镇经发中心初审' && a.status === '待审批' && a.bizType.includes('月度批次'), ) if (exists) continue // 仅当该村本批次处于镇经发初审时补一条待审批 const batch = mockVillageBatches.find( (b) => b.town === meta.town && b.village === village && b.batchNo === batchNo && b.stage.includes('镇经发'), ) if (!batch) continue mockApprovals.unshift({ id: `AP${batchNo}${meta.code}${village}`, bizType: '月度批次人员申请', batchNo, town: meta.town, village, title: `${village}${monthLabel}批次人员申请`, applicant: `${village}合作社`, node: '镇经发中心初审', status: '待审批', createdAt: batch.submittedAt || '2026-08-03 10:00', }) } } })() ;(function expandSpecials() { for (let i = 0; i < 30; i++) { const meta = pickTown(i) const village = meta.villages[i % meta.villages.length] const type = SPECIAL_TYPES[i % SPECIAL_TYPES.length] const batchNo = i % 4 === 0 ? '202607' : '202608' mockSpecials.push({ id: `SP${batchNo}${pad2(i + 10)}`, batchNo, town: meta.town, village, type, personName: pickName(i + 80), reason: type === '丧葬补贴' ? '未纳入居保体系丧葬补贴' : type.includes('调标') ? '2026年度调标差额' : `${type}业务申请`, amount: type === '丧葬补贴' ? 6000 : type.includes('调标') ? 60 + (i % 5) * 10 : 0, status: SPECIAL_STATUSES[i % SPECIAL_STATUSES.length], heirName: type === '丧葬补贴' ? pickName(i + 200) : undefined, createdAt: mockDateTime(i, 11, i % 50), }) } })() ;(function expandDeaths() { for (let i = 0; i < 18; i++) { const meta = pickTown(i) const village = meta.villages[i % meta.villages.length] mockDeaths.push({ id: `DT202608${pad2(i + 10)}`, personName: pickName(i + 120), idCard: idCardOf(i + 200), town: meta.town, village, deathDate: mockDate(10 + i), source: i % 2 === 0 ? '镇经发中心手工录入' : '民政比对导入', status: DEATH_STATUSES[i % DEATH_STATUSES.length], createdAt: mockDateTime(i, 14, 20), }) } })() ;(function expandMonthly() { const months = ['2026-06', '2026-07', '2026-08'] let idx = mockMonthlyBatches.length for (const month of months) { for (const meta of TOWN_META) { if (mockMonthlyBatches.some((m) => m.month === month && m.town === meta.town)) continue idx += 1 const peopleCount = 80 + (idx % 90) const normalAmount = peopleCount * meta.standard const extraAmount = idx % 3 === 0 ? 6000 : 0 const deductAmount = idx % 4 === 0 ? meta.standard : 0 mockMonthlyBatches.push({ id: `MB${month.replace('-', '')}${pad2(idx)}`, batchNo: month.replace('-', ''), month, town: meta.town, peopleCount, normalAmount, extraAmount, deductAmount, totalAmount: normalAmount + extraAmount - deductAmount, status: MONTHLY_STATUSES[idx % MONTHLY_STATUSES.length], }) } } })() ;(function expandDisks() { const months = ['2026-06', '2026-07', '2026-08'] let idx = 0 for (const month of months) { for (const meta of TOWN_META) { const batchNo = month.replace('-', '') if (mockDisks.some((d) => d.batchNo === batchNo && d.town === meta.town)) continue idx += 1 const peopleCount = 50 + (idx % 100) const status = DISK_STATUSES[idx % DISK_STATUSES.length] const returnStatus = status === '已回盘' ? (idx % 2 === 0 ? '成功' : '部分失败') : '待确认' mockDisks.push({ id: `DP${batchNo}${meta.code}`, batchNo, town: meta.town, fileName: `${batchNo}-${meta.town}月补贴清单.xlsx`, peopleCount, amount: peopleCount * meta.standard, status, returnStatus, failCount: returnStatus === '部分失败' ? 1 + (idx % 3) : 0, returnFileName: status === '已回盘' ? `${batchNo}-${meta.town}回盘文件.xlsx` : undefined, }) } } /** 为「部分失败」批次补演示失败人员 */ let failIdx = 0 for (const disk of mockDisks) { if (disk.returnStatus !== '部分失败' || !disk.failCount) continue for (let i = 0; i < disk.failCount; i++) { failIdx += 1 const village = TOWN_META.find((t) => t.town === disk.town)?.villages[i % 3] || '同建村' mockDiskFailPeople.push({ id: `DF${disk.id}${i}`, diskId: disk.id, batchNo: disk.batchNo, town: disk.town, village, personName: pickName(failIdx + 80), idCard: `31011719${50 + (failIdx % 20)}${pad2(failIdx)}01${pad2(failIdx)}`, bankCard: `6222021001${String(100000000 + failIdx).slice(-9)}`, failReason: ['001社会保险登记码不存在', '002清户', '003冻结', '账号不一致'][i % 4], amount: TOWN_META.find((t) => t.town === disk.town)?.standard ?? 1280, payStatus: '待确认', }) } } })() ;(function expandAlerts() { /** 优先补足同建村告警,便于村合作社与镇社区事务中心联动演示 */ for (let i = 0; i < 10; i++) { const type = ALERT_TYPES[i % ALERT_TYPES.length] const status = ALERT_STATUSES[i % ALERT_STATUSES.length] mockAlerts.push({ id: `AL202608TJ${pad2(i + 1)}`, batchNo: i % 2 === 0 ? '202608' : '202607', personName: pickName(i + 40), town: '叶榭镇', village: '同建村', type, level: ALERT_LEVELS[i % ALERT_LEVELS.length], content: type === '民政死亡比对' ? '民政死亡数据比对命中,请业务部门完成死亡状态更新并停止发放' : type === '居保匹配失败' ? '居保系统姓名与档案不一致,待村社核实、镇社区事务中心复核' : type === '银行卡异常' ? '银行卡号校验未通过,请核实开户信息' : '回盘确认提示账户异常,需下月处理', status, result: status === '已关闭' ? '已核实并处理(村/镇状态已同步)' : undefined, createdAt: mockDateTime(i, 9 + (i % 6), 5 + (i % 50)), }) } for (let i = 0; i < 26; i++) { const meta = pickTown(i) const village = meta.villages[i % meta.villages.length] const type = ALERT_TYPES[i % ALERT_TYPES.length] const status = ALERT_STATUSES[i % ALERT_STATUSES.length] mockAlerts.push({ id: `AL202608${pad2(i + 20)}`, batchNo: i % 3 === 0 ? '202607' : '202608', personName: pickName(i + 150), town: meta.town, village, type, level: ALERT_LEVELS[i % ALERT_LEVELS.length], content: type === '民政死亡比对' ? '民政死亡数据比对命中,请业务部门完成死亡状态更新并停止发放' : type === '居保匹配失败' ? '居保系统姓名与档案不一致,待人工确认' : type === '银行卡异常' ? '银行卡号校验未通过,请核实开户信息' : '回盘确认提示账户异常,需下月处理', status, result: status === '已关闭' ? '已核实并处理' : undefined, createdAt: mockDateTime(i, 10 + (i % 8), 12 + (i % 40)), }) } })() ;(function expandAccounts() { const orgs: Record = { village: '村合作社', town: '经济发展中心', social: '社区事务受理服务中心', leader: '社区事务受理服务中心', agri: '松江区农业农村委员会', hr: '松江区人力资源和社会保障局', admin: '区数据局运维账号', } for (let i = 0; i < 16; i++) { const roleKey = ROLE_KEYS[i % ROLE_KEYS.length] const meta = pickTown(i) mockAccounts.push({ id: mockAccounts.length + 1, username: `${roleKey}_${meta.code.toLowerCase()}_${pad2(i + 1)}`, name: `${orgs[roleKey]}经办${i + 1}`, roleKey, org: roleKey === 'agri' || roleKey === 'hr' || roleKey === 'admin' ? orgs[roleKey] : `${meta.town}${orgs[roleKey]}`, status: i % 7 === 0 ? '停用' : '启用', lastLogin: mockDateTime(i % 10, 8 + (i % 6), 5 + (i % 50)), }) } })() ;(function expandLogs() { const users = [ { user: '系统管理员', role: '系统管理员' }, { user: '业务经办', role: '镇社区事务受理服务中心' }, { user: '镇经发审核', role: '镇经发中心' }, { user: '村合作社经办', role: '村合作社' }, { user: '分管领导', role: '分管领导' }, ] for (let i = 0; i < 32; i++) { const u = users[i % users.length] const module = LOG_MODULES[i % LOG_MODULES.length] mockLogs.push({ id: mockLogs.length + 1, time: mockDateTime(i % 14, 8 + (i % 10), i % 60), user: u.user, role: u.role, module, action: `${module}操作演示-${i + 1}`, result: i % 9 === 0 ? '失败' : '成功', }) } })() /** 当前时间字符串(演示用) */ export function nowStr() { const d = new Date() const p = (n: number) => String(n).padStart(2, '0') return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}` }