203 lines
6.3 KiB
Vue
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>
|