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
177 additions
and
98 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> |
This diff is collapsed. Click to expand it.
| 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