// @ts-nocheck import { addNumbers, roundTo, toDecimal } from '@/lib/decimal' import { formatThousands } from '@/lib/numberFormat' import ExcelJS from "ExcelJS"; // 统一数字千分位格式化,默认保留 2 位小数。 const numberFormatter = (value: unknown, fractionDigits = 2) => formatThousands(value, fractionDigits) // 将任意输入安全转为有限数字;无效值统一按 0 处理。 const toFiniteNumber = (value: unknown) => { const num = Number(value) return Number.isFinite(num) ? num : 0 } export const industryTypeList = [ { id: '0', name: '公路工程', type: 'isRoad' }, { id: '1', name: '铁路工程', type: 'isRailway' }, { id: '2', name: '水运工程', type: 'isWaterway' } ] as const export const majorList = { 0: { code: 'E1', name: '交通运输工程通用专业', hideInIndustrySelector: true, maxCoe: null, minCoe: null, defCoe: null, desc: '', isRoad: true, isRailway: true, isWaterway: true, order: 1, hasCost: false, hasArea: false }, 1: { code: 'E1-1', name: '征地(用海)补偿', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于交通建设项目征地(用海)补偿的施工图预算、招标工程量清单及清单预算(或最高投标限价)、清理概算(仅限铁路工程)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: true, isWaterway: true, order: 2, hasCost: true, hasArea: true }, 2: { code: 'E1-2', name: '拆迁补偿', maxCoe: null, minCoe: null, defCoe: 2.5, desc: '适用于交通建设项目拆迁补偿的施工图预算、招标工程量清单及清单预算(或最高投标限价)、清理概算(仅限铁路工程)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: true, isWaterway: true, order: 3, hasCost: true, hasArea: true }, 3: { code: 'E1-3', name: '迁改工程', maxCoe: null, minCoe: null, defCoe: 2, desc: '适用于交通建设项目迁改工程的施工图预算、招标工程量清单及清单预算(或最高投标限价)、清理概算(仅限铁路工程)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: true, isWaterway: true, order: 4, hasCost: true, hasArea: false }, 4: { code: 'E1-4', name: '工程建设其他费', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于交通建设项目的工程建设其他费的施工图预算、招标工程量清单及清单预算(或最高投标限价)、清理概算(仅限铁路工程)和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: true, isWaterway: true, order: 5, hasCost: true, hasArea: false }, 5: { code: 'E1-5', name: '预备费', maxCoe: null, minCoe: null, defCoe: 1, desc: '', isRoad: true, isRailway: true, isWaterway: true, order: 6, hasCost: true, hasArea: false }, 6: { code: 'E1-6', name: '建设期贷款利息', maxCoe: null, minCoe: null, defCoe: 1, desc: '', isRoad: true, isRailway: true, isWaterway: true, order: 7, hasCost: true, hasArea: false }, 7: { code: 'E2', name: '公路工程专业', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于公路工程的全过程造价咨询、分阶段造价咨询、投资估算、初步设计概算、竣工决算和调整估算、调整概算(含征地拆迁和工程建设其他费)', isRoad: true, isRailway: false, isWaterway: false, order: 8, hasCost: false, hasArea: false, industryId: '0' }, 8: { code: 'E2-1', name: '临时工程', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于临时工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 9, hasCost: true, hasArea: false }, 9: { code: 'E2-2', name: '路基工程', maxCoe: null, minCoe: null, defCoe: 1.2, desc: '适用于路基工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 10, hasCost: true, hasArea: false }, 10: { code: 'E2-3', name: '路面工程', maxCoe: null, minCoe: null, defCoe: 0.8, desc: '适用于路面工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 11, hasCost: true, hasArea: false }, 11: { code: 'E2-4', name: '桥涵工程', maxCoe: null, minCoe: null, defCoe: 0.9, desc: '适用于桥梁涵洞工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 12, hasCost: true, hasArea: false }, 12: { code: 'E2-5', name: '隧道工程', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于隧道工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 13, hasCost: true, hasArea: false }, 13: { code: 'E2-6', name: '交叉工程', maxCoe: null, minCoe: null, defCoe: 1.1, desc: '适用于交叉工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 14, hasCost: true, hasArea: false }, 14: { code: 'E2-7', name: '机电工程', maxCoe: null, minCoe: null, defCoe: 1.2, desc: '适用于机电工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 15, hasCost: true, hasArea: false }, 15: { code: 'E2-8', name: '交通安全设施工程', maxCoe: null, minCoe: null, defCoe: 1.2, desc: '适用于交通安全设施工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 16, hasCost: true, hasArea: false }, 16: { code: 'E2-9', name: '绿化及环境保护工程', maxCoe: null, minCoe: null, defCoe: 1.2, desc: '适用于绿化工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 17, hasCost: true, hasArea: false }, 17: { code: 'E2-10', name: '房建工程', maxCoe: null, minCoe: null, defCoe: 2.5, desc: '适用于房建工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: true, isRailway: false, isWaterway: false, order: 18, hasCost: true, hasArea: false }, 18: { code: 'E3', name: '铁路工程专业', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于铁路工程的投资估算、初步设计概算、清理概算、竣工决算和调整估算、调整概算(含征地拆迁和工程建设其他费)', isRoad: false, isRailway: true, isWaterway: false, order: 19, hasCost: false, hasArea: false, industryId: '1' }, 19: { code: 'E3-1', name: '大型临时设施和过渡工程', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于大型临时设施和过渡工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: true, isWaterway: false, order: 20, hasCost: true, hasArea: false }, 20: { code: 'E3-2', name: '路基工程', maxCoe: null, minCoe: null, defCoe: 1.2, desc: '适用于路基工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: true, isWaterway: false, order: 21, hasCost: true, hasArea: false }, 21: { code: 'E3-3', name: '桥涵工程', maxCoe: null, minCoe: null, defCoe: 0.9, desc: '适用于桥涵工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: true, isWaterway: false, order: 22, hasCost: true, hasArea: false }, 22: { code: 'E3-4', name: '隧道及明洞工程', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于隧道及明洞工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算、竣工决算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: true, isWaterway: false, order: 23, hasCost: true, hasArea: false }, 23: { code: 'E3-5', name: '轨道工程', maxCoe: null, minCoe: null, defCoe: 0.3, desc: '适用于轨道工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: true, isWaterway: false, order: 24, hasCost: true, hasArea: false }, 24: { code: 'E3-6', name: '通信、信号、信息及灾害监测工程', maxCoe: null, minCoe: null, defCoe: 2, desc: '适用于通信、信号、信息及防灾监测工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: true, isWaterway: false, order: 25, hasCost: true, hasArea: false }, 25: { code: 'E3-7', name: '电力及电力牵引供电工程', maxCoe: null, minCoe: null, defCoe: 1.5, desc: '适用于电力及电力牵引供电工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: true, isWaterway: false, order: 26, hasCost: true, hasArea: false }, 26: { code: 'E3-8', name: '房建工程(房屋建筑及附属工程)', maxCoe: null, minCoe: null, defCoe: 2.5, desc: '适用于房屋建筑及附属工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: true, isWaterway: false, order: 27, hasCost: true, hasArea: false }, 27: { code: 'E3-9', name: '装饰装修工程', maxCoe: null, minCoe: null, defCoe: 2.7, desc: '适用于装饰装修工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: true, isWaterway: false, order: 28, hasCost: true, hasArea: false }, 28: { code: 'E4', name: '水运工程专业', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于水运工程的投资估算、初步设计概算、竣工决算和调整估算、调整概算(含征地拆迁和工程建设其他费)', isRoad: false, isRailway: false, isWaterway: true, order: 29, hasCost: false, hasArea: false, industryId: '2' }, 29: { code: 'E4-1', name: '临时工程', maxCoe: null, minCoe: null, defCoe: 1.1, desc: '适用于临时工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算、竣工决算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: false, isWaterway: true, order: 30, hasCost: true, hasArea: false }, 30: { code: 'E4-2', name: '土建工程', maxCoe: null, minCoe: null, defCoe: 1, desc: '适用于土建工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算、竣工决算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: false, isWaterway: true, order: 31, hasCost: true, hasArea: false }, 31: { code: 'E4-3', name: '机电与金属结构工程', maxCoe: null, minCoe: null, defCoe: 1.5, desc: '适用于机电与金属结构专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: false, isWaterway: true, order: 32, hasCost: true, hasArea: false }, 32: { code: 'E4-4', name: '设备工程', maxCoe: null, minCoe: null, defCoe: 1.5, desc: '适用于设备工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: false, isWaterway: true, order: 33, hasCost: true, hasArea: false }, 33: { code: 'E4-5', name: '附属房建工程(房屋建筑及附属工程)', maxCoe: null, minCoe: null, defCoe: 2.5, desc: '适用于房屋建筑与水运附属工程专业的施工图预算、招标工程量清单及清单预算(或最高投标限价)、合同(工程)结算和造价鉴定、计算工程量、工程变更费用咨询、工程成本测(核)算', isRoad: false, isRailway: false, isWaterway: true, order: 34, hasCost: true, hasArea: false }, }; export const serviceList = { 0: { code: 'D1', name: '全过程造价咨询', maxCoe: null, minCoe: null, defCoe: 1, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 1, scale: true, onlyCostScale: true, amount: false, workDay: true }, 1: { code: 'D2', name: '分阶段造价咨询', maxCoe: null, minCoe: null, defCoe: null, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 2, scale: null, onlyCostScale: null, amount: null, workDay: null }, 2: { code: 'D2-1', name: '前期阶段造价咨询', maxCoe: null, minCoe: null, defCoe: 0.5, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 3, scale: true, onlyCostScale: true, amount: false, workDay: true }, 3: { code: 'D2-2-1', name: '实施阶段造价咨询(公路、水运)', maxCoe: null, minCoe: null, defCoe: 0.55, desc: '本系数适用于公路和水运工程。', isRoad: true, isRailway: false, isWaterway: true, mutiple: false, order: 4, scale: true, onlyCostScale: true, amount: false, workDay: true }, 4: { code: 'D2-2-2', name: '实施阶段造价咨询(铁路)', maxCoe: null, minCoe: null, defCoe: 0.6, desc: '本系数适用于铁路工程。', isRoad: false, isRailway: true, isWaterway: false, mutiple: false, order: 5, scale: true, onlyCostScale: true, amount: false, workDay: true }, 5: { code: 'D3', name: '基本造价咨询', maxCoe: null, minCoe: null, defCoe: null, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 6, scale: null, onlyCostScale: null, amount: null, workDay: null }, 6: { code: 'D3-1', name: '投资估算', maxCoe: null, minCoe: null, defCoe: 0.1, desc: '委托同一咨询人同时负责D3-1和D3-2时,D3-1和D3-2的合计调整系数为0.25。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 7, scale: true, onlyCostScale: true, amount: false, workDay: true }, 7: { code: 'D3-2', name: '设计概算', maxCoe: null, minCoe: null, defCoe: 0.2, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 8, scale: true, onlyCostScale: true, amount: false, workDay: true }, 8: { code: 'D3-3', name: '施工图预算', maxCoe: null, minCoe: null, defCoe: 0.25, desc: '委托同一咨询人同时负责D3-3和D3-4时,D3-3和D3-4的合计调整系数为0.3。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 9, scale: true, onlyCostScale: false, amount: false, workDay: true }, 9: { code: 'D3-4', name: '招标工程量清单及清单预算(或最高投标限价)', maxCoe: null, minCoe: null, defCoe: 0.15, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 10, scale: true, onlyCostScale: false, amount: false, workDay: true }, 10: { code: 'D3-5', name: '清理概算(仅限铁路)', maxCoe: null, minCoe: null, defCoe: 0.2, desc: '本系数适用于铁路工程。', isRoad: false, isRailway: true, isWaterway: false, mutiple: false, order: 11, scale: true, onlyCostScale: true, amount: false, workDay: true }, 11: { code: 'D3-6-1', name: '合同(工程)结算', maxCoe: null, minCoe: null, defCoe: 0.3, desc: '本系数适用于公路和水运工程。', isRoad: true, isRailway: false, isWaterway: true, mutiple: false, order: 12, scale: true, onlyCostScale: false, amount: false, workDay: true }, 12: { code: 'D3-6-2', name: '合同(工程)结算', maxCoe: null, minCoe: null, defCoe: 0.2, desc: '本系数适用于铁路工程。', isRoad: false, isRailway: true, isWaterway: false, mutiple: false, order: 13, scale: true, onlyCostScale: false, amount: false, workDay: true }, 13: { code: 'D3-7', name: '竣工决算', maxCoe: null, minCoe: null, defCoe: 0.1, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 14, scale: true, onlyCostScale: true, amount: false, workDay: true }, 14: { code: 'D4', name: '专项造价咨询', maxCoe: null, minCoe: null, defCoe: null, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 15, scale: null, onlyCostScale: null, amount: null, workDay: null }, 15: { code: 'D4-1', name: '工程造价顾问', maxCoe: null, minCoe: null, defCoe: 1, desc: '本表系数适用于采用工作量计价法基准预算的调整系数。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 16, scale: false, onlyCostScale: null, amount: true, workDay: true }, 16: { code: 'D4-2', name: '造价政策制(修)订', maxCoe: null, minCoe: null, defCoe: 1, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 17, scale: false, onlyCostScale: null, amount: true, workDay: true }, 17: { code: 'D4-3', name: '造价科学与技术研究', maxCoe: null, minCoe: null, defCoe: 1, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 18, scale: false, onlyCostScale: null, amount: true, workDay: true }, 18: { code: 'D4-4', name: '定额测定', maxCoe: null, minCoe: null, defCoe: 1, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 19, scale: false, onlyCostScale: null, amount: true, workDay: true }, 19: { code: 'D4-5', name: '造价信息咨询', maxCoe: null, minCoe: null, defCoe: 1, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 20, scale: false, onlyCostScale: null, amount: true, workDay: true }, 20: { code: 'D4-6', name: '造价鉴定', maxCoe: null, minCoe: null, defCoe: 0.5, desc: '本表系数适用于采用规模计价法基准预算的调整系数。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 21, scale: true, onlyCostScale: false, amount: false, workDay: true }, 21: { code: 'D4-7', name: '工程成本测算', maxCoe: null, minCoe: null, defCoe: 0.1, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 22, scale: true, onlyCostScale: false, amount: false, workDay: true }, 22: { code: 'D4-8', name: '工程成本核算', maxCoe: null, minCoe: null, defCoe: 0.1, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 23, scale: true, onlyCostScale: false, amount: false, workDay: true }, 23: { code: 'D4-9', name: '计算工程量', maxCoe: null, minCoe: null, defCoe: 0.2, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 24, scale: true, onlyCostScale: false, amount: false, workDay: true }, 24: { code: 'D4-10', name: '工程变更费用咨询', maxCoe: null, minCoe: null, defCoe: 0.5, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 25, scale: true, onlyCostScale: false, amount: false, workDay: true }, 25: { code: 'D4-11', name: '调整估算', maxCoe: 0.2, minCoe: 0.1, defCoe: 0.15, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 26, scale: true, onlyCostScale: true, amount: false, workDay: true }, 26: { code: 'D4-12', name: '调整概算', maxCoe: 0.3, minCoe: 0.15, defCoe: 0.225, desc: '本表系数适用于采用规模计价法基准预算的系数;依据其调整时期所在建设阶段和基础资料的不同,其系数取值不同。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 27, scale: true, onlyCostScale: true, amount: false, workDay: true }, 27: { code: 'D4-13', name: '造价检查', maxCoe: null, minCoe: null, defCoe: null, desc: '可按照服务工日数量×服务工日人工单价×综合预算系数;也可按照服务工日数量×服务工日综合预算单价。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 28, scale: false, onlyCostScale: null, amount: false, workDay: true }, 28: { code: 'D4-14', name: '其他专项咨询', maxCoe: null, minCoe: null, defCoe: null, desc: '可参照相同或相似服务的系数。', isRoad: true, isRailway: true, isWaterway: true, mutiple: false, order: 29, scale: false, onlyCostScale: null, amount: false, workDay: true }, 29: { code: 'D4-15-1', name: '造价数据测试验证(估算)', maxCoe: null, minCoe: null, defCoe: 0.04, desc: '委托同一咨询人同时负责D3-1和D3-2时,D3-1和D3-2的合计调整系数为0.25。', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 30, scale: true, onlyCostScale: true, amount: false, workDay: true }, 30: { code: 'D4-15-2', name: '造价数据测试验证(概算)', maxCoe: null, minCoe: null, defCoe: 0.08, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 31, scale: true, onlyCostScale: true, amount: false, workDay: true }, 31: { code: 'D4-15-3', name: '造价数据测试验证(施工图预算)', maxCoe: null, minCoe: null, defCoe: 0.1, desc: '委托同一咨询人同时负责D3-3和D3-4时,D3-3和D3-4的合计调整系数为0.3。', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 32, scale: true, onlyCostScale: false, amount: false, workDay: true }, 32: { code: 'D4-15-4', name: '造价数据测试验证(招标工程量清单及清单预算(或最高投标限价))', maxCoe: null, minCoe: null, defCoe: 0.06, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 33, scale: true, onlyCostScale: false, amount: false, workDay: true }, 33: { code: 'D4-15-5', name: '造价数据测试验证(清理概算(仅限铁路))', maxCoe: null, minCoe: null, defCoe: 0.08, desc: '本系数适用于铁路工程。', isRoad: false, isRailway: true, isWaterway: false, mutiple: true, order: 34, scale: true, onlyCostScale: true, amount: false, workDay: true }, 34: { code: 'D4-15-6', name: '造价数据测试验证(合同(工程)结算)', maxCoe: null, minCoe: null, defCoe: 0.12, desc: '本系数适用于公路和水运工程。', isRoad: true, isRailway: false, isWaterway: true, mutiple: true, order: 35, scale: true, onlyCostScale: false, amount: false, workDay: true }, 35: { code: 'D4-15-7', name: '造价数据测试验证(合同(工程)结算)', maxCoe: null, minCoe: null, defCoe: 0.08, desc: '本系数适用于铁路工程。', isRoad: false, isRailway: true, isWaterway: false, mutiple: true, order: 36, scale: true, onlyCostScale: false, amount: false, workDay: true }, 36: { code: 'D4-15-8', name: '造价数据测试验证(竣工决算)', maxCoe: null, minCoe: null, defCoe: 0.04, desc: '', isRoad: true, isRailway: true, isWaterway: true, mutiple: true, order: 37, scale: true, onlyCostScale: true, amount: false, workDay: true }, }; export const taskList = { 0: { serviceID: 15, code: 'C4-1', name: '工程造价日常顾问', basicParam: '服务月份数', required: true, unit: '万元/月', conversion: 10000, maxPrice: 0.5, minPrice: 0.3, defPrice: 0.4, desc: '' }, 1: { serviceID: 15, code: 'C4-2', name: '工程造价专项顾问', basicParam: '服务项目的造价金额', required: true, unit: '%', conversion: 0.01, maxPrice: null, minPrice: null, defPrice: 0.01, desc: '适用于涉及造价费用类的顾问' }, 2: { serviceID: 16, code: 'C5-1', name: '组织与调研工作', basicParam: '调研次数', required: true, unit: '万元/次', conversion: 10000, maxPrice: 2, minPrice: 1, defPrice: 1.5, desc: '' }, 3: { serviceID: 16, code: 'C5-2-1', name: '文件编写工作', basicParam: '文件份数', required: true, unit: '万元/份', conversion: 10000, maxPrice: 5, minPrice: 3, defPrice: 4, desc: '主编' }, 4: { serviceID: 16, code: 'C5-2-2', name: '文件编写工作', basicParam: '文件份数', required: true, unit: '万元/份', conversion: 10000, maxPrice: 3, minPrice: 1, defPrice: 2, desc: '参编' }, 5: { serviceID: 16, code: 'C5-3-1', name: '评审工作', basicParam: '评审次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 20, minPrice: 8, defPrice: 14, desc: '大型评审' }, 6: { serviceID: 16, code: 'C5-3-2', name: '评审工作', basicParam: '评审次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 10, minPrice: 5, defPrice: 7.5, desc: '中型评审' }, 7: { serviceID: 16, code: 'C5-3-3', name: '评审工作', basicParam: '评审次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 6, minPrice: 3, defPrice: 4.5, desc: '小型评审' }, 8: { serviceID: 17, code: 'C6-1', name: '组织与调研工作', basicParam: '调研次数', required: true, unit: '万元/次', conversion: 10000, maxPrice: 2, minPrice: 1, defPrice: 1.5, desc: '' }, 9: { serviceID: 17, code: 'C6-2-1', name: '研究及编写报告', basicParam: '文件份数', required: true, unit: '万元/份', conversion: 10000, maxPrice: 50, minPrice: 20, defPrice: 35, desc: '国家级' }, 10: { serviceID: 17, code: 'C6-2-2', name: '研究及编写报告', basicParam: '文件份数', required: true, unit: '万元/份', conversion: 10000, maxPrice: 20, minPrice: 10, defPrice: 15, desc: '省部级' }, 11: { serviceID: 17, code: 'C6-2-3', name: '研究及编写报告', basicParam: '文件份数', required: true, unit: '万元/份', conversion: 10000, maxPrice: 10, minPrice: 5, defPrice: 7.5, desc: '其他级' }, 12: { serviceID: 17, code: 'C6-3-1', name: '标准与技术性指导文件的编制', basicParam: '文件与标准的数量', required: true, unit: '万元/份', conversion: 10000, maxPrice: 80, minPrice: 50, defPrice: 65, desc: '复杂标准' }, 13: { serviceID: 17, code: 'C6-3-2', name: '标准与技术性指导文件的编制', basicParam: '文件与标准的数量', required: true, unit: '万元/份', conversion: 10000, maxPrice: 50, minPrice: 20, defPrice: 35, desc: '较复杂标准' }, 14: { serviceID: 17, code: 'C6-3-3', name: '标准与技术性指导文件的编制', basicParam: '文件与标准的数量', required: true, unit: '万元/份', conversion: 10000, maxPrice: 20, minPrice: 10, defPrice: 15, desc: '一般标准' }, 15: { serviceID: 17, code: 'C6-3-4', name: '标准与技术性指导文件的编制', basicParam: '文件与标准的数量', required: true, unit: '万元/份', conversion: 10000, maxPrice: 10, minPrice: 5, defPrice: 7.5, desc: '简单标准' }, 16: { serviceID: 17, code: 'C6-4-1', name: '评审与验收工作', basicParam: '评审与验收次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 20, minPrice: 8, defPrice: 14, desc: '大型评审' }, 17: { serviceID: 17, code: 'C6-4-2', name: '评审与验收工作', basicParam: '评审与验收次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 10, minPrice: 5, defPrice: 7.5, desc: '中型评审' }, 18: { serviceID: 17, code: 'C6-4-3', name: '评审与验收工作', basicParam: '评审与验收次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 6, minPrice: 3, defPrice: 4.5, desc: '小型评审' }, 19: { serviceID: 17, code: 'C6-5-1', name: '培训与宣贯工作', basicParam: '项目数量', required: false, unit: '万元/次', conversion: 10000, maxPrice: 3, minPrice: 1, defPrice: 2, desc: '培训与宣贯材料' }, 20: { serviceID: 17, code: 'C6-5-2', name: '培训与宣贯工作', basicParam: '培训与宣贯次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 1, minPrice: 0.5, defPrice: 0.75, desc: '组织培训与宣贯' }, 21: { serviceID: 18, code: 'C7-1', name: '组织与调研工作', basicParam: '调研次数', required: true, unit: '万元/次', conversion: 10000, maxPrice: 2, minPrice: 1, defPrice: 1.5, desc: '' }, 22: { serviceID: 18, code: 'C7-2', name: '编制大纲', basicParam: '项目数量', required: true, unit: '万元/个', conversion: 10000, maxPrice: 3, minPrice: 2, defPrice: 2.5, desc: '包括技术与定额子目研究' }, 23: { serviceID: 18, code: 'C7-3', name: '数据采集与测定', basicParam: '采集组数', required: true, unit: '万元/组', conversion: 10000, maxPrice: 0.8, minPrice: 0.2, defPrice: 0.5, desc: '现场采集方式时计' }, 24: { serviceID: 18, code: 'C7-4-1', name: '数据整理与分析', basicParam: '定额子目条数', required: true, unit: '万元/条', conversion: 10000, maxPrice: 0.3, minPrice: 0.1, defPrice: 0.2, desc: '简单定额' }, 25: { serviceID: 18, code: 'C7-4-2', name: '数据整理与分析', basicParam: '定额子目条数', required: true, unit: '万元/条', conversion: 10000, maxPrice: 3, minPrice: 0.3, defPrice: 1.65, desc: '复杂定额' }, 26: { serviceID: 18, code: 'C7-5', name: '编写定额测定报告', basicParam: '项目数量', required: true, unit: '万元/份', conversion: 10000, maxPrice: 5, minPrice: 2, defPrice: 3.5, desc: '' }, 27: { serviceID: 18, code: 'C7-6-1', name: '编制定额文本和释义', basicParam: '基本费用', required: true, unit: '万元/份', conversion: 10000, maxPrice: 1, minPrice: 0.5, defPrice: 0.75, desc: '20条定额子目内' }, 28: { serviceID: 18, code: 'C7-6-2', name: '编制定额文本和释义', basicParam: '定额子目条数', required: true, unit: '万元/条', conversion: 10000, maxPrice: 0.2, minPrice: 0.1, defPrice: 0.15, desc: '超过20条每增加1条' }, 29: { serviceID: 18, code: 'C7-7-1', name: '评审与验收工作', basicParam: '评审与验收次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 20, minPrice: 8, defPrice: 14, desc: '大型评审' }, 30: { serviceID: 18, code: 'C7-7-2', name: '评审与验收工作', basicParam: '评审与验收次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 10, minPrice: 5, defPrice: 7.5, desc: '中型评审' }, 31: { serviceID: 18, code: 'C7-7-3', name: '评审与验收工作', basicParam: '评审与验收次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 6, minPrice: 3, defPrice: 4.5, desc: '小型评审' }, 32: { serviceID: 18, code: 'C7-8-1', name: '培训与宣贯工作', basicParam: '项目数量', required: false, unit: '万元/次', conversion: 10000, maxPrice: 3, minPrice: 1, defPrice: 2, desc: '培训与宣贯材料' }, 33: { serviceID: 18, code: 'C7-8-2', name: '培训与宣贯工作', basicParam: '培训与宣贯次数', required: false, unit: '万元/次', conversion: 10000, maxPrice: 1, minPrice: 0.5, defPrice: 0.75, desc: '组织培训与宣贯' }, 34: { serviceID: 19, code: 'C8-1', name: 'Q≤10条', basicParam: '价格信息数量', required: true, unit: '元/条', conversion: 1, maxPrice: null, minPrice: null, defPrice: 500, desc: '' }, 35: { serviceID: 19, code: 'C8-2', name: '10条100条', basicParam: '价格信息数量', required: true, unit: '元/条', conversion: 1, maxPrice: null, minPrice: null, defPrice: 100, desc: '' }, }; export const expertList = { 0: { code: 'C9-1-1', name: '技术员及其他', maxPrice: 800, minPrice: 600, defPrice: 700, manageCoe: 2.3 }, 1: { code: 'C9-1-2', name: '助理工程师', maxPrice: 1000, minPrice: 800, defPrice: 900, manageCoe: 2.3 }, 2: { code: 'C9-1-3', name: '中级工程师或二级造价工程师', maxPrice: 1500, minPrice: 1000, defPrice: 1250, manageCoe: 2.2 }, 3: { code: 'C9-1-4', name: '高级工程师或一级造价工程师', maxPrice: 1800, minPrice: 1500, defPrice: 1650, manageCoe: 2.1 }, 4: { code: 'C9-1-5', name: '正高级工程师或资深专家', maxPrice: 2500, minPrice: 2000, defPrice: 2250, manageCoe: 2 }, 5: { code: 'C9-2-1', name: '二级造价工程师且具备中级工程师资格', maxPrice: 1500, minPrice: 1200, defPrice: 1350, manageCoe: 2.2 }, 6: { code: 'C9-3-1', name: '一级造价工程师且具备中级工程师资格', maxPrice: 1800, minPrice: 1500, defPrice: 1650, manageCoe: 2.1 }, 7: { code: 'C9-3-2', name: '一级造价工程师且具备高级工程师资格', maxPrice: 2000, minPrice: 1800, defPrice: 1900, manageCoe: 2.05 }, }; export const additionalWorkList = [ { id:'1', code: { richText: [ { font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'F' } ] }, name: '人员驻场服务及其他附加工作' }, {id:'2', code: { richText: [ { font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'X' } ] }, name: '咨询服务协调工作' } ] export const reserveList = [ { id:1, code: { richText: [ { font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'Y' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'B' } ] }, name: '预备费' } ] //工作内容词典 export const workList = { 0: { text: '完成投资估算文件及主要技术经济指标,完成与相关单位核对投资估算', serviceid: 6, order: 1, type: 0 }, 1: { text: '根据项目建议书或可行性研究报告的变化,动态调整投资估算和技术经济指标,并对比各环节投资估算变化,完成项目建议书阶段到可行性研究阶段的量、价对比及原因分析', serviceid: 6, order: 2, type: 0 }, 2: { text: '参加项目建议书或可行性研究的技术论证会议、评审会议和与确定投资估算相关的会议', serviceid: 6, order: 3, type: 0 }, 3: { text: '对比类似项目的技术经济指标,分析技术经济指标差异原因,提供造价差异分析报告', serviceid: 6, order: 4, type: 1 }, 4: { text: '根据造价差异分析结果,提出调整工程方案的咨询意见供委托人或关联单位决策', serviceid: 6, order: 5, type: 1 }, 5: { text: '依据投资估算和项目建设计划,编制项目资金计划,供委托人决策', serviceid: 6, order: 6, type: 1 }, 6: { text: '完成设计概算文件及主要技术经济指标,完成与相关单位核对设计概算', serviceid: 7, order: 7, type: 0 }, 7: { text: '依据各版初步设计动态调整设计概算及经济指标,并对比各版设计概算结果,完成可行性研究阶段到初步设计阶段的造价对比,提供造价差异分析报告', serviceid: 7, order: 8, type: 0 }, 8: { text: '参加初步设计的技术论证会议、评审会议和与设计概算相关的会议', serviceid: 7, order: 9, type: 0 }, 9: { text: '对比类似项目的技术经济指标,分析技术经济指标差异原因,提供造价差异分析报告', serviceid: 7, order: 10, type: 1 }, 10: { text: '根据造价差异分析结果,提出调整工程设计方案的咨询意见供委托人或关联单位决策', serviceid: 7, order: 11, type: 1 }, 11: { text: '依据设计概算和项目建设计划,编制或调整项目资金计划,供委托人决策', serviceid: 7, order: 12, type: 1 }, 12: { text: '完成施工图预算文件及主要技术经济指标,完成与相关单位核对施工图预算', serviceid: 8, order: 13, type: 0 }, 13: { text: '依据各版施工图动态调整施工图预算,并对比各版施工图预算结果,完成施工图预算与设计概算的造价对比(如有必要应完成与投资估算的造价对比),提供造价差异分析报告', serviceid: 8, order: 14, type: 0 }, 14: { text: '参加施工图设计的技术论证会议、评审会议和与施工图预算相关的会议', serviceid: 8, order: 15, type: 0 }, 15: { text: '对比类似项目的技术经济指标,分析技术经济指标差异原因,提供造价差异分析报告', serviceid: 8, order: 16, type: 1 }, 16: { text: '根据造价差异分析结果,提出调整工程设计方案的咨询意见供委托人或关联单位决策', serviceid: 8, order: 17, type: 1 }, 17: { text: '依据施工图预算和项目建设计划,编制或调整项目资金计划,供委托人决策', serviceid: 8, order: 18, type: 1 }, 18: { text: '分析专项设计方案的经济合理性,提出咨询意见供委托人决策', serviceid: 8, order: 19, type: 1 }, 19: { text: '分析施工组织设计和交通组织方案的经济合理性,提出咨询意见供委托人决策', serviceid: 8, order: 20, type: 1 }, 20: { text: '完成招标工程量清单及工程量清单预算,与相关单位核对工程量清单及工程量清单预算,并完成修改与调整', serviceid: 9, order: 21, type: 0 }, 21: { text: '对比工程量清单预算和施工图预算,提供造价差异分析报告', serviceid: 9, order: 22, type: 0 }, 22: { text: '审核计量与支付条款,对招标文件和合同文件涉及合同价款、工程变更费用咨询、结算费用等与造价相关条款提供咨询意见', serviceid: 9, order: 23, type: 0 }, 23: { text: '协助招标人完成招标答疑涉及的造价问题的解答与回复', serviceid: 9, order: 24, type: 0 }, 24: { text: '参加招投标阶段与造价确定相关的会议', serviceid: 9, order: 25, type: 0 }, 25: { text: '对比中标价与工程量清单预算,并分析差异原因,协助招标人完成不平衡报价调整', serviceid: 9, order: 26, type: 1 }, 26: { text: '参与合同谈判,协助招标人合同谈判过程中相关造价分析', serviceid: 9, order: 27, type: 1 }, 27: { text: '清理施工承包合同费用、征地拆迁费用、各项价差、其他类费用及费用依据、计算资料等', serviceid: 10, order: 28, type: 0 }, 28: { text: '清理征地拆迁费用,包括清理建设项目的用地、青苗补偿、房屋拆迁等费用,清理管线迁改、改路、改河、改沟以及构筑物迁改等费用,土地复垦费用及征拆各项规费的清理等', serviceid: 10, order: 29, type: 0 }, 29: { text: '清理材料价差,包括调整甲供材料设备价差、政策性文件规定的其他材料价差等', serviceid: 10, order: 30, type: 0 }, 30: { text: '清理政策性调整费用', serviceid: 10, order: 31, type: 0 }, 31: { text: '清理基本预备费和招标降造费,包括分析降造费的使用情况', serviceid: 10, order: 32, type: 0 }, 32: { text: '对未进行初步验收的剩余工程及投资情况进行清理', serviceid: 10, order: 33, type: 0 }, 33: { text: '梳理和清理批复的概算费用,分析费用变化的原因', serviceid: 10, order: 34, type: 0 }, 34: { text: '根据收集的资料和费用分析结果,编制清理概算文件,包括清理概算申请报告、相关批复文件、设计图纸、施工组织设计、合同、验工计价、设计变更和新增工程等支撑性资料、审计报告(如有)、第三方审价报告及其他相关依据', serviceid: 10, order: 35, type: 0 }, 35: { text: '招标图纸与实际施工图纸进行验核,核实工程数量,针对分析概算费用变化的原因', serviceid: 10, order: 36, type: 1 }, 36: { text: '参加与清理概算相关的会议,协助委托人完成清理概算文件预评审工作', serviceid: 10, order: 37, type: 0 }, 37: { text: '完成工程变更费用的测算,或提出性价比更优的替代方案、材料等意见', serviceid: 11, order: 38, type: 0 }, 38: { text: '完成新增预算单价或合同单价的计算与核定', serviceid: 11, order: 39, type: 0 }, 39: { text: '按施工图预算或合同清单方式完成工程变更费用的计算', serviceid: 11, order: 40, type: 0 }, 40: { text: '完成过程结算,包括建立过程结算多维度清单体系,如:项目清单、工程量清单、分项清单;划分过程结算清单单元;依据经确认的实际完工工程量完成计价计费,完成过程结算文件', serviceid: 11, order: 41, type: 0 }, 41: { text: '完成价格波动费用的计算或审核', serviceid: 11, order: 42, type: 0 }, 42: { text: '完成索赔与补偿费用的计算或审核', serviceid: 11, order: 43, type: 0 }, 43: { text: '完成结算文件及统计技术经济指标,与相关单位核对合同(工程)结算', serviceid: 11, order: 44, type: 0 }, 44: { text: '完成合同、变更和结算三环节费用的对比,分析合同费用各环节变化原因,提供对比报表和分析报告', serviceid: 11, order: 45, type: 0 }, 45: { text: '参加与过程结算、工程变更费用确定和合同(工程)结算相关的会议', serviceid: 11, order: 46, type: 0 }, 46: { text: '现场勘查与测量实际完工工程量', serviceid: 11, order: 47, type: 1 }, 47: { text: '复核竣工图纸数量与结算数量的差异,提交数量差异报告', serviceid: 11, order: 48, type: 1 }, 48: { text: '协助委托人对项目工程变更费用审批情况进行清理,编制交工验收造价文件', serviceid: 11, order: 49, type: 1 }, 49: { text: '协助委托人处理工期索赔、造价费用等费用的争议与纠纷、仲裁与诉讼', serviceid: 11, order: 50, type: 1 }, 50: { text: '编制项目竣工决算文件', serviceid: 13, order: 51, type: 0 }, 51: { text: '编制竣工决算备案送审文件', serviceid: 13, order: 52, type: 0 }, 52: { text: '参与竣工决算相关的会议', serviceid: 13, order: 53, type: 0 }, 53: { text: '根据审计意见和决算备案意见调整竣工决算文件', serviceid: 13, order: 54, type: 0 }, 54: { text: '协助委托人编制建设项目造价执行情况报告', serviceid: 13, order: 55, type: 1 }, 55: { text: '对比类似项目的技术经济指标,分析技术经济指标差异原因,提供造价差异分析报告', serviceid: 13, order: 56, type: 1 }, 56: { text: '根据造价差异分析结果,提出调整工程方案的咨询意见供委托人或关联单位决策', serviceid: 13, order: 57, type: 1 }, 57: { text: '协助委托人指导参建单位完成与决算相关辅助文件的编制', serviceid: 13, order: 58, type: 1 }, 58: { text: '协助委托人开展项目造价管理及投资效益后评估', serviceid: 13, order: 59, type: 1 }, 59: { text: '定期向委托人介绍并解读国家及地方最新发布的关于造价管理有关的法律法规、政策文件和技术标准等信息,针对风险控制方面,提供专业的造价管理建议及实施对策', serviceid: 15, order: 60, type: 2 }, 60: { text: '为委托人在日常建设、运营及经营管理中遇到的造价编制与审核问题,提供专业咨询建议', serviceid: 15, order: 61, type: 2 }, 61: { text: '对委托人起草的工程勘察设计、工程监理、施工、物资采购和技术服务等方面的合同与协议文本,从造价控制与投资风险角度提供审阅与修改建议', serviceid: 15, order: 62, type: 2 }, 62: { text: '审查各类合同履行情况时,对识别出的潜在漏洞或风险,提出相应的修改建议或补救措施', serviceid: 15, order: 63, type: 2 }, 63: { text: '对委托人提供的造价管理相关的制度,进行审阅与修改,或提供专业咨询建议', serviceid: 15, order: 64, type: 2 }, 64: { text: '针对具体建设工程项目的重大投资决策,出具独立的造价专业意见;或根据委托人的要求, 对造价争议事项提供专业依据并出具顾问报告', serviceid: 15, order: 65, type: 3 }, 65: { text: '为委托人起草与工程建设相关的合同、协议等文书,包括且不限于工程勘察设计、监理、施 工、采购及技术服务等领域', serviceid: 15, order: 66, type: 3 }, 66: { text: '独立对各类合同的履行情况进行审查,识别潜在漏洞或风险,并提出系统性的修改建议或补 救方案', serviceid: 15, order: 67, type: 3 }, 67: { text: '为委托人制订或修订与造价管理相关的制度', serviceid: 15, order: 68, type: 3 }, 68: { text: '受托处理涉及委托人的造价纠纷、仲裁及诉讼案件,提供专业意见与支持,并出具咨询顾问 报告', serviceid: 15, order: 69, type: 3 }, 69: { text: '组织并完成调研工作,包括制定调研计划、实施调研和撰写调研报告', serviceid: 16, order: 70, type: 0 }, 70: { text: '完成各阶段文件的起草、修订、调整与校对工作;整理并分析各阶段的 反馈意见、完成反馈意见的采纳情况,编制必要的采纳情况报告或说明;完成工作报告', serviceid: 16, order: 71, type: 0 }, 71: { text: '完成评审汇报材料,编写报告;整理并汇总评审意见、复核评审意见,完成评审 意见的采纳及反馈工作', serviceid: 16, order: 72, type: 0 }, 72: { text: '负责评审会议的全过程组织工作, 包括准备会议材料、发出通知、组织召开并主持评审会。', serviceid: 16, order: 73, type: 1 }, 73: { text: '组织并完成调研工作,包括制定调研计划、实施调研和撰写调研报告', serviceid: 17, order: 74, type: 0 }, 74: { text: '完成工作大纲和编写大纲;开展专项研究工作,完成各阶段研究报告的起 草、修订、调整与校对工作;分研究阶段进行技术报告的编写、修改、调整以及相应的报审 报批工作,包括大纲与成果的评审及验收等;整理并分析各阶段的反馈意见、完成反馈意见 的采纳情况,编制必要的采纳情况报告或说明;编制工作报告', serviceid: 17, order: 75, type: 0 }, 75: { text: '确定测试与验证项目,完成测试与验证工作,完成测试与验证报告', serviceid: 17, order: 76, type: 0 }, 76: { text: '完成评审与验收所需汇报材料,编写报告;整理并汇总评审意见、复核评审意见,完成评审意见的采纳及反馈工作', serviceid: 17, order: 77, type: 0 }, 77: { text: '编写培训与宣贯相关的材料,组织研究成果的宣贯与推广工作', serviceid: 17, order: 78, type: 1 }, 78: { text: '负责评审与验收会议的全过程组织工作,包括准备会议材料、发出通知、 组织召开并主持评审与验收会议', serviceid: 17, order: 79, type: 1 }, 79: { text: '组织并完成调研工作,包括制定调研计划、实施调研和撰写调研报告', serviceid: 18, order: 80, type: 0 }, 80: { text: '制定工作计划并完成编制大纲,编制大纲包括工作大纲和编写大纲', serviceid: 18, order: 81, type: 0 }, 81: { text: '完成数据采集与测定工作,编制相应的数据采集与测定报告', serviceid: 18, order: 82, type: 0 }, 82: { text: '完成数据整理与分析工作,编制相应的数据分析报告,整理支撑性资料', serviceid: 18, order: 83, type: 0 }, 83: { text: '完成定额文本的起草、修改及调整工作;完成各阶段研究报告的起草、修订、 调整与校对工作,编制必要的采纳情况报告或说明;编制工作报告或报批报告', serviceid: 18, order: 84, type: 0 }, 84: { text: '确定测试与验证项目,完成测试与验证工作,完成测试与验证报告', serviceid: 18, order: 85, type: 0 }, 85: { text: '完成评审与验收所需汇报材料,编写报告;整理并汇总评审意见、复核评 审意见,完成评审意见的采纳及反馈工作', serviceid: 18, order: 86, type: 0 }, 86: { text: '编写培训与宣贯相关的材料,组织定额成果的培训与宣贯工作', serviceid: 18, order: 87, type: 1 }, 87: { text: '负责评审与验收会议的全过程组织工作,包括准备会议材料、发出通知、 组织召开并主持评审与验收会议', serviceid: 18, order: 88, type: 1 }, 88: { text: '开展各类造价信息的搜集、筛选与整理工作', serviceid: 19, order: 89, type: 0 }, 89: { text: '对整理后的信息与数据进行对比与分析', serviceid: 19, order: 90, type: 0 }, 90: { text: '依据分析结果,对特定价格或费用进行评估与论证,编制咨询报告', serviceid: 19, order: 91, type: 0 }, 91: { text: '预测未来特定时期的价格或费用,编制预测报告', serviceid: 19, order: 92, type: 1 }, 92: { text: '研究价格或费用的长期变动规律,编制趋势分析报告', serviceid: 19, order: 93, type: 1 }, 93: { text: '根据委托人的目标,确定鉴定工作范围', serviceid: 20, order: 94, type: 0 }, 94: { text: '收集与复核鉴定资料', serviceid: 20, order: 95, type: 0 }, 95: { text: '组织或参与必要的现场勘查工作', serviceid: 20, order: 96, type: 0 }, 96: { text: '工程量的计算与工程费用的计价或估价', serviceid: 20, order: 97, type: 0 }, 97: { text: '对争议焦点进行鉴定,提出解决争议的意见', serviceid: 20, order: 98, type: 0 }, 98: { text: '回复各方当事人的质证意见', serviceid: 20, order: 99, type: 0 }, 99: { text: '编制并出具造价鉴定报告', serviceid: 20, order: 100, type: 0 }, 100: { text: '组织或参与现场数据复测或特殊鉴定', serviceid: 20, order: 101, type: 1 }, 101: { text: '与当事人相关方进行必要的造价数据核对', serviceid: 20, order: 102, type: 1 }, 102: { text: '出庭就鉴定报告及相关专业问题接受质证', serviceid: 20, order: 103, type: 1 }, 103: { text: '编制影响工程成本的资源要素清单', serviceid: 21, order: 104, type: 0 }, 104: { text: '调查人工、材料、设备和施工机械的市场供应情况,主要是价格情况', serviceid: 21, order: 105, type: 0 }, 105: { text: '测算、分析、计算工程成本费用,结合其他相关因素,编制并出具工程成本测算报告', serviceid: 21, order: 106, type: 0 }, 106: { text: '参加与工程成本测算相关的会议', serviceid: 21, order: 107, type: 0 }, 107: { text: '审核及分析资源配置、施工措施、施工管理方案的经济合理性', serviceid: 21, order: 108, type: 1 }, 108: { text: '对比工程成本与投资估算、设计概算、施工图预算、工程量清单预算、合同价,并分析费用差异,提交分析报告', serviceid: 21, order: 109, type: 1 }, 109: { text: '编制影响工程成本的资源要素清单或成本组成科目', serviceid: 22, order: 110, type: 0 }, 110: { text: '收集人工、材料、设备、施工机械、措施、税费等实际发生的费用', serviceid: 22, order: 111, type: 0 }, 111: { text: '统计与汇总、拆解与合并、对比与分析工程成本各项费用,编制并出具工程成本核算报告', serviceid: 22, order: 112, type: 0 }, 112: { text: '参加与工程成本核算相关的会议', serviceid: 22, order: 113, type: 0 }, 113: { text: '分析实际工程成本与测算工程成本差异的合理性,提交分析报告', serviceid: 22, order: 114, type: 1 }, 114: { text: '归纳与总结实际工程成本与测算工程成本的差异原因,提出改进建议', serviceid: 22, order: 115, type: 1 }, 115: { text: '依据本项目在招标阶段确认的设计图纸工程量,结合工程量清单计量支付规则,对设计工程量进行复核,包括构件工程量、明细表工程量和汇总表工程量,同时与相关方核对工程量', serviceid: 23, order: 116, type: 0 }, 116: { text: '依据核对后确认的设计图纸数量,细化与合并招标工程量清单或合同工程量清单,建立各维度清单间的数据链接,与相关单位完成核对与确认', serviceid: 23, order: 117, type: 0 }, 117: { text: '依据确定的招标工程量清单或合同工程量清单,拆解与合并相应的清单费用,与相关单位完成核对与确认', serviceid: 23, order: 118, type: 1 }, 118: { text: '现场勘查与测量现场实施工程量', serviceid: 23, order: 119, type: 1 }, 119: { text: '完成设计图纸所载数量与复核后的数量进行对比与分析,提供对比分析报告', serviceid: 23, order: 120, type: 1 }, 120: { text: '参加计算工程量相关的会议', serviceid: 23, order: 121, type: 0 }, 121: { text: '协助委托人处理涉及工程数量的争议、纠纷、仲裁或诉讼事务', serviceid: 23, order: 122, type: 1 }, 122: { text: '完成工程变更费用的测算', serviceid: 24, order: 123, type: 0 }, 123: { text: '完成新增预算单价或合同单价的计算与核定', serviceid: 24, order: 124, type: 0 }, 124: { text: '按施工图预算或合同清单方式完成工程变更费用的计算', serviceid: 24, order: 125, type: 0 }, 125: { text: '完成价格波动引起的价差费用的计算', serviceid: 24, order: 126, type: 1 }, 126: { text: '完成索赔与补偿费用的计算,如加速施工费用、暂停施工补偿等', serviceid: 24, order: 127, type: 1 }, 127: { text: '协助委托人处理涉及工程变更费用的争议与纠纷、仲裁与诉讼', serviceid: 24, order: 128, type: 1 }, 128: { text: '分析、论证及评估影响投资估算的主要因素', serviceid: 25, order: 129, type: 0 }, 129: { text: '根据分析论证结果,完成调整后投资估算文件及主要技术经济指标,与相关单位核对数据', serviceid: 25, order: 130, type: 0 }, 130: { text: '完成调整后投资估算与原批复估算的对比及原因分析', serviceid: 25, order: 131, type: 0 }, 131: { text: '出席与投资估算调整工作相关的会议', serviceid: 25, order: 132, type: 0 }, 132: { text: '协助调规报告编制单位完成报告的造价专业部分的内容', serviceid: 25, order: 133, type: 1 }, 133: { text: '分析、论证及评估影响设计概算的主要因素', serviceid: 26, order: 134, type: 0 }, 134: { text: '根据分析论证结果,编制调整后设计概算文件及主要技术经济指标,与相关单位核对数据', serviceid: 26, order: 135, type: 0 }, 135: { text: '完成调整后概算与原批复概算的对比及原因分析,完成调整概算报告', serviceid: 26, order: 136, type: 0 }, 136: { text: '出席与概算调整工作相关的会议', serviceid: 26, order: 137, type: 0 }, 137: { text: '协助委托人开展项目预估决算费用的测算、估算工作', serviceid: 26, order: 138, type: 1 }, 138: { text: '检查相关单位对工程造价管理法律法规、规章制度以及工程造价依据的执行情况', serviceid: 27, order: 139, type: 0 }, 139: { text: '检查各阶段造价文件编制、审查(批)或备案以及对批复意见的落实情况', serviceid: 27, order: 140, type: 0 }, 140: { text: '检查造价管理台账和计量支付制度的建立与执行、造价全过程管理与控制情况', serviceid: 27, order: 141, type: 0 }, 141: { text: '检查工程变更管理情况', serviceid: 27, order: 142, type: 0 }, 142: { text: '检查项目造价信息的收集、分析及报送情况', serviceid: 27, order: 143, type: 0 }, 143: { text: '检查项目从业单位的造价人员执业情况', serviceid: 27, order: 144, type: 0 }, 144: { text: '协助委托人进行现场核查、资料抽检和台账复核工作', serviceid: 27, order: 145, type: 0 }, 145: { text: '协助委托人整理检查结果和起草检查报告等工作', serviceid: 27, order: 146, type: 0 }, 146: { text: '作为造价咨询服务总体协调单位,依据造价技术标准的具体条款或委托方的个性化需求,进一步细化各项工作的具体要求,检查其他服务单位的造价文件的组成完整性、电子文件格式是否符合要求、电子版与纸质版是否对应、造价文件报表的规范性', serviceid: -1, order: 147, type: 4 }, 147: { text: '作为造价咨询服务总体协调单位,负责总体协调其他咨询人或专家团队的工作,确保各方在项目服务中的沟通顺畅,监控造价咨询服务的进展情况,确保各咨询人按时完成工作', serviceid: -1, order: 148, type: 4 }, } let costScaleCal = [ { code: 'C1-1', staLine: 0, endLine: 100, basic: { staPrice: 0, rate: 0.01 }, optional: { staPrice: 0, rate: 0.002 } }, { code: 'C1-2', staLine: 100, endLine: 300, basic: { staPrice: 10000, rate: 0.008 }, optional: { staPrice: 2000, rate: 0.0016 } }, { code: 'C1-3', staLine: 300, endLine: 500, basic: { staPrice: 26000, rate: 0.005 }, optional: { staPrice: 5200, rate: 0.001 } }, { code: 'C1-4', staLine: 500, endLine: 1000, basic: { staPrice: 36000, rate: 0.004 }, optional: { staPrice: 7200, rate: 0.0008 } }, { code: 'C1-5', staLine: 1000, endLine: 5000, basic: { staPrice: 56000, rate: 0.003 }, optional: { staPrice: 11200, rate: 0.0006 } }, { code: 'C1-6', staLine: 5000, endLine: 10000, basic: { staPrice: 176000, rate: 0.002 }, optional: { staPrice: 35200, rate: 0.0004 } }, { code: 'C1-7', staLine: 10000, endLine: 30000, basic: { staPrice: 276000, rate: 0.0016 }, optional: { staPrice: 55200, rate: 0.00032 } }, { code: 'C1-8', staLine: 30000, endLine: 50000, basic: { staPrice: 596000, rate: 0.0013 }, optional: { staPrice: 119200, rate: 0.00026 } }, { code: 'C1-9', staLine: 50000, endLine: 100000, basic: { staPrice: 856000, rate: 0.001 }, optional: { staPrice: 171200, rate: 0.0002 } }, { code: 'C1-10', staLine: 100000, endLine: 150000, basic: { staPrice: 1356000, rate: 0.0009 }, optional: { staPrice: 271200, rate: 0.00018 } }, { code: 'C1-11', staLine: 150000, endLine: 200000, basic: { staPrice: 1806000, rate: 0.0008 }, optional: { staPrice: 361200, rate: 0.00016 } }, { code: 'C1-12', staLine: 200000, endLine: 300000, basic: { staPrice: 2206000, rate: 0.0007 }, optional: { staPrice: 441200, rate: 0.00014 } }, { code: 'C1-13', staLine: 300000, endLine: 400000, basic: { staPrice: 2906000, rate: 0.0006 }, optional: { staPrice: 581200, rate: 0.00012 } }, { code: 'C1-14', staLine: 400000, endLine: 600000, basic: { staPrice: 3506000, rate: 0.0005 }, optional: { staPrice: 701200, rate: 0.0001 } }, { code: 'C1-15', staLine: 600000, endLine: 800000, basic: { staPrice: 4506000, rate: 0.0004 }, optional: { staPrice: 901200, rate: 0.00008 } }, { code: 'C1-16', staLine: 800000, endLine: 1000000, basic: { staPrice: 5306000, rate: 0.0003 }, optional: { staPrice: 1061200, rate: 0.00006 } }, { code: 'C1-17', staLine: 1000000, endLine: null, basic: { staPrice: 5906000, rate: 0.00025 }, optional: { staPrice: 1181200, rate: 0.00005 } }, ]; let areaScaleCal = [ { code: 'C2-1', staLine: 0, endLine: 50, basic: { staPrice: 0, rate: 200 }, optional: { staPrice: 0, rate: 40 } }, { code: 'C2-2', staLine: 50, endLine: 100, basic: { staPrice: 10000, rate: 160 }, optional: { staPrice: 2000, rate: 32 } }, { code: 'C2-3', staLine: 100, endLine: 500, basic: { staPrice: 18000, rate: 120 }, optional: { staPrice: 3600, rate: 24 } }, { code: 'C2-4', staLine: 500, endLine: 1000, basic: { staPrice: 66000, rate: 80 }, optional: { staPrice: 13200, rate: 16 } }, { code: 'C2-5', staLine: 1000, endLine: 5000, basic: { staPrice: 106000, rate: 60 }, optional: { staPrice: 21200, rate: 12 } }, { code: 'C2-6', staLine: 5000, endLine: null, basic: { staPrice: 346000, rate: 20 }, optional: { staPrice: 69200, rate: 4 } }, ]; export type WorkType = '基本工作' | '可选工作' | '日常顾问' | '专项顾问' | '附加工作' | '自定义' export type IndustryType = (typeof industryTypeList)[number]['type'] type DictItem = Record type DictEntry = { id: string; rawId: string; item: DictItem } type DictByIdMap = Record type BasicFeeFromScaleResult = { basic: number optional: number basicFormula: string optionalFormula: string } const industryTypeById = new Map( industryTypeList.flatMap(item => { return [[String(item.id).trim(), item.type]] }) ) /** * 根据行业的id获取对应专业字段(isRoad/isRailway/isWaterway)。 * @returns 对应行业字段;未命中时返回 null */ export const getIndustryTypeValue = (industryId: unknown): IndustryType | null => industryTypeById.get(String(industryId || '').trim()) || null /** * 判断字典项是否在指定行业下可用。 * @returns 是否可用 */ export const isIndustryEnabledByType = ( item: DictItem | undefined, typeValue: IndustryType | null ): boolean => Boolean(typeValue && item?.[typeValue] === true) // 判断是否为“通用项”(三种行业都适用)。 const isGenericIndustryItem = (item: Record | undefined) => Boolean(item?.isRoad && item?.isRailway && item?.isWaterway) /** * 判断专业ID(majorList 的 key)是否属于指定行业范围(可选是否包含通用项)。 * @returns 是否属于行业范围 */ export const isMajorIdInIndustryScope = ( majorId: unknown, industryCode: unknown, includeGeneric = true ): boolean => { const industryType = getIndustryTypeValue(industryCode) if (!majorId || !industryType) return false const id = String(majorId).trim() const majorItem = getMajorDictEntries().find(entry => String(entry.id).trim() === id)?.item if (isIndustryEnabledByType(majorItem, industryType)) return true if (!includeGeneric) return false return isGenericIndustryItem(majorItem) } const isPlainObject = (value: unknown): value is Record => Boolean(value) && typeof value === 'object' && !Array.isArray(value) const hasCodeLike = (value: Record) => typeof value.code === 'string' const isDictLeafNode = (value: unknown): value is Record => isPlainObject(value) && hasCodeLike(value) && typeof value.name === 'string' // 递归提取字典树中的叶子节点(具有 code + name 的业务项)。 const collectDictLeafEntries = ( source: Record, prefix = '' ): Array<{ rawId: string; item: Record }> => { const result: Array<{ rawId: string; item: Record }> = [] if (!isPlainObject(source)) return result for (const [key, value] of Object.entries(source)) { const rawId = prefix ? `${prefix}.${key}` : String(key) if (isDictLeafNode(value)) { result.push({ rawId, item: value }) continue } if (isPlainObject(value)) { result.push(...collectDictLeafEntries(value, rawId)) } } return result } // 计算排序权重:优先用 order,再退化为 rawId 的数值。 const getItemSortValue = (item: Record, rawId: string) => { const order = Number(item?.order) if (Number.isFinite(order)) return order const rawIdNum = Number(rawId) if (Number.isFinite(rawIdNum)) return rawIdNum return Number.POSITIVE_INFINITY } // 按权重与编码排序,保证下拉展示顺序稳定。 const sortDictEntries = ( entries: Array<{ id: string; rawId: string; item: Record }> ) => entries.sort((a, b) => { const orderDiff = getItemSortValue(a.item, a.rawId) - getItemSortValue(b.item, b.rawId) if (orderDiff !== 0) return orderDiff const codeA = String(a.item?.code || a.id) const codeB = String(b.item?.code || b.id) return codeA.localeCompare(codeB) }) // 将原始字典对象转换为扁平 entries 列表。 const buildDictEntries = (source: Record) => sortDictEntries( collectDictLeafEntries(source).map(({ rawId, item }) => { return { id: rawId, rawId, item } }) ) /** * 获取专业字典的扁平化列表。 * @returns 专业字典条目列表 */ export const getMajorDictEntries = (): DictEntry[] => buildDictEntries(majorList as Record) /** * 获取服务字典的扁平化列表。 * @returns 服务字典条目列表 */ export const getServiceDictEntries = (): DictEntry[] => buildDictEntries(serviceList as Record) /** * 构建“专业ID -> 专业项”映射。 * @returns 专业项映射表 */ export const getMajorDictById = (): DictByIdMap => Object.fromEntries(getMajorDictEntries().map(entry => [entry.id, entry.item])) /** * 构建“服务ID -> 服务项”映射。 * @returns 服务项映射表 */ export const getServiceDictById = (): DictByIdMap => Object.fromEntries(getServiceDictEntries().map(entry => [entry.id, entry.item])) /** * 构建“专业编码(code) -> 专业ID”别名映射。 * @returns 编码到专业ID的别名映射 */ export const getMajorIdAliasMap = (): Map => new Map( getMajorDictEntries().flatMap(entry => { const code = String(entry.item?.code || '') return code ? [[code, entry.id]] : [] }) ) /** * 按 ID 或 code 获取专业项(优先 ID,找不到时按 code 别名回查)。 * @returns 匹配的专业项;未命中时返回 undefined */ export const getMajorDictItemById = (id: string | number): DictItem | undefined => { const key = String(id) const dict = getMajorDictById() as DictByIdMap if (dict[key]) return dict[key] const alias = getMajorIdAliasMap().get(key) return alias ? dict[alias] : undefined } /** * 按 ID 获取服务项。 * @returns 匹配的服务项;未命中时返回 undefined */ export const getServiceDictItemById = (id: string | number): DictItem | undefined => { const key = String(id) const dict = getServiceDictById() as DictByIdMap return dict[key] } // 判断数值是否命中分段区间:(staLine, endLine]。 const inRange = (sv: number, staLine: number, endLine: number | null) => staLine < sv && (endLine == null || sv <= endLine) // 按分段参数计算基础费用。 const calcScaleFee = (params: { staPrice: number sv: number staLine: number rate: number multiplier?: number }) => { const multiplier = params.multiplier ?? 1 return roundTo( toDecimal(params.staPrice).plus( toDecimal(params.sv) .minus(params.staLine) .mul(multiplier) .mul(params.rate) ) ) } // 将费率转成千分数文本(例如 0.012 -> 12‰)。 const scaleRatePermillage = (rate: number) => roundTo(toDecimal(rate).mul(1000), 2) // 生成“基础费用”计算公式字符串,供前端展示。 const buildScaleFormula = (params: { staPrice: number sv: number staLine: number rate: number multiplier?: number showPermillage?: boolean }) => { const multiplier = params.multiplier ?? 1 const currentValue = toDecimal(params.sv).mul(multiplier).toNumber() const staLineValue = toDecimal(params.staLine).mul(multiplier).toNumber() const rateText = params.showPermillage ? `${scaleRatePermillage(params.rate)}‰` : params.rate if (params.staPrice) { return `${numberFormatter(params.staPrice, 0)}+(${numberFormatter(currentValue, 0)}-${numberFormatter(staLineValue, 0)})×${rateText}` } return `${numberFormatter(currentValue, 0)}×${rateText}` } /** * 根据规模值与规模类型,计算基础费用及对应公式(返回基础/附加两部分)。 * @returns 基础费用计算结果;输入非法或未命中区间时返回 null */ export function getBasicFeeFromScale( scaleValue: unknown, scaleType: 'cost' | 'area' ): BasicFeeFromScaleResult | null { const sv = Number(scaleValue) if (!Number.isFinite(sv) || sv <= 0) return null if (scaleType === 'cost') { const targetRange = costScaleCal.find(f => inRange(sv, f.staLine, f.endLine)) if (!targetRange) return null return { basic: calcScaleFee({ staPrice: targetRange.basic.staPrice, sv, staLine: targetRange.staLine, rate: targetRange.basic.rate, multiplier: 10000 }), optional: calcScaleFee({ staPrice: targetRange.optional.staPrice, sv, staLine: targetRange.staLine, rate: targetRange.optional.rate, multiplier: 10000 }), basicFormula: buildScaleFormula({ staPrice: targetRange.basic.staPrice, sv, staLine: targetRange.staLine, rate: targetRange.basic.rate, multiplier: 10000, showPermillage: true }), optionalFormula: buildScaleFormula({ staPrice: targetRange.optional.staPrice, sv, staLine: targetRange.staLine, rate: targetRange.optional.rate, multiplier: 10000, showPermillage: true }) } } if (scaleType === 'area') { const targetRange = areaScaleCal.find(f => inRange(sv, f.staLine, f.endLine)) if (!targetRange) return null return { basic: calcScaleFee({ staPrice: targetRange.basic.staPrice, sv, staLine: targetRange.staLine, rate: targetRange.basic.rate }), optional: calcScaleFee({ staPrice: targetRange.optional.staPrice, sv, staLine: targetRange.staLine, rate: targetRange.optional.rate }), basicFormula: buildScaleFormula({ staPrice: targetRange.basic.staPrice, sv, staLine: targetRange.staLine, rate: targetRange.basic.rate }), optionalFormula: buildScaleFormula({ staPrice: targetRange.optional.staPrice, sv, staLine: targetRange.staLine, rate: targetRange.optional.rate }) } } return null } /** * 导出入口:生成 Excel 并触发保存(优先使用 File System Access API)。 * @returns 导出流程完成后的 Promise */ export async function exportFile(fileName: string, data: any | (() => Promise), onSaveConfirmed?: () => void): Promise { if (window.showSaveFilePicker) { const handle = await window.showSaveFilePicker({ suggestedName: fileName, types: [{ description: "Excel 文件", accept: { "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [".xlsx"] } }] }); try { onSaveConfirmed?.() const resolvedData = typeof data === 'function' ? await data() : data const workbook = await generateTemplate(resolvedData); const buffer = await workbook.xlsx.writeBuffer(); const writable = await handle.createWritable(); await writable.write(buffer); await writable.close(); // 返回 blob URL 供打开文件使用 const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); return URL.createObjectURL(blob); // ecCom.WeaLoadingGlobal.destroy(); // antd.notification['success']({ // message: '下载成功!', // }); } catch (err) { console.log('err:' + err); return null; } } else { const resolvedData = typeof data === 'function' ? await data() : data const workbook = await generateTemplate(resolvedData); const buffer = await workbook.xlsx.writeBuffer(); const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); const url1 = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.hcode = url1; a.download = `${fileName}.xlsx`; a.click(); URL.revokeObjectURL(url1); return null; } } // 按模板生成最终工作簿:填充封面、目录、各分表及汇总数据。 async function generateTemplate(data) { data.contracts[0].services[0].tasks = ['依据本项目在招标阶段确认的设计图纸工程量,结合工程量清单计量支付规则,对设计工程量进行复核,包括构件工程量、明细表工程量和汇总表工程量,同时与相关方核对工程量', '依据核对后确认的设计图纸数量,细化与合并招标工程量清单或合同工程量清单,建立各维度清单间的数据链接,与相关单位完成核对与确认', '依据确定的招标工程量清单或合同工程量清单,拆解与合并相应的清单费用,与相关单位完成核对与确认', '现场勘查与测量现场实施工程量']; // const downTextTmp = { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: '常规' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: '下标' }] }; console.log(data) // 编制说明 → 工作内容的前后默认项 let prefixIDs = [6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]; let suffixIDs = [6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]; const prefixTexts = ['收集并整理各阶段及各项造价咨询所需的基础资料,审核基础资料的完整性和合理性', '编制造价咨询服务总体及各分项的服务计划、服务大纲及工作方案', '了解建设工程项目所在地的建设条件']; const suffixTexts = ['协助委托人完成咨询服务相应造价文件的报审、报备、报批、检查与审计涉及的解释与回复、修改与调整等工作', '完成本合同造价档案的收集、整理和归档']; try { // 获取模板 let templateExcel = 'template20260226001test010'; let templateUrl = `./${templateExcel}.xlsx`; let buf = await (await fetch(templateUrl)).arrayBuffer(); let workbook = new ExcelJS.Workbook(); await workbook.xlsx.load(buf); // 生成表格 let fm_sheet = workbook.getWorksheet('封面'); let ml_sheet = workbook.getWorksheet('目录'); let yz01_sheet = workbook.getWorksheet('预总01表'); yz01_sheet.headerFooter.oddHeader = yz01_sheet.headerFooter.oddHeader.replace(/×××/g, data.name); // 更新封面 fm_sheet.getRow(2).getCell(1).value = data.name; fm_sheet.getRow(8).getCell(4).value = data.writer; fm_sheet.getRow(9).getCell(4).value = data.reviewer; fm_sheet.getRow(10).getCell(4).value = data.company; if (data.date) { const dateArr = data.date.split('-'); let year = dateArr[0]; let month = Number(dateArr[1]); let day = Number(dateArr[2]); fm_sheet.getRow(11).getCell(4).value = `${year} 年 ${month} 月 ${day} 日`; } // 更新预总01表的列数 let yz01Mod = (data.contracts.length + 1) % 4; let yz01Num = (data.contracts.length + 1 - yz01Mod) / 4; switch (yz01Mod) { case 0: yz01_sheet.spliceColumns(8, 15); break; case 1: yz01_sheet.spliceColumns(8, 11); break; case 2: yz01_sheet.spliceColumns(19, 4); yz01_sheet.spliceColumns(8, 6); break; case 3: yz01_sheet.spliceColumns(14, 9); break; } if (yz01Num == 0) { yz01_sheet.spliceColumns(1, 7); } else { if (yz01Num > 1) { for (let i = 0; i < yz01Num - 1; i++) { insertAndCopyColumn(7 * (i + 1) + 1, [1, 2, 3, 4, 5, 6, 7], yz01_sheet); } } } // 按合同段更新目录及相关表 let ml_slotRow = 14; let ml_number = 1; let allServices = []; let allAddtional = {}; let allReserve = {}; let allMajors = {}; let allServiceMajors = new Set(); let allMethods = new Set(); let allReserveMethods = { 0: { coe: [], cid: [], coeSet: new Set() }, 4: [], 5: [] }; let contractFeeSummary = []; data.scale?.forEach(sci => { allMajors[sci.major] = { [data.contracts.length]: sci }; }); data.contracts.forEach((ci, index) => { contractFeeSummary.push(`${ci.name} ${Number(ci.fee).toLocaleString()}元`); ci.allServiceMajors = new Set(); // 记录allMajors ci.scale?.forEach(sci => { if (allMajors[sci.major]) { allMajors[sci.major][index] = sci; } else { allMajors[sci.major] = { [index]: sci }; } }); ci.method1 = []; ci.method2 = []; ci.method3 = []; ci.method4 = []; // 按计价方式汇总服务数据对象 ci.services.forEach(si => { if (prefixIDs.includes(si.id)) ci.hasPreTexts = true; if (suffixIDs.includes(si.id)) ci.hasSufTexts = true; if (si.method1) ci.method1.push(si.id); if (si.method2) ci.method2.push(si.id); if (si.method3) ci.method3.push(si.id); if (si.method4) ci.method4.push(si.id); }); const addtionalM4 = []; const addtionalM5 = []; if (ci.addtional) { allAddtional[index] = ci.addtional; ci.addtional.det.forEach(f => { if (f.m4) addtionalM4.push(f); if (f.m5) addtionalM5.push(f); }); } if (addtionalM5.length) ci.method5 = { addtional: addtionalM5 }; if (ci.reserve) allReserve[index] = ci.reserve; const reserveM4 = ci.reserve?.m4; const reserveM5 = ci.reserve?.m5; if (reserveM5) ci.method5 = ci.method5 ? Object.assign({ reserve: reserveM5 }, ci.method5) : { reserve: reserveM5 }; // 创建sheet let ml_sourceRows = [ml_sheet.getRow(6)]; let sheet_1 = copyWorksheet(workbook, '预i-1表', `预${index + 1}-1表`); sheet_1.headerFooter.oddHeader = sheet_1.headerFooter.oddHeader.replace(/×××/g, ci.name).replace(/预 i-1 表/g, `预 ${index + 1}-1 表`).replace(/第i合同/g, ci.name); sheet_1.getRow(1).getCell(4).value = sheet_1.getRow(1).getCell(4).value.replace(/第i合同/g, ci.name); let sheet_2; let sheet_2_1; let sheet_2_2; let sheet_3; let sheet_4; let sheet_4_1; let sheet_5; if (ci.method1.length || ci.method2.length) { ml_sourceRows.push(ml_sheet.getRow(7)); sheet_2 = copyWorksheet(workbook, '预i-2表', `预${index + 1}-2表`); sheet_2.headerFooter.oddHeader = sheet_2.headerFooter.oddHeader.replace(/×××/g, ci.name).replace(/预 i-2 表/g, `预 ${index + 1}-2 表`).replace(/第i合同/g, ci.name); if (ci.method1.length) { allMethods.add(1); ml_sourceRows.push(ml_sheet.getRow(8)); sheet_2_1 = copyWorksheet(workbook, '预i-2-1表', `预${index + 1}-2-1表`); sheet_2_1.headerFooter.oddHeader = sheet_2_1.headerFooter.oddHeader.replace(/×××/g, ci.name).replace(/预 i-2-1 表/g, `预 ${index + 1}-2-1 表`).replace(/第i合同/g, ci.name); } if (ci.method2.length) { allMethods.add(2); ml_sourceRows.push(ml_sheet.getRow(9)); sheet_2_2 = copyWorksheet(workbook, '预i-2-2表', `预${index + 1}-2-2表`); sheet_2_2.headerFooter.oddHeader = sheet_2_2.headerFooter.oddHeader.replace(/×××/g, ci.name).replace(/预 i-2-2 表/g, `预 ${index + 1}-2-2 表`).replace(/第i合同/g, ci.name); } } if (ci.method3.length) { allMethods.add(3); ml_sourceRows.push(ml_sheet.getRow(10)); sheet_3 = copyWorksheet(workbook, '预i-3表', `预${index + 1}-3表`); sheet_3.headerFooter.oddHeader = sheet_3.headerFooter.oddHeader.replace(/×××/g, ci.name).replace(/预 i-3 表/g, `预 ${index + 1}-3 表`).replace(/第i合同/g, ci.name); } if (ci.method4.length || addtionalM4.length || reserveM4) { allMethods.add(4); ml_sourceRows.push(ml_sheet.getRow(11)); ml_sourceRows.push(ml_sheet.getRow(12)); sheet_4 = copyWorksheet(workbook, '预i-4表', `预${index + 1}-4表`); sheet_4.headerFooter.oddHeader = sheet_4.headerFooter.oddHeader.replace(/×××/g, ci.name).replace(/预 i-4 表/g, `预 ${index + 1}-4 表`).replace(/第i合同/g, ci.name); sheet_4_1 = copyWorksheet(workbook, '预i-4-1表', `预${index + 1}-4-1表`); sheet_4_1.headerFooter.oddHeader = sheet_4_1.headerFooter.oddHeader.replace(/×××/g, ci.name).replace(/预 i-4-1 表/g, `预 ${index + 1}-4-1 表`).replace(/第i合同/g, ci.name); // 更新i-4和i-4-1表的小计行 let sumArr = ci.method4.map(m => ci.services.find(f => f.id == m)?.method4).concat(addtionalM4.map(m => m.m4)); if (reserveM4) sumArr.push(reserveM4); let sumObj = sumArr.reduce((a, b) => { return { person_num: addNumbers(a.person_num, toFiniteNumber(b?.person_num)), work_day: addNumbers(a.work_day, toFiniteNumber(b?.work_day)), fee: addNumbers(a.fee, toFiniteNumber(b?.fee)) }; }, { person_num: 0, work_day: 0, fee: 0 }); sheet_4.getRow(3).getCell(4).value = numberFormatter(sumObj.person_num, 0); sheet_4.getRow(3).getCell(5).value = numberFormatter(sumObj.work_day, 2); sheet_4.getRow(3).getCell(6).value = numberFormatter(sumObj.fee, 2); sheet_4_1.getRow(3).getCell(4).value = '/'; sheet_4_1.getRow(3).getCell(5).value = '/'; sheet_4_1.getRow(3).getCell(6).value = '/'; sheet_4_1.getRow(3).getCell(7).value = numberFormatter(sumObj.person_num, 0); sheet_4_1.getRow(3).getCell(8).value = numberFormatter(sumObj.work_day, 2); sheet_4_1.getRow(3).getCell(9).value = numberFormatter(sumObj.fee, 2); } if (ci.method5) { allMethods.add(5); ml_sourceRows.push(ml_sheet.getRow(13)); sheet_5 = copyWorksheet(workbook, '预i-5表', `预${index + 1}-5表`); sheet_5.headerFooter.oddHeader = sheet_5.headerFooter.oddHeader.replace(/×××/g, ci.name).replace(/预 i-5 表/g, `预 ${index + 1}-5 表`).replace(/第i合同/g, ci.name); sheet_5.getRow(3).getCell(3).value = '/'; sheet_5.getRow(3).getCell(4).value = '/'; sheet_5.getRow(3).getCell(5).value = '/'; const method5AdditionalFee = ci.method5.addtional?.reduce((a, b) => addNumbers(a, toFiniteNumber(b?.m5?.fee)), 0) || 0; sheet_5.getRow(3).getCell(6).value = numberFormatter( addNumbers(method5AdditionalFee, toFiniteNumber(ci.method5.reserve?.fee)), 2 ); } // 更新目录的第三部分 cusInsertRowFunc(ml_slotRow, ml_sourceRows, ml_sheet, () => ml_slotRow++, (targetCell, sourceCell, colNumber) => { if (colNumber == 1) { targetCell.value = ml_number++; } else if (colNumber == 2) { targetCell.value = sourceCell.value.replaceAll('第i合同', ci.name); } else if (colNumber == 3) { targetCell.value = sourceCell.value.replaceAll('i', index + 1); } else { targetCell.value = sourceCell.value; } }); // 根据服务更新相关表 let num_2 = 1; let num_2_1 = 1; let num_2_2 = 1; let num_3 = 1; let num_4 = 1; let num_4_1 = 1; let num_5 = 1; let m1Sum = 0; let m2Sum = 0; let m3Sum = 0; let m4Sum = 0; let serviceSum1 = 0; let serviceSum2 = 0; ci.services.forEach((sobj, sindex) => { let allServicesX = allServices.find(s => s.id == sobj.id); if (allServicesX) { allServicesX.contracts[index] = sobj.fee; } else { allServices.push({ id: sobj.id, contracts: { [index]: sobj.fee, }, }); } let serviceX = serviceList[sobj.id]; cusInsertRowFunc(4 + sindex, [sheet_1.getRow(3)], sheet_1, (targetRow) => { targetRow.getCell(1).value = sindex + 1; targetRow.getCell(2).value = serviceX.code; targetRow.getCell(3).value = serviceX.name; if (sobj.method1) { targetRow.getCell(4).value = numberFormatter(sobj.method1.fee, 2); m1Sum = addNumbers(m1Sum, toFiniteNumber(sobj.method1.fee)); } if (sobj.method2) { targetRow.getCell(5).value = numberFormatter(sobj.method2.fee, 2); m2Sum = addNumbers(m2Sum, toFiniteNumber(sobj.method2.fee)); } if (sobj.method3) { targetRow.getCell(6).value = numberFormatter(sobj.method3.fee, 2); m3Sum = addNumbers(m3Sum, toFiniteNumber(sobj.method3.fee)); } if (sobj.method4) { targetRow.getCell(7).value = numberFormatter(sobj.method4.fee, 2); m4Sum = addNumbers(m4Sum, toFiniteNumber(sobj.method4.fee)); } targetRow.getCell(8).value = numberFormatter(sobj.fee, 2); targetRow.getCell(9).value = numberFormatter(sobj.finalFee, 2); serviceSum1 = addNumbers(serviceSum1, toFiniteNumber(sobj.fee)); serviceSum2 = addNumbers(serviceSum2, toFiniteNumber(sobj.finalFee)); }); if (sobj.method1 || sobj.method2) { cusInsertRowFunc(4 + num_2, [sheet_2.getRow(4)], sheet_2, (targetRow) => { targetRow.getCell(1).value = num_2++; targetRow.getCell(2).value = serviceX.code; targetRow.getCell(3).value = serviceX.name; targetRow.getCell(4).value = '/'; targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = '/'; targetRow.getCell(7).value = '/'; if (sobj.method1) { targetRow.getCell(8).value = numberFormatter(sobj.method1.basicFee, 2); targetRow.getCell(9).value = numberFormatter(sobj.method1.fee, 2); cusInsertRowFunc(4 + num_2_1, [sheet_2_1.getRow(4)], sheet_2_1, (targetRow) => { targetRow.getCell(1).value = num_2_1++; targetRow.getCell(2).value = serviceX.code; targetRow.getCell(3).value = serviceX.name; targetRow.getCell(4).value = numberFormatter(sobj.method1.cost, 2); targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = numberFormatter(sobj.method1.basicFee_basic, 2); targetRow.getCell(7).value = '/'; targetRow.getCell(8).value = numberFormatter(sobj.method1.basicFee_optional, 2); targetRow.getCell(9).value = numberFormatter(sobj.method1.basicFee, 2); }); } if (sobj.method2) { targetRow.getCell(10).value = numberFormatter(sobj.method2.basicFee, 2); targetRow.getCell(11).value = numberFormatter(sobj.method2.fee, 2); cusInsertRowFunc(4 + num_2_2, [sheet_2_2.getRow(4)], sheet_2_2, (targetRow) => { targetRow.getCell(1).value = num_2_2++; targetRow.getCell(2).value = serviceX.code; targetRow.getCell(3).value = serviceX.name; targetRow.getCell(4).value = numberFormatter(sobj.method2.area, 3); targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = numberFormatter(sobj.method2.basicFee_basic, 2); targetRow.getCell(7).value = '/'; targetRow.getCell(8).value = numberFormatter(sobj.method2.basicFee_optional, 2); targetRow.getCell(9).value = numberFormatter(sobj.method2.basicFee, 2); }); } }); if (serviceX.mutiple) { const maxProNum = Math.max(sobj.method1 ? sobj.method1.proAmount : 0, sobj.method2 ? sobj.method2.proAmount : 0); for (let pi = 1; pi <= maxProNum; pi++) { const m1PI = sobj.method1 ? sobj.method1.det.filter(m => m.proNum == pi) : []; const m2PI = sobj.method2 ? sobj.method2.det.filter(m => m.proNum == pi) : []; let m1PISum = m1PI.reduce((a, b) => { return { cost: addNumbers(a.cost, toFiniteNumber(b.cost)), basicFee_basic: addNumbers(a.basicFee_basic, toFiniteNumber(b.basicFee_basic)), basicFee_optional: addNumbers(a.basicFee_optional, toFiniteNumber(b.basicFee_optional)), basicFee: addNumbers(a.basicFee, toFiniteNumber(b.basicFee)), fee: addNumbers(a.fee, toFiniteNumber(b.fee)), } }, { cost: 0, basicFee_basic: 0, basicFee_optional: 0, basicFee: 0, fee: 0 }); let m2PISum = m2PI.reduce((a, b) => { return { area: addNumbers(a.area, toFiniteNumber(b.area)), basicFee_basic: addNumbers(a.basicFee_basic, toFiniteNumber(b.basicFee_basic)), basicFee_optional: addNumbers(a.basicFee_optional, toFiniteNumber(b.basicFee_optional)), basicFee: addNumbers(a.basicFee, toFiniteNumber(b.basicFee)), fee: addNumbers(a.fee, toFiniteNumber(b.fee)), } }, { area: 0, basicFee_basic: 0, basicFee_optional: 0, basicFee: 0, fee: 0 }); cusInsertRowFunc(4 + num_2, [sheet_2.getRow(4)], sheet_2, (targetRow) => { targetRow.getCell(1).value = num_2++; targetRow.getCell(2).value = serviceX.code + '-' + pi; targetRow.getCell(3).value = '项目' + pi; targetRow.getCell(4).value = '/'; targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = '/'; targetRow.getCell(7).value = '/'; if (m1PI.length) { targetRow.getCell(8).value = numberFormatter(m1PISum.basicFee, 2); targetRow.getCell(9).value = numberFormatter(m1PISum.fee, 2); cusInsertRowFunc(4 + num_2_1, [sheet_2_1.getRow(4)], sheet_2_1, (targetRow) => { targetRow.getCell(1).value = num_2_1++; targetRow.getCell(2).value = serviceX.code + '-' + pi; targetRow.getCell(3).value = '项目' + pi; targetRow.getCell(4).value = numberFormatter(m1PISum.cost, 2); targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = numberFormatter(m1PISum.basicFee_basic, 2); targetRow.getCell(7).value = '/'; targetRow.getCell(8).value = numberFormatter(m1PISum.basicFee_optional, 2); targetRow.getCell(9).value = numberFormatter(m1PISum.basicFee, 2); }); } if (m2PI.length) { targetRow.getCell(10).value = numberFormatter(m2PISum.basicFee, 2); targetRow.getCell(11).value = numberFormatter(m2PISum.fee, 2); cusInsertRowFunc(4 + num_2_2, [sheet_2_2.getRow(4)], sheet_2_2, (targetRow) => { targetRow.getCell(1).value = num_2_2++; targetRow.getCell(2).value = serviceX.code + '-' + pi; targetRow.getCell(3).value = '项目' + pi; targetRow.getCell(4).value = numberFormatter(m2PISum.area, 3); targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = numberFormatter(m2PISum.basicFee_basic, 2); targetRow.getCell(7).value = '/'; targetRow.getCell(8).value = numberFormatter(m2PISum.basicFee_optional, 2); targetRow.getCell(9).value = numberFormatter(m2PISum.basicFee, 2); }); } }); let det1 = m1PI.map(m => m.major); let det2 = m2PI.map(m => m.major); let allDet = [...(new Set([...det1, ...det2]))].sort((a, b) => majorList[a].order - majorList[b].order).map(m => { let d1index = det1.indexOf(m); let d2index = det2.indexOf(m); return { major: m, mth1: d1index < 0 ? null : m1PI[d1index], mth2: d2index < 0 ? null : m2PI[d2index], }; }); allDet.forEach((m, mindex) => { let majorX = majorList[m.major]; cusInsertRowFunc(4 + num_2, [sheet_2.getRow(4)], sheet_2, (targetRow) => { targetRow.getCell(1).value = num_2++; targetRow.getCell(2).value = serviceX.code + '-' + pi + '-' + (mindex + 1); targetRow.getCell(3).value = majorX.name; if (m.mth1) { targetRow.getCell(4).value = numberFormatter(m.mth1.serviceCoe, 3); targetRow.getCell(5).value = numberFormatter(m.mth1.majorCoe, 3); targetRow.getCell(6).value = numberFormatter(m.mth1.processCoe, 3); targetRow.getCell(7).value = numberFormatter(m.mth1.proportion, 2); targetRow.getCell(8).value = numberFormatter(m.mth1.basicFee, 2); targetRow.getCell(9).value = numberFormatter(m.mth1.fee, 2); targetRow.getCell(10).value = '/'; targetRow.getCell(11).value = '/'; cusInsertRowFunc(4 + num_2_1, [sheet_2_1.getRow(4)], sheet_2_1, (targetRow) => { targetRow.getCell(1).value = num_2_1++; targetRow.getCell(2).value = serviceX.code + '-' + pi + '-' + (mindex + 1); targetRow.getCell(3).value = majorX.name; targetRow.getCell(4).value = numberFormatter(m.mth1.cost, 2); targetRow.getCell(5).value = m.mth1.basicFormula; targetRow.getCell(6).value = numberFormatter(m.mth1.basicFee_basic, 2); targetRow.getCell(7).value = m.mth1.optionalFormula; targetRow.getCell(8).value = numberFormatter(m.mth1.basicFee_optional, 2); targetRow.getCell(9).value = numberFormatter(m.mth1.basicFee, 2); }); } else { targetRow.getCell(4).value = numberFormatter(m.mth2.serviceCoe, 3); targetRow.getCell(5).value = numberFormatter(m.mth2.majorCoe, 3); targetRow.getCell(6).value = numberFormatter(m.mth2.processCoe, 3); targetRow.getCell(7).value = numberFormatter(m.mth2.proportion, 2); targetRow.getCell(8).value = '/'; targetRow.getCell(9).value = '/'; targetRow.getCell(10).value = numberFormatter(m.mth2.basicFee, 2); targetRow.getCell(11).value = numberFormatter(m.mth2.fee, 2); cusInsertRowFunc(4 + num_2_2, [sheet_2_2.getRow(4)], sheet_2_2, (targetRow) => { targetRow.getCell(1).value = num_2_2++; targetRow.getCell(2).value = serviceX.code + '-' + pi + '-' + (mindex + 1); targetRow.getCell(3).value = majorX.name; targetRow.getCell(4).value = numberFormatter(m.mth2.area, 3); targetRow.getCell(5).value = m.mth2.basicFormula; targetRow.getCell(6).value = numberFormatter(m.mth2.basicFee_basic, 2); targetRow.getCell(7).value = m.mth2.optionalFormula; targetRow.getCell(8).value = numberFormatter(m.mth2.basicFee_optional, 2); targetRow.getCell(9).value = numberFormatter(m.mth2.basicFee, 2); }); } }); }); } } else { let det1 = sobj.method1 ? sobj.method1.det.map(m => m.major) : []; let det2 = sobj.method2 ? sobj.method2.det.map(m => m.major) : []; let allDet = [...(new Set([...det1, ...det2]))].sort((a, b) => majorList[a].order - majorList[b].order).map(m => { let d1index = det1.indexOf(m); let d2index = det2.indexOf(m); return { major: m, mth1: d1index < 0 ? null : sobj.method1.det[d1index], mth2: d2index < 0 ? null : sobj.method2.det[d2index], }; }); allDet.forEach((m, mindex) => { allServiceMajors.add(m.major); ci.allServiceMajors.add(m.major); let majorX = majorList[m.major]; cusInsertRowFunc(4 + num_2, [sheet_2.getRow(4)], sheet_2, (targetRow) => { targetRow.getCell(1).value = num_2++; targetRow.getCell(2).value = serviceX.code + '-' + (mindex + 1); targetRow.getCell(3).value = majorX.name; if (m.mth1) { targetRow.getCell(4).value = numberFormatter(m.mth1.serviceCoe, 3); targetRow.getCell(5).value = numberFormatter(m.mth1.majorCoe, 3); targetRow.getCell(6).value = numberFormatter(m.mth1.processCoe, 3); targetRow.getCell(7).value = numberFormatter(m.mth1.proportion, 2); targetRow.getCell(8).value = numberFormatter(m.mth1.basicFee, 2); targetRow.getCell(9).value = numberFormatter(m.mth1.fee, 2); targetRow.getCell(10).value = '/'; targetRow.getCell(11).value = '/'; cusInsertRowFunc(4 + num_2_1, [sheet_2_1.getRow(4)], sheet_2_1, (targetRow) => { targetRow.getCell(1).value = num_2_1++; targetRow.getCell(2).value = serviceX.code + '-' + (mindex + 1); targetRow.getCell(3).value = majorX.name; targetRow.getCell(4).value = numberFormatter(m.mth1.cost, 2); targetRow.getCell(5).value = m.mth1.basicFormula; targetRow.getCell(6).value = numberFormatter(m.mth1.basicFee_basic, 2); targetRow.getCell(7).value = m.mth1.optionalFormula; targetRow.getCell(8).value = numberFormatter(m.mth1.basicFee_optional, 2); targetRow.getCell(9).value = numberFormatter(m.mth1.basicFee, 2); }); } else { targetRow.getCell(4).value = numberFormatter(m.mth2.serviceCoe, 3); targetRow.getCell(5).value = numberFormatter(m.mth2.majorCoe, 3); targetRow.getCell(6).value = numberFormatter(m.mth2.processCoe, 3); targetRow.getCell(7).value = numberFormatter(m.mth2.proportion, 2); targetRow.getCell(8).value = '/'; targetRow.getCell(9).value = '/'; targetRow.getCell(10).value = numberFormatter(m.mth2.basicFee, 2); targetRow.getCell(11).value = numberFormatter(m.mth2.fee, 2); cusInsertRowFunc(4 + num_2_2, [sheet_2_2.getRow(4)], sheet_2_2, (targetRow) => { targetRow.getCell(1).value = num_2_2++; targetRow.getCell(2).value = serviceX.code + '-' + (mindex + 1); targetRow.getCell(3).value = majorX.name; targetRow.getCell(4).value = numberFormatter(m.mth2.area, 3); targetRow.getCell(5).value = m.mth2.basicFormula; targetRow.getCell(6).value = numberFormatter(m.mth2.basicFee_basic, 2); targetRow.getCell(7).value = m.mth2.optionalFormula; targetRow.getCell(8).value = numberFormatter(m.mth2.basicFee_optional, 2); targetRow.getCell(9).value = numberFormatter(m.mth2.basicFee, 2); }); } }); }); } } if (sobj.method3) { cusInsertRowFunc(3 + num_3, [sheet_3.getRow(3)], sheet_3, (targetRow) => { targetRow.getCell(1).value = num_3++; targetRow.getCell(2).value = serviceX.code; targetRow.getCell(3).value = serviceX.name; targetRow.getCell(4).value = '/'; targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = '/'; targetRow.getCell(7).value = numberFormatter(sobj.method3.basicFee, 2); targetRow.getCell(8).value = '/'; targetRow.getCell(9).value = numberFormatter(sobj.method3.fee, 2); }); sobj.method3.det.forEach((tobj, tindex) => { const taskX = taskList[tobj.task]; cusInsertRowFunc(3 + num_3, [sheet_3.getRow(3)], sheet_3, (targetRow) => { targetRow.getCell(1).value = num_3++; targetRow.getCell(2).value = taskX.code; targetRow.getCell(3).value = taskX.name + (taskX.desc ? `(${taskX.desc})` : ''); targetRow.getCell(4).value = taskX.basicParam; targetRow.getCell(5).value = numberFormatter(tobj.price * taskX.conversion, 2); targetRow.getCell(6).value = numberFormatter(tobj.amount, 2); targetRow.getCell(7).value = numberFormatter(tobj.basicFee, 2); targetRow.getCell(8).value = numberFormatter(tobj.serviceCoe, 3); targetRow.getCell(9).value = numberFormatter(tobj.fee, 2); }); }); } if (sobj.method4) { cusInsertRowFunc(4 + num_4, [sheet_4.getRow(4)], sheet_4, (targetRow) => { targetRow.getCell(1).value = num_4++; targetRow.getCell(2).value = serviceX.code; targetRow.getCell(3).value = serviceX.name; targetRow.getCell(4).value = numberFormatter(sobj.method4.person_num, 0); targetRow.getCell(5).value = numberFormatter(sobj.method4.work_day, 2); targetRow.getCell(6).value = numberFormatter(sobj.method4.fee, 2); }); cusInsertRowFunc(4 + num_4_1, [sheet_4_1.getRow(4)], sheet_4_1, (targetRow) => { targetRow.getCell(1).value = num_4_1++; targetRow.getCell(2).value = serviceX.code; targetRow.getCell(3).value = serviceX.name; targetRow.getCell(4).value = '/'; targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = '/'; targetRow.getCell(7).value = numberFormatter(sobj.method4.person_num, 0); targetRow.getCell(8).value = numberFormatter(sobj.method4.work_day, 2); targetRow.getCell(9).value = numberFormatter(sobj.method4.fee, 2); }); sobj.method4.det.forEach((eobj, eindex) => { const expertX = expertList[eobj.expert]; cusInsertRowFunc(4 + num_4_1, [sheet_4_1.getRow(4)], sheet_4_1, (targetRow) => { targetRow.getCell(1).value = num_4_1++; targetRow.getCell(2).value = expertX.code; targetRow.getCell(3).value = expertX.name; targetRow.getCell(4).value = `${expertX.minPrice}~${expertX.maxPrice}`; targetRow.getCell(5).value = `${roundTo(toDecimal(toFiniteNumber(expertX.minPrice)).mul(toFiniteNumber(expertX.manageCoe)), 0)}~${roundTo(toDecimal(toFiniteNumber(expertX.maxPrice)).mul(toFiniteNumber(expertX.manageCoe)), 0)}`; targetRow.getCell(6).value = numberFormatter(eobj.price, 2); targetRow.getCell(7).value = numberFormatter(eobj.person_num, 0); targetRow.getCell(8).value = numberFormatter(eobj.work_day, 2); targetRow.getCell(9).value = numberFormatter(eobj.fee, 2); targetRow.getCell(10).value = eobj.remark; }); }); } }); let endRows = 1; if (ci.services.length) { cusInsertRowFunc(ci.services.length + 3 + endRows, [sheet_1.getRow(3)], sheet_1, (targetRow) => { targetRow.getCell(1).value = ci.services.length + endRows; targetRow.getCell(2).value = ''; targetRow.getCell(3).value = '基本、可选工作小计'; targetRow.getCell(4).value = numberFormatter(m1Sum, 2); targetRow.getCell(5).value = numberFormatter(m2Sum, 2); targetRow.getCell(6).value = numberFormatter(m3Sum, 2); targetRow.getCell(7).value = numberFormatter(m4Sum, 2); targetRow.getCell(8).value = numberFormatter(serviceSum1, 2); targetRow.getCell(9).value = numberFormatter(serviceSum2, 2); }); } if (ci.addtional) { endRows++; cusInsertRowFunc(ci.services.length + 3 + endRows, [sheet_1.getRow(3)], sheet_1, (targetRow) => { targetRow.getCell(1).value = ci.services.length + endRows; targetRow.getCell(2).value = ci.addtional.code; targetRow.getCell(3).value = ci.addtional.name; targetRow.getCell(4).value = ''; targetRow.getCell(5).value = ''; targetRow.getCell(6).value = ''; targetRow.getCell(7).value = ''; targetRow.getCell(8).value = ''; targetRow.getCell(9).value = ''; }); ci.addtional.det.forEach((addobj, addindex) => { endRows++; cusInsertRowFunc(ci.services.length + 3 + endRows, [sheet_1.getRow(3)], sheet_1, (targetRow) => { targetRow.getCell(1).value = ci.services.length + endRows; targetRow.getCell(2).value = addobj.code; targetRow.getCell(3).value = addobj.name; let tmpArr = []; if (addobj.m0) { tmpArr.push(`按基本工作和可选工作确认预算之和的${addobj.m0.coe}计得${addobj.m0.fee}元`); allMethods.add(0); } if (addobj.m4) tmpArr.push(`按工时法计得${addobj.m4.fee}元`); if (addobj.m5) tmpArr.push(`按数量单价计得${addobj.m5.fee}元`); targetRow.getCell(4).value = tmpArr.join(';'); targetRow.getCell(9).value = numberFormatter(addobj.fee, 2); if (addobj.m4) { cusInsertRowFunc(4 + num_4, [sheet_4.getRow(4)], sheet_4, (targetRow) => { targetRow.getCell(1).value = num_4++; targetRow.getCell(2).value = addobj.code; targetRow.getCell(3).value = addobj.name; targetRow.getCell(4).value = numberFormatter(addobj.m4.person_num, 0); targetRow.getCell(5).value = numberFormatter(addobj.m4.work_day, 2); targetRow.getCell(6).value = numberFormatter(addobj.m4.fee, 2); }); cusInsertRowFunc(4 + num_4_1, [sheet_4_1.getRow(4)], sheet_4_1, (targetRow) => { targetRow.getCell(1).value = num_4_1++; targetRow.getCell(2).value = addobj.code; targetRow.getCell(3).value = addobj.name; targetRow.getCell(4).value = '/'; targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = '/'; targetRow.getCell(7).value = numberFormatter(addobj.m4.person_num, 0); targetRow.getCell(8).value = numberFormatter(addobj.m4.work_day, 2); targetRow.getCell(9).value = numberFormatter(addobj.m4.fee, 2); }); addobj.m4.det.forEach((eobj, eindex) => { const expertX = expertList[eobj.expert]; cusInsertRowFunc(4 + num_4_1, [sheet_4_1.getRow(4)], sheet_4_1, (targetRow) => { targetRow.getCell(1).value = num_4_1++; targetRow.getCell(2).value = expertX.code; targetRow.getCell(3).value = expertX.name; targetRow.getCell(4).value = `${expertX.minPrice}~${expertX.maxPrice}`; targetRow.getCell(5).value = `${roundTo(toDecimal(toFiniteNumber(expertX.minPrice)).mul(toFiniteNumber(expertX.manageCoe)), 0)}~${roundTo(toDecimal(toFiniteNumber(expertX.maxPrice)).mul(toFiniteNumber(expertX.manageCoe)), 0)}`; targetRow.getCell(6).value = numberFormatter(eobj.price, 2); targetRow.getCell(7).value = numberFormatter(eobj.person_num, 0); targetRow.getCell(8).value = numberFormatter(eobj.work_day, 2); targetRow.getCell(9).value = numberFormatter(eobj.fee, 2); targetRow.getCell(10).value = eobj.remark; }); }); } if (addobj.m5) { cusInsertRowFunc(4 + num_5, [sheet_5.getRow(4)], sheet_5, (targetRow) => { num_5++; targetRow.getCell(1).value = addobj.code; targetRow.getCell(2).value = addobj.name; targetRow.getCell(3).value = '/'; targetRow.getCell(4).value = '/'; targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = numberFormatter(addobj.m5.fee, 2); }); const tmpJSS = JSON.stringify(addobj.code); addobj.m5.det.forEach((eobj, eindex) => { let code = JSON.parse(tmpJSS); code.richText.push({ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: '-' + (eindex + 1) }); cusInsertRowFunc(4 + num_5, [sheet_5.getRow(4)], sheet_5, (targetRow) => { num_5++; targetRow.getCell(1).value = code; targetRow.getCell(2).value = eobj.name; targetRow.getCell(3).value = eobj.unit; targetRow.getCell(4).value = numberFormatter(eobj.amount, 3); targetRow.getCell(5).value = numberFormatter(eobj.price, 2); targetRow.getCell(6).value = numberFormatter(eobj.fee, 2); targetRow.getCell(7).value = eobj.remark; }); }); } }); }); endRows++; cusInsertRowFunc(ci.services.length + 3 + endRows, [sheet_1.getRow(3)], sheet_1, (targetRow) => { targetRow.getCell(1).value = ci.services.length + endRows; targetRow.getCell(2).value = ''; targetRow.getCell(3).value = '附加工作小计'; targetRow.getCell(4).value = ''; targetRow.getCell(5).value = ''; targetRow.getCell(6).value = ''; targetRow.getCell(7).value = ''; targetRow.getCell(8).value = ''; targetRow.getCell(9).value = numberFormatter(ci.addtional.fee, 2); }); } if (ci.reserve) { endRows++; cusInsertRowFunc(ci.services.length + 3 + endRows, [sheet_1.getRow(3)], sheet_1, (targetRow) => { targetRow.getCell(1).value = ci.services.length + endRows; targetRow.getCell(2).value = ci.reserve.code; targetRow.getCell(3).value = ci.reserve.name; let tmpArr = []; if (ci.reserve.m0) { tmpArr.push(`按基本工作、可选工作与附加工作确认预算之和的${ci.reserve.m0.coe}%计得${ci.reserve.m0.fee}元`); allMethods.add(0); allReserveMethods[0].cid.push(index); allReserveMethods[0].coe.push(ci.reserve.m0.coe); allReserveMethods[0].coeSet.add(ci.reserve.m0.coe); } if (ci.reserve.m4) { tmpArr.push(`按工时法计得${ci.reserve.m4.fee}元`); allReserveMethods[4].push(index); } if (ci.reserve.m5) { tmpArr.push(`按数量单价计得${ci.reserve.m5.fee}元`); allReserveMethods[5].push(index); } targetRow.getCell(4).value = tmpArr.join(';'); targetRow.getCell(9).value = numberFormatter(ci.reserve.fee, 2); }); if (ci.reserve.m4) { cusInsertRowFunc(4 + num_4, [sheet_4.getRow(4)], sheet_4, (targetRow) => { targetRow.getCell(1).value = num_4++; targetRow.getCell(2).value = ci.reserve.code; targetRow.getCell(3).value = ci.reserve.name; targetRow.getCell(4).value = numberFormatter(ci.reserve.m4.person_num, 0); targetRow.getCell(5).value = numberFormatter(ci.reserve.m4.work_day, 2); targetRow.getCell(6).value = numberFormatter(ci.reserve.m4.fee, 2); }); cusInsertRowFunc(4 + num_4_1, [sheet_4_1.getRow(4)], sheet_4_1, (targetRow) => { targetRow.getCell(1).value = num_4_1++; targetRow.getCell(2).value = ci.reserve.code; targetRow.getCell(3).value = ci.reserve.name; targetRow.getCell(4).value = '/'; targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = '/'; targetRow.getCell(7).value = numberFormatter(ci.reserve.m4.person_num, 0); targetRow.getCell(8).value = numberFormatter(ci.reserve.m4.work_day, 2); targetRow.getCell(9).value = numberFormatter(ci.reserve.m4.fee, 2); }); ci.reserve.m4.det.forEach((eobj, eindex) => { const expertX = expertList[eobj.expert]; cusInsertRowFunc(4 + num_4_1, [sheet_4_1.getRow(4)], sheet_4_1, (targetRow) => { targetRow.getCell(1).value = num_4_1++; targetRow.getCell(2).value = expertX.code; targetRow.getCell(3).value = expertX.name; targetRow.getCell(4).value = `${expertX.minPrice}~${expertX.maxPrice}`; targetRow.getCell(5).value = `${roundTo(toDecimal(toFiniteNumber(expertX.minPrice)).mul(toFiniteNumber(expertX.manageCoe)), 0)}~${roundTo(toDecimal(toFiniteNumber(expertX.maxPrice)).mul(toFiniteNumber(expertX.manageCoe)), 0)}`; targetRow.getCell(6).value = numberFormatter(eobj.price, 2); targetRow.getCell(7).value = numberFormatter(eobj.person_num, 0); targetRow.getCell(8).value = numberFormatter(eobj.work_day, 2); targetRow.getCell(9).value = numberFormatter(eobj.fee, 2); targetRow.getCell(10).value = eobj.remark; }); }); } if (ci.reserve.m5) { cusInsertRowFunc(4 + num_5, [sheet_5.getRow(4)], sheet_5, (targetRow) => { num_5++; targetRow.getCell(1).value = ci.reserve.code; targetRow.getCell(2).value = ci.reserve.name; targetRow.getCell(3).value = '/'; targetRow.getCell(4).value = '/'; targetRow.getCell(5).value = '/'; targetRow.getCell(6).value = numberFormatter(ci.reserve.m5.fee, 2); }); const tmpJSS = JSON.stringify(ci.reserve.code); ci.reserve.m5.det.forEach((eobj, eindex) => { let code = JSON.parse(tmpJSS); code.richText.push({ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: '-' + (eindex + 1) }); cusInsertRowFunc(4 + num_5, [sheet_5.getRow(4)], sheet_5, (targetRow) => { num_5++; targetRow.getCell(1).value = code; targetRow.getCell(2).value = eobj.name; targetRow.getCell(3).value = eobj.unit; targetRow.getCell(4).value = numberFormatter(eobj.amount, 3); targetRow.getCell(5).value = numberFormatter(eobj.price, 2); targetRow.getCell(6).value = numberFormatter(eobj.fee, 2); targetRow.getCell(7).value = eobj.remark; }); }); } } sheet_1.spliceRows(3, 1); sheet_1.getRow(ci.services.length + endRows + 3).getCell(1).value = ci.services.length + endRows + 1; sheet_1.getRow(ci.services.length + endRows + 3).getCell(9).value = numberFormatter(ci.fee, 2); sheet_1.mergeCells(2 + ci.services.length + endRows + 2, 2, 2 + ci.services.length + endRows + 2, 9); sheet_1.getRow(2 + ci.services.length + endRows + 2).height = 100; sheet_1.getRow(2 + ci.services.length + endRows + 2).getCell(2).border.right = { style: 'thin' }; if (sheet_2) { sheet_2.spliceRows(4, 1); sheet_2.mergeCells(3 + num_2, 2, 3 + num_2, 11); sheet_2.getRow(3 + num_2).height = 100; sheet_2.getRow(3 + num_2).getCell(2).border.right = { style: 'thin' }; if (sheet_2_1) { sheet_2_1.spliceRows(4, 1); sheet_2_1.mergeCells(3 + num_2_1, 2, 3 + num_2_1, 9); sheet_2_1.getRow(3 + num_2_1).height = 100; sheet_2_1.getRow(3 + num_2_1).getCell(2).border.right = { style: 'thin' }; } if (sheet_2_2) { sheet_2_2.spliceRows(4, 1); sheet_2_2.mergeCells(3 + num_2_2, 2, 3 + num_2_2, 9); sheet_2_2.getRow(3 + num_2_2).height = 100; sheet_2_2.getRow(3 + num_2_2).getCell(2).border.right = { style: 'thin' }; } } if (sheet_3) { sheet_3.spliceRows(3, 1); sheet_3.mergeCells(2 + num_3, 2, 2 + num_3, 9); sheet_3.getRow(2 + num_3).height = 100; sheet_3.getRow(2 + num_3).getCell(2).border.right = { style: 'thin' }; } if (sheet_4) { sheet_4.spliceRows(4, 1); sheet_4.getRow(2).height = 20.25; sheet_4_1.spliceRows(4, 1); sheet_4_1.mergeCells(3 + num_4_1, 2, 3 + num_4_1, 10); sheet_4_1.getRow(3 + num_4_1).height = 100; sheet_4_1.getRow(3 + num_4_1).getCell(2).border.right = { style: 'thin' }; } if (sheet_5) { sheet_5.spliceRows(4, 1); sheet_5.getRow(2).height = 20.25; } for (let i = 1; i <= endRows; i++) { sheet_1.mergeCells(ci.services.length + 3 + i, 4, ci.services.length + 3 + i, 8); if (sheet_1.getRow(ci.services.length + 3 + i).getCell(4).style.alignment) { sheet_1.getRow(ci.services.length + 3 + i).getCell(4).style.alignment.horizontal = 'center'; sheet_1.getRow(ci.services.length + 3 + i).getCell(4).style.alignment.wrapText = true; if (i != 1 && i != endRows && sheet_1.getRow(ci.services.length + 3 + i).getCell(2).value) sheet_1.getRow(ci.services.length + 3 + i).getCell(9).style.font.size = 22; } } }); allServices.sort((a, b) => serviceList[a.id].order - serviceList[b.id].order); allServices.forEach((s, sindex) => { const serviceX = serviceList[s.id]; cusInsertRowFunc(3 + sindex, [yz01_sheet.getRow(2)], yz01_sheet, (targetRow) => { let siSum = 0; for (let i = 0; i < yz01Num; i++) { targetRow.getCell(i * 7 + 1).value = sindex + 1; targetRow.getCell(i * 7 + 2).value = serviceX.code; targetRow.getCell(i * 7 + 3).value = serviceX.name; targetRow.getCell(i * 7 + 4).value = numberFormatter(s.contracts[i * 4], 2); targetRow.getCell(i * 7 + 5).value = numberFormatter(s.contracts[i * 4 + 1], 2); targetRow.getCell(i * 7 + 6).value = numberFormatter(s.contracts[i * 4 + 2], 2); siSum = addNumbers( siSum, toFiniteNumber(s.contracts[i * 4]), toFiniteNumber(s.contracts[i * 4 + 1]), toFiniteNumber(s.contracts[i * 4 + 2]) ); if (i == yz01Num - 1 && yz01Mod == 0) { targetRow.getCell(i * 7 + 7).value = numberFormatter(siSum, 2); } else { targetRow.getCell(i * 7 + 7).value = numberFormatter(s.contracts[i * 4 + 3], 2); siSum = addNumbers(siSum, toFiniteNumber(s.contracts[i * 4 + 3])); } } if (yz01Mod) { targetRow.getCell(yz01Num * 7 + 1).value = sindex + 1; targetRow.getCell(yz01Num * 7 + 2).value = serviceX.code; targetRow.getCell(yz01Num * 7 + 3).value = serviceX.name; if (yz01Mod == 1) { targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(siSum, 2); } else if (yz01Mod == 2) { targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(s.contracts[yz01Num * 4], 2); siSum = addNumbers(siSum, toFiniteNumber(s.contracts[yz01Num * 4])); targetRow.getCell(yz01Num * 7 + 5).value = numberFormatter(siSum, 2); } else { targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(s.contracts[yz01Num * 4], 2); targetRow.getCell(yz01Num * 7 + 5).value = numberFormatter(s.contracts[yz01Num * 4 + 1], 2); siSum = addNumbers( siSum, toFiniteNumber(s.contracts[yz01Num * 4]), toFiniteNumber(s.contracts[yz01Num * 4 + 1]) ); targetRow.getCell(yz01Num * 7 + 6).value = numberFormatter(siSum, 2); } } }); }); let endRows = 0; if (Object.keys(allAddtional).length) { endRows++; let firstNum = Object.keys(allAddtional)[0]; cusInsertRowFunc(3 + allServices.length, [yz01_sheet.getRow(2)], yz01_sheet, (targetRow) => { let siSum = 0; for (let i = 0; i < yz01Num; i++) { targetRow.getCell(i * 7 + 1).value = allServices.length + 1; targetRow.getCell(i * 7 + 2).value = allAddtional[firstNum].code; targetRow.getCell(i * 7 + 3).value = allAddtional[firstNum].name; targetRow.getCell(i * 7 + 4).value = numberFormatter(allAddtional[i * 4]?.fee, 2); targetRow.getCell(i * 7 + 5).value = numberFormatter(allAddtional[i * 4 + 1]?.fee, 2); targetRow.getCell(i * 7 + 6).value = numberFormatter(allAddtional[i * 4 + 2]?.fee, 2); siSum = addNumbers( siSum, toFiniteNumber(allAddtional[i * 4]?.fee), toFiniteNumber(allAddtional[i * 4 + 1]?.fee), toFiniteNumber(allAddtional[i * 4 + 2]?.fee) ); if (i == yz01Num - 1 && yz01Mod == 0) { targetRow.getCell(i * 7 + 7).value = numberFormatter(siSum, 2); } else { targetRow.getCell(i * 7 + 7).value = numberFormatter(allAddtional[i * 4 + 3]?.fee, 2); siSum = addNumbers(siSum, toFiniteNumber(allAddtional[i * 4 + 3]?.fee)); } } if (yz01Mod) { targetRow.getCell(yz01Num * 7 + 1).value = allServices.length + 1; targetRow.getCell(yz01Num * 7 + 2).value = allAddtional[firstNum].code; targetRow.getCell(yz01Num * 7 + 3).value = allAddtional[firstNum].name; if (yz01Mod == 1) { targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(siSum, 2); } else if (yz01Mod == 2) { targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(allAddtional[yz01Num * 4]?.fee, 2); siSum = addNumbers(siSum, toFiniteNumber(allAddtional[yz01Num * 4]?.fee)); targetRow.getCell(yz01Num * 7 + 5).value = numberFormatter(siSum, 2); } else { targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(allAddtional[yz01Num * 4]?.fee, 2); targetRow.getCell(yz01Num * 7 + 5).value = numberFormatter(allAddtional[yz01Num * 4 + 1]?.fee, 2); siSum = addNumbers( siSum, toFiniteNumber(allAddtional[yz01Num * 4]?.fee), toFiniteNumber(allAddtional[yz01Num * 4 + 1]?.fee) ); targetRow.getCell(yz01Num * 7 + 6).value = numberFormatter(siSum, 2); } } }); } if (Object.keys(allReserve).length) { endRows++; let firstNum = Object.keys(allReserve)[0]; cusInsertRowFunc(3 + allServices.length + endRows - 1, [yz01_sheet.getRow(2)], yz01_sheet, (targetRow) => { let siSum = 0; for (let i = 0; i < yz01Num; i++) { targetRow.getCell(i * 7 + 1).value = allServices.length + endRows; targetRow.getCell(i * 7 + 2).value = allReserve[firstNum].code; targetRow.getCell(i * 7 + 3).value = allReserve[firstNum].name; targetRow.getCell(i * 7 + 4).value = numberFormatter(allReserve[i * 4]?.fee, 2); targetRow.getCell(i * 7 + 5).value = numberFormatter(allReserve[i * 4 + 1]?.fee, 2); targetRow.getCell(i * 7 + 6).value = numberFormatter(allReserve[i * 4 + 2]?.fee, 2); siSum = addNumbers( siSum, toFiniteNumber(allReserve[i * 4]?.fee), toFiniteNumber(allReserve[i * 4 + 1]?.fee), toFiniteNumber(allReserve[i * 4 + 2]?.fee) ); if (i == yz01Num - 1 && yz01Mod == 0) { targetRow.getCell(i * 7 + 7).value = numberFormatter(siSum, 2); } else { targetRow.getCell(i * 7 + 7).value = numberFormatter(allReserve[i * 4 + 3]?.fee, 2); siSum = addNumbers(siSum, toFiniteNumber(allReserve[i * 4 + 3]?.fee)); } } if (yz01Mod) { targetRow.getCell(yz01Num * 7 + 1).value = allServices.length + endRows; targetRow.getCell(yz01Num * 7 + 2).value = allReserve[firstNum].code; targetRow.getCell(yz01Num * 7 + 3).value = allReserve[firstNum].name; if (yz01Mod == 1) { targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(siSum, 2); } else if (yz01Mod == 2) { targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(allReserve[yz01Num * 4]?.fee, 2); siSum = addNumbers(siSum, toFiniteNumber(allReserve[yz01Num * 4]?.fee)); targetRow.getCell(yz01Num * 7 + 5).value = numberFormatter(siSum, 2); } else { targetRow.getCell(yz01Num * 7 + 4).value = numberFormatter(allReserve[yz01Num * 4]?.fee, 2); targetRow.getCell(yz01Num * 7 + 5).value = numberFormatter(allReserve[yz01Num * 4 + 1]?.fee, 2); siSum = addNumbers( siSum, toFiniteNumber(allReserve[yz01Num * 4]?.fee), toFiniteNumber(allReserve[yz01Num * 4 + 1]?.fee) ); targetRow.getCell(yz01Num * 7 + 6).value = numberFormatter(siSum, 2); } } }); } const yz01SumRow = yz01_sheet.getRow(allServices.length + endRows + 3); for (let i = 0; i < yz01Num; i++) { yz01SumRow.getCell(i * 7 + 1).value = allServices.length + endRows + 1; yz01SumRow.getCell(i * 7 + 4).value = numberFormatter(data.contracts[i * 4]?.fee, 2); yz01SumRow.getCell(i * 7 + 5).value = numberFormatter(data.contracts[i * 4 + 1]?.fee, 2); yz01SumRow.getCell(i * 7 + 6).value = numberFormatter(data.contracts[i * 4 + 2]?.fee, 2); if (i == yz01Num - 1 && yz01Mod == 0) { yz01SumRow.getCell(i * 7 + 7).value = numberFormatter(data.fee, 2); } else { yz01SumRow.getCell(i * 7 + 7).value = numberFormatter(data.contracts[i * 4 + 3]?.fee, 2); } } if (yz01Mod) { yz01SumRow.getCell(yz01Num * 7 + 1).value = allServices.length + endRows + 1; if (yz01Mod == 1) { yz01SumRow.getCell(yz01Num * 7 + 4).value = numberFormatter(data.fee, 2); } else if (yz01Mod == 2) { yz01SumRow.getCell(yz01Num * 7 + 4).value = numberFormatter(data.contracts[yz01Num * 4]?.fee, 2); yz01SumRow.getCell(yz01Num * 7 + 5).value = numberFormatter(data.fee, 2); } else { yz01SumRow.getCell(yz01Num * 7 + 4).value = numberFormatter(data.contracts[yz01Num * 4]?.fee, 2); yz01SumRow.getCell(yz01Num * 7 + 5).value = numberFormatter(data.contracts[yz01Num * 4 + 1]?.fee, 2); yz01SumRow.getCell(yz01Num * 7 + 6).value = numberFormatter(data.fee, 2); } } ml_sheet.spliceRows(6, 7); ml_sheet.spliceRows(6, 1); yz01_sheet.spliceRows(2, 1); // 合并说明 let yz01_lastCol; if (yz01Num) { for (let i = 0; i < yz01Num; i++) { yz01_sheet.getRow(1).getCell(i * 7 + 4).value = `${data.contracts[i * 4].name}预算(元)`; yz01_sheet.getRow(1).getCell(i * 7 + 4 + 1).value = `${data.contracts[i * 4 + 1].name}预算(元)`; yz01_sheet.getRow(1).getCell(i * 7 + 4 + 2).value = `${data.contracts[i * 4 + 2].name}预算(元)`; if (i == yz01Num - 1 && yz01Mod == 0) { yz01_sheet.getRow(1).getCell(i * 7 + 4 + 3).value = `预算小计(元)`; yz01_lastCol = i * 7 + 4; } else { yz01_sheet.getRow(1).getCell(i * 7 + 4 + 3).value = `${data.contracts[i * 4 + 3].name}预算(元)`; } yz01_sheet.mergeCells(allServices.length + endRows + 3, i * 7 + 2, allServices.length + endRows + 3, i * 7 + 7); yz01_sheet.getRow(allServices.length + endRows + 3).getCell(i * 7 + 2).border.right = { style: 'thin' }; } } if (yz01Mod) { for (let i = 0; i < yz01Mod; i++) { if (i == yz01Mod - 1) { yz01_sheet.getRow(1).getCell(yz01Num * 7 + 4 + i).value = `预算小计(元)`; yz01_lastCol = yz01Num * 7 + 4 + i; } else { yz01_sheet.getRow(1).getCell(yz01Num * 7 + 4 + i).value = `${data.contracts[yz01Num * 4 + i].name}预算(元)`; } } yz01_sheet.mergeCells(allServices.length + endRows + 3, yz01Num * 7 + 2, allServices.length + endRows + 3, yz01Num * 7 + 3 + yz01Mod); yz01_sheet.getRow(allServices.length + endRows + 3).getCell(yz01Num * 7 + 2).border.right = { style: 'thin' }; } yz01_sheet.getRow(allServices.length + endRows + 3).height = 100; // for (let i = 1; i <= allServices.length + endRows + 3; i++) { // yz01_sheet.getRow(i).getCell(yz01_lastCol + 1).value = ' '; // if (yz01_sheet.getRow(i).getCell(yz01_lastCol + 1).style.font) { // yz01_sheet.getRow(i).getCell(yz01_lastCol + 1).style.font.size = i == 1 ? 22 : 20; // } else { // yz01_sheet.getRow(i).getCell(yz01_lastCol + 1).style = { font: { size: i == 1 ? 22 : 20 } }; // // } // // } // yz01_sheet.pageSetup.printArea = `A1:${yz01_sheet.getRow(allServices.length + endRows + 3).getCell(yz01_lastCol)._address}`; ml_sheet.mergeCells(ml_slotRow - 8, 1, ml_slotRow - 8, 4); workbook.removeWorksheet('预i-1表'); workbook.removeWorksheet('预i-2表'); workbook.removeWorksheet('预i-2-1表'); workbook.removeWorksheet('预i-2-2表'); workbook.removeWorksheet('预i-3表'); workbook.removeWorksheet('预i-4表'); workbook.removeWorksheet('预i-4-1表'); workbook.removeWorksheet('预i-5表'); // 编辑辅01/02/03表内容 // 辅01表 if (Object.keys(allMajors).length) { let f01_sheet = workbook.getWorksheet('辅01表'); let f01Mod = (data.contracts.length + 1) % 3; let f01Num = (data.contracts.length + 1 - f01Mod) / 3; switch (f01Mod) { case 0: f01_sheet.spliceColumns(11, 14); break; case 1: f01_sheet.spliceColumns(11, 8); break; case 2: f01_sheet.spliceColumns(19, 6); break; } if (f01Num == 0) { f01_sheet.spliceColumns(1, 10); } else { if (f01Num > 1) { for (let i = 0; i < f01Num - 1; i++) { insertAndCopyColumn(10 * (i + 1) + 1, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], f01_sheet); } } for (let i = 0; i < f01Num; i++) { f01_sheet.mergeCells(1, i * 10 + 1, 2, i * 10 + 1); f01_sheet.mergeCells(1, i * 10 + 2, 2, i * 10 + 2); f01_sheet.mergeCells(1, i * 10 + 3, 2, i * 10 + 3); f01_sheet.mergeCells(1, i * 10 + 10, 2, i * 10 + 10); f01_sheet.mergeCells(1, i * 10 + 4, 1, i * 10 + 5); f01_sheet.getRow(1).getCell(i * 10 + 4).value = data.contracts[i * 3].name; f01_sheet.mergeCells(1, i * 10 + 6, 1, i * 10 + 7); f01_sheet.getRow(1).getCell(i * 10 + 6).value = data.contracts[i * 3 + 1].name; f01_sheet.mergeCells(1, i * 10 + 8, 1, i * 10 + 9); if (f01Mod == 0 && i == f01Num - 1) { f01_sheet.getRow(1).getCell(i * 10 + 8).value = '项目小计'; } else { f01_sheet.getRow(1).getCell(i * 10 + 8).value = data.contracts[i * 3 + 2].name; } } } if (f01Mod > 0) { f01_sheet.mergeCells(1, f01Num * 10 + 1, 2, f01Num * 10 + 1); f01_sheet.mergeCells(1, f01Num * 10 + 2, 2, f01Num * 10 + 2); f01_sheet.mergeCells(1, f01Num * 10 + 3, 2, f01Num * 10 + 3); f01_sheet.mergeCells(1, f01Num * 10 + 2 * f01Mod + 4, 2, f01Num * 10 + 2 * f01Mod + 4); f01_sheet.mergeCells(1, f01Num * 10 + 4, 1, f01Num * 10 + 5); f01_sheet.getRow(1).getCell(f01Num * 10 + 4).value = f01Mod == 1 ? '项目小计' : data.contracts[f01Num * 3].name; if (f01Mod == 2) { f01_sheet.mergeCells(1, f01Num * 10 + 6, 1, f01Num * 10 + 7); f01_sheet.getRow(1).getCell(f01Num * 10 + 6).value = '项目小计'; } } let num_f01 = 1; Object.keys(allMajors).sort((a, b) => (a < 0 ? a : majorList[a].order) - (b < 0 ? b : majorList[b].order)).forEach(majorid => { let scaleX = allMajors[majorid]; let code; let name; let hasCost; let hasArea; if (majorid == -1) { code = ''; name = '总投资'; hasCost = true; hasArea = false; } else { code = majorList[majorid].code; name = majorList[majorid].name; hasCost = majorList[majorid].hasCost; hasArea = majorList[majorid].hasArea; } cusInsertRowFunc(3 + num_f01, [f01_sheet.getRow(3)], f01_sheet, (targetRow) => { let rowNum = num_f01++; for (let i = 0; i < f01Num; i++) { targetRow.getCell(i * 10 + 1).value = rowNum; targetRow.getCell(i * 10 + 2).value = code; targetRow.getCell(i * 10 + 3).value = name; if (hasCost) { targetRow.getCell(i * 10 + 4).value = numberFormatter(scaleX[i * 3]?.cost, 2); targetRow.getCell(i * 10 + 6).value = numberFormatter(scaleX[i * 3 + 1]?.cost, 2); targetRow.getCell(i * 10 + 8).value = numberFormatter(scaleX[i * 3 + 2]?.cost, 2); } else { targetRow.getCell(i * 10 + 4).value = '/'; targetRow.getCell(i * 10 + 6).value = '/'; targetRow.getCell(i * 10 + 8).value = '/'; } if (hasArea) { targetRow.getCell(i * 10 + 5).value = numberFormatter(scaleX[i * 3]?.area, 3); targetRow.getCell(i * 10 + 7).value = numberFormatter(scaleX[i * 3 + 1]?.area, 3); targetRow.getCell(i * 10 + 9).value = numberFormatter(scaleX[i * 3 + 2]?.area, 3); } else { targetRow.getCell(i * 10 + 5).value = '/'; targetRow.getCell(i * 10 + 7).value = '/'; targetRow.getCell(i * 10 + 9).value = '/'; } } if (f01Mod > 0) { targetRow.getCell(f01Num * 10 + 1).value = rowNum; targetRow.getCell(f01Num * 10 + 2).value = code; targetRow.getCell(f01Num * 10 + 3).value = name; for (let i = 0; i < f01Mod; i++) { if (hasCost) { targetRow.getCell(f01Num * 10 + 4 + i * 2).value = numberFormatter(scaleX[f01Num * 3 + i]?.cost, 2); } else { targetRow.getCell(f01Num * 10 + 4 + i * 2).value = '/'; } if (hasArea) { targetRow.getCell(f01Num * 10 + 5 + i * 2).value = numberFormatter(scaleX[f01Num * 3 + i]?.area, 3); } else { targetRow.getCell(f01Num * 10 + 5 + i * 2).value = '/'; } } } }); }); f01_sheet.spliceRows(3, 1); // f01_sheet.getRow(2).height = 27; f01_sheet.orderNo = ml_number + 2 + 12; } else { workbook.removeWorksheet('辅01表'); } // 辅02/03表 if (data.serviceCoes?.length) { let f02_sheet = workbook.getWorksheet('辅02表'); let num_f02 = 1; data.serviceCoes.sort((a, b) => serviceList[a.serviceid].order - serviceList[b.serviceid].order).forEach(scoei => { let serviceCoeX = serviceList[scoei.serviceid]; cusInsertRowFunc(2 + num_f02, [f02_sheet.getRow(2)], f02_sheet, (targetRow) => { targetRow.getCell(1).value = num_f02++; targetRow.getCell(2).value = serviceCoeX.code; targetRow.getCell(3).value = serviceCoeX.name; targetRow.getCell(4).value = numberFormatter(serviceCoeX.defCoe, 3); targetRow.getCell(5).value = numberFormatter(scoei.coe, 3); targetRow.getCell(6).value = scoei.remark; }); }); f02_sheet.spliceRows(2, 1); f02_sheet.orderNo = ml_number + 3 + 12; } else { workbook.removeWorksheet('辅02表'); } if (data.majorCoes?.length) { let f03_sheet = workbook.getWorksheet('辅03表'); let num_f03 = 1; data.majorCoes.sort((a, b) => (a < 0 ? a : majorList[a.majorid].order) - (b < 0 ? b : majorList[b.majorid].order)).forEach(mcoei => { let majorCoeX = majorList[mcoei.majorid]; cusInsertRowFunc(2 + num_f03, [f03_sheet.getRow(2)], f03_sheet, (targetRow) => { targetRow.getCell(1).value = num_f03++; targetRow.getCell(2).value = majorCoeX.code; targetRow.getCell(3).value = majorCoeX.name; targetRow.getCell(4).value = numberFormatter(majorCoeX.defCoe, 3); targetRow.getCell(5).value = numberFormatter(mcoei.coe, 3); targetRow.getCell(6).value = mcoei.remark; }); }); f03_sheet.spliceRows(2, 1); f03_sheet.orderNo = ml_number + 4 + 12; } else { workbook.removeWorksheet('辅03表'); } // 统一设置页眉页脚字体 workbook._worksheets.forEach(sheet => { if (sheet) { if (sheet.headerFooter.oddHeader) sheet.headerFooter.oddHeader = sheet.headerFooter.oddHeader.replace(/&([CLR])&/g, '&$1&"宋体"&'); if (sheet.headerFooter.oddFooter) sheet.headerFooter.oddFooter = sheet.headerFooter.oddFooter.replace(/&([CLR])&/g, '&$1&"宋体"&'); } }); // 更新编制说明 const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); // const pageMaxHei = 733; // 20.25 25.5 33.75 const titleHeiDet = { fir: 11.25, els: 22.5 }; const textHeiDet = { fir: 6.75, els: 13.5 }; const descSheet = workbook.getWorksheet('编制说明'); let descRowNum = 1; let titleArr = paragraphLineBreakFor1112(data.name, ctx, 0); descSheet.getRow(descRowNum).getCell(1).value = titleArr[0]; descRowNum++; if (titleArr.length > 1) { for (let i = 1; i < titleArr.length; i++) { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = titleArr[i]; }); } } descRowNum += 2; if (data.overview?.length) { let overviewArr = paragraphLineBreakFor1112(' (一)' + data.overview + (/。$/.test(data.overview) ? '' : '。'), ctx); descSheet.getRow(descRowNum).getCell(1).value = overviewArr[0]; descRowNum++; if (overviewArr.length > 1) { for (let i = 1; i < overviewArr.length; i++) { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = overviewArr[i]; }); } } } else { descRowNum++; } if (data.scale?.length) { let engCosts = data.scale.filter(f => f.major > 6 && majorList[f.major].hasCost); if (engCosts.length) { descRowNum++; let copyRowNum = descRowNum; engCosts.forEach((sci, scindex) => { descRowNum++; cusInsertRowFunc(descRowNum, [descSheet.getRow(copyRowNum)], descSheet, (targetRow) => { targetRow.getCell(1).value = ` ${scindex + 1}.${majorList[sci.major].name}${Number(sci.cost).toLocaleString()}万元${scindex == engCosts.length - 1 ? '。' : ';'}`; }); }); descSheet.spliceRows(copyRowNum, 1); } else { descSheet.spliceRows(descRowNum, 2); descSheet.getRow(descRowNum).getCell(1).value = ' (二)本项目征地拆迁费包括:'; descSheet.getRow(descRowNum + 2).getCell(1).value = ' (三)其他费用包括:'; } let zcScales = data.scale.filter(f => [1, 2, 3].includes(f.major)); if (zcScales.length) { descRowNum++; let copyRowNum = descRowNum; zcScales.forEach((sci, scindex) => { descRowNum++; cusInsertRowFunc(descRowNum, [descSheet.getRow(copyRowNum)], descSheet, (targetRow) => { switch (sci.major) { case 1: let desc1 = []; if (sci.area) desc1.push(`征地总面积约${Number(sci.area).toLocaleString()}亩`); if (sci.cost) desc1.push(`征地补偿费用${Number(sci.cost).toLocaleString()}万元`); targetRow.getCell(1).value = ` ${scindex + 1}.征地情况:${desc1.join(',')}。`; break; case 2: let desc2 = []; if (sci.area) desc2.push(`拆迁总面积约${Number(sci.area).toLocaleString()}亩`); if (sci.cost) desc2.push(`拆迁补偿费用${Number(sci.cost).toLocaleString()}万元`); targetRow.getCell(1).value = ` ${scindex + 1}.拆迁补偿情况:${desc2.join(',')}。`; break; case 3: targetRow.getCell(1).value = ` ${scindex + 1}.迁改工程情况:费用${Number(sci.cost).toLocaleString()}万元。`; break; } }); }); descSheet.spliceRows(copyRowNum, 1); } else { descSheet.spliceRows(descRowNum, 2); descSheet.getRow(descRowNum).getCell(1).value = engCosts.length ? ' (三)其他费用包括:' : ' (二)其他费用包括:'; } let otherCosts = data.scale.filter(f => [4, 5, 6].includes(f.major)); if (otherCosts.length) { let desc = []; otherCosts.forEach(sci => { desc.push(`${majorList[sci.major].name}${Number(sci.cost).toLocaleString()}万元`); }); let text = descSheet.getRow(descRowNum).getCell(1).value + `${desc.join(',')}。`; let textArr = paragraphLineBreakFor1112(text, ctx); descSheet.getRow(descRowNum).getCell(1).value = textArr[0]; descRowNum++; if (textArr.length > 1) { for (let i = 1; i < textArr.length; i++) { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = textArr[i]; }); } } } else { descSheet.spliceRows(descRowNum, 1); } } else { descSheet.spliceRows(descRowNum, 5); descRowNum++; } descRowNum++; if (allServiceMajors.size > 0) { if (allServiceMajors.has(7) || allServiceMajors.has(18) || allServiceMajors.has(28)) { descSheet.getRow(descRowNum).getCell(1).value = descSheet.getRow(descRowNum).getCell(1).value + '本次委托包括全部工程及费用。'; descRowNum++; } else { let majorTexts = []; let hasOther = false; [...allServiceMajors].sort((a, b) => majorList[a].order - majorList[b].order).forEach(mid => { if (mid > 6) { majorTexts.push(majorList[mid].name); } else { hasOther = true; } }); let text = descSheet.getRow(descRowNum).getCell(1).value + '本项目的' + majorTexts.join('、') + (hasOther ? '及其他专项工程。' : '。'); let textArr = paragraphLineBreakFor1112(text, ctx); descSheet.getRow(descRowNum).getCell(1).value = textArr[0]; descRowNum++; if (textArr.length > 1) { for (let i = 1; i < textArr.length; i++) { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = textArr[i]; }); } } } } else { descSheet.getRow(descRowNum).getCell(1).value = descSheet.getRow(descRowNum).getCell(1).value + '×××××。'; descRowNum++; } if (data.contracts.length == 1) { // 合同段划分 descSheet.spliceRows(descRowNum, 1); // 咨询服务类型 let serviceText = ' (二)咨询服务类型:' + data.contracts[0].services.slice(0, -1).map(si => serviceList[si.id].name).join('、') + (data.contracts[0].services.length > 1 ? '和' : '') + serviceList[data.contracts[0].services.slice(-1)[0].id].name + '。'; let serviceTextArr = paragraphLineBreakFor1112(serviceText, ctx); serviceTextArr.forEach(ti => { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = ti; }); }); // 工作内容及要求 descRowNum++; } else { descSheet.getRow(descRowNum).getCell(1).value = descSheet.getRow(descRowNum).getCell(1).value.replace(/×××/g, data.contracts.length); let descRowNum1 = descRowNum + 1; let descRowNum2 = descRowNum + 2; let descRowNum3 = descRowNum + 2; console.log( data.contracts) data.contracts.forEach((ci, cindex) => { descRowNum3 = descRowNum3 - descRowNum2; descRowNum2 = descRowNum2 - descRowNum1 - 1; // 合同段划分 let ciMajorText = ` ${cindex + 1}.${ci.name}:`; if (ci.allServiceMajors.size > 0) { if (ci.allServiceMajors.has(7) || ci.allServiceMajors.has(18) || ci.allServiceMajors.has(28)) { ciMajorText = ciMajorText + '全部工程及费用。'; } else { let ciMajorTexts = []; let hasOther = false; [...ci.allServiceMajors].sort((a, b) => majorList[a].order - majorList[b].order).forEach(mid => { if (mid > 6) { ciMajorTexts.push(majorList[mid].name); } else { hasOther = true; } }); ciMajorText = ciMajorText + ciMajorTexts.join('、') + (hasOther ? '及其他专项工程。' : '。'); } } else { ciMajorText = ciMajorText + '×××××。'; } let majorTextArr = paragraphLineBreakFor1112(ciMajorText, ctx); majorTextArr.forEach(ti => { cusInsertRowFunc(descRowNum1, [descSheet.getRow(descRowNum1 - 1)], descSheet, (targetRow) => { descRowNum1++; targetRow.getCell(1).value = ti; }); }); // 咨询服务类型 descRowNum2 = descRowNum1 + descRowNum2 + 1; console.log(ci.services) let ciServiceText = ` ${cindex + 1}.${ci.name}:` + ci.services.slice(0, -1).map(si => serviceList[si.id].name).join('、') + (ci.services.length > 1 ? '和' : '') + serviceList[ci.services.slice(-1)[0].id].name + '。'; let ciServiceTextArr = paragraphLineBreakFor1112(ciServiceText, ctx); ciServiceTextArr.forEach(ti => { cusInsertRowFunc(descRowNum2, [descSheet.getRow(descRowNum2 - 1)], descSheet, (targetRow) => { descRowNum2++; targetRow.getCell(1).value = ti; }); }); // 工作内容及要求 descRowNum3 = descRowNum2 + descRowNum3; let ciTastNum = 1; let ciTaskTitle = ` (${ArabicToChinese(cindex + 4)})${ci.name}工作内容及要求`; let ciTaskTitleArr = paragraphLineBreakFor1112(ciTaskTitle, ctx); ciTaskTitleArr.forEach(ti => { cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ti; }); }); cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ` 1.工作内容包括:`; }); if (ci.hasPreTexts) { prefixTexts.forEach(ti => { let texts = paragraphLineBreakFor1112(` (${ciTastNum})${ti}。`, ctx); texts.forEach(ti => { cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ti; ciTastNum++; }); }); }); } ci.services.forEach((si, sindex) => { let siTextArr = paragraphLineBreakFor1112(` (${ciTastNum})${si.process == 1 ? '审核' : '编制'}${serviceList[si.id].name},具体工作内容包括:`, ctx); siTextArr.forEach(ti => { cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ti; ciTastNum++; }); }); si.tasks?.forEach((sti, stindex) => { let stiTextArr = paragraphLineBreakFor1112(` ${stindex + 1})${sti}。`, ctx); stiTextArr.forEach(ti => { cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ti; }); }); }); }); if (ci.addtional && ci.addtional.det.length) { ci.addtional.det.forEach(ai => { cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ` (${ciTastNum})${ai.id == 1 ? '负责协调工作,具体工作内容包括:' : '其他附加工作,具体工作内容包括:'}`; ciTastNum++; }); ai.tasks?.forEach((ati, atindex) => { let atiTextArr = paragraphLineBreakFor1112(` ${atindex + 1})${ati}。`, ctx); atiTextArr.forEach(ti => { cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ti; }); }); }); }); } if (ci.hasSufTexts) { suffixTexts.forEach(ti => { let texts = paragraphLineBreakFor1112(` (${ciTastNum})${ti}。`, ctx); texts.forEach(ti => { cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ti; ciTastNum++; }); }); }); } cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ` 2.质量标准:${ci.quality || '×××'}${/。$/.test(ci.quality) ? '' : '。'}`; }); cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { descRowNum3++; targetRow.getCell(1).value = ` 3.服务工期:${ci.duration || '×××'}${/。$/.test(ci.duration) ? '' : '。'}`; }); }); descRowNum = descRowNum3; } descSheet.spliceRows(descRowNum, 1); descRowNum += 3; const methodNames = ['费率计取', '投资规模法', '用地规模法', '工作量法', '工时法', '数量单价']; let allMethodsArr = [...allMethods].sort((a, b) => a - b).map(mid => methodNames[mid]); let allMethodsTextArr = paragraphLineBreakFor1112(' 3.预算编制采用方法:' + allMethodsArr.slice(0, -1).join('、') + (allMethodsArr.length > 1 ? '和' : '') + allMethodsArr.slice(-1) + '。', ctx); allMethodsTextArr.forEach(ti => { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = ti; }); }); if (allReserveMethods[0].cid.length || allReserveMethods[4].length || allReserveMethods[5].length) { if (allReserveMethods[0].coeSet.size == 1 && allReserveMethods[4].length == 0 && allReserveMethods[5].length == 0) { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = ` 4.预备费:本预算按${allReserveMethods[0].coe[0]}%费率计列预备费。`; }); } else { let rmta = []; data.contracts.forEach((ci, cindex) => { let ciTextArr = []; if (allReserveMethods[0].cid.includes(cindex)) ciTextArr.push(`${ci.reserve.m0.coe}%费率`); if (allReserveMethods[4].includes(cindex)) ciTextArr.push('工时法'); if (allReserveMethods[5].includes(cindex)) ciTextArr.push('数量单价'); if (ciTextArr.length) { rmta.push(`${ci.name}按${ciTextArr.join('、')}计列`) } else { rmta.push(`${ci.name}不计列`); } }); let rmtaa = paragraphLineBreakFor1112(` 4.预备费:${rmta.join(';')}。`, ctx); rmtaa.forEach(ti => { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = ti; }); }); } } else { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = ' 4.预备费:本预算不计列预备费。'; }); } descRowNum++; let contractFeeSummaryArr = paragraphLineBreakFor1112(` 本项目造价咨询服务总预算为${Number(data.fee).toLocaleString()}元。其中,${contractFeeSummary.join(',')}。计算过程详见相关计算表格。`, ctx); descSheet.getRow(descRowNum).getCell(1).value = contractFeeSummaryArr[0]; descRowNum++; if (contractFeeSummaryArr.length > 1) { for (let i = 1; i < contractFeeSummaryArr.length; i++) { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = contractFeeSummaryArr[i]; }); } } if (data.desc) { descRowNum++; let otherDesc = paragraphLineBreakFor1112(` ${data.desc}${/。$/.test(ci.duration) ? '' : '。'}`, ctx); descSheet.getRow(descRowNum).getCell(1).value = otherDesc[0]; descRowNum++; if (otherDesc.length > 1) { for (let i = 1; i < otherDesc.length; i++) { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { descRowNum++; targetRow.getCell(1).value = otherDesc[i]; }); } } } else { descSheet.spliceRows(descRowNum, 2); } window.workbook = workbook; return workbook; } catch (error) { console.log(error) throw error } } function paragraphLineBreakFor1112(paragraph, ctx, type = 1) {// 仅适用于内容为11或12号字体的段落 // // number,最后判断是否数字 // const numberNoBreak1 = /\d[\d,]*(?:\.\d+)?$/; // const numberNoBreak2 = /^[\d,]*\.*\d+/; // // number&unit // const numberUnitNoBreak1 = /\d[\d,]*(?:\.\d+)?\s*(?:[a-zA-Z](?:[a-zA-Z\d·/]*[a-zA-Z\d])?)?$/; // const numberUnitNoBreak2 = /^[\d,]*(?:\.\d+)?\s*[a-zA-Z](?:[a-zA-Z\d·/]*[a-zA-Z\d])?/; // // 英文单词 // const ewordNoBreak1 = /[a-zA-Z]+$/; // const ewordNoBreak2 = /^[a-zA-Z]+/; /* 西文字符串 */ const matchASCIIChar1 = /[\x21-\x7E]+$/; const matchASCIIChar2 = /^[\x21-\x7E]+/; /* 行首禁则 1.不能为段落第一个字符 2.将上一行最后1~2个字符转至下一行 3.上一行最后1~2个字符如符合数字、英文单词、公式等规则,应整个下移至本行 */ const forbidLineSta1 = /^[,,.。、::;;!!??)\]\})〕]}〉》」』】〗〙〛”’」』﹂﹄%‰℃°—–~·]/; const forbidLineSta2 = /[,,.。、::;;!!??)\]\})〕]}〉》」』】〗〙〛”’」』﹂﹄%‰℃°—–~·]$/; /* 行尾禁则 1.不能为段落最后一个字符(即后续内容不能为空) 2.将本行最后1~2个字符转至下一行 */ const forbidLineEnd = /[¥$€£@([{(〔[{〈《「『【〖〘〚“‘「『﹁﹃—–~·]+$/; ctx.font = "12pt 宋体"; const maxWidth = type == 1 ? 720 : 336; const minChar = Math.ceil(maxWidth / 8); let text = paragraph; let res = []; // let enterRows = new Set(); while (ctx.measureText(text).width > maxWidth) { let t1 = text.slice(0, minChar); let t1PX = ctx.measureText(t1).width; while (t1PX > maxWidth) { let t1MinChar = Math.floor((t1PX - maxWidth) / 16) || 1; t1 = t1.slice(0, -1 * t1MinChar); t1PX = ctx.measureText(t1).width; } let t2 = text.slice(t1.length); if (matchASCIIChar1.test(t1) && matchASCIIChar2.test(t2)) { let strASCII1 = t1.match(matchASCIIChar1)[0]; if (strASCII1.length < t1.length) { t1 = t1.slice(0, -1 * strASCII1.length); t2 = text.slice(t1.length); // enterRows.add(res.length); } } if (forbidLineSta1.test(t2)) { if (forbidLineSta2.test(t1)) { t1 = t1.slice(0, -2); } else { t1 = t1.slice(0, -1); } t2 = text.slice(t1.length); // enterRows.add(res.length); } if (forbidLineEnd.test(t1)) { let endTrans = t1.match(forbidLineEnd)[0]; t1 = t1.slice(0, -1 * (Math.min(endTrans.length, 2))); t2 = text.slice(t1.length); // enterRows.add(res.length); } res.push(t1); text = text.slice(t1.length); } res.push(text); // [...enterRows].forEach(ri => { // res[ri] = res[ri] + '\n'; // }); return res; } // 在指定位置插入行,并按模板行复制样式,可选回调填充值。 function cusInsertRowFunc(insertRowNum, sourceRows, worksheet, RowFun, cellFun) { // 插入行 let newRows = []; for (let i = 0; i < sourceRows.length; i++) { newRows.push([]); } worksheet.insertRows(insertRowNum, newRows); for (let i = 0; i < sourceRows.length; i++) { const sourceRow = sourceRows[i]; const targetRowNum = insertRowNum + i; const targetRow = worksheet.getRow(targetRowNum); targetRow.height = undefined; sourceRow.eachCell({ includeEmpty: true }, (cell, colNumber) => { const targetCell = targetRow.getCell(colNumber); // targetCell.value = cell.value; // 复制内容 // 复制样式 if (cell.style) { targetCell.style = JSON.parse(JSON.stringify(cell.style)); } if (cellFun) { cellFun(targetCell, cell, colNumber, targetRow, sourceRow); } }); if (RowFun) { RowFun(targetRow, sourceRow, i); } } } // 复制整张工作表(含页眉页脚、合并单元格、列宽样式与单元格值)。 function copyWorksheet(workbook, sourceName, targetName) { const source = workbook.getWorksheet(sourceName); if (!source) throw new Error("Source sheet not found"); const target = workbook.addWorksheet(targetName, { properties: JSON.parse(JSON.stringify(source.properties || {})), pageSetup: JSON.parse(JSON.stringify(source.pageSetup || {})), views: source.views ? JSON.parse(JSON.stringify(source.views || {})) : [], }); /* 复制页眉页脚(关键补充) */ if (source.headerFooter) { target.headerFooter = JSON.parse(JSON.stringify(source.headerFooter)); } /* 复制合并单元格 */ Object.keys(source._merges).forEach(range => { let rangeModel = source._merges[range].model; target.mergeCells(rangeModel.top, rangeModel.left, rangeModel.bottom, rangeModel.right); }); /* 复制列(宽度、样式) */ source.columns.forEach((col, i) => { const targetCol = target.getColumn(i + 1); targetCol.width = col.width; targetCol.style = JSON.parse(JSON.stringify(col.style || {})); }); /* 复制行 & 单元格 */ source.eachRow({ includeEmpty: true }, (row, rowNumber) => { const targetRow = target.getRow(rowNumber); targetRow.height = undefined; row.eachCell({ includeEmpty: true }, (cell, colNumber) => { const targetCell = targetRow.getCell(colNumber); // value(富文本/公式安全) targetCell.value = cloneCellValue(cell.value); // style(必须深拷贝) targetCell.style = JSON.parse(JSON.stringify(cell.style || {})); }); }); return target; } // 在指定列位置批量插入新列,并从给定来源列复制样式与内容。 function insertAndCopyColumn(insertAt, cols, ws) { let insertAti = insertAt; cols.forEach((col, index) => { // 在 insertAti 位置插入空列 ws.spliceColumns(insertAti, 0, []); // 插入后,col 可能需要偏移 const srcColIndex = col >= insertAti ? col + 1 + index : col; // 复制列 copyColumn(insertAti, srcColIndex, ws); insertAti++; }); } // 复制单列:列级属性 + 该列所有单元格值和样式。 function copyColumn(toCol, fromCol, ws) { const srcCol = ws.getColumn(fromCol); const dstCol = ws.getColumn(toCol); // 列级属性 dstCol.width = srcCol.width; dstCol.hidden = srcCol.hidden; dstCol.style = JSON.parse(JSON.stringify(srcCol.style || {})); // 单元格 srcCol.eachCell({ includeEmpty: true }, (cell, rowNumber) => { const dstCell = ws.getRow(rowNumber).getCell(toCol); dstCell.value = cloneCellValue(cell.value); dstCell.style = JSON.parse(JSON.stringify(cell.style || {})); }); } // 深拷贝单元格值,避免对象类型(公式/富文本)引用同一实例。 function cloneCellValue(value) { if (value == null) return value; if (typeof value === "object") { return JSON.parse(JSON.stringify(value)); } return value; } function ArabicToChinese(Arabic_numerals) { let strI = String(Arabic_numerals); let mixNumerals = ""; let positionArr = ["", "十", "百", "千", "万", "十", "百", "千", "亿", "十", "百", "千", "万", "十", "百", "千", "亿"]; for (let j = 1; j <= strI.length; j++) { let poistion = (strI.slice(-j)[0] == 0 && positionArr[j - 1] !== "万" && positionArr[j - 1] !== "亿") ? "" : positionArr[j - 1]; let strIJ = (strI.slice(-j)[0] == 0 && (positionArr[j - 1] == "万" || positionArr[j - 1] == "亿")) ? "" : strI.slice(-j)[0]; mixNumerals = strIJ + poistion + mixNumerals; } for (let j = Math.floor((strI.length - 1) / 4); j >= 1; j--) { if (j % 2 !== 0) { let regExp1 = new RegExp(`000+${positionArr[j * 4]}`, "g"); mixNumerals = mixNumerals.replaceAll(regExp1, ""); } let regExp2 = new RegExp(`0+${positionArr[j * 4]}`, "g"); mixNumerals = mixNumerals.replaceAll(regExp2, `${positionArr[j * 4]}0`); } mixNumerals = mixNumerals.replaceAll(/00+/g, "0"); mixNumerals = mixNumerals.replace(/^1十/, "十"); mixNumerals = mixNumerals.replace(/0+$/, ""); mixNumerals = mixNumerals.replace(/[0-9]/g, match => { const chineseNumerals = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"]; return chineseNumerals[parseInt(match)]; }); return mixNumerals; }