hookehuyr

refactor(cycle-selection): 使用van-popup组件重构周期选择弹窗

移除手动计算弹窗尺寸的逻辑,改用vant组件内置的弹窗功能
添加自定义单选按钮样式,优化用户体验
1 <template> 1 <template>
2 - <div class="cycle-selection-page"> 2 + <van-config-provider :theme-vars="themeVars">
3 - <van-config-provider :theme-vars="themeVars"> 3 + <van-popup
4 - <div class="cycle-popup"> 4 + v-model:show="showPopup"
5 + position="center"
6 + :style="{ width: '85vw', maxWidth: '500px', borderRadius: '12px' }"
7 + :close-on-click-overlay="false"
8 + >
9 + <div class="cycle-popup-content">
5 <div class="popup-header"> 10 <div class="popup-header">
6 <h3>选择周期</h3> 11 <h3>选择周期</h3>
7 </div> 12 </div>
8 <div class="popup-content"> 13 <div class="popup-content">
9 - <p style="padding-top: 0.85rem; text-align: justify;">{{ cycle_note }}</p> 14 + <p class="cycle-note">{{ cycle_note }}</p>
10 <van-radio-group v-model="selectedCycle"> 15 <van-radio-group v-model="selectedCycle">
11 <div v-for="cycle in cycleList" :key="cycle.id" class="cycle-item"> 16 <div v-for="cycle in cycleList" :key="cycle.id" class="cycle-item">
12 <van-radio :name="cycle.id"> 17 <van-radio :name="cycle.id">
18 + <template #icon="{ checked }">
19 + <div class="custom-icon" :class="{ checked }">
20 + <van-icon name="success" v-if="checked" />
21 + </div>
22 + </template>
13 <div class="cycle-info"> 23 <div class="cycle-info">
14 <div class="cycle-title">{{ cycle.title }}</div> 24 <div class="cycle-title">{{ cycle.title }}</div>
15 <div class="cycle-time"> 25 <div class="cycle-time">
...@@ -25,17 +35,23 @@ ...@@ -25,17 +35,23 @@
25 </van-radio-group> 35 </van-radio-group>
26 </div> 36 </div>
27 <div class="popup-footer"> 37 <div class="popup-footer">
28 - <van-button type="primary" class="confirm-btn" @click="confirmCycleSelection" :disabled="!selectedCycle"> 38 + <van-button
39 + type="primary"
40 + block
41 + round
42 + @click="confirmCycleSelection"
43 + :disabled="!selectedCycle"
44 + >
29 确认选择 45 确认选择
30 </van-button> 46 </van-button>
31 </div> 47 </div>
32 </div> 48 </div>
33 - </van-config-provider> 49 + </van-popup>
34 - </div> 50 + </van-config-provider>
35 </template> 51 </template>
36 52
37 <script setup> 53 <script setup>
38 -import { ref, onMounted, nextTick, onUnmounted } from 'vue'; 54 +import { ref, onMounted } from 'vue';
39 import { useRouter, useRoute } from 'vue-router'; 55 import { useRouter, useRoute } from 'vue-router';
40 import { getCycleListAPI } from '@/api/cycle'; 56 import { getCycleListAPI } from '@/api/cycle';
41 import { styleColor } from '@/constant.js'; 57 import { styleColor } from '@/constant.js';
...@@ -57,74 +73,9 @@ const themeVars = { ...@@ -57,74 +73,9 @@ const themeVars = {
57 const cycleList = ref([]); 73 const cycleList = ref([]);
58 const selectedCycle = ref(''); 74 const selectedCycle = ref('');
59 const cycle_note = ref(''); 75 const cycle_note = ref('');
76 +const showPopup = ref(false);
60 77
61 -/**
62 - * 动态设置弹框尺寸
63 - */
64 -const setPopupSize = () => {
65 - nextTick(() => {
66 - const popupElement = document.querySelector('.cycle-popup');
67 - if (popupElement) {
68 - const screenWidth = window.innerWidth;
69 - const screenHeight = window.innerHeight;
70 -
71 - // 设置宽度为屏幕的85%
72 - const popupWidth = screenWidth * 0.85;
73 - popupElement.style.width = `${popupWidth}px`;
74 -
75 - // 根据屏幕尺寸动态计算最大宽度
76 - let maxWidth;
77 - if (screenWidth <= 480) {
78 - // 小屏设备
79 - maxWidth = screenWidth * 0.9;
80 - } else if (screenWidth <= 768) {
81 - // 中等屏幕设备
82 - maxWidth = 420;
83 - } else {
84 - // 大屏设备
85 - maxWidth = 500;
86 - }
87 -
88 - popupElement.style.maxWidth = `${maxWidth}px`;
89 78
90 - // 设置高度为屏幕的70%
91 - const popupHeight = screenHeight * 0.7;
92 - popupElement.style.height = `${popupHeight}px`;
93 -
94 - // 计算内容区域高度
95 - calculatePopupContentHeight();
96 - }
97 - });
98 -};
99 -
100 -/**
101 - * 动态计算弹窗内容区域高度
102 - */
103 -const calculatePopupContentHeight = () => {
104 - nextTick(() => {
105 - const popupElement = document.querySelector('.cycle-popup');
106 - const headerElement = document.querySelector('.popup-header');
107 - const footerElement = document.querySelector('.popup-footer');
108 - const contentElement = document.querySelector('.popup-content');
109 -
110 - if (popupElement && headerElement && footerElement && contentElement) {
111 - const popupHeight = popupElement.offsetHeight;
112 - const headerHeight = headerElement.offsetHeight;
113 - const footerHeight = footerElement.offsetHeight;
114 - const padding = 40; // 上下padding的总和
115 -
116 - const contentHeight = popupHeight - headerHeight - footerHeight - padding;
117 - contentElement.style.height = `${contentHeight}px`;
118 - }
119 - });
120 -};
121 -
122 -/**
123 - * 窗口尺寸变化处理函数
124 - */
125 -const handleResize = () => {
126 - setPopupSize();
127 -};
128 79
129 /** 80 /**
130 * 获取周期列表 81 * 获取周期列表
...@@ -142,8 +93,8 @@ const getCycleList = async (form_code) => { ...@@ -142,8 +93,8 @@ const getCycleList = async (form_code) => {
142 // 设置周期列表 93 // 设置周期列表
143 cycleList.value = data.cycle_list; 94 cycleList.value = data.cycle_list;
144 cycle_note.value = data.cycle_note; 95 cycle_note.value = data.cycle_note;
145 - // 设置弹框尺寸 96 + // 显示弹框
146 - setPopupSize(); 97 + showPopup.value = true;
147 } else { 98 } else {
148 // 如果不需要周期选择,检查未完成表单后跳转到目标页面 99 // 如果不需要周期选择,检查未完成表单后跳转到目标页面
149 const targetRoute = sessionStorage.getItem('cycle_target_route'); 100 const targetRoute = sessionStorage.getItem('cycle_target_route');
...@@ -245,40 +196,15 @@ onMounted(() => { ...@@ -245,40 +196,15 @@ onMounted(() => {
245 console.error('缺少表单代码参数'); 196 console.error('缺少表单代码参数');
246 $router.replace('/'); 197 $router.replace('/');
247 } 198 }
248 -
249 - // 监听窗口尺寸变化
250 - window.addEventListener('resize', handleResize);
251 - // 监听屏幕方向变化
252 - window.addEventListener('orientationchange', handleResize);
253 -});
254 -
255 -onUnmounted(() => {
256 - // 清理事件监听器
257 - window.removeEventListener('resize', handleResize);
258 - window.removeEventListener('orientationchange', handleResize);
259 }); 199 });
260 </script> 200 </script>
261 201
262 <style lang="less"> 202 <style lang="less">
263 -.cycle-selection-page { 203 +.cycle-popup-content {
264 - width: 100vw;
265 - height: 100vh;
266 - background-color: rgba(0, 0, 0, 0.5);
267 display: flex; 204 display: flex;
268 - align-items: center; 205 + flex-direction: column;
269 - justify-content: center; 206 + max-height: 70vh;
270 - .cycle-popup { 207 +
271 - width: 85vw;
272 - height: 70vh;
273 - background: white;
274 - border-radius: 12px;
275 - display: flex;
276 - flex-direction: column;
277 - overflow: hidden;
278 - position: relative;
279 - margin: 0 auto;
280 - }
281 -
282 .popup-header { 208 .popup-header {
283 padding: 20px; 209 padding: 20px;
284 text-align: center; 210 text-align: center;
...@@ -300,6 +226,14 @@ onUnmounted(() => { ...@@ -300,6 +226,14 @@ onUnmounted(() => {
300 min-height: 0; 226 min-height: 0;
301 } 227 }
302 228
229 + .cycle-note {
230 + padding-top: 0.85rem;
231 + text-align: justify;
232 + margin-bottom: 1rem;
233 + color: #666;
234 + line-height: 1.5;
235 + }
236 +
303 .cycle-item { 237 .cycle-item {
304 padding: 15px 0; 238 padding: 15px 0;
305 border-bottom: 1px solid #f5f5f5; 239 border-bottom: 1px solid #f5f5f5;
...@@ -340,13 +274,21 @@ onUnmounted(() => { ...@@ -340,13 +274,21 @@ onUnmounted(() => {
340 flex-shrink: 0; 274 flex-shrink: 0;
341 } 275 }
342 276
343 - .confirm-btn { 277 + .custom-icon {
344 - width: 100%; 278 + width: 20px;
345 - height: 44px; 279 + height: 20px;
346 - border-radius: 8px; 280 + border: 2px solid #ddd;
347 - font-size: 16px; 281 + border-radius: 50%;
348 - font-weight: 500; 282 + display: flex;
283 + align-items: center;
284 + justify-content: center;
285 + transition: all 0.3s;
286 +
287 + &.checked {
288 + border-color: var(--van-radio-checked-icon-color);
289 + background-color: var(--van-radio-checked-icon-color);
290 + color: white;
291 + }
349 } 292 }
350 } 293 }
351 -
352 </style> 294 </style>
......