JGJS2026/src/sql.ts

1939 lines
132 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @ts-nocheck
import { addNumbers, roundTo, toDecimal } from '@/lib/decimal'
import { formatThousands } from '@/lib/numberFormat'
import ExcelJS from "ExcelJS";
import { number } from 'motion-v/es';
// 统一数字千分位格式化,默认保留 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条<Q≤30条', basicParam: '价格信息数量', required: true, unit: '元/条', conversion: 1, maxPrice: null, minPrice: null, defPrice: 400, desc: '' },
36: { serviceID: 19, code: 'C8-3', name: '30条<Q≤50条', basicParam: '价格信息数量', required: true, unit: '元/条', conversion: 1, maxPrice: null, minPrice: null, defPrice: 300, desc: '' },
37: { serviceID: 19, code: 'C8-4', name: '50条<Q≤100条', basicParam: '价格信息数量', required: true, unit: '元/条', conversion: 1, maxPrice: null, minPrice: null, defPrice: 200, desc: '' },
38: { serviceID: 19, code: 'C8-5', name: 'Q>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 = ['人员驻场服务及其他附加工作', '咨询服务协调工作'];
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 IndustryType = (typeof industryTypeList)[number]['type']
type DictItem = Record<string, any>
type DictEntry = { id: string; rawId: string; item: DictItem }
type DictByIdMap = Record<string, DictItem>
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<string, any> | undefined) =>
Boolean(item?.isRoad && item?.isRailway && item?.isWaterway)
/**
* 判断专业IDmajorList 的 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<string, any> =>
Boolean(value) && typeof value === 'object' && !Array.isArray(value)
const hasCodeLike = (value: Record<string, any>) =>
typeof value.code === 'string'
const isDictLeafNode = (value: unknown): value is Record<string, any> =>
isPlainObject(value) && hasCodeLike(value) && typeof value.name === 'string'
// 递归提取字典树中的叶子节点(具有 code + name 的业务项)。
const collectDictLeafEntries = (
source: Record<string, any>,
prefix = ''
): Array<{ rawId: string; item: Record<string, any> }> => {
const result: Array<{ rawId: string; item: Record<string, any> }> = []
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<string, any>, 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<string, any> }>
) =>
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<string, any>) =>
sortDictEntries(
collectDictLeafEntries(source).map(({ rawId, item }) => {
return {
id: rawId,
rawId,
item
}
})
)
/**
* 获取专业字典的扁平化列表。
* @returns 专业字典条目列表
*/
export const getMajorDictEntries = (): DictEntry[] => buildDictEntries(majorList as Record<string, any>)
/**
* 获取服务字典的扁平化列表。
* @returns 服务字典条目列表
*/
export const getServiceDictEntries = (): DictEntry[] => buildDictEntries(serviceList as Record<string, any>)
/**
* 构建“专业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<string, string> =>
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<void> {
if (window.showSaveFilePicker) {
const handle = await window.showSaveFilePicker({
suggestedName: fileName,
types: [{
description: "Excel 文件",
accept: { "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [".xlsx"] }
}]
});
// ecCom.WeaLoadingGlobal.start({
// tip: "下载中,结束前请勿打开文件...",
// });
try {
const workbook = await generateTemplate(data);
const buffer = await workbook.xlsx.writeBuffer();
const writable = await handle.createWritable();
await writable.write(buffer);
await writable.close();
// ecCom.WeaLoadingGlobal.destroy();
// antd.notification['success']({
// message: '下载成功!',
// });
} catch (err) {
console.log('err:' + err);
// ecCom.WeaLoadingGlobal.destroy();
// antd.notification['error']({
// message: '下载失败!',
// });
}
} else {
const workbook = await generateTemplate(data);
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);
}
}
// 按模板生成最终工作簿:填充封面、目录、各分表及汇总数据。
async function generateTemplate(data) {
// 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)
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 = {};
data.scale?.forEach(sci => {
allMajors[sci.major] = { [data.contracts.length]: sci };
});
data.contracts.forEach((ci, index) => {
// 记录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 (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) {
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) {
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) {
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) {
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) {
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 = '/';
sheet_5.getRow(3).getCell(6).value = numberFormatter((ci.method5.addtional?.reduce((a, b) => a + b.m5.fee, 0) || 0) + (ci.method5.reserve?.fee || 0), 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 serviceSum = 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 += sobj.method1.fee;
}
if (sobj.method2) {
targetRow.getCell(5).value = numberFormatter(sobj.method2.fee, 2);
m2Sum += sobj.method2.fee;
}
if (sobj.method3) {
targetRow.getCell(6).value = numberFormatter(sobj.method3.fee, 2);
m3Sum += sobj.method3.fee;
}
if (sobj.method4) {
targetRow.getCell(7).value = numberFormatter(sobj.method4.fee, 2);
m4Sum += sobj.method4.fee;
}
targetRow.getCell(8).value = numberFormatter(sobj.fee, 2);
serviceSum += sobj.fee;
});
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) => {
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, 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;
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(serviceSum, 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 = '';
});
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}`);
if (addobj.m4) tmpArr.push(`按工时法计得${addobj.m4.fee}`);
if (addobj.m5) tmpArr.push(`按数量单价计得${addobj.m5.fee}`);
targetRow.getCell(4).value = tmpArr.join(';');
targetRow.getCell(8).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 = 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}`);
if (ci.reserve.m4) tmpArr.push(`按工时法计得${ci.reserve.m4.fee}`);
if (ci.reserve.m5) tmpArr.push(`按数量单价计得${ci.reserve.m5.fee}`);
targetRow.getCell(4).value = tmpArr.join(';');
targetRow.getCell(8).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(8).value = numberFormatter(ci.fee, 2);
sheet_1.mergeCells(2 + ci.services.length + endRows + 2, 2, 2 + ci.services.length + endRows + 2, 8);
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, 7);
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&"宋体"&');
}
});
window.workbook = workbook;
// 更新编制说明
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// ctx.font = "12pt 宋体";
// const maxTitleWidth = 336;
// const maxTextWidth = 720;
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('编制说明');
descSheet.getRow(1).getCell(1).value = data.name;
let descRowNum = 5;
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()}万元`);
});
descSheet.getRow(descRowNum).getCell(1).value = descSheet.getRow(descRowNum).getCell(1).value + `${desc.join('')}`;
descRowNum++;
} else {
descSheet.spliceRows(descRowNum, 1);
}
} else {
descSheet.spliceRows(descRowNum, 5);
descRowNum++;
}
console.log(descRowNum);
return workbook;
} catch (error) {
console.log(error)
throw error
}
}
// 在指定位置插入行,并按模板行复制样式,可选回调填充值。
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;
}