final
This commit is contained in:
parent
398fca9265
commit
f4f6e5c618
@ -119,6 +119,7 @@ const buildDefaultRows = (): DetailRow[] => {
|
||||
const mergeWithDictRows = (rowsFromDb: DetailRow[] | undefined): DetailRow[] => {
|
||||
const dbValueMap = new Map<string, DetailRow>()
|
||||
for (const row of rowsFromDb || []) {
|
||||
if (row?.isGroupRow === true) continue
|
||||
const rowId = String(row.id)
|
||||
dbValueMap.set(rowId, row)
|
||||
const aliasId = majorIdAliasMap.get(rowId)
|
||||
@ -140,6 +141,45 @@ const mergeWithDictRows = (rowsFromDb: DetailRow[] | undefined): DetailRow[] =>
|
||||
})
|
||||
}
|
||||
|
||||
const buildGroupRows = (rows: DetailRow[]): DetailRow[] => {
|
||||
const rowById = new Map(rows.map(row => [String(row.id || ''), row] as const))
|
||||
const groupRows: DetailRow[] = []
|
||||
for (const group of detailDict.value) {
|
||||
let amountTotal = 0
|
||||
let hasAmount = false
|
||||
let landAreaTotal = 0
|
||||
let hasLandArea = false
|
||||
for (const child of group.children) {
|
||||
const leaf = rowById.get(String(child.id || ''))
|
||||
const amount = leaf?.amount
|
||||
if (typeof amount === 'number' && Number.isFinite(amount)) {
|
||||
amountTotal += amount
|
||||
hasAmount = true
|
||||
}
|
||||
const landArea = leaf?.landArea
|
||||
if (typeof landArea === 'number' && Number.isFinite(landArea)) {
|
||||
landAreaTotal += landArea
|
||||
hasLandArea = true
|
||||
}
|
||||
}
|
||||
groupRows.push({
|
||||
id: group.id,
|
||||
groupCode: group.code,
|
||||
groupName: group.name,
|
||||
majorCode: group.code,
|
||||
majorName: group.name,
|
||||
hasCost: true,
|
||||
hasArea: true,
|
||||
amount: hasAmount ? roundTo(amountTotal, 3) : null,
|
||||
landArea: hasLandArea ? roundTo(landAreaTotal, 3) : null,
|
||||
path: [`${group.code} ${group.name}`],
|
||||
hide: false,
|
||||
isGroupRow: true
|
||||
})
|
||||
}
|
||||
return groupRows
|
||||
}
|
||||
|
||||
|
||||
const applyPinnedTotalAmount = (
|
||||
api: GridApi<DetailRow> | null | undefined,
|
||||
@ -239,6 +279,7 @@ interface DetailRow {
|
||||
landArea: number | null
|
||||
path: string[]
|
||||
hide?: boolean
|
||||
isGroupRow?: boolean
|
||||
}
|
||||
|
||||
interface XmBaseInfoState {
|
||||
@ -398,11 +439,13 @@ const pinnedTopRowData = ref<DetailRow[]>([
|
||||
|
||||
const saveToIndexedDB = async () => {
|
||||
try {
|
||||
const payload: GridPersistState = {
|
||||
detailRows: detailRows.value.map(row => ({
|
||||
const leafRows = detailRows.value.map(row => ({
|
||||
...JSON.parse(JSON.stringify(row)),
|
||||
hide: Boolean(row.hide)
|
||||
hide: Boolean(row.hide),
|
||||
isGroupRow: false
|
||||
}))
|
||||
const payload: GridPersistState = {
|
||||
detailRows: [...leafRows, ...buildGroupRows(leafRows)]
|
||||
}
|
||||
payload.roughCalcEnabled = roughCalcEnabled.value
|
||||
payload.totalAmount = pinnedTopRowData.value[0].amount
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import { computed, defineComponent, h, nextTick, onActivated, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import type { ComponentPublicInstance, PropType } from 'vue'
|
||||
import { AgGridVue } from 'ag-grid-vue3'
|
||||
import type { ColDef, GridOptions, ICellRendererParams, IHeaderParams } from 'ag-grid-community'
|
||||
import type { ColDef, GridOptions, ICellRendererParams } from 'ag-grid-community'
|
||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
|
||||
import { addNumbers } from '@/lib/decimal'
|
||||
@ -14,7 +14,7 @@ import {
|
||||
getPricingMethodTotalsForServices,
|
||||
type PricingMethodTotals
|
||||
} from '@/lib/pricingMethodTotals'
|
||||
import { Pencil, Eraser, Trash2, CircleHelp } from 'lucide-vue-next'
|
||||
import { Pencil, Eraser, Trash2 } from 'lucide-vue-next'
|
||||
import {
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
@ -554,46 +554,38 @@ const ProcessCellRenderer = defineComponent({
|
||||
return () => {
|
||||
const row = props.params.data
|
||||
if (!row || isFixedRow(row)) return null
|
||||
const checked = row.process === 1
|
||||
const onToggle = (event: Event) => {
|
||||
const processValue = row.process === 1 ? 1 : 0
|
||||
const onSelect = (event: Event, value: 0 | 1) => {
|
||||
event.stopPropagation()
|
||||
const target = event.target as HTMLInputElement | null
|
||||
void props.params.context?.onToggleProcess?.(row.id, Boolean(target?.checked))
|
||||
void props.params.context?.onSetProcess?.(row.id, value)
|
||||
}
|
||||
return h('div', { class: 'flex items-center justify-center w-full' }, [
|
||||
const radioName = `process-${row.id}`
|
||||
return h('div', { class: 'flex items-center justify-center gap-4 w-full text-sm' }, [
|
||||
h('label', { class: 'inline-flex items-center gap-1.5 cursor-pointer' }, [
|
||||
h('input', {
|
||||
type: 'checkbox',
|
||||
checked,
|
||||
class: 'cursor-pointer',
|
||||
onChange: onToggle
|
||||
})
|
||||
])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const ProcessHeaderRenderer = defineComponent({
|
||||
name: 'ProcessHeaderRenderer',
|
||||
props: {
|
||||
params: {
|
||||
type: Object as PropType<IHeaderParams>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const tooltipText = '默认为编制,勾选为审核'
|
||||
props.params.setTooltip?.(tooltipText, () => true)
|
||||
return () =>
|
||||
h('div', { class: 'flex items-center justify-center gap-1 w-full' }, [
|
||||
h('span', props.params.displayName || '工作环节'),
|
||||
h('span', { class: 'inline-flex items-center pointer-events-auto' }, [
|
||||
h(CircleHelp, {
|
||||
size: 20,
|
||||
class: 'text-muted-foreground pointer-events-none'
|
||||
})
|
||||
type: 'radio',
|
||||
name: radioName,
|
||||
checked: processValue === 0,
|
||||
class: 'cursor-pointer h-4 w-4',
|
||||
onClick: (event: Event) => event.stopPropagation(),
|
||||
onChange: (event: Event) => onSelect(event, 0)
|
||||
}),
|
||||
h('span', '编制')
|
||||
]),
|
||||
h('label', { class: 'inline-flex items-center gap-1.5 cursor-pointer' }, [
|
||||
h('input', {
|
||||
type: 'radio',
|
||||
name: radioName,
|
||||
checked: processValue === 1,
|
||||
class: 'cursor-pointer h-4 w-4',
|
||||
onClick: (event: Event) => event.stopPropagation(),
|
||||
onChange: (event: Event) => onSelect(event, 1)
|
||||
}),
|
||||
h('span', '审核')
|
||||
])
|
||||
])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const columnDefs: ColDef<DetailRow>[] = [
|
||||
@ -624,10 +616,10 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
{
|
||||
headerName: '工作环节',
|
||||
field: 'process',
|
||||
headerClass: 'ag-center-header',
|
||||
minWidth: 90,
|
||||
maxWidth: 110,
|
||||
flex: 1,
|
||||
headerClass: 'ag-center-header zxfw-process-header',
|
||||
minWidth: 170,
|
||||
maxWidth: 220,
|
||||
flex: 1.2,
|
||||
editable: false,
|
||||
sortable: false,
|
||||
filter: false,
|
||||
@ -641,7 +633,6 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
if (!params.data || isFixedRow(params.data)) return null
|
||||
return params.data.process === 1 ? 1 : 0
|
||||
},
|
||||
headerComponent: ProcessHeaderRenderer,
|
||||
cellRenderer: ProcessCellRenderer
|
||||
},
|
||||
{
|
||||
@ -754,15 +745,17 @@ const detailGridOptions: GridOptions<DetailRow> = {
|
||||
treeData: false,
|
||||
getDataPath: undefined,
|
||||
context: {
|
||||
onToggleProcess: async (rowId: string, checked: boolean) => {
|
||||
onSetProcess: async (rowId: string, value: 0 | 1) => {
|
||||
const currentState = getCurrentContractState()
|
||||
let changed = false
|
||||
const nextRows = currentState.detailRows.map(row => {
|
||||
if (isFixedRow(row) || String(row.id) !== String(rowId)) return row
|
||||
const nextValue = value === 1 ? 1 : 0
|
||||
if ((row.process === 1 ? 1 : 0) === nextValue) return row
|
||||
changed = true
|
||||
return {
|
||||
...row,
|
||||
process: checked ? 1 : 0
|
||||
process: nextValue
|
||||
}
|
||||
})
|
||||
if (!changed) return
|
||||
@ -1208,3 +1201,13 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.zxfw-process-header .ag-header-cell-label) {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep(.zxfw-process-header .ag-header-cell-text) {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -20,6 +20,11 @@ import {
|
||||
AlertDialogRoot,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
ToastDescription,
|
||||
ToastProvider,
|
||||
ToastRoot,
|
||||
ToastTitle,
|
||||
ToastViewport,
|
||||
} from 'reka-ui'
|
||||
import { decodeZwArchive, encodeZwArchive, ZW_FILE_EXTENSION } from '@/lib/zwArchive'
|
||||
import { addNumbers, roundTo } from '@/lib/decimal'
|
||||
@ -62,6 +67,7 @@ interface ScaleRowLike {
|
||||
id: string
|
||||
amount: number | null
|
||||
landArea: number | null
|
||||
isGroupRow?: boolean
|
||||
}
|
||||
|
||||
interface XmInfoStorageLike extends XmInfoLike {
|
||||
@ -457,6 +463,10 @@ const pendingImportPayload = shallowRef<DataPackage | null>(null)
|
||||
const pendingImportFileName = ref('')
|
||||
const userGuideOpen = ref(false)
|
||||
const userGuideStepIndex = ref(0)
|
||||
const reportExportToastOpen = ref(false)
|
||||
const reportExportProgress = ref(0)
|
||||
const reportExportStatus = ref<'running' | 'success' | 'error'>('running')
|
||||
const reportExportText = ref('')
|
||||
const tabItemElMap = new Map<string, HTMLElement>()
|
||||
const tabTitleElMap = new Map<string, HTMLElement>()
|
||||
const tabPanelElMap = new Map<string, HTMLElement>()
|
||||
@ -468,6 +478,7 @@ const isTabDragging = ref(false)
|
||||
const tabTitleOverflowMap = ref<Record<string, boolean>>({})
|
||||
let tabStripViewportEl: HTMLElement | null = null
|
||||
let tabTitleOverflowRafId: number | null = null
|
||||
let reportExportToastTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
const tabsModel = computed({
|
||||
get: () => tabStore.tabs,
|
||||
@ -502,6 +513,31 @@ const closeMenus = () => {
|
||||
dataMenuOpen.value = false
|
||||
}
|
||||
|
||||
const clearReportExportToastTimer = () => {
|
||||
if (!reportExportToastTimer) return
|
||||
clearTimeout(reportExportToastTimer)
|
||||
reportExportToastTimer = null
|
||||
}
|
||||
|
||||
const showReportExportProgress = (progress: number, text: string) => {
|
||||
clearReportExportToastTimer()
|
||||
reportExportStatus.value = 'running'
|
||||
reportExportProgress.value = Math.max(0, Math.min(100, progress))
|
||||
reportExportText.value = text
|
||||
reportExportToastOpen.value = true
|
||||
}
|
||||
|
||||
const finishReportExportProgress = (success: boolean, text: string) => {
|
||||
clearReportExportToastTimer()
|
||||
reportExportStatus.value = success ? 'success' : 'error'
|
||||
reportExportProgress.value = 100
|
||||
reportExportText.value = text
|
||||
reportExportToastOpen.value = true
|
||||
reportExportToastTimer = setTimeout(() => {
|
||||
reportExportToastOpen.value = false
|
||||
}, success ? 1200 : 1800)
|
||||
}
|
||||
|
||||
const markGuideCompleted = () => {
|
||||
try {
|
||||
localStorage.setItem(USER_GUIDE_COMPLETED_KEY, '1')
|
||||
@ -1016,6 +1052,16 @@ const toExportScaleRows = (rows: ScaleRowLike[] | undefined): ExportScaleRow[] =
|
||||
.filter((item): item is ExportScaleRow => Boolean(item))
|
||||
}
|
||||
|
||||
const sumLeafScaleCost = (rows: ScaleRowLike[] | undefined) => {
|
||||
if (!Array.isArray(rows)) return 0
|
||||
return sumNumbers(
|
||||
rows.map(row => {
|
||||
if (row?.isGroupRow === true) return null
|
||||
return toFiniteNumber(row?.amount)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | null => {
|
||||
@ -1365,9 +1411,8 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
|
||||
|
||||
const projectInfo = projectInfoRaw || {}
|
||||
const projectScaleSource = projectScaleRaw || {}
|
||||
|
||||
const projectScale = projectScaleSource.roughCalcEnabled ? [] : toExportScaleRows(projectScaleSource.detailRows)
|
||||
const projectScaleCost = toFiniteNumber(projectScaleSource.totalAmount) ?? sumNumbers(projectScale.map(item => item.cost))
|
||||
const projectScaleCost = toFiniteNumber(projectScaleSource.totalAmount) ?? sumLeafScaleCost(projectScaleSource.detailRows)
|
||||
projectScale.push({
|
||||
major: -1, cost: projectScaleCost,
|
||||
area: null
|
||||
@ -1572,14 +1617,17 @@ const exportData = async () => {
|
||||
|
||||
const exportReport = async () => {
|
||||
try {
|
||||
showReportExportProgress(10, '正在准备报表导出...')
|
||||
const now = new Date()
|
||||
showReportExportProgress(40, '正在汇总报表数据...')
|
||||
const payload = await buildExportReportPayload()
|
||||
showReportExportProgress(80, '正在生成并写出报表文件...')
|
||||
const fileName = `${sanitizeFileNamePart(payload.name)}-报表-${formatExportTimestamp(now)}`
|
||||
console.log(payload)
|
||||
await exportFile(fileName, payload)
|
||||
finishReportExportProgress(true, '报表导出完成')
|
||||
} catch (error) {
|
||||
console.error('export report failed:', error)
|
||||
// window.alert('导出报表失败,请重试。')
|
||||
finishReportExportProgress(false, '报表导出失败,请重试')
|
||||
} finally {
|
||||
dataMenuOpen.value = false
|
||||
}
|
||||
@ -1710,6 +1758,7 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearReportExportToastTimer()
|
||||
window.removeEventListener('mousedown', handleGlobalMouseDown)
|
||||
window.removeEventListener('keydown', handleGlobalKeyDown)
|
||||
window.removeEventListener('resize', scheduleUpdateTabTitleOverflow)
|
||||
@ -1761,6 +1810,7 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ToastProvider>
|
||||
<TooltipProvider>
|
||||
<div class="flex flex-col w-full h-screen bg-background overflow-hidden">
|
||||
<div class="grid grid-cols-[minmax(0,1fr)_auto] items-start gap-2 border-b bg-muted/30 px-2 pt-1 h-15 flex-none">
|
||||
@ -1966,8 +2016,30 @@ watch(
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ToastRoot
|
||||
:open="reportExportToastOpen"
|
||||
:duration="0"
|
||||
class="pointer-events-auto rounded-xl border border-border bg-card px-4 py-3 text-foreground shadow-lg"
|
||||
>
|
||||
<ToastTitle class="text-sm font-semibold text-foreground">
|
||||
{{ reportExportStatus === 'running' ? '导出报表' : (reportExportStatus === 'success' ? '导出成功' : '导出失败') }}
|
||||
</ToastTitle>
|
||||
<ToastDescription class="mt-1 text-xs text-muted-foreground">{{ reportExportText }}</ToastDescription>
|
||||
<div class="mt-2 h-1.5 w-full overflow-hidden rounded-full bg-muted">
|
||||
<div
|
||||
class="h-full transition-all duration-300"
|
||||
:class="reportExportStatus === 'error'
|
||||
? 'bg-red-500'
|
||||
: (reportExportStatus === 'success' ? 'bg-foreground/70' : 'bg-foreground')"
|
||||
:style="{ width: `${reportExportProgress}%` }"
|
||||
/>
|
||||
</div>
|
||||
</ToastRoot>
|
||||
<ToastViewport class="fixed bottom-5 right-5 z-[85] flex w-[380px] max-w-[92vw] flex-col gap-2 outline-none" />
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</ToastProvider>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@ -30,5 +30,14 @@ export const sumByNumber = <T>(list: T[], pick: (item: T) => MaybeNumber) => {
|
||||
return total.toNumber()
|
||||
}
|
||||
|
||||
export const decimalAggSum = (params: { values?: unknown[] }) =>
|
||||
sumFiniteValues(params.values || [])
|
||||
export const decimalAggSum = (params: { values?: unknown[] }) => {
|
||||
const values = params.values || []
|
||||
let hasFinite = false
|
||||
for (const value of values) {
|
||||
if (!isFiniteNumber(value)) continue
|
||||
hasFinite = true
|
||||
break
|
||||
}
|
||||
if (!hasFinite) return null
|
||||
return sumFiniteValues(values)
|
||||
}
|
||||
|
||||
@ -164,6 +164,12 @@ const toStoredDetailRowsState = <TRow = unknown>(state: { detailRows?: TRow[] }
|
||||
const hasOwn = (obj: unknown, key: string) =>
|
||||
Object.prototype.hasOwnProperty.call(obj || {}, key)
|
||||
|
||||
const isGroupScaleRow = (row: unknown) =>
|
||||
Boolean(row && typeof row === 'object' && (row as Record<string, unknown>).isGroupRow === true)
|
||||
|
||||
const stripGroupScaleRows = <TRow>(rows: TRow[] | undefined): TRow[] =>
|
||||
(rows || []).filter(row => !isGroupScaleRow(row))
|
||||
|
||||
const getRowNumberOrFallback = (
|
||||
row: Record<string, unknown> | undefined,
|
||||
key: string,
|
||||
@ -306,8 +312,9 @@ const mergeScaleRows = (
|
||||
consultCategoryFactorMap?: Map<string, number | null>,
|
||||
majorFactorMap?: Map<string, number | null>
|
||||
): ScaleRow[] => {
|
||||
const dbValueMap = toRowMap(rowsFromDb)
|
||||
for (const row of rowsFromDb || []) {
|
||||
const sourceRows = stripGroupScaleRows(rowsFromDb)
|
||||
const dbValueMap = toRowMap(sourceRows)
|
||||
for (const row of sourceRows) {
|
||||
const rowId = String(row.id)
|
||||
const nextId = majorIdAliasMap.get(rowId)
|
||||
if (nextId && !dbValueMap.has(nextId)) {
|
||||
@ -370,7 +377,7 @@ const getOnlyCostScaleBudgetFee = (
|
||||
industryId?: string | null
|
||||
) => {
|
||||
const industryMajorEntry = getIndustryMajorEntryByIndustryId(industryId)
|
||||
const sourceRows = rowsFromDb || []
|
||||
const sourceRows = stripGroupScaleRows(rowsFromDb)
|
||||
const defaultConsultCategoryFactor =
|
||||
consultCategoryFactorMap?.get(String(serviceId)) ?? getDefaultConsultCategoryFactor(serviceId)
|
||||
const defaultMajorFactor =
|
||||
@ -426,14 +433,15 @@ const buildOnlyCostScaleDetailRows = (
|
||||
majorFactorMap?: Map<string, number | null>,
|
||||
industryId?: string | null
|
||||
) => {
|
||||
const totalAmount = sumByNumberNullable(rowsFromDb || [], row =>
|
||||
const sourceRows = stripGroupScaleRows(rowsFromDb)
|
||||
const totalAmount = sumByNumberNullable(sourceRows, row =>
|
||||
typeof row?.amount === 'number' && Number.isFinite(row.amount) ? row.amount : null
|
||||
)
|
||||
const industryMajorEntry = getIndustryMajorEntryByIndustryId(industryId)
|
||||
const onlyCostRowId = industryMajorEntry?.id || ONLY_COST_SCALE_ROW_ID
|
||||
const onlyRow =
|
||||
(rowsFromDb || []).find(row => String(row?.id || '') === ONLY_COST_SCALE_ROW_ID) ||
|
||||
(rowsFromDb || []).find(row => String(row?.id || '') === onlyCostRowId)
|
||||
sourceRows.find(row => String(row?.id || '') === ONLY_COST_SCALE_ROW_ID) ||
|
||||
sourceRows.find(row => String(row?.id || '') === onlyCostRowId)
|
||||
const consultCategoryFactor = getRowNumberOrFallback(
|
||||
onlyRow,
|
||||
'consultCategoryFactor',
|
||||
@ -598,7 +606,7 @@ const resolveScaleRows = (
|
||||
if (htData?.detailRows != null) {
|
||||
return mergeScaleRows(
|
||||
serviceId,
|
||||
htData.detailRows as any,
|
||||
stripGroupScaleRows(htData.detailRows as any),
|
||||
consultCategoryFactorMap,
|
||||
majorFactorMap
|
||||
)
|
||||
|
||||
21
src/sql.ts
21
src/sql.ts
@ -2,7 +2,6 @@
|
||||
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)
|
||||
@ -552,7 +551,6 @@ export async function exportFile(fileName: string, data: any): Promise<void> {
|
||||
// 按模板生成最终工作簿:填充封面、目录、各分表及汇总数据。
|
||||
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';
|
||||
@ -724,7 +722,12 @@ async function generateTemplate(data) {
|
||||
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);
|
||||
const method5AdditionalFee = ci.method5.addtional?.reduce((a, b) =>
|
||||
addNumbers(a, toFiniteNumber(b?.m5?.fee)), 0) || 0;
|
||||
sheet_5.getRow(3).getCell(6).value = numberFormatter(
|
||||
addNumbers(method5AdditionalFee, toFiniteNumber(ci.method5.reserve?.fee)),
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
// 更新目录的第三部分
|
||||
@ -772,22 +775,22 @@ async function generateTemplate(data) {
|
||||
targetRow.getCell(3).value = serviceX.name;
|
||||
if (sobj.method1) {
|
||||
targetRow.getCell(4).value = numberFormatter(sobj.method1.fee, 2);
|
||||
m1Sum += sobj.method1.fee;
|
||||
m1Sum = addNumbers(m1Sum, toFiniteNumber(sobj.method1.fee));
|
||||
}
|
||||
if (sobj.method2) {
|
||||
targetRow.getCell(5).value = numberFormatter(sobj.method2.fee, 2);
|
||||
m2Sum += sobj.method2.fee;
|
||||
m2Sum = addNumbers(m2Sum, toFiniteNumber(sobj.method2.fee));
|
||||
}
|
||||
if (sobj.method3) {
|
||||
targetRow.getCell(6).value = numberFormatter(sobj.method3.fee, 2);
|
||||
m3Sum += sobj.method3.fee;
|
||||
m3Sum = addNumbers(m3Sum, toFiniteNumber(sobj.method3.fee));
|
||||
}
|
||||
if (sobj.method4) {
|
||||
targetRow.getCell(7).value = numberFormatter(sobj.method4.fee, 2);
|
||||
m4Sum += sobj.method4.fee;
|
||||
m4Sum = addNumbers(m4Sum, toFiniteNumber(sobj.method4.fee));
|
||||
}
|
||||
targetRow.getCell(8).value = numberFormatter(sobj.fee, 2);
|
||||
serviceSum += sobj.fee;
|
||||
serviceSum = addNumbers(serviceSum, toFiniteNumber(sobj.fee));
|
||||
});
|
||||
if (sobj.method1 || sobj.method2) {
|
||||
cusInsertRowFunc(4 + num_2, [sheet_2.getRow(4)], sheet_2, (targetRow) => {
|
||||
@ -1800,8 +1803,6 @@ async function generateTemplate(data) {
|
||||
descSheet.spliceRows(descRowNum, 5);
|
||||
descRowNum++;
|
||||
}
|
||||
console.log(descRowNum);
|
||||
|
||||
return workbook;
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user