hookehuyr

fix(PointsList): 修复列表高度计算问题并添加平板设备适配

refactor(PointsList): 优化高度计算逻辑,添加重试机制和设备适配
style: 统一按钮和文本的字体大小样式
1 <!-- 1 <!--
2 * @Date: 2022-09-19 14:11:06 2 * @Date: 2022-09-19 14:11:06
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-16 11:16:53 4 + * @LastEditTime: 2025-09-20 22:26:29
5 * @FilePath: /lls_program/src/pages/MyFamily/index.vue 5 * @FilePath: /lls_program/src/pages/MyFamily/index.vue
6 * @Description: 我的家庭页面 - 展示用户加入的家庭列表 6 * @Description: 我的家庭页面 - 展示用户加入的家庭列表
7 --> 7 -->
...@@ -117,7 +117,7 @@ ...@@ -117,7 +117,7 @@
117 <view v-if="isShowBtn" class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-100 p-4 z-10"> 117 <view v-if="isShowBtn" class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-100 p-4 z-10">
118 <view 118 <view
119 @tap="joinNewFamily" 119 @tap="joinNewFamily"
120 - class="w-full bg-blue-500 text-white text-center py-3 rounded-lg font-medium" 120 + class="w-full bg-blue-500 text-white text-center py-3 rounded-lg font-medium text-sm"
121 > 121 >
122 加入新家庭 122 加入新家庭
123 </view> 123 </view>
...@@ -197,13 +197,13 @@ ...@@ -197,13 +197,13 @@
197 <view class="flex gap-3"> 197 <view class="flex gap-3">
198 <view 198 <view
199 @tap="closeMemberPopup" 199 @tap="closeMemberPopup"
200 - class="flex-1 py-3 bg-gray-200 text-gray-700 text-center rounded-lg" 200 + class="flex-1 py-3 bg-gray-200 text-gray-700 text-center rounded-lg text-sm"
201 > 201 >
202 关闭 202 关闭
203 </view> 203 </view>
204 <view 204 <view
205 @tap="removeSelectedMembers" 205 @tap="removeSelectedMembers"
206 - class="flex-1 py-3 bg-red-500 text-white text-center rounded-lg" 206 + class="flex-1 py-3 bg-red-500 text-white text-center rounded-lg text-sm"
207 :class="selectedMembers.length === 0 ? 'opacity-50' : ''" 207 :class="selectedMembers.length === 0 ? 'opacity-50' : ''"
208 > 208 >
209 移除 ({{ selectedMembers.length }}) 209 移除 ({{ selectedMembers.length }})
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
8 <!-- Points display --> 8 <!-- Points display -->
9 <view class="pt-4 pb-6 flex flex-col items-center"> 9 <view class="pt-4 pb-6 flex flex-col items-center">
10 <h2 class="text-4xl font-bold text-white mb-1">{{ totalPoints }}</h2> 10 <h2 class="text-4xl font-bold text-white mb-1">{{ totalPoints }}</h2>
11 - <p class="text-white text-opacity-80">当前可用积分</p> 11 + <p class="text-white text-opacity-80 text-sm">当前可用积分</p>
12 </view> 12 </view>
13 <!-- Points strategy section --> 13 <!-- Points strategy section -->
14 <view class="bg-white rounded-t-3xl px-4 pt-5"> 14 <view class="bg-white rounded-t-3xl px-4 pt-5">
......
...@@ -326,12 +326,23 @@ const categorySectionHeight = ref(0) ...@@ -326,12 +326,23 @@ const categorySectionHeight = ref(0)
326 */ 326 */
327 const scrollStyle = computed(() => { 327 const scrollStyle = computed(() => {
328 const totalFixedHeight = searchSectionHeight.value + categorySectionHeight.value 328 const totalFixedHeight = searchSectionHeight.value + categorySectionHeight.value
329 +
330 + // 添加安全边距,避免内容被遮挡
331 + const safeMargin = 20
332 + const finalHeight = totalFixedHeight + safeMargin
333 +
329 return { 334 return {
330 - height: `calc(100vh - ${totalFixedHeight}rpx)` 335 + height: `calc(100vh - ${finalHeight}rpx)`,
336 + minHeight: '400rpx' // 设置最小高度,防止在某些设备上过小
331 } 337 }
332 }) 338 })
333 339
334 /** 340 /**
341 + * 获取系统信息
342 + */
343 +const systemInfo = ref({})
344 +
345 +/**
335 * 获取元素高度 346 * 获取元素高度
336 * @param {string} selector - 元素选择器 347 * @param {string} selector - 元素选择器
337 * @returns {Promise<number>} 元素高度(rpx) 348 * @returns {Promise<number>} 元素高度(rpx)
...@@ -340,10 +351,14 @@ const getElementHeight = (selector) => { ...@@ -340,10 +351,14 @@ const getElementHeight = (selector) => {
340 return new Promise((resolve) => { 351 return new Promise((resolve) => {
341 const query = Taro.createSelectorQuery() 352 const query = Taro.createSelectorQuery()
342 query.select(selector).boundingClientRect((rect) => { 353 query.select(selector).boundingClientRect((rect) => {
343 - if (rect) { 354 + if (rect && rect.height > 0) {
344 - // 将px转换为rpx 355 + // 根据设备像素比动态计算rpx
345 - const height = rect.height * 2 356 + const pixelRatio = systemInfo.value.pixelRatio || 2
346 - resolve(height) 357 + const screenWidth = systemInfo.value.screenWidth || 375
358 + // 标准转换:750rpx = 屏幕宽度px
359 + const rpxRatio = 750 / screenWidth
360 + const height = rect.height * rpxRatio
361 + resolve(Math.ceil(height))
347 } else { 362 } else {
348 resolve(0) 363 resolve(0)
349 } 364 }
...@@ -356,16 +371,40 @@ const getElementHeight = (selector) => { ...@@ -356,16 +371,40 @@ const getElementHeight = (selector) => {
356 */ 371 */
357 const calculateFixedHeight = async () => { 372 const calculateFixedHeight = async () => {
358 try { 373 try {
359 - const searchHeight = await getElementHeight('.search-section') 374 + // 获取系统信息
360 - const categoryHeight = await getElementHeight('.category-section') 375 + systemInfo.value = await Taro.getWindowInfo()
376 +
377 + // 多次尝试获取高度,确保DOM完全渲染
378 + let attempts = 0
379 + const maxAttempts = 3
380 +
381 + while (attempts < maxAttempts) {
382 + const searchHeight = await getElementHeight('.search-section')
383 + const categoryHeight = await getElementHeight('.category-section')
384 +
385 + // 如果获取到有效高度,则使用
386 + if (searchHeight > 0 && categoryHeight > 0) {
387 + searchSectionHeight.value = searchHeight
388 + categorySectionHeight.value = categoryHeight
389 + return
390 + }
391 +
392 + attempts++
393 + // 等待更长时间再重试
394 + await new Promise(resolve => setTimeout(resolve, 200))
395 + }
396 +
397 + // 如果多次尝试都失败,使用基于设备的默认值
398 + const isTablet = systemInfo.value.screenWidth >= 768
399 + searchSectionHeight.value = isTablet ? 180 : 144 // 平板设备使用更大的默认值
400 + categorySectionHeight.value = isTablet ? 240 : 200
361 401
362 - searchSectionHeight.value = searchHeight
363 - categorySectionHeight.value = categoryHeight
364 } catch (error) { 402 } catch (error) {
365 console.error('计算高度失败:', error) 403 console.error('计算高度失败:', error)
366 - // 使用默认值 404 + // 使用保守的默认值
367 - searchSectionHeight.value = 144 // 搜索区域默认高度 405 + const isTablet = systemInfo.value?.screenWidth >= 768
368 - categorySectionHeight.value = 200 // 分类区域默认高度 406 + searchSectionHeight.value = isTablet ? 180 : 144
407 + categorySectionHeight.value = isTablet ? 240 : 200
369 } 408 }
370 } 409 }
371 410
...@@ -431,11 +470,29 @@ const closeDetail = () => { ...@@ -431,11 +470,29 @@ const closeDetail = () => {
431 } 470 }
432 471
433 // ==================== 生命周期 ==================== 472 // ==================== 生命周期 ====================
434 -onMounted(() => { 473 +onMounted(async () => {
474 + // 先获取系统信息
475 + try {
476 + systemInfo.value = await Taro.getWindowInfo()
477 + } catch (error) {
478 + console.error('获取系统信息失败:', error)
479 + }
480 +
435 // 延时计算高度,确保DOM渲染完成 481 // 延时计算高度,确保DOM渲染完成
482 + // 对于平板设备,需要更长的等待时间
483 + const isTablet = systemInfo.value?.screenWidth >= 768
484 + const delay = isTablet ? 300 : 150
485 +
436 setTimeout(() => { 486 setTimeout(() => {
437 calculateFixedHeight() 487 calculateFixedHeight()
438 - }, 100) 488 + }, delay)
489 +
490 + // 监听窗口大小变化,重新计算高度
491 + Taro.onWindowResize(() => {
492 + setTimeout(() => {
493 + calculateFixedHeight()
494 + }, 100)
495 + })
439 }) 496 })
440 </script> 497 </script>
441 498
...@@ -444,3 +501,316 @@ export default { ...@@ -444,3 +501,316 @@ export default {
444 name: 'PointsListPage' 501 name: 'PointsListPage'
445 } 502 }
446 </script> 503 </script>
504 +
505 +<style lang="less" scoped>
506 +.points-list-page {
507 + height: 100vh;
508 + display: flex;
509 + flex-direction: column;
510 +
511 + .search-section {
512 + flex-shrink: 0;
513 + padding: 20rpx;
514 +
515 + // 平板设备适配
516 + @media (min-width: 768px) {
517 + padding: 30rpx 40rpx;
518 + }
519 +
520 + .search-box {
521 + .search-input {
522 + width: 100%;
523 + height: 80rpx;
524 + padding: 0 20rpx;
525 + border-radius: 40rpx;
526 + background-color: #f5f5f5;
527 + font-size: 28rpx;
528 +
529 + // 平板设备适配
530 + @media (min-width: 768px) {
531 + height: 100rpx;
532 + font-size: 32rpx;
533 + padding: 0 30rpx;
534 + }
535 + }
536 + }
537 + }
538 +
539 + .category-section {
540 + flex-shrink: 0;
541 + padding: 20rpx;
542 +
543 + // 平板设备适配
544 + @media (min-width: 768px) {
545 + padding: 30rpx 40rpx;
546 + }
547 +
548 + .category-title {
549 + font-size: 32rpx;
550 + font-weight: bold;
551 + margin-bottom: 20rpx;
552 +
553 + // 平板设备适配
554 + @media (min-width: 768px) {
555 + font-size: 36rpx;
556 + margin-bottom: 30rpx;
557 + }
558 + }
559 +
560 + .category-grid {
561 + display: flex;
562 + flex-wrap: wrap;
563 + gap: 20rpx;
564 +
565 + // 平板设备适配
566 + @media (min-width: 768px) {
567 + gap: 30rpx;
568 + }
569 +
570 + .category-item {
571 + padding: 16rpx 32rpx;
572 + border-radius: 40rpx;
573 + background-color: #f8f8f8;
574 + border: 2rpx solid transparent;
575 + transition: all 0.3s ease;
576 +
577 + // 平板设备适配
578 + @media (min-width: 768px) {
579 + padding: 20rpx 40rpx;
580 + font-size: 30rpx;
581 + }
582 +
583 + &.active {
584 + background-color: #007aff;
585 + color: white;
586 + }
587 +
588 + .category-name {
589 + font-size: 28rpx;
590 +
591 + // 平板设备适配
592 + @media (min-width: 768px) {
593 + font-size: 32rpx;
594 + }
595 + }
596 + }
597 + }
598 + }
599 +
600 + .points-list-section {
601 + flex: 1;
602 + overflow: hidden;
603 +
604 + .points-list {
605 + height: 100%;
606 +
607 + .points-items {
608 + padding: 0 20rpx 20rpx;
609 +
610 + // 平板设备适配
611 + @media (min-width: 768px) {
612 + padding: 0 40rpx 40rpx;
613 + }
614 +
615 + .points-item {
616 + display: flex;
617 + align-items: center;
618 + padding: 30rpx 20rpx;
619 + margin-bottom: 20rpx;
620 + background-color: white;
621 + border-radius: 20rpx;
622 + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
623 +
624 + // 平板设备适配
625 + @media (min-width: 768px) {
626 + padding: 40rpx 30rpx;
627 + margin-bottom: 30rpx;
628 + border-radius: 24rpx;
629 + }
630 +
631 + .points-item-left {
632 + flex: 1;
633 +
634 + .points-content {
635 + .points-title {
636 + font-size: 32rpx;
637 + font-weight: bold;
638 + color: #333;
639 + margin-bottom: 10rpx;
640 + display: block;
641 +
642 + // 平板设备适配
643 + @media (min-width: 768px) {
644 + font-size: 36rpx;
645 + margin-bottom: 15rpx;
646 + }
647 + }
648 +
649 + .points-desc {
650 + font-size: 26rpx;
651 + color: #666;
652 + margin-bottom: 15rpx;
653 + display: block;
654 + line-height: 1.4;
655 +
656 + // 平板设备适配
657 + @media (min-width: 768px) {
658 + font-size: 30rpx;
659 + margin-bottom: 20rpx;
660 + }
661 + }
662 +
663 + .points-reward {
664 + .reward-text {
665 + font-size: 24rpx;
666 + color: #007aff;
667 + font-weight: bold;
668 +
669 + // 平板设备适配
670 + @media (min-width: 768px) {
671 + font-size: 28rpx;
672 + }
673 + }
674 + }
675 + }
676 + }
677 +
678 + .points-item-right {
679 + margin-left: 20rpx;
680 +
681 + // 平板设备适配
682 + @media (min-width: 768px) {
683 + margin-left: 30rpx;
684 + }
685 +
686 + .arrow-text {
687 + font-size: 32rpx;
688 + color: #ccc;
689 +
690 + // 平板设备适配
691 + @media (min-width: 768px) {
692 + font-size: 36rpx;
693 + }
694 + }
695 + }
696 + }
697 + }
698 +
699 + .empty-state {
700 + display: flex;
701 + justify-content: center;
702 + align-items: center;
703 + height: 400rpx;
704 +
705 + .empty-text {
706 + font-size: 28rpx;
707 + color: #999;
708 +
709 + // 平板设备适配
710 + @media (min-width: 768px) {
711 + font-size: 32rpx;
712 + }
713 + }
714 + }
715 + }
716 + }
717 +}
718 +
719 +// 详情弹窗样式
720 +.detail-popup {
721 + height: 100%;
722 + display: flex;
723 + flex-direction: column;
724 + background-color: #f8f8f8;
725 +
726 + .detail-header {
727 + display: flex;
728 + justify-content: space-between;
729 + align-items: center;
730 + padding: 40rpx 30rpx;
731 + background-color: white;
732 + border-bottom: 2rpx solid #eee;
733 +
734 + // 平板设备适配
735 + @media (min-width: 768px) {
736 + padding: 50rpx 40rpx;
737 + }
738 +
739 + .detail-title {
740 + font-size: 36rpx;
741 + font-weight: bold;
742 + color: #333;
743 +
744 + // 平板设备适配
745 + @media (min-width: 768px) {
746 + font-size: 40rpx;
747 + }
748 + }
749 +
750 + .close-btn1 {
751 + padding: 10rpx 20rpx;
752 +
753 + .close-text {
754 + font-size: 28rpx;
755 + color: #007aff;
756 +
757 + // 平板设备适配
758 + @media (min-width: 768px) {
759 + font-size: 32rpx;
760 + }
761 + }
762 + }
763 + }
764 +
765 + .detail-content {
766 + flex: 1;
767 +
768 + .detail-body {
769 + padding: 30rpx;
770 +
771 + // 平板设备适配
772 + @media (min-width: 768px) {
773 + padding: 40rpx;
774 + }
775 +
776 + .detail-section {
777 + margin-bottom: 40rpx;
778 + padding: 30rpx;
779 + background-color: white;
780 + border-radius: 20rpx;
781 +
782 + // 平板设备适配
783 + @media (min-width: 768px) {
784 + margin-bottom: 50rpx;
785 + padding: 40rpx;
786 + border-radius: 24rpx;
787 + }
788 +
789 + .section-title {
790 + font-size: 32rpx;
791 + font-weight: bold;
792 + color: #333;
793 + margin-bottom: 20rpx;
794 +
795 + // 平板设备适配
796 + @media (min-width: 768px) {
797 + font-size: 36rpx;
798 + margin-bottom: 30rpx;
799 + }
800 + }
801 +
802 + .section-content {
803 + font-size: 28rpx;
804 + color: #666;
805 + line-height: 1.5;
806 +
807 + // 平板设备适配
808 + @media (min-width: 768px) {
809 + font-size: 32rpx;
810 + }
811 + }
812 + }
813 + }
814 + }
815 +}
816 +</style>
......