2026-03-27 09:35:46 +08:00

203 lines
6.3 KiB
Vue

<template>
<TypeLine
scene="zxfw-pricing-tab"
:title="`${contractName ? `${t('zxFwView.contractPrefix', { name: contractName })} · ` : ''}${fwName}${t('zxFwView.calcSuffix')}`"
:subtitle="t('zxFwView.contractId', { id: contractId })"
:copy-text="contractId"
:storage-key="`zxfw-pricing-active-cat-${contractId}-${serviceId}`"
:default-category="defaultCategory"
:categories="pricingCategories"
/>
</template>
<script setup lang="ts">
import { computed, defineAsyncComponent, defineComponent, h, markRaw, type Component } from 'vue'
import { useI18n } from 'vue-i18n'
import TypeLine from '@/layout/typeLine.vue'
import MethodUnavailableNotice from '@/features/shared/components/MethodUnavailableNotice.vue'
import ScaleFormulaReadonlyPane from '@/features/pricing/components/ScaleFormulaReadonlyPane.vue'
interface ServiceMethodType {
scale?: boolean | null
onlyCostScale?: boolean | null
amount?: boolean | null
workDay?: boolean | null
}
const props = defineProps<{
contractId: string
contractName?: string
serviceId: string|number
fwName:string
type?: ServiceMethodType
projectInfoKey?: string
}>()
const { t } = useI18n()
interface PricingCategoryItem {
key: string
label: string
component: Component
}
const resolveMethodEnabled = (value: boolean | null | undefined, fallback = true) =>
typeof value === 'boolean' ? value : fallback
const methodAvailability = computed(() => {
const scale = resolveMethodEnabled(props.type?.scale, true)
const onlyCostScale = resolveMethodEnabled(props.type?.onlyCostScale, false)
const amount = resolveMethodEnabled(props.type?.amount, true)
const workDay = resolveMethodEnabled(props.type?.workDay, true)
return {
investmentScale: scale,
landScale: scale && !onlyCostScale,
workload: amount,
hourly: workDay
}
})
const createPricingPane = (name: string) =>
markRaw(
defineComponent({
name,
setup() {
const AsyncPricingView = defineAsyncComponent({
loader: () => import(`@/features/pricing/components/${name}.vue`),
onError: err => {
console.error('load PricingMethodView failed:', err)
}
})
return () => h(AsyncPricingView, {
contractId: props.contractId,
serviceId: props.serviceId,
projectInfoKey: props.projectInfoKey
})
}
})
)
const createMethodUnavailablePane = (title: string, message: string) =>
markRaw(
defineComponent({
name: 'MethodUnavailablePane',
setup() {
return () => h(MethodUnavailableNotice, { title, message })
}
})
)
const investmentScaleView = createPricingPane('InvestmentScalePricingPane')
const landScaleView = createPricingPane('LandScalePricingPane')
const workloadView = createPricingPane('WorkloadPricingPane')
const hourlyView = createPricingPane('HourlyPricingPane')
const createScaleFormulaPane = (
method: 'investScale' | 'landScale',
name: 'InvestmentScaleFormulaPane' | 'LandScaleFormulaPane'
) =>
markRaw(
defineComponent({
name,
setup() {
return () => h(ScaleFormulaReadonlyPane, {
contractId: props.contractId,
serviceId: props.serviceId,
method
})
}
})
)
const investmentScaleFormulaView = createScaleFormulaPane('investScale', 'InvestmentScaleFormulaPane')
const landScaleFormulaView = createScaleFormulaPane('landScale', 'LandScaleFormulaPane')
const workContentPane = markRaw(
defineComponent({
name: 'WorkContentPane',
setup() {
const AsyncWorkContentGrid = defineAsyncComponent({
loader: () => import('@/features/shared/components/WorkContentGrid.vue'),
onError: err => {
console.error('load WorkContentGrid failed:', err)
}
})
return () => h(AsyncWorkContentGrid, {
title: t('zxFwView.workContentTitle'),
storageKey: `work-content-${props.contractId}-${props.serviceId}`,
contractId: props.contractId,
projectInfoKey: props.projectInfoKey,
serviceId: props.serviceId,
dictMode: 'service'
})
}
})
)
const investmentScaleUnavailableView = createMethodUnavailablePane(
t('zxFwView.unavailable.investmentScaleTitle'),
t('zxFwView.unavailable.investmentScaleMessage')
)
const landScaleUnavailableView = createMethodUnavailablePane(
t('zxFwView.unavailable.landScaleTitle'),
t('zxFwView.unavailable.landScaleMessage')
)
const workloadUnavailableView = createMethodUnavailablePane(
t('zxFwView.unavailable.workloadTitle'),
t('zxFwView.unavailable.workloadMessage')
)
const hourlyUnavailableView = createMethodUnavailablePane(
t('zxFwView.unavailable.hourlyTitle'),
t('zxFwView.unavailable.hourlyMessage')
)
const pricingCategories = computed<PricingCategoryItem[]>(() => [
{
key: 'investment-scale-method',
label: t('zxFwView.categories.investmentScale'),
component: methodAvailability.value.investmentScale ? investmentScaleView : investmentScaleUnavailableView
},
{
key: 'investment-scale-formula',
label: t('zxFwView.categories.investmentScaleFormula'),
component: methodAvailability.value.investmentScale ? investmentScaleFormulaView : investmentScaleUnavailableView
},
{
key: 'land-scale-method',
label: t('zxFwView.categories.landScale'),
component: methodAvailability.value.landScale ? landScaleView : landScaleUnavailableView
},
{
key: 'land-scale-formula',
label: t('zxFwView.categories.landScaleFormula'),
component: methodAvailability.value.landScale ? landScaleFormulaView : landScaleUnavailableView
},
{
key: 'workload-method',
label: t('zxFwView.categories.workload'),
component: methodAvailability.value.workload ? workloadView : workloadUnavailableView
},
{
key: 'hourly-method',
label: t('zxFwView.categories.hourly'),
component: methodAvailability.value.hourly ? hourlyView : hourlyUnavailableView
},
{
key: 'work-content',
label: t('zxFwView.categories.workContent'),
component: workContentPane
},
])
const defaultCategory = computed(() => {
const m = methodAvailability.value
if (m.investmentScale) return 'investment-scale-method'
if (m.landScale) return 'land-scale-method'
if (m.workload) return 'workload-method'
if (m.hourly) return 'hourly-method'
return 'work-content'
})
</script>