hookehuyr

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

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
<template>
<div class="flex flex-col h-full bg-gray-50">
<!-- Header -->
<div class="flex justify-between items-center px-5 py-5 bg-white rounded-t-xl">
<span class="text-lg font-normal text-gray-900">{{ title }}</span>
<IconFont name="close" size="16" color="#9CA3AF" @click="handleClose" />
</div>
<!-- Scrollable Content -->
<div class="flex-1 overflow-y-auto p-4">
<div class="bg-white rounded-xl p-5 shadow-sm">
<slot />
</div>
</div>
<!-- Footer Buttons -->
<div class="p-4 pt-2 pb-8 flex justify-between gap-3 bg-gray-50">
<nut-button
plain
type="primary"
class="flex-1 !h-auto !py-2.5 !text-sm"
@click="handleClose"
>
取消
</nut-button>
<nut-button
type="primary"
class="flex-1 !h-auto !py-2.5 !text-sm"
@click="handleSubmit"
>
提交申请
</nut-button>
</div>
</div>
</template>
<script setup>
/**
* @description 计划书弹窗容器组件
* @description 提供统一的头部、底部按钮和布局结构
*
* @props {string} title - 弹窗标题
*
* @emits close - 关闭弹窗事件
* @emits submit - 提交事件
*
* @example
* <PlanPopup title="申请计划书" @close="handleClose" @submit="handleSubmit">
* <!-- 具体的表单内容 -->
* </PlanPopup>
*/
import IconFont from '@/components/IconFont.vue'
defineProps({
/** 弹窗标题 */
title: {
type: String,
default: '计划书'
}
})
const emit = defineEmits(['close', 'submit'])
/**
* 处理关闭事件
*/
const handleClose = () => {
emit('close')
}
/**
* 处理提交事件
*/
const handleSubmit = () => {
emit('submit')
}
</script>
<style lang="less" scoped>
/* 确保 NutUI 按钮样式正确 */
:deep(.nut-button) {
border-radius: 0.5rem /* 8px */;
font-size: 1rem /* 16px */;
}
</style>
This diff is collapsed. Click to expand it.
<template>
<div class="flex flex-col h-full bg-gray-50">
<!-- Header -->
<div class="flex justify-between items-center px-5 py-5 bg-white rounded-t-xl">
<span class="text-lg font-normal text-gray-900">保险计划书申请</span>
<IconFont name="close" size="16" color="#9CA3AF" @click="close" />
<PlanPopup title="保险计划书申请" @close="close" @submit="submit">
<!-- 币种 -->
<div class="flex justify-between items-start mb-5">
<span class="text-sm text-gray-600 mt-1.5">币种</span>
<div class="bg-blue-50 rounded-md px-3 py-1.5">
<span class="text-sm text-blue-600">美元保单</span>
</div>
</div>
<!-- Scrollable Content -->
<div class="flex-1 overflow-y-auto p-4">
<div class="bg-white rounded-xl p-5 shadow-sm">
<!-- 币种 -->
<div class="flex justify-between items-start mb-5">
<span class="text-sm text-gray-600 mt-1.5">币种</span>
<div class="bg-blue-50 rounded-md px-3 py-1.5">
<span class="text-sm text-blue-600">美元保单</span>
</div>
</div>
<!-- 计划 -->
<div class="flex justify-between items-start mb-5">
<span class="text-sm text-gray-600 mt-1.5">计划</span>
<div class="bg-blue-50 rounded-md px-3 py-1.5">
<span class="text-sm text-blue-600">基础情景</span>
</div>
</div>
<!-- 附加计划 -->
<div class="mb-5">
<span class="text-base text-gray-900">附加计划</span>
</div>
<!-- 性别 -->
<div class="text-sm text-gray-600 mb-2">性别</div>
<nut-radio-group v-model="form.gender" direction="horizontal" class="mb-4">
<nut-radio label="female" class="mr-8">女</nut-radio>
<nut-radio label="male">男</nut-radio>
</nut-radio-group>
<!-- 年龄 -->
<div class="text-sm text-gray-600 mb-2">年龄</div>
<div class="border border-gray-200 rounded-lg mb-4 overflow-hidden">
<nut-input
v-model="form.age"
type="digit"
placeholder="请输入年龄"
class="!p-0 !bg-transparent !text-sm !text-gray-900"
:border="false"
/>
</div>
<!-- 计划 -->
<div class="flex justify-between items-start mb-5">
<span class="text-sm text-gray-600 mt-1.5">计划</span>
<div class="bg-blue-50 rounded-md px-3 py-1.5">
<span class="text-sm text-blue-600">基础情景</span>
</div>
</div>
<!-- 保险期间 -->
<div class="flex justify-between items-start mb-5">
<span class="text-sm text-gray-600 mt-1.5">保险期间</span>
<div class="bg-blue-50 rounded-md px-3 py-1.5">
<span class="text-sm text-blue-600">终身</span>
</div>
</div>
<!-- 附加计划 -->
<div class="mb-5">
<span class="text-base text-gray-900">附加计划</span>
</div>
<!-- 交费期间 -->
<div class="text-sm text-gray-600 mb-3">交费期间</div>
<nut-radio-group v-model="form.paymentPeriod" direction="horizontal" class="mb-4">
<nut-radio
v-for="period in paymentPeriods"
:key="period"
:label="period"
class="mr-6"
>
{{ period }}
</nut-radio>
</nut-radio-group>
<!-- 性别 -->
<div class="text-sm text-gray-600 mb-2">性别</div>
<nut-radio-group v-model="form.gender" direction="horizontal" class="mb-4">
<nut-radio label="female" class="mr-8">女</nut-radio>
<nut-radio label="male">男</nut-radio>
</nut-radio-group>
<!-- 年龄 -->
<div class="text-sm text-gray-600 mb-2">年龄</div>
<div class="border border-gray-200 rounded-lg mb-4 overflow-hidden">
<nut-input
v-model="form.age"
type="digit"
placeholder="请输入年龄"
class="!p-0 !bg-transparent !text-sm !text-gray-900"
:border="false"
/>
</div>
<!-- 年交保费 -->
<div class="text-sm text-gray-600 mb-2">年交保费</div>
<div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden">
<nut-input
v-model="form.premium"
type="digit"
placeholder="请输入保费"
class="!p-0 !bg-transparent flex-1 !text-sm !text-gray-900"
:border="false"
/>
<span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">美元</span>
</div>
<!-- 保险期间 -->
<div class="flex justify-between items-start mb-5">
<span class="text-sm text-gray-600 mt-1.5">保险期间</span>
<div class="bg-blue-50 rounded-md px-3 py-1.5">
<span class="text-sm text-blue-600">终身</span>
</div>
</div>
<!-- Footer Buttons -->
<div class="p-4 pt-2 pb-8 flex justify-between gap-4 bg-gray-50">
<div class="flex-1 py-3 text-center border border-blue-600 text-blue-600 rounded-lg text-base bg-white"
@click="close">
取消
</div>
<div class="flex-1 py-3 text-center bg-blue-600 text-white rounded-lg text-base" @click="submit">
提交申请
</div>
<!-- 交费期间 -->
<div class="text-sm text-gray-600 mb-3">交费期间</div>
<nut-radio-group v-model="form.paymentPeriod" direction="horizontal" class="mb-4">
<nut-radio
v-for="period in paymentPeriods"
:key="period"
:label="period"
class="mr-6"
>
{{ period }}
</nut-radio>
</nut-radio-group>
<!-- 年交保费 -->
<div class="text-sm text-gray-600 mb-2">年交保费</div>
<div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden">
<nut-input
v-model="form.premium"
type="digit"
placeholder="请输入保费"
class="!p-0 !bg-transparent flex-1 !text-sm !text-gray-900"
:border="false"
/>
<span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">美元</span>
</div>
</div>
</PlanPopup>
</template>
<script setup>
/**
* @description 录入计划书 - 方案B 内容组件
* @description 使用 PlanPopup 容器组件提供统一的布局和按钮
*
* @emits close - 关闭弹窗事件
* @emits submit - 提交事件,携带表单数据
*/
import { reactive, defineEmits } from 'vue';
import { reactive } from 'vue'
import PlanPopup from './PlanPopup.vue'
const emit = defineEmits(['close', 'submit']);
const emit = defineEmits(['close', 'submit'])
/**
* 表单数据
*/
const form = reactive({
currency: '美元保单',
plan: '基础情景',
......@@ -115,22 +99,32 @@ const form = reactive({
age: '30',
insurancePeriod: '终身',
paymentPeriod: '10年交',
premium: '100000',
});
premium: '100000'
})
const paymentPeriods = ['10年交', '3年交', '5年交', '2年交'];
/**
* 交费期间选项
*/
const paymentPeriods = ['10年交', '3年交', '5年交', '2年交']
/**
* 关闭弹窗
*/
const close = () => {
emit('close');
};
emit('close')
}
/**
* 提交表单
*/
const submit = () => {
console.log('SchemeB Submit:', form);
emit('submit', form);
};
console.log('SchemeB Submit:', form)
emit('submit', form)
}
</script>
<style lang="less" scoped>
/* Override NutUI input styles to match design */
:deep(.nut-input) {
padding: 0;
background: transparent;
......