185 lines
5.0 KiB
Vue
185 lines
5.0 KiB
Vue
<script setup lang="ts">
|
|
import { computed, onActivated, onMounted, ref } from 'vue'
|
|
import localforage from 'localforage'
|
|
import { getMajorDictEntries, isMajorIdInIndustryScope } from '@/sql'
|
|
import CommonAgGrid from '@/components/common/xmCommonAgGrid.vue'
|
|
|
|
interface DictLeaf {
|
|
id: string
|
|
code: string
|
|
name: string
|
|
hasCost: boolean
|
|
hasArea: boolean
|
|
}
|
|
|
|
interface DictGroup {
|
|
id: string
|
|
code: string
|
|
name: string
|
|
children: DictLeaf[]
|
|
}
|
|
|
|
interface DetailRow {
|
|
id: string
|
|
groupCode: string
|
|
groupName: string
|
|
majorCode: string
|
|
majorName: string
|
|
hasCost: boolean
|
|
hasArea: boolean
|
|
amount: number | null
|
|
landArea: number | null
|
|
path: string[]
|
|
}
|
|
|
|
interface XmInfoState {
|
|
projectName: string
|
|
detailRows: DetailRow[]
|
|
}
|
|
interface XmBaseInfoState {
|
|
projectIndustry?: string
|
|
}
|
|
|
|
const props = defineProps<{
|
|
contractId: string
|
|
}>()
|
|
const DB_KEY = computed(() => `ht-info-v3-${props.contractId}`)
|
|
const XM_DB_KEY = 'xm-info-v3'
|
|
const BASE_INFO_KEY = 'xm-base-info-v1'
|
|
const activeIndustryId = ref('')
|
|
|
|
const detailRows = ref<DetailRow[]>([])
|
|
|
|
type majorLite = { code: string; name: string; hasCost?: boolean; hasArea?: boolean }
|
|
const serviceEntries = getMajorDictEntries().map(({ id, item }) => [id, item] as [string, majorLite])
|
|
const majorIdAliasMap = new Map(getMajorDictEntries().map(({ rawId, id }) => [rawId, id]))
|
|
|
|
const detailDict: DictGroup[] = (() => {
|
|
const groupMap = new Map<string, DictGroup>()
|
|
const groupOrder: string[] = []
|
|
const codeLookup = new Map(serviceEntries.map(([key, item]) => [item.code, { id: key, code: item.code, name: item.name }]))
|
|
|
|
for (const [key, item] of serviceEntries) {
|
|
const code = item.code
|
|
const isGroup = !code.includes('-')
|
|
if (isGroup) {
|
|
if (!groupMap.has(code)) groupOrder.push(code)
|
|
groupMap.set(code, {
|
|
id: key,
|
|
code,
|
|
name: item.name,
|
|
children: []
|
|
})
|
|
continue
|
|
}
|
|
|
|
const parentCode = code.split('-')[0]
|
|
if (!groupMap.has(parentCode)) {
|
|
const parent = codeLookup.get(parentCode)
|
|
if (!groupOrder.includes(parentCode)) groupOrder.push(parentCode)
|
|
groupMap.set(parentCode, {
|
|
id: parent?.id || `group-${parentCode}`,
|
|
code: parentCode,
|
|
name: parent?.name || parentCode,
|
|
children: []
|
|
})
|
|
}
|
|
|
|
groupMap.get(parentCode)!.children.push({
|
|
id: key,
|
|
code,
|
|
name: item.name,
|
|
hasCost: item.hasCost !== false,
|
|
hasArea: item.hasArea !== false
|
|
})
|
|
}
|
|
|
|
return groupOrder.map(code => groupMap.get(code)).filter((group): group is DictGroup => Boolean(group))
|
|
})()
|
|
|
|
const buildDefaultRows = (): DetailRow[] => {
|
|
if (!activeIndustryId.value) return []
|
|
const rows: DetailRow[] = []
|
|
for (const group of detailDict) {
|
|
if (activeIndustryId.value && !isMajorIdInIndustryScope(group.id, activeIndustryId.value)) continue
|
|
for (const child of group.children) {
|
|
rows.push({
|
|
id: child.id,
|
|
groupCode: group.code,
|
|
groupName: group.name,
|
|
majorCode: child.code,
|
|
majorName: child.name,
|
|
hasCost: child.hasCost,
|
|
hasArea: child.hasArea,
|
|
amount: null,
|
|
landArea: null,
|
|
path: [`${group.code} ${group.name}`, `${child.code} ${child.name}`]
|
|
})
|
|
}
|
|
}
|
|
return rows
|
|
}
|
|
|
|
const mergeWithDictRows = (rowsFromDb: DetailRow[] | undefined): DetailRow[] => {
|
|
const dbValueMap = new Map<string, DetailRow>()
|
|
for (const row of rowsFromDb || []) {
|
|
const rowId = String(row.id)
|
|
dbValueMap.set(rowId, row)
|
|
const aliasId = majorIdAliasMap.get(rowId)
|
|
if (aliasId && !dbValueMap.has(aliasId)) {
|
|
dbValueMap.set(aliasId, row)
|
|
}
|
|
}
|
|
|
|
return buildDefaultRows().map(row => {
|
|
const fromDb = dbValueMap.get(row.id)
|
|
if (!fromDb) return row
|
|
|
|
return {
|
|
...row,
|
|
amount: row.hasCost && typeof fromDb.amount === 'number' ? fromDb.amount : null,
|
|
landArea: row.hasArea && typeof fromDb.landArea === 'number' ? fromDb.landArea : null
|
|
}
|
|
})
|
|
}
|
|
|
|
const loadFromIndexedDB = async () => {
|
|
try {
|
|
const baseInfo = await localforage.getItem<XmBaseInfoState>(BASE_INFO_KEY)
|
|
activeIndustryId.value =
|
|
typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : ''
|
|
|
|
const data = await localforage.getItem<XmInfoState>(DB_KEY.value)
|
|
if (data) {
|
|
detailRows.value = mergeWithDictRows(data.detailRows)
|
|
return
|
|
}
|
|
|
|
// 首次创建合同段时,默认继承项目规模信息(同一套专业字典,按 id 对齐)
|
|
const xmData =
|
|
(await localforage.getItem<XmInfoState>(XM_DB_KEY))
|
|
if (xmData?.detailRows) {
|
|
detailRows.value = mergeWithDictRows(xmData.detailRows)
|
|
return
|
|
}
|
|
|
|
detailRows.value = buildDefaultRows()
|
|
} catch (error) {
|
|
console.error('loadFromIndexedDB failed:', error)
|
|
detailRows.value = buildDefaultRows()
|
|
}
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await loadFromIndexedDB()
|
|
})
|
|
|
|
onActivated(() => {
|
|
void loadFromIndexedDB()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<CommonAgGrid title="合同规模明细" :rowData="detailRows" :dbKey="DB_KEY" />
|
|
</template>
|