hookehuyr

refactor(components): 重构计划书方案组件,提取公共布局

- 新增 PlanPopup 容器组件,统一头部和底部按钮
- 使用 NutUI Button 组件替换原生 div 按钮
- 重构 SchemeA 和 SchemeB,使用 PlanPopup 容器
- 减少约 60 行重复代码
- 添加详细的 JSDoc 注释

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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;
......