PlanFormContainer.vue 4.71 KB
<template>
  <!-- 使用 PlanPopup 容器组件 -->
  <PlanPopup
    :visible="props.visible"
    :title="templateConfig?.name || '计划书'"
    @close="close"
    @submit="submit"
  >
    <!-- 动态加载模版组件 -->
    <component
      :is="currentTemplateComponent"
      v-model="formData"
      :config="templateConfig?.config"
      v-if="currentTemplateComponent && templateConfig?.config"
    />

    <!-- 错误提示 -->
    <div v-else class="text-center text-gray-500 py-10">
      <p>⚠️ 未找到对应的计划书模版</p>
      <p class="text-sm mt-2">form_sn: {{ product?.form_sn }}</p>
    </div>
  </PlanPopup>
</template>

<script setup>
/**
 * 计划书表单容器
 *
 * @description 根据产品的 form_sn 动态加载对应的计划书模版组件
 *              - 自动识别产品并加载模版
 *              - 支持后端 plan_config 动态配置
 *              - 统一的表单提交处理
 * @author Claude Code
 * @example
 * <PlanFormContainer
 *   v-model:visible="showPlanPopup"
 *   :product="selectedProduct"
 *   @close="handleClose"
 *   @submit="handleSubmit"
 * />
 */
import { ref, computed, watch } from 'vue'
import PlanPopup from './PlanPopup/index.vue'
import LifeInsuranceTemplate from './PlanTemplates/LifeInsuranceTemplate.vue'
import CriticalIllnessTemplate from './PlanTemplates/CriticalIllnessTemplate.vue'
import SavingsTemplate from './PlanTemplates/SavingsTemplate.vue'
import { PLAN_TEMPLATES } from '@/config/plan-templates'

/**
 * 组件属性
 */
const props = defineProps({
  /**
   * 是否显示弹窗
   * @type {boolean}
   */
  visible: {
    type: Boolean,
    default: false
  },

  /**
   * 产品对象
   * @type {Object}
   * @property {number} id - 产品 ID
   * @property {string} product_name - 产品名称
   * @property {string} form_sn - 模版标识(必需)
   * @property {Object} plan_config - 模版配置(可选,后端返回)
   */
  product: {
    type: Object,
    required: true
  }
})

/**
 * 组件事件
 */
const emit = defineEmits([
  /**
   * 更新显示状态事件
   * @event update:visible
   * @param {boolean} value - 显示状态
   */
  'update:visible',

  /**
   * 关闭事件
   * @event close
   */
  'close',

  /**
   * 提交事件
   * @event submit
   * @param {Object} formData - 表单数据
   */
  'submit'
])

/**
 * 当前模版配置
 * @description 根据 form_sn 从配置文件中查找,并合并后端 plan_config
 *
 * @example
 * // product.form_sn = 'life-insurance-wiop3e'
 * // templateConfig() 返回: {
 * //   name: 'WIOP3E...',
 * //   component: 'LifeInsuranceTemplate',
 * //   config: { currency: 'USD', ... }
 * // }
 */
const templateConfig = computed(() => {
  if (!props.product?.form_sn) {
    console.warn('[PlanFormContainer] 产品缺少 form_sn 字段', props.product)
    return null
  }

  // 从配置文件中查找模版
  const config = PLAN_TEMPLATES[props.product.form_sn]
  if (!config) {
    console.error(`[PlanFormContainer] 未找到模版配置: ${props.product.form_sn}`)
    return null
  }

  // 合并配置:优先使用后端返回的 plan_config,否则使用配置文件中的默认配置
  return {
    ...config,
    config: {
      ...config.config,
      ...(props.product.plan_config || {})
    }
  }
})

/**
 * 当前模版组件
 * @description 根据 component 名称动态加载对应的组件
 */
const currentTemplateComponent = computed(() => {
  if (!templateConfig.value) {
    return null
  }

  const componentMap = {
    'LifeInsuranceTemplate': LifeInsuranceTemplate,
    'CriticalIllnessTemplate': CriticalIllnessTemplate,
    'SavingsTemplate': SavingsTemplate
  }

  const componentName = templateConfig.value.component
  return componentMap[componentName] || null
})

/**
 * 表单数据
 */
const formData = ref({})

/**
 * 监听产品变化,重置表单数据
 */
watch(
  () => props.product,
  (newProduct) => {
    if (newProduct) {
      // 重置表单数据
      formData.value = {}
    }
  },
  { immediate: true }
)

/**
 * 监听显示状态变化,弹窗打开时重置表单
 */
watch(
  () => props.visible,
  (newVal) => {
    if (newVal) {
      // 弹窗打开时重置表单
      formData.value = {}
    }
  }
)

/**
 * 关闭弹窗
 */
const close = () => {
  emit('close')
}

/**
 * 提交表单
 * @description 将表单数据和产品信息一起提交
 */
const submit = () => {
  console.log('[PlanFormContainer] 提交计划书:', {
    product_id: props.product.id,
    product_name: props.product.product_name,
    form_sn: props.product.form_sn,
    form_data: formData.value
  })

  emit('submit', {
    product_id: props.product.id,
    form_sn: props.product.form_sn,
    form_data: formData.value
  })
}
</script>

<style lang="less">
/* 容器样式 */
</style>