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
182 additions
and
85 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> | ||
| 5 | - <IconFont name="close" size="16" color="#9CA3AF" @click="close" /> | ||
| 6 | - </div> | ||
| 7 | - | ||
| 8 | - <div class="flex-1 overflow-y-auto p-4"> | ||
| 9 | - <div class="bg-white rounded-xl p-5 shadow-sm"> | ||
| 10 | <div class="text-sm text-gray-600 mb-2">客户姓名</div> | 4 | <div class="text-sm text-gray-600 mb-2">客户姓名</div> |
| 11 | <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> | 5 | <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> |
| 12 | <nut-input | 6 | <nut-input |
| ... | @@ -17,12 +11,14 @@ | ... | @@ -17,12 +11,14 @@ |
| 17 | /> | 11 | /> |
| 18 | </div> | 12 | </div> |
| 19 | 13 | ||
| 14 | + <!-- 性别 --> | ||
| 20 | <div class="text-sm text-gray-600 mb-2">性别</div> | 15 | <div class="text-sm text-gray-600 mb-2">性别</div> |
| 21 | <nut-radio-group v-model="form.gender" direction="horizontal" class="mb-4"> | 16 | <nut-radio-group v-model="form.gender" direction="horizontal" class="mb-4"> |
| 22 | <nut-radio label="male" class="mr-8">男</nut-radio> | 17 | <nut-radio label="male" class="mr-8">男</nut-radio> |
| 23 | <nut-radio label="female">女</nut-radio> | 18 | <nut-radio label="female">女</nut-radio> |
| 24 | </nut-radio-group> | 19 | </nut-radio-group> |
| 25 | 20 | ||
| 21 | + <!-- 年龄 --> | ||
| 26 | <div class="text-sm text-gray-600 mb-2">年龄</div> | 22 | <div class="text-sm text-gray-600 mb-2">年龄</div> |
| 27 | <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> | 23 | <div class="border border-gray-200 rounded-lg mb-4 overflow-hidden"> |
| 28 | <nut-input | 24 | <nut-input |
| ... | @@ -34,6 +30,7 @@ | ... | @@ -34,6 +30,7 @@ |
| 34 | /> | 30 | /> |
| 35 | </div> | 31 | </div> |
| 36 | 32 | ||
| 33 | + <!-- 行业 --> | ||
| 37 | <div class="text-sm text-gray-600 mb-2">行业</div> | 34 | <div class="text-sm text-gray-600 mb-2">行业</div> |
| 38 | <div | 35 | <div |
| 39 | class="flex justify-between items-center border border-gray-200 rounded-lg p-3 mb-4 overflow-hidden" | 36 | class="flex justify-between items-center border border-gray-200 rounded-lg p-3 mb-4 overflow-hidden" |
| ... | @@ -45,6 +42,7 @@ | ... | @@ -45,6 +42,7 @@ |
| 45 | <IconFont name="right" size="14" color="#9CA3AF" /> | 42 | <IconFont name="right" size="14" color="#9CA3AF" /> |
| 46 | </div> | 43 | </div> |
| 47 | 44 | ||
| 45 | + <!-- 年收入区间 --> | ||
| 48 | <div class="text-sm text-gray-600 mb-2">年收入区间</div> | 46 | <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"> | 47 | <div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden"> |
| 50 | <nut-input | 48 | <nut-input |
| ... | @@ -57,6 +55,7 @@ | ... | @@ -57,6 +55,7 @@ |
| 57 | <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">万元</span> | 55 | <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">万元</span> |
| 58 | </div> | 56 | </div> |
| 59 | 57 | ||
| 58 | + <!-- 家庭结构 --> | ||
| 60 | <div class="text-sm text-gray-600 mb-3">家庭结构(多选)</div> | 59 | <div class="text-sm text-gray-600 mb-3">家庭结构(多选)</div> |
| 61 | <div class="flex flex-wrap gap-3 mb-5"> | 60 | <div class="flex flex-wrap gap-3 mb-5"> |
| 62 | <div | 61 | <div |
| ... | @@ -70,6 +69,7 @@ | ... | @@ -70,6 +69,7 @@ |
| 70 | </div> | 69 | </div> |
| 71 | </div> | 70 | </div> |
| 72 | 71 | ||
| 72 | + <!-- 保险需求 --> | ||
| 73 | <div class="text-sm text-gray-600 mb-3">保险需求(多选)</div> | 73 | <div class="text-sm text-gray-600 mb-3">保险需求(多选)</div> |
| 74 | <div class="flex flex-wrap gap-3 mb-5"> | 74 | <div class="flex flex-wrap gap-3 mb-5"> |
| 75 | <div | 75 | <div |
| ... | @@ -83,6 +83,7 @@ | ... | @@ -83,6 +83,7 @@ |
| 83 | </div> | 83 | </div> |
| 84 | </div> | 84 | </div> |
| 85 | 85 | ||
| 86 | + <!-- 期望收益率 --> | ||
| 86 | <div class="text-sm text-gray-600 mb-2">期望收益率</div> | 87 | <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 | <div class="border border-gray-200 rounded-lg mb-4 flex items-center overflow-hidden"> |
| 88 | <nut-input | 89 | <nut-input |
| ... | @@ -94,23 +95,6 @@ | ... | @@ -94,23 +95,6 @@ |
| 94 | /> | 95 | /> |
| 95 | <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">%</span> | 96 | <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">%</span> |
| 96 | </div> | 97 | </div> |
| 97 | - </div> | ||
| 98 | - </div> | ||
| 99 | - | ||
| 100 | - <div class="p-4 pt-2 pb-8 flex justify-between gap-4 bg-gray-50"> | ||
| 101 | - <div | ||
| 102 | - class="flex-1 py-3 text-center border border-blue-600 text-blue-600 rounded-lg text-base bg-white" | ||
| 103 | - @click="close" | ||
| 104 | - > | ||
| 105 | - 取消 | ||
| 106 | - </div> | ||
| 107 | - <div | ||
| 108 | - class="flex-1 py-3 text-center bg-blue-600 text-white rounded-lg text-base" | ||
| 109 | - @click="submit" | ||
| 110 | - > | ||
| 111 | - 提交申请 | ||
| 112 | - </div> | ||
| 113 | - </div> | ||
| 114 | 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"> |
| ... | @@ -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 --> | ||
| 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">保险计划书申请</span> | ||
| 6 | - <IconFont name="close" size="16" color="#9CA3AF" @click="close" /> | ||
| 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 | <!-- 币种 --> | 3 | <!-- 币种 --> |
| 13 | <div class="flex justify-between items-start mb-5"> | 4 | <div class="flex justify-between items-start mb-5"> |
| 14 | <span class="text-sm text-gray-600 mt-1.5">币种</span> | 5 | <span class="text-sm text-gray-600 mt-1.5">币种</span> |
| ... | @@ -82,32 +73,25 @@ | ... | @@ -82,32 +73,25 @@ |
| 82 | /> | 73 | /> |
| 83 | <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">美元</span> | 74 | <span class="text-sm text-gray-500 shrink-0 ml-2 mr-5">美元</span> |
| 84 | </div> | 75 | </div> |
| 85 | - </div> | 76 | + </PlanPopup> |
| 86 | - </div> | ||
| 87 | - | ||
| 88 | - <!-- Footer Buttons --> | ||
| 89 | - <div class="p-4 pt-2 pb-8 flex justify-between gap-4 bg-gray-50"> | ||
| 90 | - <div class="flex-1 py-3 text-center border border-blue-600 text-blue-600 rounded-lg text-base bg-white" | ||
| 91 | - @click="close"> | ||
| 92 | - 取消 | ||
| 93 | - </div> | ||
| 94 | - <div class="flex-1 py-3 text-center bg-blue-600 text-white rounded-lg text-base" @click="submit"> | ||
| 95 | - 提交申请 | ||
| 96 | - </div> | ||
| 97 | - </div> | ||
| 98 | - </div> | ||
| 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