refactor(components): 重构计划书方案组件,提取公共布局
- 新增 PlanPopup 容器组件,统一头部和底部按钮 - 使用 NutUI Button 组件替换原生 div 按钮 - 重构 SchemeA 和 SchemeB,使用 PlanPopup 容器 - 减少约 60 行重复代码 - 添加详细的 JSDoc 注释 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
3 changed files
with
322 additions
and
225 deletions
src/components/PlanSchemes/PlanPopup.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="flex flex-col h-full bg-gray-50"> | ||
| 3 | + <!-- Header --> | ||
| 4 | + <div class="flex justify-between items-center px-5 py-5 bg-white rounded-t-xl"> | ||
| 5 | + <span class="text-lg font-normal text-gray-900">{{ title }}</span> | ||
| 6 | + <IconFont name="close" size="16" color="#9CA3AF" @click="handleClose" /> | ||
| 7 | + </div> | ||
| 8 | + | ||
| 9 | + <!-- Scrollable Content --> | ||
| 10 | + <div class="flex-1 overflow-y-auto p-4"> | ||
| 11 | + <div class="bg-white rounded-xl p-5 shadow-sm"> | ||
| 12 | + <slot /> | ||
| 13 | + </div> | ||
| 14 | + </div> | ||
| 15 | + | ||
| 16 | + <!-- Footer Buttons --> | ||
| 17 | + <div class="p-4 pt-2 pb-8 flex justify-between gap-3 bg-gray-50"> | ||
| 18 | + <nut-button | ||
| 19 | + plain | ||
| 20 | + type="primary" | ||
| 21 | + class="flex-1 !h-auto !py-2.5 !text-sm" | ||
| 22 | + @click="handleClose" | ||
| 23 | + > | ||
| 24 | + 取消 | ||
| 25 | + </nut-button> | ||
| 26 | + <nut-button | ||
| 27 | + type="primary" | ||
| 28 | + class="flex-1 !h-auto !py-2.5 !text-sm" | ||
| 29 | + @click="handleSubmit" | ||
| 30 | + > | ||
| 31 | + 提交申请 | ||
| 32 | + </nut-button> | ||
| 33 | + </div> | ||
| 34 | + </div> | ||
| 35 | +</template> | ||
| 36 | + | ||
| 37 | +<script setup> | ||
| 38 | +/** | ||
| 39 | + * @description 计划书弹窗容器组件 | ||
| 40 | + * @description 提供统一的头部、底部按钮和布局结构 | ||
| 41 | + * | ||
| 42 | + * @props {string} title - 弹窗标题 | ||
| 43 | + * | ||
| 44 | + * @emits close - 关闭弹窗事件 | ||
| 45 | + * @emits submit - 提交事件 | ||
| 46 | + * | ||
| 47 | + * @example | ||
| 48 | + * <PlanPopup title="申请计划书" @close="handleClose" @submit="handleSubmit"> | ||
| 49 | + * <!-- 具体的表单内容 --> | ||
| 50 | + * </PlanPopup> | ||
| 51 | + */ | ||
| 52 | +import IconFont from '@/components/IconFont.vue' | ||
| 53 | + | ||
| 54 | +defineProps({ | ||
| 55 | + /** 弹窗标题 */ | ||
| 56 | + title: { | ||
| 57 | + type: String, | ||
| 58 | + default: '计划书' | ||
| 59 | + } | ||
| 60 | +}) | ||
| 61 | + | ||
| 62 | +const emit = defineEmits(['close', 'submit']) | ||
| 63 | + | ||
| 64 | +/** | ||
| 65 | + * 处理关闭事件 | ||
| 66 | + */ | ||
| 67 | +const handleClose = () => { | ||
| 68 | + emit('close') | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +/** | ||
| 72 | + * 处理提交事件 | ||
| 73 | + */ | ||
| 74 | +const handleSubmit = () => { | ||
| 75 | + emit('submit') | ||
| 76 | +} | ||
| 77 | +</script> | ||
| 78 | + | ||
| 79 | +<style lang="less" scoped> | ||
| 80 | +/* 确保 NutUI 按钮样式正确 */ | ||
| 81 | +:deep(.nut-button) { | ||
| 82 | + border-radius: 0.5rem /* 8px */; | ||
| 83 | + font-size: 1rem /* 16px */; | ||
| 84 | +} | ||
| 85 | +</style> |
| 1 | <template> | 1 | <template> |
| 2 | - <div class="flex flex-col h-full bg-gray-50"> | 2 | + <PlanPopup title="申请计划书" @close="close" @submit="submit"> |
| 3 | - <div class="flex justify-between items-center px-5 py-5 bg-white rounded-t-xl"> | 3 | + <!-- 客户姓名 --> |
| 4 | - <span class="text-lg font-normal text-gray-900">申请计划书</span> | 4 | + <div class="text-sm text-gray-600 mb-2">客户姓名</div> |
| 5 | - <IconFont name="close" size="16" color="#9CA3AF" @click="close" /> | 5 | + <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> |
| 6 | + <nut-input | ||
| 7 | + v-model="form.name" | ||
| 8 | + placeholder="请输入客户姓名" | ||
| 9 | + class="!p-0 !bg-transparent !text-sm !text-gray-900" | ||
| 10 | + :border="false" | ||
| 11 | + /> | ||
| 6 | </div> | 12 | </div> |
| 7 | 13 | ||
| 8 | - <div class="flex-1 overflow-y-auto p-4"> | 14 | + <!-- 性别 --> |
| 9 | - <div class="bg-white rounded-xl p-5 shadow-sm"> | 15 | + <div class="text-sm text-gray-600 mb-2">性别</div> |
| 10 | - <div class="text-sm text-gray-600 mb-2">客户姓名</div> | 16 | + <nut-radio-group v-model="form.gender" direction="horizontal" class="mb-4"> |
| 11 | - <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> | 17 | + <nut-radio label="male" class="mr-8">男</nut-radio> |
| 12 | - <nut-input | 18 | + <nut-radio label="female">女</nut-radio> |
| 13 | - v-model="form.name" | 19 | + </nut-radio-group> |
| 14 | - placeholder="请输入客户姓名" | 20 | + |
| 15 | - class="!p-0 !bg-transparent !text-sm !text-gray-900" | 21 | + <!-- 年龄 --> |
| 16 | - :border="false" | 22 | + <div class="text-sm text-gray-600 mb-2">年龄</div> |
| 17 | - /> | 23 | + <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> |
| 18 | - </div> | 24 | + <nut-input |
| 19 | - | 25 | + v-model="form.age" |
| 20 | - <div class="text-sm text-gray-600 mb-2">性别</div> | 26 | + type="digit" |
| 21 | - <nut-radio-group v-model="form.gender" direction="horizontal" class="mb-4"> | 27 | + placeholder="请输入年龄" |
| 22 | - <nut-radio label="male" class="mr-8">男</nut-radio> | 28 | + class="!p-0 !bg-transparent !text-sm !text-gray-900" |
| 23 | - <nut-radio label="female">女</nut-radio> | 29 | + :border="false" |
| 24 | - </nut-radio-group> | 30 | + /> |
| 25 | - | 31 | + </div> |
| 26 | - <div class="text-sm text-gray-600 mb-2">年龄</div> | 32 | + |
| 27 | - <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> | 33 | + <!-- 行业 --> |
| 28 | - <nut-input | 34 | + <div class="text-sm text-gray-600 mb-2">行业</div> |
| 29 | - v-model="form.age" | 35 | + <div |
| 30 | - type="digit" | 36 | + class="flex justify-between items-center border border-gray-200 rounded-lg p-3 mb-4 overflow-hidden" |
| 31 | - placeholder="请输入年龄" | 37 | + @click="showIndustryPicker = true" |
| 32 | - class="!p-0 !bg-transparent !text-sm !text-gray-900" | 38 | + > |
| 33 | - :border="false" | 39 | + <span :class="form.industry ? 'text-gray-900' : 'text-gray-400'" class="text-sm"> |
| 34 | - /> | 40 | + {{ form.industry || '请选择职业' }} |
| 35 | - </div> | 41 | + </span> |
| 36 | - | 42 | + <IconFont name="right" size="14" color="#9CA3AF" /> |
| 37 | - <div class="text-sm text-gray-600 mb-2">行业</div> | ||
| 38 | - <div | ||
| 39 | - class="flex justify-between items-center border border-gray-200 rounded-lg p-3 mb-4 overflow-hidden" | ||
| 40 | - @click="showIndustryPicker = true" | ||
| 41 | - > | ||
| 42 | - <span :class="form.industry ? 'text-gray-900' : 'text-gray-400'" class="text-sm"> | ||
| 43 | - {{ form.industry || '请选择职业' }} | ||
| 44 | - </span> | ||
| 45 | - <IconFont name="right" size="14" color="#9CA3AF" /> | ||
| 46 | - </div> | ||
| 47 | - | ||
| 48 | - <div class="text-sm text-gray-600 mb-2">年收入区间</div> | ||
| 49 | - <div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden"> | ||
| 50 | - <nut-input | ||
| 51 | - v-model="form.income" | ||
| 52 | - type="digit" | ||
| 53 | - placeholder="请输入年收入" | ||
| 54 | - class="!p-0 !bg-transparent flex-1 !text-sm !text-gray-900" | ||
| 55 | - :border="false" | ||
| 56 | - /> | ||
| 57 | - <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">万元</span> | ||
| 58 | - </div> | ||
| 59 | - | ||
| 60 | - <div class="text-sm text-gray-600 mb-3">家庭结构(多选)</div> | ||
| 61 | - <div class="flex flex-wrap gap-3 mb-5"> | ||
| 62 | - <div | ||
| 63 | - v-for="item in familyOptions" | ||
| 64 | - :key="item.value" | ||
| 65 | - class="px-4 py-2 rounded-lg text-sm cursor-pointer transition-colors border" | ||
| 66 | - :class="form.family.includes(item.value) ? 'bg-blue-600 text-white border-blue-600' : 'bg-gray-50 text-gray-600 border-gray-200'" | ||
| 67 | - @click="toggleSelection('family', item.value)" | ||
| 68 | - > | ||
| 69 | - {{ item.label }} | ||
| 70 | - </div> | ||
| 71 | - </div> | ||
| 72 | - | ||
| 73 | - <div class="text-sm text-gray-600 mb-3">保险需求(多选)</div> | ||
| 74 | - <div class="flex flex-wrap gap-3 mb-5"> | ||
| 75 | - <div | ||
| 76 | - v-for="item in insuranceOptions" | ||
| 77 | - :key="item.value" | ||
| 78 | - class="px-4 py-2 rounded-lg text-sm cursor-pointer transition-colors border" | ||
| 79 | - :class="form.insurance.includes(item.value) ? 'bg-blue-600 text-white border-blue-600' : 'bg-gray-50 text-gray-600 border-gray-200'" | ||
| 80 | - @click="toggleSelection('insurance', item.value)" | ||
| 81 | - > | ||
| 82 | - {{ item.label }} | ||
| 83 | - </div> | ||
| 84 | - </div> | ||
| 85 | - | ||
| 86 | - <div class="text-sm text-gray-600 mb-2">期望收益率</div> | ||
| 87 | - <div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden"> | ||
| 88 | - <nut-input | ||
| 89 | - v-model="form.returnRate" | ||
| 90 | - type="digit" | ||
| 91 | - placeholder="请输入期望收益率" | ||
| 92 | - class="!p-0 !bg-transparent flex-1 !text-sm !text-gray-900" | ||
| 93 | - :border="false" | ||
| 94 | - /> | ||
| 95 | - <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">%</span> | ||
| 96 | - </div> | ||
| 97 | - </div> | ||
| 98 | </div> | 43 | </div> |
| 99 | 44 | ||
| 100 | - <div class="p-4 pt-2 pb-8 flex justify-between gap-4 bg-gray-50"> | 45 | + <!-- 年收入区间 --> |
| 46 | + <div class="text-sm text-gray-600 mb-2">年收入区间</div> | ||
| 47 | + <div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden"> | ||
| 48 | + <nut-input | ||
| 49 | + v-model="form.income" | ||
| 50 | + type="digit" | ||
| 51 | + placeholder="请输入年收入" | ||
| 52 | + class="!p-0 !bg-transparent flex-1 !text-sm !text-gray-900" | ||
| 53 | + :border="false" | ||
| 54 | + /> | ||
| 55 | + <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">万元</span> | ||
| 56 | + </div> | ||
| 57 | + | ||
| 58 | + <!-- 家庭结构 --> | ||
| 59 | + <div class="text-sm text-gray-600 mb-3">家庭结构(多选)</div> | ||
| 60 | + <div class="flex flex-wrap gap-3 mb-5"> | ||
| 101 | <div | 61 | <div |
| 102 | - class="flex-1 py-3 text-center border border-blue-600 text-blue-600 rounded-lg text-base bg-white" | 62 | + v-for="item in familyOptions" |
| 103 | - @click="close" | 63 | + :key="item.value" |
| 64 | + class="px-4 py-2 rounded-lg text-sm cursor-pointer transition-colors border" | ||
| 65 | + :class="form.family.includes(item.value) ? 'bg-blue-600 text-white border-blue-600' : 'bg-gray-50 text-gray-600 border-gray-200'" | ||
| 66 | + @click="toggleSelection('family', item.value)" | ||
| 104 | > | 67 | > |
| 105 | - 取消 | 68 | + {{ item.label }} |
| 106 | </div> | 69 | </div> |
| 70 | + </div> | ||
| 71 | + | ||
| 72 | + <!-- 保险需求 --> | ||
| 73 | + <div class="text-sm text-gray-600 mb-3">保险需求(多选)</div> | ||
| 74 | + <div class="flex flex-wrap gap-3 mb-5"> | ||
| 107 | <div | 75 | <div |
| 108 | - class="flex-1 py-3 text-center bg-blue-600 text-white rounded-lg text-base" | 76 | + v-for="item in insuranceOptions" |
| 109 | - @click="submit" | 77 | + :key="item.value" |
| 78 | + class="px-4 py-2 rounded-lg text-sm cursor-pointer transition-colors border" | ||
| 79 | + :class="form.insurance.includes(item.value) ? 'bg-blue-600 text-white border-blue-600' : 'bg-gray-50 text-gray-600 border-gray-200'" | ||
| 80 | + @click="toggleSelection('insurance', item.value)" | ||
| 110 | > | 81 | > |
| 111 | - 提交申请 | 82 | + {{ item.label }} |
| 112 | </div> | 83 | </div> |
| 113 | </div> | 84 | </div> |
| 114 | 85 | ||
| 86 | + <!-- 期望收益率 --> | ||
| 87 | + <div class="text-sm text-gray-600 mb-2">期望收益率</div> | ||
| 88 | + <div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden"> | ||
| 89 | + <nut-input | ||
| 90 | + v-model="form.returnRate" | ||
| 91 | + type="digit" | ||
| 92 | + placeholder="请输入期望收益率" | ||
| 93 | + class="!p-0 !bg-transparent flex-1 !text-sm !text-gray-900" | ||
| 94 | + :border="false" | ||
| 95 | + /> | ||
| 96 | + <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">%</span> | ||
| 97 | + </div> | ||
| 98 | + | ||
| 115 | <!-- Industry Picker --> | 99 | <!-- Industry Picker --> |
| 116 | <nut-popup position="bottom" v-model:visible="showIndustryPicker"> | 100 | <nut-popup position="bottom" v-model:visible="showIndustryPicker"> |
| 117 | <nut-picker | 101 | <nut-picker |
| ... | @@ -121,19 +105,26 @@ | ... | @@ -121,19 +105,26 @@ |
| 121 | @cancel="showIndustryPicker = false" | 105 | @cancel="showIndustryPicker = false" |
| 122 | /> | 106 | /> |
| 123 | </nut-popup> | 107 | </nut-popup> |
| 124 | - </div> | 108 | + </PlanPopup> |
| 125 | </template> | 109 | </template> |
| 126 | 110 | ||
| 127 | <script setup> | 111 | <script setup> |
| 128 | /** | 112 | /** |
| 129 | * @description 录入计划书 - 方案A 内容组件 | 113 | * @description 录入计划书 - 方案A 内容组件 |
| 114 | + * @description 使用 PlanPopup 容器组件提供统一的布局和按钮 | ||
| 115 | + * | ||
| 130 | * @emits close - 关闭弹窗事件 | 116 | * @emits close - 关闭弹窗事件 |
| 131 | * @emits submit - 提交事件,携带表单数据 | 117 | * @emits submit - 提交事件,携带表单数据 |
| 132 | */ | 118 | */ |
| 133 | -import { ref, reactive } from 'vue'; | 119 | +import { ref, reactive } from 'vue' |
| 120 | +import PlanPopup from './PlanPopup.vue' | ||
| 121 | +import IconFont from '@/components/IconFont.vue' | ||
| 134 | 122 | ||
| 135 | -const emit = defineEmits(['close', 'submit']); | 123 | +const emit = defineEmits(['close', 'submit']) |
| 136 | 124 | ||
| 125 | +/** | ||
| 126 | + * 表单数据 | ||
| 127 | + */ | ||
| 137 | const form = reactive({ | 128 | const form = reactive({ |
| 138 | name: '', | 129 | name: '', |
| 139 | gender: '', // 'male' | 'female' | 130 | gender: '', // 'male' | 'female' |
| ... | @@ -142,56 +133,83 @@ const form = reactive({ | ... | @@ -142,56 +133,83 @@ const form = reactive({ |
| 142 | income: '', | 133 | income: '', |
| 143 | family: [], | 134 | family: [], |
| 144 | insurance: [], | 135 | insurance: [], |
| 145 | - returnRate: '', | 136 | + returnRate: '' |
| 146 | -}); | 137 | +}) |
| 147 | 138 | ||
| 148 | -const showIndustryPicker = ref(false); | 139 | +/** |
| 140 | + * 控制行业选择器显示 | ||
| 141 | + */ | ||
| 142 | +const showIndustryPicker = ref(false) | ||
| 149 | 143 | ||
| 144 | +/** | ||
| 145 | + * 行业选项 | ||
| 146 | + */ | ||
| 150 | const industryColumns = [ | 147 | const industryColumns = [ |
| 151 | { text: 'IT/互联网', value: 'it' }, | 148 | { text: 'IT/互联网', value: 'it' }, |
| 152 | { text: '金融', value: 'finance' }, | 149 | { text: '金融', value: 'finance' }, |
| 153 | { text: '教育', value: 'education' }, | 150 | { text: '教育', value: 'education' }, |
| 154 | { text: '医疗', value: 'medical' }, | 151 | { text: '医疗', value: 'medical' }, |
| 155 | - { text: '其他', value: 'other' }, | 152 | + { text: '其他', value: 'other' } |
| 156 | -]; | 153 | +] |
| 157 | 154 | ||
| 155 | +/** | ||
| 156 | + * 家庭结构选项 | ||
| 157 | + */ | ||
| 158 | const familyOptions = [ | 158 | const familyOptions = [ |
| 159 | { label: '配偶', value: 'spouse' }, | 159 | { label: '配偶', value: 'spouse' }, |
| 160 | { label: '子女', value: 'children' }, | 160 | { label: '子女', value: 'children' }, |
| 161 | { label: '父母', value: 'parents' }, | 161 | { label: '父母', value: 'parents' }, |
| 162 | - { label: '其他', value: 'others' }, | 162 | + { label: '其他', value: 'others' } |
| 163 | -]; | 163 | +] |
| 164 | 164 | ||
| 165 | +/** | ||
| 166 | + * 保险需求选项 | ||
| 167 | + */ | ||
| 165 | const insuranceOptions = [ | 168 | const insuranceOptions = [ |
| 166 | { label: '人身保障', value: 'life' }, | 169 | { label: '人身保障', value: 'life' }, |
| 167 | { label: '财富传承', value: 'wealth' }, | 170 | { label: '财富传承', value: 'wealth' }, |
| 168 | { label: '子女教育', value: 'education' }, | 171 | { label: '子女教育', value: 'education' }, |
| 169 | - { label: '养老规划', value: 'pension' }, | 172 | + { label: '养老规划', value: 'pension' } |
| 170 | -]; | 173 | +] |
| 171 | 174 | ||
| 175 | +/** | ||
| 176 | + * 切换多选项的选择状态 | ||
| 177 | + * @param {string} field - 字段名 | ||
| 178 | + * @param {string} value - 选项值 | ||
| 179 | + */ | ||
| 172 | const toggleSelection = (field, value) => { | 180 | const toggleSelection = (field, value) => { |
| 173 | - const index = form[field].indexOf(value); | 181 | + const index = form[field].indexOf(value) |
| 174 | if (index === -1) { | 182 | if (index === -1) { |
| 175 | - form[field].push(value); | 183 | + form[field].push(value) |
| 176 | } else { | 184 | } else { |
| 177 | - form[field].splice(index, 1); | 185 | + form[field].splice(index, 1) |
| 178 | } | 186 | } |
| 179 | -}; | 187 | +} |
| 180 | 188 | ||
| 181 | -const confirmIndustry = ({ selectedValue, selectedOptions }) => { | 189 | +/** |
| 182 | - form.industry = selectedOptions[0].text; | 190 | + * 确认行业选择 |
| 183 | - showIndustryPicker.value = false; | 191 | + * @param {Object} params - 选择器返回参数 |
| 184 | -}; | 192 | + * @param {Array} params.selectedOptions - 选中的选项 |
| 193 | + */ | ||
| 194 | +const confirmIndustry = ({ selectedOptions }) => { | ||
| 195 | + form.industry = selectedOptions[0].text | ||
| 196 | + showIndustryPicker.value = false | ||
| 197 | +} | ||
| 185 | 198 | ||
| 199 | +/** | ||
| 200 | + * 关闭弹窗 | ||
| 201 | + */ | ||
| 186 | const close = () => { | 202 | const close = () => { |
| 187 | - emit('close'); | 203 | + emit('close') |
| 188 | -}; | 204 | +} |
| 189 | 205 | ||
| 206 | +/** | ||
| 207 | + * 提交表单 | ||
| 208 | + */ | ||
| 190 | const submit = () => { | 209 | const submit = () => { |
| 191 | - // Validate form if needed | 210 | + console.log('SchemeA Submit:', form) |
| 192 | - console.log('Submit form:', form); | 211 | + emit('submit', form) |
| 193 | - emit('submit', form); | 212 | +} |
| 194 | -}; | ||
| 195 | </script> | 213 | </script> |
| 196 | 214 | ||
| 197 | <style lang="less" scoped> | 215 | <style lang="less" scoped> | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | - <div class="flex flex-col h-full bg-gray-50"> | 2 | + <PlanPopup title="保险计划书申请" @close="close" @submit="submit"> |
| 3 | - <!-- Header --> | 3 | + <!-- 币种 --> |
| 4 | - <div class="flex justify-between items-center px-5 py-5 bg-white rounded-t-xl"> | 4 | + <div class="flex justify-between items-start mb-5"> |
| 5 | - <span class="text-lg font-normal text-gray-900">保险计划书申请</span> | 5 | + <span class="text-sm text-gray-600 mt-1.5">币种</span> |
| 6 | - <IconFont name="close" size="16" color="#9CA3AF" @click="close" /> | 6 | + <div class="bg-blue-50 rounded-md px-3 py-1.5"> |
| 7 | + <span class="text-sm text-blue-600">美元保单</span> | ||
| 8 | + </div> | ||
| 7 | </div> | 9 | </div> |
| 8 | 10 | ||
| 9 | - <!-- Scrollable Content --> | 11 | + <!-- 计划 --> |
| 10 | - <div class="flex-1 overflow-y-auto p-4"> | 12 | + <div class="flex justify-between items-start mb-5"> |
| 11 | - <div class="bg-white rounded-xl p-5 shadow-sm"> | 13 | + <span class="text-sm text-gray-600 mt-1.5">计划</span> |
| 12 | - <!-- 币种 --> | 14 | + <div class="bg-blue-50 rounded-md px-3 py-1.5"> |
| 13 | - <div class="flex justify-between items-start mb-5"> | 15 | + <span class="text-sm text-blue-600">基础情景</span> |
| 14 | - <span class="text-sm text-gray-600 mt-1.5">币种</span> | 16 | + </div> |
| 15 | - <div class="bg-blue-50 rounded-md px-3 py-1.5"> | 17 | + </div> |
| 16 | - <span class="text-sm text-blue-600">美元保单</span> | ||
| 17 | - </div> | ||
| 18 | - </div> | ||
| 19 | - | ||
| 20 | - <!-- 计划 --> | ||
| 21 | - <div class="flex justify-between items-start mb-5"> | ||
| 22 | - <span class="text-sm text-gray-600 mt-1.5">计划</span> | ||
| 23 | - <div class="bg-blue-50 rounded-md px-3 py-1.5"> | ||
| 24 | - <span class="text-sm text-blue-600">基础情景</span> | ||
| 25 | - </div> | ||
| 26 | - </div> | ||
| 27 | - | ||
| 28 | - <!-- 附加计划 --> | ||
| 29 | - <div class="mb-5"> | ||
| 30 | - <span class="text-base text-gray-900">附加计划</span> | ||
| 31 | - </div> | ||
| 32 | - | ||
| 33 | - <!-- 性别 --> | ||
| 34 | - <div class="text-sm text-gray-600 mb-2">性别</div> | ||
| 35 | - <nut-radio-group v-model="form.gender" direction="horizontal" class="mb-4"> | ||
| 36 | - <nut-radio label="female" class="mr-8">女</nut-radio> | ||
| 37 | - <nut-radio label="male">男</nut-radio> | ||
| 38 | - </nut-radio-group> | ||
| 39 | - | ||
| 40 | - <!-- 年龄 --> | ||
| 41 | - <div class="text-sm text-gray-600 mb-2">年龄</div> | ||
| 42 | - <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> | ||
| 43 | - <nut-input | ||
| 44 | - v-model="form.age" | ||
| 45 | - type="digit" | ||
| 46 | - placeholder="请输入年龄" | ||
| 47 | - class="!p-0 !bg-transparent !text-sm !text-gray-900" | ||
| 48 | - :border="false" | ||
| 49 | - /> | ||
| 50 | - </div> | ||
| 51 | 18 | ||
| 52 | - <!-- 保险期间 --> | 19 | + <!-- 附加计划 --> |
| 53 | - <div class="flex justify-between items-start mb-5"> | 20 | + <div class="mb-5"> |
| 54 | - <span class="text-sm text-gray-600 mt-1.5">保险期间</span> | 21 | + <span class="text-base text-gray-900">附加计划</span> |
| 55 | - <div class="bg-blue-50 rounded-md px-3 py-1.5"> | 22 | + </div> |
| 56 | - <span class="text-sm text-blue-600">终身</span> | ||
| 57 | - </div> | ||
| 58 | - </div> | ||
| 59 | 23 | ||
| 60 | - <!-- 交费期间 --> | 24 | + <!-- 性别 --> |
| 61 | - <div class="text-sm text-gray-600 mb-3">交费期间</div> | 25 | + <div class="text-sm text-gray-600 mb-2">性别</div> |
| 62 | - <nut-radio-group v-model="form.paymentPeriod" direction="horizontal" class="mb-4"> | 26 | + <nut-radio-group v-model="form.gender" direction="horizontal" class="mb-4"> |
| 63 | - <nut-radio | 27 | + <nut-radio label="female" class="mr-8">女</nut-radio> |
| 64 | - v-for="period in paymentPeriods" | 28 | + <nut-radio label="male">男</nut-radio> |
| 65 | - :key="period" | 29 | + </nut-radio-group> |
| 66 | - :label="period" | 30 | + |
| 67 | - class="mr-6" | 31 | + <!-- 年龄 --> |
| 68 | - > | 32 | + <div class="text-sm text-gray-600 mb-2">年龄</div> |
| 69 | - {{ period }} | 33 | + <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> |
| 70 | - </nut-radio> | 34 | + <nut-input |
| 71 | - </nut-radio-group> | 35 | + v-model="form.age" |
| 36 | + type="digit" | ||
| 37 | + placeholder="请输入年龄" | ||
| 38 | + class="!p-0 !bg-transparent !text-sm !text-gray-900" | ||
| 39 | + :border="false" | ||
| 40 | + /> | ||
| 41 | + </div> | ||
| 72 | 42 | ||
| 73 | - <!-- 年交保费 --> | 43 | + <!-- 保险期间 --> |
| 74 | - <div class="text-sm text-gray-600 mb-2">年交保费</div> | 44 | + <div class="flex justify-between items-start mb-5"> |
| 75 | - <div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden"> | 45 | + <span class="text-sm text-gray-600 mt-1.5">保险期间</span> |
| 76 | - <nut-input | 46 | + <div class="bg-blue-50 rounded-md px-3 py-1.5"> |
| 77 | - v-model="form.premium" | 47 | + <span class="text-sm text-blue-600">终身</span> |
| 78 | - type="digit" | ||
| 79 | - placeholder="请输入保费" | ||
| 80 | - class="!p-0 !bg-transparent flex-1 !text-sm !text-gray-900" | ||
| 81 | - :border="false" | ||
| 82 | - /> | ||
| 83 | - <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">美元</span> | ||
| 84 | - </div> | ||
| 85 | </div> | 48 | </div> |
| 86 | </div> | 49 | </div> |
| 87 | 50 | ||
| 88 | - <!-- Footer Buttons --> | 51 | + <!-- 交费期间 --> |
| 89 | - <div class="p-4 pt-2 pb-8 flex justify-between gap-4 bg-gray-50"> | 52 | + <div class="text-sm text-gray-600 mb-3">交费期间</div> |
| 90 | - <div class="flex-1 py-3 text-center border border-blue-600 text-blue-600 rounded-lg text-base bg-white" | 53 | + <nut-radio-group v-model="form.paymentPeriod" direction="horizontal" class="mb-4"> |
| 91 | - @click="close"> | 54 | + <nut-radio |
| 92 | - 取消 | 55 | + v-for="period in paymentPeriods" |
| 93 | - </div> | 56 | + :key="period" |
| 94 | - <div class="flex-1 py-3 text-center bg-blue-600 text-white rounded-lg text-base" @click="submit"> | 57 | + :label="period" |
| 95 | - 提交申请 | 58 | + class="mr-6" |
| 96 | - </div> | 59 | + > |
| 60 | + {{ period }} | ||
| 61 | + </nut-radio> | ||
| 62 | + </nut-radio-group> | ||
| 63 | + | ||
| 64 | + <!-- 年交保费 --> | ||
| 65 | + <div class="text-sm text-gray-600 mb-2">年交保费</div> | ||
| 66 | + <div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden"> | ||
| 67 | + <nut-input | ||
| 68 | + v-model="form.premium" | ||
| 69 | + type="digit" | ||
| 70 | + placeholder="请输入保费" | ||
| 71 | + class="!p-0 !bg-transparent flex-1 !text-sm !text-gray-900" | ||
| 72 | + :border="false" | ||
| 73 | + /> | ||
| 74 | + <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">美元</span> | ||
| 97 | </div> | 75 | </div> |
| 98 | - </div> | 76 | + </PlanPopup> |
| 99 | </template> | 77 | </template> |
| 100 | 78 | ||
| 101 | <script setup> | 79 | <script setup> |
| 102 | /** | 80 | /** |
| 103 | * @description 录入计划书 - 方案B 内容组件 | 81 | * @description 录入计划书 - 方案B 内容组件 |
| 82 | + * @description 使用 PlanPopup 容器组件提供统一的布局和按钮 | ||
| 83 | + * | ||
| 104 | * @emits close - 关闭弹窗事件 | 84 | * @emits close - 关闭弹窗事件 |
| 105 | * @emits submit - 提交事件,携带表单数据 | 85 | * @emits submit - 提交事件,携带表单数据 |
| 106 | */ | 86 | */ |
| 107 | -import { reactive, defineEmits } from 'vue'; | 87 | +import { reactive } from 'vue' |
| 88 | +import PlanPopup from './PlanPopup.vue' | ||
| 108 | 89 | ||
| 109 | -const emit = defineEmits(['close', 'submit']); | 90 | +const emit = defineEmits(['close', 'submit']) |
| 110 | 91 | ||
| 92 | +/** | ||
| 93 | + * 表单数据 | ||
| 94 | + */ | ||
| 111 | const form = reactive({ | 95 | const form = reactive({ |
| 112 | currency: '美元保单', | 96 | currency: '美元保单', |
| 113 | plan: '基础情景', | 97 | plan: '基础情景', |
| ... | @@ -115,22 +99,32 @@ const form = reactive({ | ... | @@ -115,22 +99,32 @@ const form = reactive({ |
| 115 | age: '30', | 99 | age: '30', |
| 116 | insurancePeriod: '终身', | 100 | insurancePeriod: '终身', |
| 117 | paymentPeriod: '10年交', | 101 | paymentPeriod: '10年交', |
| 118 | - premium: '100000', | 102 | + premium: '100000' |
| 119 | -}); | 103 | +}) |
| 120 | 104 | ||
| 121 | -const paymentPeriods = ['10年交', '3年交', '5年交', '2年交']; | 105 | +/** |
| 106 | + * 交费期间选项 | ||
| 107 | + */ | ||
| 108 | +const paymentPeriods = ['10年交', '3年交', '5年交', '2年交'] | ||
| 122 | 109 | ||
| 110 | +/** | ||
| 111 | + * 关闭弹窗 | ||
| 112 | + */ | ||
| 123 | const close = () => { | 113 | const close = () => { |
| 124 | - emit('close'); | 114 | + emit('close') |
| 125 | -}; | 115 | +} |
| 126 | 116 | ||
| 117 | +/** | ||
| 118 | + * 提交表单 | ||
| 119 | + */ | ||
| 127 | const submit = () => { | 120 | const submit = () => { |
| 128 | - console.log('SchemeB Submit:', form); | 121 | + console.log('SchemeB Submit:', form) |
| 129 | - emit('submit', form); | 122 | + emit('submit', form) |
| 130 | -}; | 123 | +} |
| 131 | </script> | 124 | </script> |
| 132 | 125 | ||
| 133 | <style lang="less" scoped> | 126 | <style lang="less" scoped> |
| 127 | +/* Override NutUI input styles to match design */ | ||
| 134 | :deep(.nut-input) { | 128 | :deep(.nut-input) { |
| 135 | padding: 0; | 129 | padding: 0; |
| 136 | background: transparent; | 130 | background: transparent; | ... | ... |
-
Please register or login to post a comment