index.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <template>
  2. <view class="notice-container" :style="{ padding: `0 ${gap}px` }">
  3. <view class="notice-wrapper">
  4. <view class="notice-icon">公告</view>
  5. <view class="notice-content">
  6. <swiper class="notice-swiper" vertical :circular="true" :autoplay="true" :interval="swiper * 1000" :duration="500" :display-multiple-items="1" @change="handleChange">
  7. <swiper-item v-for="(notice, index) in noticeList" :key="index" @click="handleNoticeClick(notice)">
  8. <view class="notice-item">{{ notice.text }}</view>
  9. </swiper-item>
  10. </swiper>
  11. <view class="notice-more">更多 ></view>
  12. </view>
  13. </view>
  14. </view>
  15. </template>
  16. <script setup>
  17. import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
  18. // 定义props
  19. const props = defineProps({
  20. noticeList: {
  21. type: Array,
  22. default: () => [],
  23. validator: (value) => {
  24. return value.every((item) => typeof item.text === 'string' && typeof item.url === 'string');
  25. },
  26. },
  27. gap: {
  28. type: Number,
  29. default: 0,
  30. },
  31. swiper: {
  32. type: Number,
  33. default: 3,
  34. },
  35. });
  36. // 响应式状态
  37. const currentIndex = ref(0);
  38. let timer = null;
  39. // 计算循环列表
  40. const displayList = computed(() => {
  41. return [props.noticeList[props.noticeList.length - 1], ...props.noticeList, props.noticeList[0]];
  42. });
  43. // 处理点击事件
  44. const handleNoticeClick = (notice) => {
  45. if (notice.url) {
  46. // window.location.href = notice.url;
  47. uni.navigateTo({
  48. url: notice.url,
  49. });
  50. }
  51. };
  52. // 启动轮播
  53. const startSwiper = () => {
  54. if (props.noticeList.length <= 1) return;
  55. // 设置初始位置
  56. currentIndex.value = 1;
  57. timer = setInterval(() => {
  58. const nextIndex = currentIndex.value + 1;
  59. if (nextIndex >= displayList.value.length - 1) {
  60. currentIndex.value = nextIndex;
  61. // 当到达最后一个元素时,等待过渡动画完成后重置到第一个元素
  62. setTimeout(() => {
  63. currentIndex.value = 1;
  64. }, 500);
  65. } else {
  66. currentIndex.value = nextIndex;
  67. }
  68. }, props.swiper * 1000);
  69. };
  70. // 监听列表变化
  71. watch(
  72. () => props.noticeList.length,
  73. () => {
  74. if (timer) {
  75. clearInterval(timer);
  76. }
  77. startSwiper();
  78. }
  79. );
  80. // 组件挂载时启动轮播
  81. onMounted(() => {
  82. startSwiper();
  83. });
  84. // 组件卸载时清理定时器
  85. onUnmounted(() => {
  86. if (timer) {
  87. clearInterval(timer);
  88. }
  89. });
  90. </script>
  91. <style lang="less" scoped>
  92. .notice-container {
  93. margin: 4px 0;
  94. position: relative;
  95. .notice-wrapper {
  96. display: flex;
  97. align-items: center;
  98. height: 40px;
  99. background: #fff;
  100. border-radius: 130rpx 130rpx 130rpx 130rpx;
  101. }
  102. .notice-icon {
  103. width: 68rpx;
  104. height: 32rpx;
  105. color: #f89c33;
  106. font-size: 14px;
  107. border: 1px solid #f89c33;
  108. border-radius: 8rpx;
  109. text-align: center;
  110. line-height: 32rpx;
  111. margin: 0 10px;
  112. }
  113. .notice-content {
  114. flex: 1;
  115. height: 40px;
  116. overflow: hidden;
  117. }
  118. .notice-slider {
  119. height: 100%;
  120. }
  121. .notice-item {
  122. height: 40px;
  123. line-height: 40px;
  124. color: #333;
  125. font-size: 14px;
  126. cursor: pointer;
  127. white-space: nowrap;
  128. overflow: hidden;
  129. text-overflow: ellipsis;
  130. width: 80%;
  131. }
  132. .notice-more {
  133. color: #999;
  134. position: absolute;
  135. right: 25px;
  136. top: 50%;
  137. transform: translateY(-50%);
  138. font-size: 12px;
  139. }
  140. }
  141. </style>