refactor(cycle-selection): 使用van-popup组件重构周期选择弹窗
移除手动计算弹窗尺寸的逻辑,改用vant组件内置的弹窗功能 添加自定义单选按钮样式,优化用户体验
Showing
1 changed file
with
50 additions
and
108 deletions
| 1 | <template> | 1 | <template> |
| 2 | - <div class="cycle-selection-page"> | ||
| 3 | <van-config-provider :theme-vars="themeVars"> | 2 | <van-config-provider :theme-vars="themeVars"> |
| 4 | - <div class="cycle-popup"> | 3 | + <van-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> |
| 49 | + </van-popup> | ||
| 33 | </van-config-provider> | 50 | </van-config-provider> |
| 34 | - </div> | ||
| 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 | - | ||
| 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 | 78 | ||
| 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,39 +196,14 @@ onMounted(() => { | ... | @@ -245,39 +196,14 @@ 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; | ||
| 268 | - align-items: center; | ||
| 269 | - justify-content: center; | ||
| 270 | - .cycle-popup { | ||
| 271 | - width: 85vw; | ||
| 272 | - height: 70vh; | ||
| 273 | - background: white; | ||
| 274 | - border-radius: 12px; | ||
| 275 | display: flex; | 204 | display: flex; |
| 276 | flex-direction: column; | 205 | flex-direction: column; |
| 277 | - overflow: hidden; | 206 | + max-height: 70vh; |
| 278 | - position: relative; | ||
| 279 | - margin: 0 auto; | ||
| 280 | - } | ||
| 281 | 207 | ||
| 282 | .popup-header { | 208 | .popup-header { |
| 283 | padding: 20px; | 209 | padding: 20px; |
| ... | @@ -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> | ... | ... |
-
Please register or login to post a comment