This commit is contained in:
wintsa 2026-03-05 11:44:00 +08:00
parent 75d5066b62
commit 53c1b2c0db
2 changed files with 137 additions and 20 deletions

View File

@ -43,6 +43,10 @@ interface UserGuideStep {
type XmInfoLike = { type XmInfoLike = {
projectName?: unknown projectName?: unknown
preparedBy?: unknown
reviewedBy?: unknown
preparedDate?: unknown
projectIndustry?: unknown
} }
interface ScaleRowLike { interface ScaleRowLike {
@ -111,6 +115,13 @@ interface DetailRowsStorageLike<T> {
detailRows?: T[] detailRows?: T[]
} }
interface FactorRowLike {
id: string
standardFactor?: unknown
budgetValue?: unknown
remark?: unknown
}
interface ExportScaleRow { interface ExportScaleRow {
major: number major: number
cost: number cost: number
@ -127,8 +138,10 @@ interface ExportMethod1Detail {
basicFee_optional: number basicFee_optional: number
serviceCoe: number serviceCoe: number
majorCoe: number majorCoe: number
processCoe: number
proportion: number
fee: number fee: number
desc: string remark: string
} }
interface ExportMethod1 { interface ExportMethod1 {
@ -150,8 +163,10 @@ interface ExportMethod2Detail {
basicFee_optional: number basicFee_optional: number
serviceCoe: number serviceCoe: number
majorCoe: number majorCoe: number
processCoe: number
proportion: number
fee: number fee: number
desc: string remark: string
} }
interface ExportMethod2 { interface ExportMethod2 {
@ -170,7 +185,7 @@ interface ExportMethod3Detail {
basicFee: number basicFee: number
serviceCoe: number serviceCoe: number
fee: number fee: number
desc: string remark: string
} }
interface ExportMethod3 { interface ExportMethod3 {
@ -185,7 +200,7 @@ interface ExportMethod4Detail {
person_num: number person_num: number
work_day: number work_day: number
fee: number fee: number
desc: string remark: string
} }
interface ExportMethod4 { interface ExportMethod4 {
@ -198,29 +213,58 @@ interface ExportMethod4 {
interface ExportService { interface ExportService {
id: number id: number
fee: number fee: number
process: number
method1?: ExportMethod1 method1?: ExportMethod1
method2?: ExportMethod2 method2?: ExportMethod2
method3?: ExportMethod3 method3?: ExportMethod3
method4?: ExportMethod4 method4?: ExportMethod4
} }
interface ExportServiceCoe {
serviceid: number
coe: number
remark: string
}
interface ExportMajorCoe {
majorid: number
coe: number
remark: string
}
interface ExportContract { interface ExportContract {
name: string name: string
serviceFee: number
addtionalFee: number
reserveFee: number
fee: number fee: number
scale: ExportScaleRow[] scale: ExportScaleRow[]
serviceCoes: ExportServiceCoe[]
majorCoes: ExportMajorCoe[]
services: ExportService[] services: ExportService[]
addtional: []
reserve: []
} }
interface ExportReportPayload { interface ExportReportPayload {
name: string name: string
writer: string
reviewer: string
date: string
industry: number
fee: number fee: number
scaleCost: number
scale: ExportScaleRow[] scale: ExportScaleRow[]
serviceCoes: ExportServiceCoe[]
majorCoes: ExportMajorCoe[]
contracts: ExportContract[] contracts: ExportContract[]
} }
const USER_GUIDE_COMPLETED_KEY = 'jgjs-user-guide-completed-v1' const USER_GUIDE_COMPLETED_KEY = 'jgjs-user-guide-completed-v1'
const PROJECT_INFO_DB_KEY = 'xm-base-info-v1' const PROJECT_INFO_DB_KEY = 'xm-base-info-v1'
const LEGACY_PROJECT_DB_KEY = 'xm-info-v3' const LEGACY_PROJECT_DB_KEY = 'xm-info-v3'
const CONSULT_CATEGORY_FACTOR_DB_KEY = 'xm-consult-category-factor-v1'
const MAJOR_FACTOR_DB_KEY = 'xm-major-factor-v1'
const userGuideSteps: UserGuideStep[] = [ const userGuideSteps: UserGuideStep[] = [
{ {
title: '欢迎使用', title: '欢迎使用',
@ -761,6 +805,47 @@ const getExpertIdFromRowId = (value: string): number | null => {
const hasServiceId = (serviceId: string) => const hasServiceId = (serviceId: string) =>
Object.prototype.hasOwnProperty.call(serviceList as Record<string, unknown>, serviceId) Object.prototype.hasOwnProperty.call(serviceList as Record<string, unknown>, serviceId)
const mapIndustryCodeToExportIndustry = (value: unknown): number => {
const raw = typeof value === 'string' ? value.trim().toUpperCase() : ''
if (!raw) return 0
if (raw === '0' || raw === 'E2') return 0
if (raw === '1' || raw === 'E3') return 1
if (raw === '2' || raw === 'E4') return 2
return 0
}
const buildProjectServiceCoes = (rows: FactorRowLike[] | undefined): ExportServiceCoe[] => {
if (!Array.isArray(rows)) return []
return rows
.map(row => {
const serviceid = toSafeInteger(row.id)
if (serviceid == null) return null
const coe = toFiniteNumber(row.budgetValue) ?? toFiniteNumber(row.standardFactor) ?? 0
return {
serviceid,
coe,
remark: typeof row.remark === 'string' ? row.remark : ''
}
})
.filter((item): item is ExportServiceCoe => Boolean(item))
}
const buildProjectMajorCoes = (rows: FactorRowLike[] | undefined): ExportMajorCoe[] => {
if (!Array.isArray(rows)) return []
return rows
.map(row => {
const majorid = toSafeInteger(row.id)
if (majorid == null) return null
const coe = toFiniteNumber(row.budgetValue) ?? toFiniteNumber(row.standardFactor) ?? 0
return {
majorid,
coe,
remark: typeof row.remark === 'string' ? row.remark : ''
}
})
.filter((item): item is ExportMajorCoe => Boolean(item))
}
const buildScaleRows = (rows: ScaleRowLike[] | undefined): ExportScaleRow[] => { const buildScaleRows = (rows: ScaleRowLike[] | undefined): ExportScaleRow[] => {
if (!Array.isArray(rows)) return [] if (!Array.isArray(rows)) return []
return rows return rows
@ -788,13 +873,13 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
const basicFee = toFiniteNumber(row.budgetFee) const basicFee = toFiniteNumber(row.budgetFee)
const basicFeeBasic = toFiniteNumber(row.budgetFeeBasic) const basicFeeBasic = toFiniteNumber(row.budgetFeeBasic)
const basicFeeOptional = toFiniteNumber(row.budgetFeeOptional) const basicFeeOptional = toFiniteNumber(row.budgetFeeOptional)
const desc = typeof row.remark === 'string' ? row.remark : '' const remark = typeof row.remark === 'string' ? row.remark : ''
const hasValue = const hasValue =
cost != null || cost != null ||
basicFee != null || basicFee != null ||
basicFeeBasic != null || basicFeeBasic != null ||
basicFeeOptional != null || basicFeeOptional != null ||
isNonEmptyString(desc) isNonEmptyString(remark)
if (!hasValue) return null if (!hasValue) return null
return { return {
major, major,
@ -806,8 +891,10 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
basicFee_optional: basicFeeOptional ?? 0, basicFee_optional: basicFeeOptional ?? 0,
serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor), serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
majorCoe: toFiniteNumberOrZero(row.majorFactor), majorCoe: toFiniteNumberOrZero(row.majorFactor),
processCoe: 1,
proportion: 1,
fee: basicFee ?? 0, fee: basicFee ?? 0,
desc remark
} }
}) })
.filter((item): item is ExportMethod1Detail => Boolean(item)) .filter((item): item is ExportMethod1Detail => Boolean(item))
@ -833,13 +920,13 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n
const basicFee = toFiniteNumber(row.budgetFee) const basicFee = toFiniteNumber(row.budgetFee)
const basicFeeBasic = toFiniteNumber(row.budgetFeeBasic) const basicFeeBasic = toFiniteNumber(row.budgetFeeBasic)
const basicFeeOptional = toFiniteNumber(row.budgetFeeOptional) const basicFeeOptional = toFiniteNumber(row.budgetFeeOptional)
const desc = typeof row.remark === 'string' ? row.remark : '' const remark = typeof row.remark === 'string' ? row.remark : ''
const hasValue = const hasValue =
area != null || area != null ||
basicFee != null || basicFee != null ||
basicFeeBasic != null || basicFeeBasic != null ||
basicFeeOptional != null || basicFeeOptional != null ||
isNonEmptyString(desc) isNonEmptyString(remark)
if (!hasValue) return null if (!hasValue) return null
return { return {
major, major,
@ -851,8 +938,10 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n
basicFee_optional: basicFeeOptional ?? 0, basicFee_optional: basicFeeOptional ?? 0,
serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor), serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
majorCoe: toFiniteNumberOrZero(row.majorFactor), majorCoe: toFiniteNumberOrZero(row.majorFactor),
processCoe: 1,
proportion: 1,
fee: basicFee ?? 0, fee: basicFee ?? 0,
desc remark
} }
}) })
.filter((item): item is ExportMethod2Detail => Boolean(item)) .filter((item): item is ExportMethod2Detail => Boolean(item))
@ -877,8 +966,8 @@ const buildMethod3 = (rows: WorkloadMethodRowLike[] | undefined): ExportMethod3
const amount = toFiniteNumber(row.workload) const amount = toFiniteNumber(row.workload)
const basicFee = toFiniteNumber(row.basicFee) const basicFee = toFiniteNumber(row.basicFee)
const fee = toFiniteNumber(row.serviceFee) const fee = toFiniteNumber(row.serviceFee)
const desc = typeof row.remark === 'string' ? row.remark : '' const remark = typeof row.remark === 'string' ? row.remark : ''
const hasValue = amount != null || basicFee != null || fee != null || isNonEmptyString(desc) const hasValue = amount != null || basicFee != null || fee != null || isNonEmptyString(remark)
if (!hasValue) return null if (!hasValue) return null
return { return {
task, task,
@ -887,7 +976,7 @@ const buildMethod3 = (rows: WorkloadMethodRowLike[] | undefined): ExportMethod3
basicFee: basicFee ?? 0, basicFee: basicFee ?? 0,
serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor), serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
fee: fee ?? 0, fee: fee ?? 0,
desc remark
} }
}) })
.filter((item): item is ExportMethod3Detail => Boolean(item)) .filter((item): item is ExportMethod3Detail => Boolean(item))
@ -909,8 +998,8 @@ const buildMethod4 = (rows: HourlyMethodRowLike[] | undefined): ExportMethod4 |
const personNum = toFiniteNumber(row.personnelCount) const personNum = toFiniteNumber(row.personnelCount)
const workDay = toFiniteNumber(row.workdayCount) const workDay = toFiniteNumber(row.workdayCount)
const fee = toFiniteNumber(row.serviceBudget) const fee = toFiniteNumber(row.serviceBudget)
const desc = typeof row.remark === 'string' ? row.remark : '' const remark = typeof row.remark === 'string' ? row.remark : ''
const hasValue = personNum != null || workDay != null || fee != null || isNonEmptyString(desc) const hasValue = personNum != null || workDay != null || fee != null || isNonEmptyString(remark)
if (!hasValue) return null if (!hasValue) return null
return { return {
expert, expert,
@ -918,7 +1007,7 @@ const buildMethod4 = (rows: HourlyMethodRowLike[] | undefined): ExportMethod4 |
person_num: personNum ?? 0, person_num: personNum ?? 0,
work_day: workDay ?? 0, work_day: workDay ?? 0,
fee: fee ?? 0, fee: fee ?? 0,
desc remark
} }
}) })
.filter((item): item is ExportMethod4Detail => Boolean(item)) .filter((item): item is ExportMethod4Detail => Boolean(item))
@ -954,16 +1043,25 @@ const buildServiceFee = (
} }
const buildExportReportPayload = async (): Promise<ExportReportPayload> => { const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const [projectInfoRaw, projectScaleRaw, contractCardsRaw] = await Promise.all([ const [projectInfoRaw, projectScaleRaw, consultCategoryFactorRaw, majorFactorRaw, contractCardsRaw] = await Promise.all([
localforage.getItem<XmInfoLike>(PROJECT_INFO_DB_KEY), localforage.getItem<XmInfoLike>(PROJECT_INFO_DB_KEY),
localforage.getItem<XmInfoStorageLike>(LEGACY_PROJECT_DB_KEY), localforage.getItem<XmInfoStorageLike>(LEGACY_PROJECT_DB_KEY),
localforage.getItem<DetailRowsStorageLike<FactorRowLike>>(CONSULT_CATEGORY_FACTOR_DB_KEY),
localforage.getItem<DetailRowsStorageLike<FactorRowLike>>(MAJOR_FACTOR_DB_KEY),
localforage.getItem<ContractCardItem[]>('ht-card-v1') localforage.getItem<ContractCardItem[]>('ht-card-v1')
]) ])
const projectInfo = projectInfoRaw || {} const projectInfo = projectInfoRaw || {}
const projectScaleSource = projectScaleRaw || {} const projectScaleSource = projectScaleRaw || {}
const projectScale = buildScaleRows(projectScaleSource.detailRows) const projectScale = buildScaleRows(projectScaleSource.detailRows)
const projectScaleCost = sumNumbers(projectScale.map(item => item.cost))
const projectServiceCoes = buildProjectServiceCoes(consultCategoryFactorRaw?.detailRows)
const projectMajorCoes = buildProjectMajorCoes(majorFactorRaw?.detailRows)
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 reviewer = isNonEmptyString(projectInfo.reviewedBy) ? projectInfo.reviewedBy.trim() : ''
const date = isNonEmptyString(projectInfo.preparedDate) ? projectInfo.preparedDate.trim() : ''
const industry = mapIndustryCodeToExportIndustry(projectInfo.projectIndustry)
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')
@ -1005,6 +1103,7 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
const service: ExportService = { const service: ExportService = {
id: serviceId, id: serviceId,
process: 0,
fee fee
} }
if (method1) service.method1 = method1 if (method1) service.method1 = method1
@ -1024,20 +1123,37 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
toFiniteNumber(fixedRow?.workload), toFiniteNumber(fixedRow?.workload),
toFiniteNumber(fixedRow?.hourly) toFiniteNumber(fixedRow?.hourly)
]) ])
const contractFee = fixedSubtotal ?? (serviceFeeSum !== 0 ? serviceFeeSum : fixedMethodSum) const serviceFee = fixedSubtotal ?? (serviceFeeSum !== 0 ? serviceFeeSum : fixedMethodSum)
const addtionalFee = 0
const reserveFee = 0
const contractFee = serviceFee + addtionalFee + reserveFee
contracts.push({ contracts.push({
name: isNonEmptyString(contract.name) ? contract.name : `合同段-${index + 1}`, name: isNonEmptyString(contract.name) ? contract.name : `合同段-${index + 1}`,
serviceFee,
addtionalFee,
reserveFee,
fee: contractFee, fee: contractFee,
scale: buildScaleRows(htInfoRaw?.detailRows), scale: buildScaleRows(htInfoRaw?.detailRows),
services serviceCoes: projectServiceCoes.map(item => ({ ...item })),
majorCoes: projectMajorCoes.map(item => ({ ...item })),
services,
addtional: [],
reserve: []
}) })
} }
return { return {
name: projectName, name: projectName,
writer,
reviewer,
date,
industry,
fee: sumNumbers(contracts.map(item => item.fee)), fee: sumNumbers(contracts.map(item => item.fee)),
scaleCost: projectScaleCost,
scale: projectScale, scale: projectScale,
serviceCoes: projectServiceCoes,
majorCoes: projectMajorCoes,
contracts contracts
} }
} }

View File

@ -624,6 +624,7 @@ let data1 = {
export async function exportFile(fileName, data) { export async function exportFile(fileName, data) {
console.log(data)
if (window.showSaveFilePicker) { if (window.showSaveFilePicker) {
const handle = await window.showSaveFilePicker({ const handle = await window.showSaveFilePicker({
suggestedName: fileName, suggestedName: fileName,
@ -1034,7 +1035,7 @@ async function generateTemplate(data) {
targetRow.getCell(7).value = eobj.person_num; targetRow.getCell(7).value = eobj.person_num;
targetRow.getCell(8).value = eobj.work_day; targetRow.getCell(8).value = eobj.work_day;
targetRow.getCell(9).value = eobj.fee; targetRow.getCell(9).value = eobj.fee;
targetRow.getCell(10).value = eobj.desc; targetRow.getCell(10).value = eobj.remark;
}); });
}); });
} }