Compare commits

..

No commits in common. "c04ed4acb1477487b72569b00b7396bbf774711d" and "49c86d566858774b1d8e0e78f9735faa39027ae6" have entirely different histories.

3 changed files with 52 additions and 255 deletions

View File

@ -62,14 +62,6 @@ type XmInfoLike = {
reviewedBy?: unknown reviewedBy?: unknown
preparedDate?: unknown preparedDate?: unknown
projectIndustry?: unknown projectIndustry?: unknown
preparedCompany?: unknown
overview?: unknown
desc?: unknown
}
type HtBaseInfoLike = {
quality?: unknown
duration?: unknown
} }
interface ScaleRowLike { interface ScaleRowLike {
@ -97,26 +89,12 @@ interface ZxFwRowLike {
id: string id: string
process?: unknown process?: unknown
subtotal?: unknown subtotal?: unknown
finalFee?: unknown
investScale?: unknown investScale?: unknown
landScale?: unknown landScale?: unknown
workload?: unknown workload?: unknown
hourly?: unknown hourly?: unknown
} }
interface WorkContentRowLike {
id?: unknown
content?: unknown
checked?: unknown
custom?: unknown
serviceGroup?: unknown
isAddTrigger?: unknown
}
interface WorkContentStateLike {
detailRows?: WorkContentRowLike[]
}
interface ZxFwStorageLike { interface ZxFwStorageLike {
selectedIds?: string[] selectedIds?: string[]
selectedCodes?: string[] selectedCodes?: string[]
@ -197,7 +175,6 @@ interface ExportScaleRow {
} }
interface ExportMethod1Detail { interface ExportMethod1Detail {
proNum: number
major: number major: number
cost: number cost: number
basicFee: number basicFee: number
@ -214,7 +191,6 @@ interface ExportMethod1Detail {
} }
interface ExportMethod1 { interface ExportMethod1 {
proAmount: number
cost: number cost: number
basicFee: number basicFee: number
basicFee_basic: number basicFee_basic: number
@ -224,7 +200,6 @@ interface ExportMethod1 {
} }
interface ExportMethod2Detail { interface ExportMethod2Detail {
proNum: number
major: number major: number
area: number area: number
basicFee: number basicFee: number
@ -241,7 +216,6 @@ interface ExportMethod2Detail {
} }
interface ExportMethod2 { interface ExportMethod2 {
proAmount: number
area: number area: number
basicFee: number basicFee: number
basicFee_basic: number basicFee_basic: number
@ -285,20 +259,13 @@ interface ExportMethod4 {
interface ExportService { interface ExportService {
id: number id: number
fee: number fee: number
finalFee: number
process: number process: number
tasks: ExportTaskGroup[]
method1?: ExportMethod1 method1?: ExportMethod1
method2?: ExportMethod2 method2?: ExportMethod2
method3?: ExportMethod3 method3?: ExportMethod3
method4?: ExportMethod4 method4?: ExportMethod4
} }
interface ExportTaskGroup {
serviceid?: number
text: string[]
}
interface ExportServiceCoe { interface ExportServiceCoe {
serviceid: number serviceid: number
coe: number coe: number
@ -317,8 +284,6 @@ interface ExportContract {
addtionalFee: number addtionalFee: number
reserveFee: number reserveFee: number
fee: number fee: number
quality: string
duration: string
scale: ExportScaleRow[] scale: ExportScaleRow[]
serviceCoes: ExportServiceCoe[] serviceCoes: ExportServiceCoe[]
majorCoes: ExportMajorCoe[] majorCoes: ExportMajorCoe[]
@ -349,11 +314,10 @@ interface ExportMethod5 {
} }
interface ExportAdditionalDetail { interface ExportAdditionalDetail {
id: number | string id: number
code?: unknown code?: unknown
name: string name: string
fee: number fee: number
tasks: ExportTaskGroup[]
m0?: ExportMethod0 m0?: ExportMethod0
m4?: ExportMethod4 m4?: ExportMethod4
m5?: ExportMethod5 m5?: ExportMethod5
@ -370,7 +334,6 @@ interface ExportReserve {
code?: unknown code?: unknown
name: string name: string
fee: number fee: number
tasks: ExportTaskGroup[]
m0?: ExportMethod0 m0?: ExportMethod0
m4?: ExportMethod4 m4?: ExportMethod4
m5?: ExportMethod5 m5?: ExportMethod5
@ -380,13 +343,10 @@ interface ExportReportPayload {
name: string name: string
writer: string writer: string
reviewer: string reviewer: string
company: string
date: string date: string
industry: number industry: number
fee: number fee: number
scaleCost: number scaleCost: number
overview: string
desc: string
scale: ExportScaleRow[] scale: ExportScaleRow[]
serviceCoes: ExportServiceCoe[] serviceCoes: ExportServiceCoe[]
majorCoes: ExportMajorCoe[] majorCoes: ExportMajorCoe[]
@ -1072,83 +1032,6 @@ const mapIndustryCodeToExportIndustry = (value: unknown): number => {
return 0 return 0
} }
const parseScaleScopedRowId = (rowId: unknown) => {
const raw = String(rowId || '').trim()
const scopedMatch = /^(\d+)::(.+)$/.exec(raw)
if (scopedMatch) {
return {
proNum: toSafeInteger(scopedMatch[1]) ?? 1,
majorPart: String(scopedMatch[2] || '').trim()
}
}
return {
proNum: 1,
majorPart: raw
}
}
const toScaleMajorId = (row: ScaleMethodRowLike): number | null => {
const direct = toSafeInteger((row as { majorDictId?: unknown }).majorDictId)
if (direct != null) return direct
const parsed = parseScaleScopedRowId(row.id)
return toSafeInteger(parsed.majorPart)
}
const toScaleProNum = (row: ScaleMethodRowLike): number => {
const parsed = parseScaleScopedRowId(row.id)
return parsed.proNum > 0 ? parsed.proNum : 1
}
const normalizeTaskText = (value: unknown): string => String(value || '').trim()
const groupWorkContentTasks = (
rows: WorkContentRowLike[] | undefined,
options?: { forceUngroup?: boolean; serviceLabelToId?: Map<string, number> }
): ExportTaskGroup[] => {
const source = Array.isArray(rows) ? rows : []
const selected = source.filter(item => {
if (item && item.isAddTrigger === true) return false
const isCustom = Boolean(item?.custom)
const isChecked = Boolean(item?.checked)
return isCustom || isChecked
})
if (selected.length === 0) return []
const hasGroup = !options?.forceUngroup && selected.some(item => normalizeTaskText(item?.serviceGroup).length > 0)
if (!hasGroup) {
const text = selected
.map(item => normalizeTaskText(item?.content))
.filter(Boolean)
return text.length > 0 ? [{ text }] : []
}
const grouped = new Map<string, string[]>()
const orderedGroupKeys: string[] = []
for (const item of selected) {
const groupName = normalizeTaskText(item?.serviceGroup)
const key = groupName || '__ungrouped__'
if (!grouped.has(key)) {
grouped.set(key, [])
orderedGroupKeys.push(key)
}
const content = normalizeTaskText(item?.content)
if (!content) continue
grouped.get(key)?.push(content)
}
const byLabel = options?.serviceLabelToId || new Map<string, number>()
return orderedGroupKeys
.map(groupName => {
const text = grouped.get(groupName) || []
if (text.length === 0) return null
const entry: ExportTaskGroup = { text }
const resolvedServiceId = byLabel.get(groupName)
if (resolvedServiceId != null) entry.serviceid = resolvedServiceId
return entry
})
.filter((item): item is ExportTaskGroup => Boolean(item))
}
const buildProjectServiceCoes = (rows: FactorRowLike[] | undefined): ExportServiceCoe[] => { const buildProjectServiceCoes = (rows: FactorRowLike[] | undefined): ExportServiceCoe[] => {
if (!Array.isArray(rows)) return [] if (!Array.isArray(rows)) return []
return rows return rows
@ -1201,13 +1084,10 @@ const toExportScaleRows = (rows: ScaleRowLike[] | undefined): ExportScaleRow[] =
const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | null => { const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | null => {
if (!Array.isArray(rows)) return null if (!Array.isArray(rows)) return null
let hasTotalValue = false let hasTotalValue = false
const proSet = new Set<number>()
const det = rows const det = rows
.map(row => { .map(row => {
const major = toScaleMajorId(row) const major = toSafeInteger(row.id)
if (major == null || row.budgetFee == null) return null if (major == null || row.budgetFee == null) return null
const proNum = toScaleProNum(row)
proSet.add(proNum)
const cost = toFiniteNumber(row.amount) const cost = toFiniteNumber(row.amount)
const basicFee = toFiniteNumber(row.budgetFee) const basicFee = toFiniteNumber(row.budgetFee)
if (basicFee != null) hasTotalValue = true if (basicFee != null) hasTotalValue = true
@ -1222,7 +1102,6 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
isNonEmptyString(remark) isNonEmptyString(remark)
if (!hasValue) return null if (!hasValue) return null
return { return {
proNum,
major, major,
cost: cost ?? 0, cost: cost ?? 0,
basicFee: basicFee ?? 0, basicFee: basicFee ?? 0,
@ -1242,7 +1121,6 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
if (det.length === 0 || !hasTotalValue) return null if (det.length === 0 || !hasTotalValue) return null
return { return {
proAmount: proSet.size > 0 ? proSet.size : 1,
cost: sumNumbers(det.map(item => item.cost)), cost: sumNumbers(det.map(item => item.cost)),
basicFee: sumNumbers(det.map(item => item.basicFee)), basicFee: sumNumbers(det.map(item => item.basicFee)),
basicFee_basic: sumNumbers(det.map(item => item.basicFee_basic)), basicFee_basic: sumNumbers(det.map(item => item.basicFee_basic)),
@ -1255,13 +1133,10 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | null => { const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | null => {
if (!Array.isArray(rows)) return null if (!Array.isArray(rows)) return null
let hasTotalValue = false let hasTotalValue = false
const proSet = new Set<number>()
const det = rows const det = rows
.map(row => { .map(row => {
const major = toScaleMajorId(row) const major = toSafeInteger(row.id)
if (major == null || row.budgetFee == null) return null if (major == null || row.budgetFee == null) return null
const proNum = toScaleProNum(row)
proSet.add(proNum)
const area = toFiniteNumber(row.landArea) const area = toFiniteNumber(row.landArea)
const basicFee = toFiniteNumber(row.budgetFee) const basicFee = toFiniteNumber(row.budgetFee)
if (basicFee != null) hasTotalValue = true if (basicFee != null) hasTotalValue = true
@ -1276,7 +1151,6 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n
isNonEmptyString(remark) isNonEmptyString(remark)
if (!hasValue) return null if (!hasValue) return null
return { return {
proNum,
major, major,
area: area ?? 0, area: area ?? 0,
basicFee: basicFee ?? 0, basicFee: basicFee ?? 0,
@ -1296,7 +1170,6 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n
if (det.length === 0 || !hasTotalValue) return null if (det.length === 0 || !hasTotalValue) return null
return { return {
proAmount: proSet.size > 0 ? proSet.size : 1,
area: sumNumbers(det.map(item => item.area)), area: sumNumbers(det.map(item => item.area)),
basicFee: sumNumbers(det.map(item => item.basicFee)), basicFee: sumNumbers(det.map(item => item.basicFee)),
basicFee_basic: sumNumbers(det.map(item => item.basicFee_basic)), basicFee_basic: sumNumbers(det.map(item => item.basicFee_basic)),
@ -1480,50 +1353,6 @@ const loadHtFeeMethodsByRow = async (mainStorageKey: string, rowId: string) => {
} }
} }
const buildServiceGroupLabelToIdMap = (serviceIds: string[]): Map<string, number> => {
const map = new Map<string, number>()
for (const serviceId of serviceIds) {
const item = (serviceList as Record<string, any>)[serviceId]
if (!item) continue
const id = toSafeInteger(serviceId)
if (id == null) continue
const label = `${String(item.code || '').trim()} ${String(item.name || '').trim()}`.trim()
if (!label) continue
map.set(label, id)
}
return map
}
const buildServiceTasks = async (
contractId: string,
serviceId: string,
serviceLabelToId: Map<string, number>
): Promise<ExportTaskGroup[]> => {
const taskState = await zxFwPricingStore.loadKeyState<WorkContentStateLike>(`work-content-${contractId}-${serviceId}`)
return groupWorkContentTasks(taskState?.detailRows, { serviceLabelToId })
}
const buildAdditionalRowTasks = async (contractId: string, rowId: string): Promise<ExportTaskGroup[]> => {
const taskStorageKey = `work-content-htExtraFee-${contractId}-additional-work-${rowId}`
const taskState = await zxFwPricingStore.loadKeyState<WorkContentStateLike>(taskStorageKey)
return groupWorkContentTasks(taskState?.detailRows, { forceUngroup: true })
}
const buildServiceFinalFee = (
row: ZxFwRowLike | null | undefined,
method1: ExportMethod1 | null,
method2: ExportMethod2 | null,
method3: ExportMethod3 | null,
method4: ExportMethod4 | null
) => {
const finalFee = toFiniteNumber(row?.finalFee)
if (finalFee != null) return finalFee
const subtotal = toFiniteNumber(row?.subtotal)
if (subtotal != null) return subtotal
const methodSum = sumNumbers([method1?.fee, method2?.fee, method3?.fee, method4?.fee])
return methodSum
}
const buildAdditionalExport = async (contractId: string): Promise<ExportAdditional | null> => { const buildAdditionalExport = async (contractId: string): Promise<ExportAdditional | null> => {
@ -1534,17 +1363,15 @@ const buildAdditionalExport = async (contractId: string): Promise<ExportAddition
const det = ( const det = (
await Promise.all( await Promise.all(
rows.map(async row => { rows.map(async (row, index) => {
const methodPayload = await loadHtFeeMethodsByRow(storageKey, row.id) const methodPayload = await loadHtFeeMethodsByRow(storageKey, row.id)
if (!methodPayload) return null if (!methodPayload) return null
const tasks = await buildAdditionalRowTasks(contractId, row.id)
const item: ExportAdditionalDetail = { const item: ExportAdditionalDetail = {
id: row.id, id: index,
code: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'F' }] }, code: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'C' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'F' }] },
name: row.name, name: row.name,
fee: methodPayload.fee, fee: methodPayload.fee
tasks
} }
if (methodPayload.m0) item.m0 = methodPayload.m0 if (methodPayload.m0) item.m0 = methodPayload.m0
if (methodPayload.m4) item.m4 = methodPayload.m4 if (methodPayload.m4) item.m4 = methodPayload.m4
@ -1575,8 +1402,7 @@ const buildReserveExport = async (contractId: string): Promise<ExportReserve | n
if (!methodPayload) continue if (!methodPayload) continue
const reserve: ExportReserve = { const reserve: ExportReserve = {
name: row.name || '预备费', name: row.name || '预备费',
fee: methodPayload.fee, fee: methodPayload.fee
tasks: []
} }
if (methodPayload.m0) reserve.m0 = methodPayload.m0 if (methodPayload.m0) reserve.m0 = methodPayload.m0
if (methodPayload.m4) reserve.m4 = methodPayload.m4 if (methodPayload.m4) reserve.m4 = methodPayload.m4
@ -1610,11 +1436,8 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const projectName = isNonEmptyString(projectInfo.projectName) ? projectInfo.projectName.trim() : '造价项目' const projectName = isNonEmptyString(projectInfo.projectName) ? projectInfo.projectName.trim() : '造价项目'
const writer = isNonEmptyString(projectInfo.preparedBy) ? projectInfo.preparedBy.trim() : '' const writer = isNonEmptyString(projectInfo.preparedBy) ? projectInfo.preparedBy.trim() : ''
const reviewer = isNonEmptyString(projectInfo.reviewedBy) ? projectInfo.reviewedBy.trim() : '' const reviewer = isNonEmptyString(projectInfo.reviewedBy) ? projectInfo.reviewedBy.trim() : ''
const company = isNonEmptyString(projectInfo.preparedCompany) ? projectInfo.preparedCompany.trim() : ''
const date = isNonEmptyString(projectInfo.preparedDate) ? projectInfo.preparedDate.trim() : '' const date = isNonEmptyString(projectInfo.preparedDate) ? projectInfo.preparedDate.trim() : ''
const industry = mapIndustryCodeToExportIndustry(projectInfo.projectIndustry) const industry = mapIndustryCodeToExportIndustry(projectInfo.projectIndustry)
const overview = isNonEmptyString(projectInfo.overview) ? projectInfo.overview.trim() : ''
const desc = isNonEmptyString(projectInfo.desc) ? projectInfo.desc.trim() : ''
const contractCards = (Array.isArray(contractCardsRaw) ? contractCardsRaw : []) const contractCards = (Array.isArray(contractCardsRaw) ? contractCardsRaw : [])
.filter(item => item && typeof item.id === 'string') .filter(item => item && typeof item.id === 'string')
@ -1626,12 +1449,11 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const contract = contractCards[index] const contract = contractCards[index]
const contractId = contract.id const contractId = contract.id
await zxFwPricingStore.loadContract(contractId) await zxFwPricingStore.loadContract(contractId)
const [htInfoRaw, zxFwRaw, htConsultCategoryFactorRaw, htMajorFactorRaw, htBaseInfoRaw] = await Promise.all([ const [htInfoRaw, zxFwRaw, htConsultCategoryFactorRaw, htMajorFactorRaw] = await Promise.all([
kvStore.getItem<DetailRowsStorageLike<ScaleRowLike>>(`ht-info-v3-${contractId}`), kvStore.getItem<DetailRowsStorageLike<ScaleRowLike>>(`ht-info-v3-${contractId}`),
kvStore.getItem<ZxFwStorageLike>(`zxFW-${contractId}`), kvStore.getItem<ZxFwStorageLike>(`zxFW-${contractId}`),
kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(`ht-consult-category-factor-v1-${contractId}`), kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(`ht-consult-category-factor-v1-${contractId}`),
kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(`ht-major-factor-v1-${contractId}`), kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(`ht-major-factor-v1-${contractId}`)
kvStore.getItem<HtBaseInfoLike>(`ht-base-info-${contractId}`)
]) ])
const contractState = zxFwPricingStore.getContractState(contractId) const contractState = zxFwPricingStore.getContractState(contractId)
@ -1640,7 +1462,6 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
id: String(row.id || ''), id: String(row.id || ''),
process: row.process, process: row.process,
subtotal: row.subtotal, subtotal: row.subtotal,
finalFee: row.finalFee,
investScale: row.investScale, investScale: row.investScale,
landScale: row.landScale, landScale: row.landScale,
workload: row.workload, workload: row.workload,
@ -1673,7 +1494,6 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const serviceIdTexts = sortServiceIdsByDict( const serviceIdTexts = sortServiceIdsByDict(
(selectedIds.length > 0 ? selectedIds : fallbackServiceIds).filter(hasServiceId) (selectedIds.length > 0 ? selectedIds : fallbackServiceIds).filter(hasServiceId)
) )
const serviceLabelToId = buildServiceGroupLabelToIdMap(serviceIdTexts)
const services = ( const services = (
await Promise.all( await Promise.all(
@ -1698,15 +1518,11 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const method3 = buildMethod3(method3Raw?.detailRows) const method3 = buildMethod3(method3Raw?.detailRows)
const method4 = buildMethod4(method4Raw?.detailRows) const method4 = buildMethod4(method4Raw?.detailRows)
const fee = buildServiceFee(sourceRow, method1, method2, method3, method4) const fee = buildServiceFee(sourceRow, method1, method2, method3, method4)
const finalFee = buildServiceFinalFee(sourceRow, method1, method2, method3, method4)
const tasks = await buildServiceTasks(contractId, serviceIdText, serviceLabelToId)
const process = Number(sourceRow?.process) === 1 ? 1 : 0 const process = Number(sourceRow?.process) === 1 ? 1 : 0
const service: ExportService = { const service: ExportService = {
id: serviceId, id: serviceId,
process, process,
fee, fee
finalFee,
tasks
} }
if (method1) service.method1 = method1 if (method1) service.method1 = method1
if (method2) service.method2 = method2 if (method2) service.method2 = method2
@ -1748,8 +1564,6 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
addtionalFee, addtionalFee,
reserveFee, reserveFee,
fee: contractFee, fee: contractFee,
quality: isNonEmptyString(htBaseInfoRaw?.quality) ? htBaseInfoRaw.quality.trim() : '',
duration: isNonEmptyString(htBaseInfoRaw?.duration) ? htBaseInfoRaw.duration.trim() : '',
scale: contractScale, scale: contractScale,
serviceCoes: contractServiceCoesRaw, serviceCoes: contractServiceCoesRaw,
majorCoes: contractMajorCoesRaw, majorCoes: contractMajorCoesRaw,
@ -1763,13 +1577,10 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
name: projectName, name: projectName,
writer, writer,
reviewer, reviewer,
company,
date, date,
industry, industry,
fee: sumNumbers(contracts.map(item => item.fee)), fee: sumNumbers(contracts.map(item => item.fee)),
scaleCost: projectScaleCost, scaleCost: projectScaleCost,
overview,
desc,
scale: projectScale, scale: projectScale,
serviceCoes: projectServiceCoes, serviceCoes: projectServiceCoes,
majorCoes: projectMajorCoes, majorCoes: projectMajorCoes,

View File

@ -9,7 +9,7 @@ import {
PinnedRowModule, PinnedRowModule,
RowAutoHeightModule, RowAutoHeightModule,
TextEditorModule, TextEditorModule,
TooltipModule,ClientSideRowModelApiModule , TooltipModule,
UndoRedoEditModule,RenderApiModule ,ColumnApiModule ,CellSpanModule ,RowStyleModule ,RowSelectionModule UndoRedoEditModule,RenderApiModule ,ColumnApiModule ,CellSpanModule ,RowStyleModule ,RowSelectionModule
} from 'ag-grid-community' } from 'ag-grid-community'
@ -39,7 +39,7 @@ const AG_GRID_MODULES = [
RowAutoHeightModule,ContextMenuModule, RowAutoHeightModule,ContextMenuModule,
LargeTextEditorModule, LargeTextEditorModule,
UndoRedoEditModule, UndoRedoEditModule,
CellStyleModule,ClientSideRowModelApiModule , CellStyleModule,
PinnedRowModule,RenderApiModule ,ColumnApiModule , PinnedRowModule,RenderApiModule ,ColumnApiModule ,
TooltipModule, TooltipModule,
TreeDataModule, TreeDataModule,

View File

@ -11,34 +11,6 @@ const toFiniteNumber = (value: unknown) => {
const num = Number(value) const num = Number(value)
return Number.isFinite(num) ? num : 0 return Number.isFinite(num) ? num : 0
} }
// 兼容导出 tasks 对象结构:[{ text: [] }, { serviceid, text: [] }]
const normalizeTaskTexts = (tasks: unknown): string[] => {
if (!Array.isArray(tasks)) return [];
const result: string[] = [];
const seen = new Set<string>();
const pushText = (value: unknown) => {
const text = String(value ?? '').trim();
if (!text) return;
if (seen.has(text)) return;
seen.add(text);
result.push(text);
};
tasks.forEach(item => {
if (typeof item === 'string') {
pushText(item);
return;
}
if (!item || typeof item !== 'object') return;
const textField = item.text;
if (Array.isArray(textField)) {
textField.forEach(pushText);
return;
}
pushText(textField);
});
return result;
}
export type WorkType = '基本工作' | '可选工作' | '日常顾问' | '专项顾问' | '附加工作' | '自定义' export type WorkType = '基本工作' | '可选工作' | '日常顾问' | '专项顾问' | '附加工作' | '自定义'
export const TYPE_LABEL_MAP: Record<number, WorkType> = { export const TYPE_LABEL_MAP: Record<number, WorkType> = {
@ -825,7 +797,7 @@ export async function exportFile(fileName: string, data: any | (() => Promise<an
const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url1 = window.URL.createObjectURL(blob); const url1 = window.URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url1; a.hcode = url1;
a.download = `${fileName}.xlsx`; a.download = `${fileName}.xlsx`;
a.click(); a.click();
URL.revokeObjectURL(url1); URL.revokeObjectURL(url1);
@ -839,6 +811,7 @@ export async function exportFile(fileName: string, data: any | (() => Promise<an
// 按模板生成最终工作簿:填充封面、目录、各分表及汇总数据。 // 按模板生成最终工作簿:填充封面、目录、各分表及汇总数据。
async function generateTemplate(data) { 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: '下标' }] }; // const downTextTmp = { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: '常规' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: '下标' }] };
console.log(data)
// 编制说明 → 工作内容的前后默认项 // 编制说明 → 工作内容的前后默认项
let prefixIDs = [6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]; let prefixIDs = [6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27];
let suffixIDs = [6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]; let suffixIDs = [6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27];
@ -2286,9 +2259,8 @@ async function generateTemplate(data) {
targetRow.getCell(1).value = ` ${ciTastNum}${ai.id == 1 ? '负责协调工作,具体工作内容包括:' : '其他附加工作,具体工作内容包括:'}`; targetRow.getCell(1).value = ` ${ciTastNum}${ai.id == 1 ? '负责协调工作,具体工作内容包括:' : '其他附加工作,具体工作内容包括:'}`;
ciTastNum++; ciTastNum++;
}); });
const additionalTaskTexts = normalizeTaskTexts(ai.tasks); if (ai.tasks?.length) {
if (additionalTaskTexts.length) { ai.tasks.forEach((ati, atindex) => {
additionalTaskTexts.forEach((ati, atindex) => {
let atiTextArr = paragraphLineBreakFor1112(` ${atindex + 1}${ati}`, ctx); let atiTextArr = paragraphLineBreakFor1112(` ${atindex + 1}${ati}`, ctx);
atiTextArr.forEach(ti => { atiTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => {
@ -2403,25 +2375,41 @@ async function generateTemplate(data) {
}); });
} }
ci.services.forEach((si, sindex) => { ci.services.forEach((si, sindex) => {
let siTextArr = paragraphLineBreakFor1112(` ${ciTastNum}${si.process == null ? '' : (si.process == 1 ? '审核' : '编制')}${serviceList[si.id].name},具体工作内容包括:`, ctx); let flag = false;
siTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => {
descRowNum3++;
targetRow.getCell(1).value = ti;
ciTastNum++;
});
});
if (si.tasks?.length) { if (si.tasks?.length) {
si.tasks.forEach((sti, stindex) => { si.tasks.forEach(tsi => {
let stiTextArr = paragraphLineBreakFor1112(` ${stindex + 1}${sti}`, ctx); if (tsi.text?.length) {
stiTextArr.forEach(ti => { flag = true;
cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { let sx = tsi.serviceid == null ? { id: si.id, process: si.process } : { id: tsi.serviceid, process: tsi.process };
descRowNum3++; let tsiTextArr = paragraphLineBreakFor1112(` ${ciTastNum}${sx.process == null ? '完成' : (sx.process == 1 ? '审核' : '编制')}${serviceList[sx.id].name},具体工作内容包括:`, ctx);
targetRow.getCell(1).value = ti; tsiTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => {
descRowNum3++;
targetRow.getCell(1).value = ti;
ciTastNum++;
});
}); });
tsi.text.forEach((sti, stindex) => {
let stiTextArr = paragraphLineBreakFor1112(` ${stindex + 1}${sti}`, ctx);
stiTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => {
descRowNum3++;
targetRow.getCell(1).value = ti;
});
});
});
}
});
}
if (!flag) {
let siTextArr = paragraphLineBreakFor1112(` ${ciTastNum}${si.process == null ? '完成' : (si.process == 1 ? '审核' : '编制')}${serviceList[si.id].name},具体工作内容包括:`, ctx);
siTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => {
descRowNum3++;
targetRow.getCell(1).value = ti;
ciTastNum++;
}); });
}); });
} else {
cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => {
descRowNum3++; descRowNum3++;
targetRow.getCell(1).value = ' 1×××××。'; targetRow.getCell(1).value = ' 1×××××。';
@ -2435,9 +2423,8 @@ async function generateTemplate(data) {
targetRow.getCell(1).value = ` ${ciTastNum}${ai.id == 1 ? '负责协调工作,具体工作内容包括:' : '其他附加工作,具体工作内容包括:'}`; targetRow.getCell(1).value = ` ${ciTastNum}${ai.id == 1 ? '负责协调工作,具体工作内容包括:' : '其他附加工作,具体工作内容包括:'}`;
ciTastNum++; ciTastNum++;
}); });
const additionalTaskTexts = normalizeTaskTexts(ai.tasks); if (ai.tasks?.length) {
if (additionalTaskTexts.length) { ai.tasks.forEach((ati, atindex) => {
additionalTaskTexts.forEach((ati, atindex) => {
let atiTextArr = paragraphLineBreakFor1112(` ${atindex + 1}${ati}`, ctx); let atiTextArr = paragraphLineBreakFor1112(` ${atindex + 1}${ati}`, ctx);
atiTextArr.forEach(ti => { atiTextArr.forEach(ti => {
cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => { cusInsertRowFunc(descRowNum3, [descSheet.getRow(descRowNum3 - 1)], descSheet, (targetRow) => {
@ -2533,10 +2520,9 @@ async function generateTemplate(data) {
}); });
} }
} }
if (String(data.desc || '').trim()) { if (data.desc) {
descRowNum++; descRowNum++;
const otherDescText = String(data.desc || '').trim(); let otherDesc = paragraphLineBreakFor1112(` ${data.desc}${/$/.test(ci.duration) ? '' : '。'}`, ctx);
let otherDesc = paragraphLineBreakFor1112(` ${otherDescText}${/$/.test(otherDescText) ? '' : '。'}`, ctx);
descSheet.getRow(descRowNum).getCell(1).value = otherDesc[0]; descSheet.getRow(descRowNum).getCell(1).value = otherDesc[0];
descRowNum++; descRowNum++;
if (otherDesc.length > 1) { if (otherDesc.length > 1) {