完成大部分
This commit is contained in:
1 parent
e97707ac59
commit
13b03e016e
21 files changed
+1628
-275
No files matched your search
@@ -3,10 +3,13 @@ import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { AgGridVue } from 'ag-grid-vue3'
|
||||
import type { ColDef } from 'ag-grid-community'
|
||||
import localforage from 'localforage'
|
||||
import { serviceList, taskList } from '@/sql'
|
||||
import { taskList } from '@/sql'
|
||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||
import { decimalAggSum, roundTo, toDecimal } from '@/lib/decimal'
|
||||
import { decimalAggSum, roundTo, sumByNumber, toDecimal } from '@/lib/decimal'
|
||||
import { formatThousands } from '@/lib/numberFormat'
|
||||
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
|
||||
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
|
||||
import { loadConsultCategoryFactorMap } from '@/lib/xmFactorDefaults'
|
||||
|
||||
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale';
|
||||
|
||||
@@ -40,6 +43,17 @@ const DB_KEY = computed(() => `gzlF-${props.contractId}-${props.serviceId}`)
|
||||
const PRICING_CLEAR_SKIP_PREFIX = 'pricing-clear-skip:'
|
||||
const PRICING_FORCE_DEFAULT_PREFIX = 'pricing-force-default:'
|
||||
const pricingPaneReloadStore = usePricingPaneReloadStore()
|
||||
const consultCategoryFactorMap = ref<Map<string, number | null>>(new Map())
|
||||
let factorDefaultsLoaded = false
|
||||
|
||||
const getDefaultConsultCategoryFactor = () =>
|
||||
consultCategoryFactorMap.value.get(String(props.serviceId)) ?? null
|
||||
|
||||
const ensureFactorDefaultsLoaded = async () => {
|
||||
if (factorDefaultsLoaded) return
|
||||
consultCategoryFactorMap.value = await loadConsultCategoryFactorMap()
|
||||
factorDefaultsLoaded = true
|
||||
}
|
||||
|
||||
const shouldSkipPersist = () => {
|
||||
const storageKey = `${PRICING_CLEAR_SKIP_PREFIX}${DB_KEY.value}`
|
||||
@@ -62,7 +76,6 @@ const shouldForceDefaultLoad = () => {
|
||||
|
||||
const detailRows = ref<DetailRow[]>([])
|
||||
|
||||
type serviceLite = { defCoe: number | null }
|
||||
type taskLite = {
|
||||
serviceID: number
|
||||
code: string
|
||||
@@ -76,11 +89,6 @@ type taskLite = {
|
||||
desc: string | null
|
||||
}
|
||||
|
||||
const defaultConsultCategoryFactor = computed<number | null>(() => {
|
||||
const service = (serviceList as Record<string, serviceLite | undefined>)[String(props.serviceId)]
|
||||
return typeof service?.defCoe === 'number' && Number.isFinite(service.defCoe) ? service.defCoe : null
|
||||
})
|
||||
|
||||
const formatTaskReferenceUnitPrice = (task: taskLite) => {
|
||||
const unit = task.unit || ''
|
||||
const hasMin = typeof task.minPrice === 'number' && Number.isFinite(task.minPrice)
|
||||
@@ -121,7 +129,7 @@ const buildDefaultRows = (): DetailRow[] => {
|
||||
budgetReferenceUnitPrice: formatTaskReferenceUnitPrice(task),
|
||||
budgetAdoptedUnitPrice:
|
||||
typeof task.defPrice === 'number' && Number.isFinite(task.defPrice) ? task.defPrice : null,
|
||||
consultCategoryFactor: defaultConsultCategoryFactor.value,
|
||||
consultCategoryFactor: getDefaultConsultCategoryFactor(),
|
||||
serviceFee: null,
|
||||
remark: task.desc|| '',
|
||||
path: [rowId]
|
||||
@@ -193,12 +201,6 @@ const formatEditableNumber = (params: any) => {
|
||||
return Number(params.value).toFixed(2)
|
||||
}
|
||||
|
||||
const formatReadonlyNumber = (params: any) => {
|
||||
if (isNoTaskRow(params.data)) return '无'
|
||||
if (params.value == null || params.value === '') return ''
|
||||
return roundTo(params.value, 2).toFixed(2)
|
||||
}
|
||||
|
||||
const spanRowsByTaskName = (params: any) => {
|
||||
const rowA = params?.nodeA?.data as DetailRow | undefined
|
||||
const rowB = params?.nodeB?.data as DetailRow | undefined
|
||||
@@ -216,7 +218,8 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
minWidth: 100,
|
||||
width: 120,
|
||||
pinned: 'left',
|
||||
valueFormatter: params => params.value || ''
|
||||
colSpan: params => (params.node?.rowPinned ? 3 : 1),
|
||||
valueFormatter: params => (params.node?.rowPinned ? '总合计' : params.value || '')
|
||||
},
|
||||
{
|
||||
headerName: '名称',
|
||||
@@ -227,7 +230,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
autoHeight: true,
|
||||
|
||||
spanRows: true,
|
||||
valueFormatter: params => params.value || ''
|
||||
valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')
|
||||
},
|
||||
{
|
||||
headerName: '预算基数',
|
||||
@@ -238,7 +241,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
width: 180,
|
||||
pinned: 'left',
|
||||
spanRows: spanRowsByTaskName,
|
||||
valueFormatter: params => params.value || ''
|
||||
valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')
|
||||
},
|
||||
{
|
||||
headerName: '预算参考单价',
|
||||
@@ -250,10 +253,11 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
{
|
||||
headerName: '预算采用单价',
|
||||
field: 'budgetAdoptedUnitPrice',
|
||||
headerClass: 'ag-right-aligned-header',
|
||||
minWidth: 170,
|
||||
flex: 1,
|
||||
editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'ag-right-aligned-cell editable-cell-line' : 'ag-right-aligned-cell'),
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params =>
|
||||
!params.node?.group &&
|
||||
@@ -269,7 +273,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
}
|
||||
if (params.value == null) return ''
|
||||
const unit = params.data?.unit || ''
|
||||
return `${Number(params.value).toFixed(2)}${unit}`
|
||||
return `${formatThousands(params.value)}${unit}`
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -293,8 +297,9 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
{
|
||||
headerName: '咨询分类系数',
|
||||
field: 'consultCategoryFactor',
|
||||
minWidth: 150,
|
||||
flex: 1,
|
||||
width: 80,
|
||||
minWidth: 70,
|
||||
maxWidth: 90,
|
||||
editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
|
||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
|
||||
cellClassRules: {
|
||||
@@ -310,12 +315,18 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
{
|
||||
headerName: '服务费用(元)',
|
||||
field: 'serviceFee',
|
||||
headerClass: 'ag-right-aligned-header',
|
||||
minWidth: 150,
|
||||
flex: 1,
|
||||
cellClass: 'ag-right-aligned-cell',
|
||||
editable: false,
|
||||
valueGetter: params => calcServiceFee(params.data),
|
||||
valueGetter: params => (params.node?.rowPinned ? params.data?.serviceFee ?? null : calcServiceFee(params.data)),
|
||||
aggFunc: decimalAggSum,
|
||||
valueFormatter: formatReadonlyNumber
|
||||
valueFormatter: params => {
|
||||
if (isNoTaskRow(params.data)) return '无'
|
||||
if (params.value == null || params.value === '') return ''
|
||||
return formatThousands(roundTo(params.value, 2))
|
||||
}
|
||||
},
|
||||
{
|
||||
headerName: '说明',
|
||||
@@ -343,6 +354,27 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
}
|
||||
]
|
||||
|
||||
const totalWorkload = computed(() => sumByNumber(detailRows.value, row => row.workload))
|
||||
|
||||
const totalServiceFee = computed(() => sumByNumber(detailRows.value, row => calcServiceFee(row)))
|
||||
const pinnedTopRowData = computed(() => [
|
||||
{
|
||||
id: 'pinned-total-row',
|
||||
taskCode: '总合计',
|
||||
taskName: '',
|
||||
unit: '',
|
||||
conversion: null,
|
||||
workload: totalWorkload.value,
|
||||
budgetBase: '',
|
||||
budgetReferenceUnitPrice: '',
|
||||
budgetAdoptedUnitPrice: null,
|
||||
consultCategoryFactor: null,
|
||||
serviceFee: totalServiceFee.value,
|
||||
remark: '',
|
||||
path: ['TOTAL']
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
|
||||
const saveToIndexedDB = async () => {
|
||||
@@ -354,6 +386,15 @@ const saveToIndexedDB = async () => {
|
||||
}
|
||||
console.log('Saving to IndexedDB:', payload)
|
||||
await localforage.setItem(DB_KEY.value, payload)
|
||||
const synced = await syncPricingTotalToZxFw({
|
||||
contractId: props.contractId,
|
||||
serviceId: props.serviceId,
|
||||
field: 'workload',
|
||||
value: totalServiceFee.value
|
||||
})
|
||||
if (synced) {
|
||||
pricingPaneReloadStore.markReload(props.contractId, ZXFW_RELOAD_SERVICE_KEY)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('saveToIndexedDB failed:', error)
|
||||
}
|
||||
@@ -366,6 +407,8 @@ const loadFromIndexedDB = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
await ensureFactorDefaultsLoaded()
|
||||
|
||||
if (shouldForceDefaultLoad()) {
|
||||
detailRows.value = buildDefaultRows()
|
||||
return
|
||||
@@ -430,6 +473,7 @@ const processCellFromClipboard = (params: any) => {
|
||||
}
|
||||
return params.value;
|
||||
};
|
||||
|
||||
const mydiyTheme = myTheme.withParams({
|
||||
rowBorder: {
|
||||
style: "solid",
|
||||
@@ -455,15 +499,15 @@ const mydiyTheme = myTheme.withParams({
|
||||
</div>
|
||||
|
||||
<div v-if="isWorkloadMethodApplicable" class="ag-theme-quartz h-full min-h-0 w-full flex-1">
|
||||
<AgGridVue :style="{ height: '100%' }" :rowData="detailRows"
|
||||
<AgGridVue :style="{ height: '100%' }" :rowData="detailRows" :pinnedTopRowData="pinnedTopRowData"
|
||||
:columnDefs="columnDefs" :gridOptions="gridOptions" :theme="mydiyTheme" :treeData="false"
|
||||
:enableCellSpan="true"
|
||||
@cell-value-changed="handleCellValueChanged" :suppressColumnVirtualisation="true"
|
||||
:suppressRowVirtualisation="true"
|
||||
|
||||
|
||||
:cellSelection="{ handle: { mode: 'range' } }" :enableClipboard="true"
|
||||
|
||||
|
||||
:enableClipboard="true"
|
||||
:localeText="AG_GRID_LOCALE_CN" :tooltipShowDelay="500" :headerHeight="50"
|
||||
:processCellForClipboard="processCellForClipboard" :processCellFromClipboard="processCellFromClipboard"
|
||||
:undoRedoCellEditing="true" :undoRedoCellEditingLimit="20" />
|
||||
@@ -480,6 +524,3 @@ const mydiyTheme = myTheme.withParams({
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- :rowSelection="'multiple'"
|
||||
:enableClickSelection="false" -->
|
||||
<!-- :suppressRowTransform="true" -->
|
||||
Reference in new issue
Block a user