fix
This commit is contained in:
parent
626513bc21
commit
c482faacbf
@ -35,6 +35,7 @@ type DictSource = Record<string, DictItem>
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
storageKey: string
|
||||
parentStorageKey?: string
|
||||
dict: DictSource
|
||||
disableBudgetEditWhenStandardNull?: boolean
|
||||
excludeNotshowByZxflxs?: boolean
|
||||
@ -219,13 +220,31 @@ const saveToIndexedDB = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const loadGridState = async (storageKey: string): Promise<GridState | null> => {
|
||||
if (!storageKey) return null
|
||||
const data = await localforage.getItem<GridState>(storageKey)
|
||||
if (!data?.detailRows || !Array.isArray(data.detailRows)) return null
|
||||
return data
|
||||
}
|
||||
|
||||
const loadFromIndexedDB = async () => {
|
||||
try {
|
||||
const data = await localforage.getItem<GridState>(props.storageKey)
|
||||
if (data?.detailRows) {
|
||||
const data = await loadGridState(props.storageKey)
|
||||
if (data) {
|
||||
detailRows.value = mergeWithDictRows(data.detailRows)
|
||||
return
|
||||
}
|
||||
|
||||
const parentStorageKey = props.parentStorageKey?.trim()
|
||||
if (parentStorageKey) {
|
||||
const parentData = await loadGridState(parentStorageKey)
|
||||
if (parentData) {
|
||||
detailRows.value = mergeWithDictRows(parentData.detailRows)
|
||||
await saveToIndexedDB()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
detailRows.value = buildDefaultRows()
|
||||
} catch (error) {
|
||||
console.error('loadFromIndexedDB failed:', error)
|
||||
|
||||
@ -28,18 +28,7 @@ interface DictGroup {
|
||||
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 XmScaleState {
|
||||
detailRows?: DetailRow[]
|
||||
@ -151,60 +140,75 @@ const mergeWithDictRows = (rowsFromDb: DetailRow[] | undefined): DetailRow[] =>
|
||||
}
|
||||
|
||||
|
||||
const loadFromIndexedDB = async (gridApi: any) => {
|
||||
|
||||
const applyPinnedTotalAmount = (
|
||||
api: GridApi<DetailRow> | null | undefined,
|
||||
totalAmount: number | null | undefined
|
||||
) => {
|
||||
const normalized = typeof totalAmount === 'number' && Number.isFinite(totalAmount)
|
||||
? roundTo(totalAmount, 2)
|
||||
: null
|
||||
pinnedTopRowData.value[0].amount = normalized
|
||||
const pinnedTopNode = api?.getPinnedTopRow(0)
|
||||
if (pinnedTopNode) {
|
||||
pinnedTopNode.setDataValue('amount', normalized)
|
||||
}
|
||||
}
|
||||
|
||||
const loadFromIndexedDB = async (api: GridApi<DetailRow>) => {
|
||||
try {
|
||||
const [baseInfo, contractData] = await Promise.all([
|
||||
localforage.getItem<XmBaseInfoState>(BASE_INFO_KEY),
|
||||
localforage.getItem<XmScaleState>(props.dbKey)
|
||||
])
|
||||
|
||||
const baseInfo = await localforage.getItem<XmBaseInfoState>(BASE_INFO_KEY)
|
||||
activeIndustryId.value =
|
||||
typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : ''
|
||||
|
||||
if (!activeIndustryId.value) {
|
||||
detailDict.value = []
|
||||
return
|
||||
}
|
||||
const filteredEntries = majorEntries.filter(([id]) => isMajorIdInIndustryScope(id, activeIndustryId.value))
|
||||
detailDict.value = buildDetailDict(filteredEntries)
|
||||
if (!activeIndustryId.value) {
|
||||
detailRows.value = []
|
||||
roughCalcEnabled.value = false
|
||||
applyPinnedTotalAmount(api, null)
|
||||
return
|
||||
}
|
||||
|
||||
const data = await localforage.getItem<XmScaleState>(props.dbKey)
|
||||
const filteredEntries = majorEntries.filter(([id]) =>
|
||||
isMajorIdInIndustryScope(id, activeIndustryId.value)
|
||||
)
|
||||
detailDict.value = buildDetailDict(filteredEntries)
|
||||
|
||||
roughCalcEnabled.value = data?.roughCalcEnabled || false
|
||||
const pinnedTopNode = gridApi.getPinnedTopRow(0)
|
||||
if (pinnedTopNode) {
|
||||
pinnedTopNode.setDataValue('amount', data?.totalAmount || null)
|
||||
}
|
||||
//
|
||||
if (data?.detailRows) {
|
||||
detailRows.value = mergeWithDictRows(data.detailRows)
|
||||
roughCalcEnabled.value = Boolean(contractData?.roughCalcEnabled)
|
||||
applyPinnedTotalAmount(api, contractData?.totalAmount)
|
||||
if (contractData?.detailRows) {
|
||||
detailRows.value = mergeWithDictRows(contractData.detailRows)
|
||||
return
|
||||
}
|
||||
|
||||
if (props.xmInfoKey) {
|
||||
// 首次创建合同段时,默认继承项目规模信息(同一套专业字典,按 id 对齐)
|
||||
const xmData =
|
||||
(await localforage.getItem<XmScaleState>(props.xmInfoKey))
|
||||
roughCalcEnabled.value = xmData?.roughCalcEnabled || false
|
||||
if (pinnedTopNode) {
|
||||
pinnedTopNode.setDataValue('amount', xmData?.totalAmount || null)
|
||||
}
|
||||
const xmData = await localforage.getItem<XmScaleState>(props.xmInfoKey)
|
||||
roughCalcEnabled.value = Boolean(xmData?.roughCalcEnabled)
|
||||
applyPinnedTotalAmount(api, xmData?.totalAmount)
|
||||
|
||||
if (xmData?.detailRows) {
|
||||
detailRows.value = mergeWithDictRows(xmData.detailRows)
|
||||
|
||||
detailRows.value = mergeWithDictRows(xmData.detailRows.map(e => ({...e,
|
||||
amount: null,
|
||||
landArea: null
|
||||
})))
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
detailRows.value = buildDefaultRows()
|
||||
|
||||
saveToIndexedDB()
|
||||
|
||||
void saveToIndexedDB()
|
||||
} catch (error) {
|
||||
console.error('loadFromIndexedDB failed:', error)
|
||||
activeIndustryId.value = ''
|
||||
detailRows.value = []
|
||||
roughCalcEnabled.value = false
|
||||
applyPinnedTotalAmount(api, null)
|
||||
}
|
||||
}
|
||||
|
||||
@ -406,17 +410,19 @@ const setDetailRowsHidden = (hidden: boolean) => {
|
||||
}
|
||||
}
|
||||
|
||||
let oldValue:number|null
|
||||
const onRoughCalcSwitch = (checked: boolean) => {
|
||||
gridApi.value?.stopEditing(true)
|
||||
roughCalcEnabled.value = checked
|
||||
setDetailRowsHidden(checked)
|
||||
if (!checked) {
|
||||
oldValue=pinnedTopRowData.value[0].amount
|
||||
syncPinnedTotalForNormalMode()
|
||||
} else {
|
||||
pinnedTopRowData.value[0].amount = null
|
||||
pinnedTopRowData.value[0].amount = oldValue
|
||||
const pinnedTopNode = gridApi.value?.getPinnedTopRow(0)
|
||||
if (pinnedTopNode) {
|
||||
pinnedTopNode.setDataValue('amount', null)
|
||||
pinnedTopNode.setDataValue('amount', oldValue)
|
||||
}
|
||||
}
|
||||
schedulePersist()
|
||||
|
||||
@ -55,6 +55,8 @@ const CONTRACT_SEGMENT_FILE_EXTENSION = '.htzw'
|
||||
const CONTRACT_SEGMENT_VERSION = 1
|
||||
const CONTRACT_KEY_PREFIX = 'ht-info-v3-'
|
||||
const SERVICE_KEY_PREFIX = 'zxFW-'
|
||||
const CONTRACT_CONSULT_FACTOR_KEY_PREFIX = 'ht-consult-category-factor-v1-'
|
||||
const CONTRACT_MAJOR_FACTOR_KEY_PREFIX = 'ht-major-factor-v1-'
|
||||
const PRICING_KEY_PREFIXES = ['tzGMF-', 'ydGMF-', 'gzlF-', 'hourlyPricing-']
|
||||
const PROJECT_INFO_KEY = 'xm-base-info-v1'
|
||||
|
||||
@ -383,6 +385,8 @@ const isContractSegmentPackage = (value: unknown): value is ContractSegmentPacka
|
||||
const isContractRelatedForageKey = (key: string, contractId: string) => {
|
||||
if (key === `${CONTRACT_KEY_PREFIX}${contractId}`) return true
|
||||
if (key === `${SERVICE_KEY_PREFIX}${contractId}`) return true
|
||||
if (key === `${CONTRACT_CONSULT_FACTOR_KEY_PREFIX}${contractId}`) return true
|
||||
if (key === `${CONTRACT_MAJOR_FACTOR_KEY_PREFIX}${contractId}`) return true
|
||||
if (PRICING_KEY_PREFIXES.some(prefix => key.startsWith(`${prefix}${contractId}-`))) return true
|
||||
return false
|
||||
}
|
||||
@ -407,6 +411,12 @@ const readContractRelatedForageEntries = async (contractIds: string[]) => {
|
||||
const rewriteKeyWithContractId = (key: string, fromId: string, toId: string) => {
|
||||
if (key === `${CONTRACT_KEY_PREFIX}${fromId}`) return `${CONTRACT_KEY_PREFIX}${toId}`
|
||||
if (key === `${SERVICE_KEY_PREFIX}${fromId}`) return `${SERVICE_KEY_PREFIX}${toId}`
|
||||
if (key === `${CONTRACT_CONSULT_FACTOR_KEY_PREFIX}${fromId}`) {
|
||||
return `${CONTRACT_CONSULT_FACTOR_KEY_PREFIX}${toId}`
|
||||
}
|
||||
if (key === `${CONTRACT_MAJOR_FACTOR_KEY_PREFIX}${fromId}`) {
|
||||
return `${CONTRACT_MAJOR_FACTOR_KEY_PREFIX}${toId}`
|
||||
}
|
||||
for (const prefix of PRICING_KEY_PREFIXES) {
|
||||
if (key.startsWith(`${prefix}${fromId}-`)) {
|
||||
return key.replace(`${prefix}${fromId}-`, `${prefix}${toId}-`)
|
||||
|
||||
19
src/components/views/HtConsultCategoryFactor.vue
Normal file
19
src/components/views/HtConsultCategoryFactor.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { serviceList } from '@/sql'
|
||||
import XmFactorGrid from '@/components/common/XmFactorGrid.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
contractId: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<XmFactorGrid
|
||||
title="咨询分类系数明细"
|
||||
:storage-key="`ht-consult-category-factor-v1-${props.contractId}`"
|
||||
parent-storage-key="xm-consult-category-factor-v1"
|
||||
:dict="serviceList"
|
||||
:disable-budget-edit-when-standard-null="true"
|
||||
:exclude-notshow-by-zxflxs="true"
|
||||
/>
|
||||
</template>
|
||||
64
src/components/views/HtMajorFactor.vue
Normal file
64
src/components/views/HtMajorFactor.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onActivated, onMounted, ref } from 'vue'
|
||||
import localforage from 'localforage'
|
||||
import { getMajorDictEntries, isMajorIdInIndustryScope } from '@/sql'
|
||||
import XmFactorGrid from '@/components/common/XmFactorGrid.vue'
|
||||
|
||||
interface XmBaseInfoState {
|
||||
projectIndustry?: string
|
||||
}
|
||||
|
||||
type MajorItem = {
|
||||
code: string
|
||||
name: string
|
||||
defCoe: number | null
|
||||
desc?: string | null
|
||||
notshowByzxflxs?: boolean
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
contractId: string
|
||||
}>()
|
||||
|
||||
const PROJECT_INFO_KEY = 'xm-base-info-v1'
|
||||
const projectIndustry = ref('')
|
||||
|
||||
const loadProjectIndustry = async () => {
|
||||
try {
|
||||
const data = await localforage.getItem<XmBaseInfoState>(PROJECT_INFO_KEY)
|
||||
projectIndustry.value =
|
||||
typeof data?.projectIndustry === 'string' ? data.projectIndustry.trim() : ''
|
||||
} catch (error) {
|
||||
console.error('loadProjectIndustry failed:', error)
|
||||
projectIndustry.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const filteredMajorDict = computed<Record<string, MajorItem>>(() => {
|
||||
const industry = projectIndustry.value
|
||||
if (!industry) return {}
|
||||
const entries = getMajorDictEntries()
|
||||
.filter(({ id }) => isMajorIdInIndustryScope(id, industry))
|
||||
.map(({ id, item }) => [id, item as MajorItem] as const)
|
||||
return Object.fromEntries(entries)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
void loadProjectIndustry()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
void loadProjectIndustry()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<XmFactorGrid
|
||||
title="工程专业系数明细"
|
||||
:storage-key="`ht-major-factor-v1-${props.contractId}`"
|
||||
parent-storage-key="xm-major-factor-v1"
|
||||
:dict="filteredMajorDict"
|
||||
:disable-budget-edit-when-standard-null="true"
|
||||
:exclude-notshow-by-zxflxs="true"
|
||||
/>
|
||||
</template>
|
||||
@ -36,7 +36,7 @@ const clearAll = () => {
|
||||
<label class="block text-[11px] font-medium text-foreground leading-none">选择服务</label>
|
||||
<button
|
||||
type="button"
|
||||
class="h-6 rounded-md border px-2 text-[13px] text-muted-foreground transition hover:bg-accent"
|
||||
class="cursor-pointer h-6 rounded-md border px-2 text-[13px] text-muted-foreground transition hover:bg-accent"
|
||||
@click="clearAll"
|
||||
>
|
||||
清空
|
||||
|
||||
@ -59,9 +59,42 @@ const zxfwView = markRaw(
|
||||
})
|
||||
);
|
||||
|
||||
const consultCategoryFactorView = markRaw(
|
||||
defineComponent({
|
||||
name: 'HtConsultCategoryFactorWithProps',
|
||||
setup() {
|
||||
const AsyncHtConsultCategoryFactor = defineAsyncComponent({
|
||||
loader: () => import('@/components/views/HtConsultCategoryFactor.vue'),
|
||||
onError: (err) => {
|
||||
console.error('加载 HtConsultCategoryFactor 组件失败:', err);
|
||||
}
|
||||
});
|
||||
return () => h(AsyncHtConsultCategoryFactor, { contractId: props.contractId });
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const majorFactorView = markRaw(
|
||||
defineComponent({
|
||||
name: 'HtMajorFactorWithProps',
|
||||
setup() {
|
||||
const AsyncHtMajorFactor = defineAsyncComponent({
|
||||
loader: () => import('@/components/views/HtMajorFactor.vue'),
|
||||
onError: (err) => {
|
||||
console.error('加载 HtMajorFactor 组件失败:', err);
|
||||
}
|
||||
});
|
||||
return () => h(AsyncHtMajorFactor, { contractId: props.contractId });
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// 4. 给分类数组添加严格类型标注
|
||||
const xmCategories: XmCategoryItem[] = [
|
||||
{ key: 'info', label: '规模信息', component: htView },
|
||||
{ key: 'contract', label: '咨询服务', component: zxfwView }
|
||||
{ key: 'consult-category-factor', label: '咨询分类系数', component: consultCategoryFactorView },
|
||||
{ key: 'major-factor', label: '工程专业系数', component: majorFactorView },
|
||||
{ key: 'contract', label: '咨询服务', component: zxfwView },
|
||||
|
||||
];
|
||||
</script>
|
||||
@ -341,8 +341,8 @@ const userGuideSteps: UserGuideStep[] = [
|
||||
]
|
||||
|
||||
const componentMap: Record<string, any> = {
|
||||
XmView: markRaw(defineAsyncComponent(() => import('@/components/views/Xm.vue'))),
|
||||
ContractDetailView: markRaw(defineAsyncComponent(() => import('@/components/views/ContractDetailView.vue'))),
|
||||
XmView: markRaw(defineAsyncComponent(() => import('@/components/views/xmCard.vue'))),
|
||||
ContractDetailView: markRaw(defineAsyncComponent(() => import('@/components/views/htCard.vue'))),
|
||||
ZxFwView: markRaw(defineAsyncComponent(() => import('@/components/views/ZxFwView.vue'))),
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user