fix
This commit is contained in:
parent
0f7deb0f1b
commit
083c8f1886
@ -5,9 +5,9 @@ import { AgGridVue } from 'ag-grid-vue3'
|
||||
import type { ColDef, GridOptions, ICellRendererParams } from 'ag-grid-community'
|
||||
import { myTheme, gridOptions, agGridWrapClass, agGridStyle } from '@/lib/diyAgGridOptions'
|
||||
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
|
||||
import { addNumbers } from '@/lib/decimal'
|
||||
import { addNumbers, roundTo } from '@/lib/decimal'
|
||||
import { parseNumberOrNull } from '@/lib/number'
|
||||
import { formatThousandsFlexible } from '@/lib/numberFormat'
|
||||
import { formatThousands, formatThousandsFlexible } from '@/lib/numberFormat'
|
||||
import {
|
||||
ensurePricingMethodDetailRowsForServices,
|
||||
getPricingMethodTotalsForService,
|
||||
@ -51,6 +51,7 @@ interface DetailRow {
|
||||
workload: number | null
|
||||
hourly: number | null
|
||||
subtotal?: number | null
|
||||
finalFee?: number | null
|
||||
actions?: unknown
|
||||
}
|
||||
|
||||
@ -154,6 +155,7 @@ const detailRows = computed<DetailRow[]>(() =>
|
||||
workload: typeof row.workload === 'number' ? row.workload : null,
|
||||
hourly: typeof row.hourly === 'number' ? row.hourly : null,
|
||||
subtotal: typeof row.subtotal === 'number' ? row.subtotal : null,
|
||||
finalFee: typeof (row as any).finalFee === 'number' ? (row as any).finalFee : null,
|
||||
actions: row.actions
|
||||
}))
|
||||
)
|
||||
@ -174,6 +176,7 @@ const getCurrentContractState = (): ZxFwViewState => {
|
||||
workload: typeof row.workload === 'number' ? row.workload : null,
|
||||
hourly: typeof row.hourly === 'number' ? row.hourly : null,
|
||||
subtotal: typeof row.subtotal === 'number' ? row.subtotal : null,
|
||||
finalFee: typeof (row as any).finalFee === 'number' ? (row as any).finalFee : null,
|
||||
actions: row.actions
|
||||
}))
|
||||
}
|
||||
@ -457,36 +460,21 @@ const clearRowValues = async (row: DetailRow) => {
|
||||
})
|
||||
const sanitizedTotals = sanitizePricingTotalsByService(row.id, totals)
|
||||
const currentState = getCurrentContractState()
|
||||
const clearedRows = currentState.detailRows.map(item =>
|
||||
item.id !== row.id
|
||||
? item
|
||||
: {
|
||||
const clearedRows = currentState.detailRows.map(item => {
|
||||
if (item.id !== row.id) return item
|
||||
const newSubtotal = sumNullableNumbers([sanitizedTotals.investScale, sanitizedTotals.landScale, sanitizedTotals.workload, sanitizedTotals.hourly])
|
||||
return {
|
||||
...item,
|
||||
investScale: sanitizedTotals.investScale,
|
||||
landScale: sanitizedTotals.landScale,
|
||||
workload: sanitizedTotals.workload,
|
||||
hourly: sanitizedTotals.hourly
|
||||
hourly: sanitizedTotals.hourly,
|
||||
finalFee: newSubtotal != null ? roundTo(newSubtotal, 2) : null
|
||||
}
|
||||
)
|
||||
const nextInvestScale = getMethodTotalFromRows(clearedRows, 'investScale')
|
||||
const nextLandScale = getMethodTotalFromRows(clearedRows, 'landScale')
|
||||
const nextWorkload = getMethodTotalFromRows(clearedRows, 'workload')
|
||||
const nextHourly = getMethodTotalFromRows(clearedRows, 'hourly')
|
||||
const nextRows = clearedRows.map(item =>
|
||||
isFixedRow(item)
|
||||
? {
|
||||
...item,
|
||||
investScale: nextInvestScale,
|
||||
landScale: nextLandScale,
|
||||
workload: nextWorkload,
|
||||
hourly: nextHourly,
|
||||
subtotal: sumNullableNumbers([nextInvestScale, nextLandScale, nextWorkload, nextHourly])
|
||||
}
|
||||
: item
|
||||
)
|
||||
})
|
||||
await setCurrentContractState({
|
||||
...currentState,
|
||||
detailRows: nextRows
|
||||
detailRows: applyFixedRowTotals(clearedRows)
|
||||
})
|
||||
}
|
||||
|
||||
@ -728,6 +716,35 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
},
|
||||
valueFormatter: params => (params.value == null ? '' : formatThousandsFlexible(params.value, 3))
|
||||
},
|
||||
{
|
||||
headerName: '确认金额',
|
||||
field: 'finalFee',
|
||||
headerClass: 'ag-right-aligned-header',
|
||||
flex: 3,
|
||||
minWidth: 140,
|
||||
cellClass: 'ag-right-aligned-cell',
|
||||
editable: params => !isFixedRow(params.data),
|
||||
valueGetter: params => {
|
||||
if (!params.data) return null
|
||||
if (isFixedRow(params.data)) {
|
||||
return sumNullableNumbers(
|
||||
detailRows.value.filter(r => !isFixedRow(r)).map(r => r.finalFee)
|
||||
)
|
||||
}
|
||||
if (params.data.finalFee != null) return params.data.finalFee
|
||||
return sumNullableNumbers([
|
||||
params.data.investScale,
|
||||
params.data.landScale,
|
||||
params.data.workload,
|
||||
params.data.hourly
|
||||
])
|
||||
},
|
||||
valueParser: params => {
|
||||
const parsed = parseNumberOrNull(params.newValue, { precision: 2 })
|
||||
return parsed != null ? roundTo(parsed, 2) : null
|
||||
},
|
||||
valueFormatter: params => (params.value == null ? '' : formatThousands(params.value, 2))
|
||||
},
|
||||
{
|
||||
headerName: '操作',
|
||||
field: 'actions',
|
||||
@ -791,7 +808,16 @@ const applyFixedRowTotals = (rows: DetailRow[]) => {
|
||||
const nextLandScale = getMethodTotalFromRows(rows, 'landScale')
|
||||
const nextWorkload = getMethodTotalFromRows(rows, 'workload')
|
||||
const nextHourly = getMethodTotalFromRows(rows, 'hourly')
|
||||
return rows.map(row =>
|
||||
// 先更新普通行:finalFee 跟随小计(若未手动编辑过或值为 null 则同步)
|
||||
const updatedRows = rows.map(row => {
|
||||
if (isFixedRow(row)) return row
|
||||
const rowSubtotal = sumNullableNumbers([row.investScale, row.landScale, row.workload, row.hourly])
|
||||
const nextFinalFee = row.finalFee != null ? row.finalFee : (rowSubtotal != null ? roundTo(rowSubtotal, 2) : null)
|
||||
return { ...row, finalFee: nextFinalFee }
|
||||
})
|
||||
// 再计算固定行汇总
|
||||
const totalFinalFee = sumNullableNumbers(updatedRows.filter(r => !isFixedRow(r)).map(r => r.finalFee))
|
||||
return updatedRows.map(row =>
|
||||
isFixedRow(row)
|
||||
? {
|
||||
...row,
|
||||
@ -799,7 +825,8 @@ const applyFixedRowTotals = (rows: DetailRow[]) => {
|
||||
landScale: nextLandScale,
|
||||
workload: nextWorkload,
|
||||
hourly: nextHourly,
|
||||
subtotal: sumNullableNumbers([nextInvestScale, nextLandScale, nextWorkload, nextHourly])
|
||||
subtotal: sumNullableNumbers([nextInvestScale, nextLandScale, nextWorkload, nextHourly]),
|
||||
finalFee: totalFinalFee != null ? roundTo(totalFinalFee, 2) : null
|
||||
}
|
||||
: row
|
||||
)
|
||||
@ -856,12 +883,14 @@ const fillPricingTotalsForServiceIds = async (serviceIds: string[]) => {
|
||||
const totalsRaw = totalsByServiceId.get(String(row.id))
|
||||
const totals = totalsRaw ? sanitizePricingTotalsByService(String(row.id), totalsRaw) : null
|
||||
if (!totals) return row
|
||||
const newSubtotal = sumNullableNumbers([totals.investScale, totals.landScale, totals.workload, totals.hourly])
|
||||
return {
|
||||
...row,
|
||||
investScale: totals.investScale,
|
||||
landScale: totals.landScale,
|
||||
workload: totals.workload,
|
||||
hourly: totals.hourly
|
||||
hourly: totals.hourly,
|
||||
finalFee: newSubtotal != null ? roundTo(newSubtotal, 2) : null
|
||||
}
|
||||
})
|
||||
|
||||
@ -899,7 +928,8 @@ const applySelection = async (codes: string[]) => {
|
||||
investScale: nextValues.investScale,
|
||||
landScale: nextValues.landScale,
|
||||
workload: nextValues.workload,
|
||||
hourly: nextValues.hourly
|
||||
hourly: nextValues.hourly,
|
||||
finalFee: typeof old?.finalFee === 'number' ? old.finalFee : null
|
||||
}
|
||||
})
|
||||
.filter((row): row is DetailRow => row !== null)
|
||||
@ -918,6 +948,7 @@ const applySelection = async (codes: string[]) => {
|
||||
workload: typeof fixedOld?.workload === 'number' ? fixedOld.workload : null,
|
||||
hourly: typeof fixedOld?.hourly === 'number' ? fixedOld.hourly : null,
|
||||
subtotal: null,
|
||||
finalFee: null,
|
||||
actions: null
|
||||
}
|
||||
|
||||
@ -1092,6 +1123,7 @@ const initializeContractState = async () => {
|
||||
workload: null,
|
||||
hourly: null,
|
||||
subtotal: null,
|
||||
finalFee: null,
|
||||
actions: null
|
||||
}])
|
||||
})
|
||||
@ -1117,7 +1149,20 @@ watch(serviceIdSignature, () => {
|
||||
}
|
||||
})
|
||||
|
||||
const handleCellValueChanged = () => {}
|
||||
const handleCellValueChanged = async (event: any) => {
|
||||
if (event.colDef?.field !== 'finalFee') return
|
||||
const row = event.data as DetailRow | undefined
|
||||
if (!row || isFixedRow(row)) return
|
||||
const newValue = event.newValue != null ? roundTo(Number(event.newValue), 2) : null
|
||||
const currentState = getCurrentContractState()
|
||||
const nextRows = currentState.detailRows.map(item =>
|
||||
item.id === row.id ? { ...item, finalFee: newValue } : item
|
||||
)
|
||||
await setCurrentContractState({
|
||||
...currentState,
|
||||
detailRows: applyFixedRowTotals(nextRows)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadProjectIndustry()
|
||||
|
||||
@ -918,7 +918,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
||||
if (!params.node?.group && !params.node?.rowPinned && !params.value) return '点击输入'
|
||||
return params.value || ''
|
||||
},
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line remark-wrap-cell' : ''),
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? ' remark-wrap-cell' : ''),
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params =>
|
||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||
|
||||
@ -772,7 +772,7 @@ const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
|
||||
if (!params.node?.group && !params.node?.rowPinned && !params.value) return '点击输入'
|
||||
return params.value || ''
|
||||
},
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line remark-wrap-cell' : ''),
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? ' remark-wrap-cell' : ''),
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params =>
|
||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||
|
||||
@ -386,7 +386,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
|
||||
return params.value || ''
|
||||
},
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line remark-wrap-cell' : ''),
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? ' remark-wrap-cell' : ''),
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params =>
|
||||
!params.node?.group &&
|
||||
|
||||
@ -413,7 +413,7 @@ const columnDefs: (ColDef<DetailRow> | ColGroupDef<DetailRow>)[] = [
|
||||
if (!params.node?.group && !params.node?.rowPinned && !params.value) return '点击输入'
|
||||
return params.value || ''
|
||||
},
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line remark-wrap-cell' : ''),
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? ' remark-wrap-cell' : ''),
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params =>
|
||||
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
|
||||
|
||||
@ -351,7 +351,7 @@ const columnDefs: ColDef<FeeRow>[] = [
|
||||
autoHeight: true,
|
||||
cellStyle: { whiteSpace: 'normal', lineHeight: '1.4' },
|
||||
valueFormatter: formatEditableText,
|
||||
cellClass: 'editable-cell-line remark-wrap-cell',
|
||||
cellClass: ' remark-wrap-cell',
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params => params.value == null || params.value === ''
|
||||
}
|
||||
|
||||
@ -205,13 +205,11 @@ const columnDefs: ColDef<WorkContentRow>[] = [
|
||||
minWidth: 320,
|
||||
flex: 2,
|
||||
editable: params => Boolean(params.data?.custom),
|
||||
cellClass: params => (params.data?.custom ? 'editable-cell-line' : ''),
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params => Boolean(params.data?.custom) && (params.value == null || params.value === '')
|
||||
},
|
||||
|
||||
valueParser: params => String(params.newValue || '').trim(),
|
||||
wrapText: true,
|
||||
autoHeight: true,
|
||||
|
||||
cellStyle: { whiteSpace: 'normal', lineHeight: '1.5' },
|
||||
cellRenderer: contentCellRenderer
|
||||
},
|
||||
@ -233,7 +231,7 @@ const columnDefs: ColDef<WorkContentRow>[] = [
|
||||
wrapText: true,
|
||||
autoHeight: true,
|
||||
cellStyle: { whiteSpace: 'normal', lineHeight: '1.4' },
|
||||
cellClass: 'editable-cell-line remark-wrap-cell',
|
||||
cellClass: 'remark-wrap-cell',
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params => params.value == null || params.value === ''
|
||||
},
|
||||
|
||||
@ -201,7 +201,7 @@ const columnDefs: ColDef<FactorRow>[] = [
|
||||
cellStyle: { whiteSpace: 'normal', lineHeight: '1.4' },
|
||||
editable: true,
|
||||
valueFormatter: params => params.value || '点击输入',
|
||||
cellClass: 'editable-cell-line remark-wrap-cell',
|
||||
cellClass: ' remark-wrap-cell',
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params => params.value == null || params.value === ''
|
||||
}
|
||||
|
||||
@ -198,7 +198,7 @@ useMotionValueEvent(
|
||||
|
||||
<template>
|
||||
<TooltipProvider>
|
||||
<div class="flex h-full w-full bg-background">
|
||||
<div class="flex h-full w-full bg-background p-2">
|
||||
<div class="w-[200px] shrink-0 border-r px-4 py-3 flex flex-col gap-6 relative ">
|
||||
<div v-if="props.title || props.subtitle || props.metaText" class="space-y-1">
|
||||
<TooltipRoot>
|
||||
@ -228,7 +228,7 @@ useMotionValueEvent(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-5 relative">
|
||||
<div :class="['flex flex-col gap-6 relative', (props.title || props.subtitle || props.metaText) ? 'mt-4' : '']">
|
||||
<div class="absolute left-[9px] top-3 bottom-3 w-[1.5px] bg-border/60"></div>
|
||||
|
||||
<div v-for="item in props.categories" :key="item.key"
|
||||
@ -242,7 +242,7 @@ useMotionValueEvent(
|
||||
<div v-if="activeCategory === item.key" class="w-1.5 h-1.5 bg-background rounded-full"></div>
|
||||
</div>
|
||||
<span :class="[
|
||||
'text-[13px] leading-5 transition-colors duration-200',
|
||||
'text-[12px] leading-4 transition-colors duration-200',
|
||||
activeCategory === item.key
|
||||
? 'font-semibold text-primary'
|
||||
: 'text-muted-foreground group-hover:text-foreground'
|
||||
|
||||
@ -144,7 +144,8 @@ html {
|
||||
}
|
||||
|
||||
/* When one column uses auto-height rows, keep other columns vertically centered. */
|
||||
.xmMx .ag-row .ag-cell:not(.ag-cell-auto-height) .ag-cell-wrapper {
|
||||
.xmMx .ag-row .ag-cell-wrapper {
|
||||
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user