plan-fields.js 8.31 KB
/**
 * 计划书字段配置
 *
 * @description 统一管理所有计划书字段的配置信息,包括字段类型、验证规则、API 映射等
 * @module config/plan-fields
 * @author Claude Code
 * @created 2026-02-14
 */

/**
 * 字段类型枚举
 * @enum {string}
 */
export const FIELD_TYPES = {
  TEXT: 'text',
  NUMBER: 'number',
  AMOUNT: 'amount',
  PERCENTAGE: 'percentage',
  SELECT: 'select',
  RADIO: 'radio',
  DATE: 'date',
  NAME: 'name'
}

/**
 * 数据转换类型枚举
 * @enum {string}
 */
export const TRANSFORM_TYPES = {
  FEN_TO_YUAN: 'fen_to_yuan',    // 分转元
  YUAN_TO_FEN: 'yuan_to_fen',    // 元转分
  NONE: 'none'                     // 无需转换
}

/**
 * 计划书字段定义
 * @type {Object<string, FieldDefinition>}
 * @property {Object} customer_name - 申请人姓名
 * @property {Object} gender - 性别
 * @property {Object} birthday - 出生日期
 * @property {Object} smoker - 是否吸烟
 * @property {Object} coverage - 保额
 * @property {Object} payment_period - 缴费年期
 * @property {Object} withdrawal_enabled - 是否启用提取
 * @property {Object} withdrawal_mode - 提取模式
 * @property {Object} withdrawal_start_age - 开始提取年龄
 * @property {Object} withdrawal_period - 提取年期
 * @property {Object} withdrawal_method - 提取方式
 * @property {Object} annual_withdrawal_amount - 年提取金额
 * @property {Object} annual_increase_percentage - 年递增比例
 * @property {Object} total_amount - 总保费
 */
export const PLAN_FIELD_DEFINITIONS = {
  /**
   * 申请人姓名
   */
  customer_name: {
    label: '申请人',
    type: FIELD_TYPES.TEXT,
    required: true,
    api_field: 'customer_name',
    placeholder: '请输入申请人姓名',
    component: 'PlanFieldName',
    validation: {
      required: (value) => value?.trim()?.length >= 2
    }
  },

  /**
   * 性别
   */
  gender: {
    label: '性别',
    type: FIELD_TYPES.RADIO,
    required: true,
    api_field: 'customer_gender',
    component: 'PlanFieldRadio',
    options: [
      { label: '男', value: 'male' },
      { label: '女', value: 'female' }
    ],
    default: 'male'
  },

  /**
   * 出生日期
   */
  birthday: {
    label: '出生年月日',
    type: FIELD_TYPES.DATE,
    required: true,
    api_field: 'customer_birthday',
    component: 'PlanFieldDatePicker',
    placeholder: '请选择出生年月日'
  },

  /**
   * 是否吸烟
   */
  smoker: {
    label: '是否吸烟',
    type: FIELD_TYPES.RADIO,
    required: true,
    api_field: 'smoking_status',
    component: 'PlanFieldRadio',
    options: [
      { label: '是', value: 'yes' },
      { label: '否', value: 'no' }
    ],
    default: 'no'
  },

  /**
   * 保额(年缴)
   */
  coverage: {
    label: '保额',
    type: FIELD_TYPES.AMOUNT,
    required: true,
    api_field: 'annual_premium',
    transform: TRANSFORM_TYPES.FEN_TO_YUAN,
    component: 'PlanFieldAmount',
    placeholder: '请输入保额',
    validation: {
      required: (value) => value > 0,
      min: (value, config) => value >= (config?.min_coverage || 1000),
      max: (value, config) => value <= (config?.max_coverage || 10000000)
    }
  },

  /**
   * 缴费年期
   */
  payment_period: {
    label: '缴费年期',
    type: FIELD_TYPES.SELECT,
    required: true,
    api_field: 'payment_years',
    component: 'PlanFieldSelect',
    options_from: 'payment_periods', // 从模板配置获取选项
    placeholder: '请选择缴费年期'
  },

  /**
   * 是否启用提取
   */
  withdrawal_enabled: {
    label: '启用提取计划',
    type: FIELD_TYPES.RADIO,
    required: false,
    api_field: 'allow_reduce_amount',
    component: 'PlanFieldRadio',
    options: [
      { label: '是', value: true },
      { label: '否', value: false }
    ],
    default: false,
    affects: ['withdrawal_mode', 'withdrawal_start_age', 'withdrawal_period', 'withdrawal_method', 'annual_withdrawal_amount']
  },

  /**
   * 提取模式
   */
  withdrawal_mode: {
    label: '提取模式',
    type: FIELD_TYPES.SELECT,
    required: false,
    api_field: 'withdrawal_option',
    component: 'PlanFieldSelect',
    options_from: 'withdrawal_plan.withdrawal_modes',
    depends_on: 'withdrawal_enabled',
    show_when: { withdrawal_enabled: true }
  },

  /**
   * 开始提取年龄
   */
  withdrawal_start_age: {
    label: '开始提取年龄',
    type: FIELD_TYPES.NUMBER,
    required: false,
    api_field: 'withdrawal_start_age',
    component: 'PlanFieldAgePicker',
    depends_on: 'withdrawal_enabled',
    show_when: { withdrawal_enabled: true },
    default_from: 'age_range.min'
  },

  /**
   * 提取年期
   */
  withdrawal_period: {
    label: '提取年期',
    type: FIELD_TYPES.SELECT,
    required: false,
    api_field: 'withdrawal_period',
    component: 'PlanFieldSelect',
    options_from: 'withdrawal_plan.withdrawal_periods',
    depends_on: 'withdrawal_enabled',
    show_when: { withdrawal_enabled: true }
  },

  /**
   * 提取方式
   */
  withdrawal_method: {
    label: '提取方式',
    type: FIELD_TYPES.SELECT,
    required: false,
    api_field: 'withdrawal_method',
    component: 'PlanFieldSelect',
    options: ['现金', '抵缴保费'],
    depends_on: 'withdrawal_enabled',
    show_when: { withdrawal_enabled: true }
  },

  /**
   * 年提取金额
   */
  annual_withdrawal_amount: {
    label: '年提取金额',
    type: FIELD_TYPES.AMOUNT,
    required: false,
    api_field: 'annual_withdrawal_amount',
    transform: TRANSFORM_TYPES.FEN_TO_YUAN,
    component: 'PlanFieldAmount',
    depends_on: 'withdrawal_enabled',
    show_when: { withdrawal_enabled: true },
    placeholder: '请输入年提取金额'
  },

  /**
   * 年递增比例
   */
  annual_increase_percentage: {
    label: '年递增比例',
    type: FIELD_TYPES.PERCENTAGE,
    required: false,
    api_field: 'annual_increase_percentage',
    transform: TRANSFORM_TYPES.NONE,
    component: 'PlanFieldAmount',
    validation: {
      range: (value) => {
        const num = parseFloat(value)
        return !Number.isNaN(num) && num >= 0 && num <= 100
      }
    }
  },

  /**
   * 总保费
   */
  total_amount: {
    label: '总保费',
    type: FIELD_TYPES.AMOUNT,
    required: false,
    api_field: 'total_premium',
    transform: TRANSFORM_TYPES.FEN_TO_YUAN,
    component: 'PlanFieldAmount',
    placeholder: '请输入总保费'
  }
}

/**
 * 获取字段定义
 * @param {string} fieldKey - 字段键名
 * @returns {FieldDefinition|null} 字段定义
 */
export function getFieldDefinition(fieldKey) {
  return PLAN_FIELD_DEFINITIONS[fieldKey] || null
}

/**
 * 获取字段对应的所有依赖字段
 * @param {string} fieldKey - 字段键名
 * @returns {string[]} 依赖字段的键名数组
 */
export function getFieldDependencies(fieldKey) {
  const definition = getFieldDefinition(fieldKey)
  return definition?.affects || []
}

/**
 * 获取字段的 API 字段名
 * @param {string} fieldKey - 字段键名
 * @returns {string} API 字段名
 */
export function getFieldApiField(fieldKey) {
  const definition = getFieldDefinition(fieldKey)
  return definition?.api_field || fieldKey
}

/**
 * 检查字段是否需要值转换
 * @param {string} fieldKey - 字段键名
 * @returns {boolean} 是否需要转换
 */
export function fieldNeedsTransform(fieldKey) {
  const definition = getFieldDefinition(fieldKey)
  return definition?.transform && definition.transform !== TRANSFORM_TYPES.NONE
}

/**
 * 字段定义类型
 * @typedef {Object} FieldDefinition
 * @property {string} label - 字段显示名称
 * @property {string} type - 字段类型(见 FIELD_TYPES)
 * @property {boolean} required - 是否必填
 * @property {string} api_field - API 字段名
 * @property {string} [component] - 对应组件名
 * @property {string} [placeholder] - 占位符文本
 * @property {Array} [options] - 选项列表(select/radio 类型)
 * @property {string} [options_from] - 选项来源(从模板配置获取)
 * @property {*} [default] - 默认值
 * @property {string} [transform] - 值转换类型(见 TRANSFORM_TYPES)
 * @property {Object} [validation] - 验证规则
 * @property {Function} [validation.required] - 必填验证函数
 * @property {string[]} [affects] - 影响的字段列表
 * @property {string} [depends_on] - 依赖的字段
 * @property {Object} [show_when] - 显示条件
 * @property {string} [default_from] - 默认值来源(从其他字段获取)
 */