This commit is contained in:
zwgczx committed 2026-02-26 15:55:36 +08:00
1 parent 1609f19b9c
commit 37f4a99914
14 files changed
+953 -427

No files matched your search

@@ -5,38 +5,11 @@ import type { ColDef, GridOptions } from 'ag-grid-community'
import localforage from 'localforage'
import { majorList } from '@/sql'
import 'ag-grid-enterprise'
import {
import { myTheme } from '@/lib/diyAgGridTheme'
themeQuartz
} from "ag-grid-community"
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale';
// 精简的边框配置(细线条+浅灰色,弱化分割线视觉)
const borderConfig = {
style: "solid", // 虚线改实线更简洁,也可保留 dotted 但建议用 solid
width: 0.5, // 更细的边框,减少视觉干扰
color: "#e5e7eb" // 浅灰色边框,清新不刺眼
};
// 简洁清新风格的主题配置
const myTheme = themeQuartz.withParams({
// 核心:移除外边框,减少视觉包裹感
wrapperBorder: false,
// 表头样式(柔和浅蓝,无加粗,更轻盈)
headerBackgroundColor: "#f9fafb", // 极浅的背景色,替代深一点的 #e7f3fc
headerTextColor: "#374151", // 深灰色文字,比纯黑更柔和
headerFontSize: 15, // 字体稍大一点,更易读
headerFontWeight: "normal", // 取消加粗,降低视觉重量
// 行/列/表头边框(统一浅灰细边框)
rowBorder: borderConfig,
columnBorder: borderConfig,
headerRowBorder: borderConfig,
// 可选:偶数行背景色(轻微区分,更清新)
dataBackgroundColor: "#fefefe"
});
interface DictLeaf {
id: string
code: string
@@ -57,7 +30,11 @@ interface DetailRow {
majorCode: string
majorName: string
amount: number | null
landArea: number | null
benchmarkBudget: number | null
consultCategoryFactor: number | null
majorFactor: number | null
budgetFee: number | null
remark: string
path: string[]
}
@@ -67,9 +44,10 @@ interface XmInfoState {
}
const props = defineProps<{
contractId: string
contractId: string,
serviceId: string|number
}>()
const DB_KEY = computed(() => `ht-info-v3-${props.contractId}`)
const DB_KEY = computed(() => `tzGMF-${props.contractId}-${props.serviceId}`)
const detailRows = ref<DetailRow[]>([])
@@ -141,7 +119,11 @@ const buildDefaultRows = (): DetailRow[] => {
majorCode: child.code,
majorName: child.name,
amount: null,
landArea: null,
benchmarkBudget: null,
consultCategoryFactor: null,
majorFactor: null,
budgetFee: null,
remark: '',
path: [group.id, child.id]
})
}
@@ -162,18 +144,46 @@ const mergeWithDictRows = (rowsFromDb: DetailRow[] | undefined): DetailRow[] =>
return {
...row,
amount: typeof fromDb.amount === 'number' ? fromDb.amount : null,
landArea: typeof fromDb.landArea === 'number' ? fromDb.landArea : null
benchmarkBudget: typeof fromDb.benchmarkBudget === 'number' ? fromDb.benchmarkBudget : null,
consultCategoryFactor:
typeof fromDb.consultCategoryFactor === 'number' ? fromDb.consultCategoryFactor : null,
majorFactor: typeof fromDb.majorFactor === 'number' ? fromDb.majorFactor : null,
budgetFee: typeof fromDb.budgetFee === 'number' ? fromDb.budgetFee : null,
remark: typeof fromDb.remark === 'string' ? fromDb.remark : ''
}
})
}
const parseNumberOrNull = (value: unknown) => {
if (value === '' || value == null) return null
const v = Number(value)
return Number.isFinite(v) ? v : null
}
const formatEditableNumber = (params: any) => {
if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
return '点击输入'
}
if (params.value == null) return ''
return Number(params.value).toFixed(2)
}
const columnDefs: ColDef<DetailRow>[] = [
{
headerName: '工程专业名称',
minWidth: 220,
width: 240,
pinned: 'left',
valueGetter: params => {
if (params.node?.rowPinned) return ''
return params.node?.group ? params.data?.groupName || '' : params.data?.majorName || ''
}
},
{
headerName: '造价金额(万元)',
field: 'amount',
minWidth: 170,
flex: 1, // 核心:开启弹性布局,自动占满剩余空间
flex: 1,
editable: params => !params.node?.group && !params.node?.rowPinned,
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
@@ -182,24 +192,14 @@ const columnDefs: ColDef<DetailRow>[] = [
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
},
aggFunc: 'sum',
valueParser: params => {
if (params.newValue === '' || params.newValue == null) return null
const v = Number(params.newValue)
return Number.isFinite(v) ? v : null
},
valueFormatter: params => {
if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
return '点击输入'
}
if (params.value == null) return ''
return Number(params.value).toFixed(2)
}
valueParser: params => parseNumberOrNull(params.newValue),
valueFormatter: formatEditableNumber
},
{
headerName: '用地面积(亩)',
field: 'landArea',
headerName: '基准预算(元)',
field: 'benchmarkBudget',
minWidth: 170,
flex: 1, // 核心:开启弹性布局,自动占满剩余空间
flex: 1,
editable: params => !params.node?.group && !params.node?.rowPinned,
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
@@ -208,26 +208,75 @@ const columnDefs: ColDef<DetailRow>[] = [
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
},
aggFunc: 'sum',
valueParser: params => {
if (params.newValue === '' || params.newValue == null) return null
const v = Number(params.newValue)
return Number.isFinite(v) ? v : null
valueParser: params => parseNumberOrNull(params.newValue),
valueFormatter: formatEditableNumber
},
{
headerName: '咨询分类系数',
field: 'consultCategoryFactor',
minWidth: 150,
flex: 1,
editable: params => !params.node?.group && !params.node?.rowPinned,
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
cellClassRules: {
'editable-cell-empty': params =>
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
},
valueParser: params => parseNumberOrNull(params.newValue),
valueFormatter: formatEditableNumber
},
{
headerName: '专业系数',
field: 'majorFactor',
minWidth: 130,
flex: 1,
editable: params => !params.node?.group && !params.node?.rowPinned,
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
cellClassRules: {
'editable-cell-empty': params =>
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
},
valueParser: params => parseNumberOrNull(params.newValue),
valueFormatter: formatEditableNumber
},
{
headerName: '预算费用',
field: 'budgetFee',
minWidth: 150,
flex: 1,
editable: params => !params.node?.group && !params.node?.rowPinned,
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
cellClassRules: {
'editable-cell-empty': params =>
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
},
aggFunc: 'sum',
valueParser: params => parseNumberOrNull(params.newValue),
valueFormatter: formatEditableNumber
},
{
headerName: '说明',
field: 'remark',
minWidth: 180,
flex: 1.2,
editable: params => !params.node?.group && !params.node?.rowPinned,
valueFormatter: params => {
if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
return '点击输入'
}
if (params.value == null) return ''
return Number(params.value).toFixed(2)
if (!params.node?.group && !params.node?.rowPinned && !params.value) return '点击输入'
return params.value || ''
},
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
cellClassRules: {
'editable-cell-empty': params =>
!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
}
}
]
const autoGroupColumnDef: ColDef = {
headerName: '专业编码以及工程专业名称',
minWidth: 320,
headerName: '专业编码',
minWidth: 160,
pinned: 'left',
flex:2, // 核心:开启弹性布局,自动占满剩余空间
width: 170,
cellRendererParams: {
suppressCount: true
@@ -237,7 +286,8 @@ const autoGroupColumnDef: ColDef = {
return '总合计'
}
const nodeId = String(params.value || '')
return idLabelMap.get(nodeId) || nodeId
const label = idLabelMap.get(nodeId) || nodeId
return label.includes(' ') ? label.split(' ')[0] : label
}
}
@@ -262,8 +312,12 @@ const totalAmount = computed(() =>
detailRows.value.reduce((sum, row) => sum + (row.amount || 0), 0)
)
const totalLandArea = computed(() =>
detailRows.value.reduce((sum, row) => sum + (row.landArea || 0), 0)
const totalBenchmarkBudget = computed(() =>
detailRows.value.reduce((sum, row) => sum + (row.benchmarkBudget || 0), 0)
)
const totalBudgetFee = computed(() =>
detailRows.value.reduce((sum, row) => sum + (row.budgetFee || 0), 0)
)
const pinnedTopRowData = computed(() => [
{
@@ -273,7 +327,11 @@ const pinnedTopRowData = computed(() => [
majorCode: '',
majorName: '',
amount: totalAmount.value,
landArea: totalLandArea.value,
benchmarkBudget: totalBenchmarkBudget.value,
consultCategoryFactor: null,
majorFactor: null,
budgetFee: totalBudgetFee.value,
remark: '',
path: ['TOTAL']
}
])
@@ -349,16 +407,16 @@ const processCellFromClipboard = (params:any) => {
</script>
<template>
<div class="space-y-6">
<div class="h-full min-h-0 flex flex-col">
<div class="rounded-lg border bg-card xmMx">
<div class="rounded-lg border bg-card xmMx flex min-h-0 flex-1 flex-col">
<div class="flex items-center justify-between border-b px-4 py-3">
<h3 class="text-sm font-semibold text-foreground">合同规模明细</h3>
<h3 class="text-sm font-semibold text-foreground">投资规模明细</h3>
<div class="text-xs text-muted-foreground">导入导出</div>
</div>
<div class="ag-theme-quartz h-[580px] w-full">
<div class="ag-theme-quartz h-full min-h-0 w-full flex-1">
<AgGridVue
:style="{ height: '100%' }"
:rowData="detailRows"