ImageFeed.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. <template>
  2. <div class="image-feed-wrap">
  3. <SlideVertical
  4. v-model:index="farmIndex"
  5. class="farm-feed"
  6. :name="`${slideName}-farm`"
  7. >
  8. <SlideItem
  9. v-for="(farm, farmIdx) in farms"
  10. :key="farm.id"
  11. class="feed-item"
  12. >
  13. <SlideHorizontal
  14. v-model:index="imageIndexes[farmIdx]"
  15. class="image-feed"
  16. :name="`${slideName}-img-${farm.id}`"
  17. >
  18. <SlideItem
  19. v-for="item in farm.images"
  20. :key="item.aweme_id"
  21. class="feed-item"
  22. >
  23. <BaseImage :item="item" />
  24. <GuardImageOverlay :level="farm.level" />
  25. <GuardInfoBoard :badge-icon="badgeIcon" class="info-board-layer" />
  26. </SlideItem>
  27. </SlideHorizontal>
  28. </SlideItem>
  29. </SlideVertical>
  30. <button
  31. type="button"
  32. class="nav-btn prev"
  33. :disabled="currentImageIndex <= 0"
  34. aria-label="上一张"
  35. @click="prevImage"
  36. >
  37. <Icon icon="eva:arrow-ios-back-fill" />
  38. </button>
  39. <button
  40. type="button"
  41. class="nav-btn next"
  42. :disabled="currentImageIndex >= currentFarmImages.length - 1"
  43. aria-label="下一张"
  44. @click="nextImage"
  45. >
  46. <Icon icon="eva:arrow-ios-forward-fill" />
  47. </button>
  48. </div>
  49. </template>
  50. <script setup lang="ts">
  51. import { Icon } from '@iconify/vue'
  52. import { computed, ref } from 'vue'
  53. import SlideVertical from '@/components/slide/SlideVertical.vue'
  54. import SlideHorizontal from '@/components/slide/SlideHorizontal.vue'
  55. import SlideItem from '@/components/slide/SlideItem.vue'
  56. import BaseImage from '@/components/image/BaseImage.vue'
  57. import GuardImageOverlay from './GuardImageOverlay.vue'
  58. import GuardInfoBoard from './GuardInfoBoard.vue'
  59. import type { GuardFarm } from '@/mock/homeData'
  60. const props = defineProps({
  61. feedId: { type: String, required: true },
  62. farms: { type: Array as () => GuardFarm[], required: true },
  63. /** 第四张卡片顶部徽章图,路径由外部传入 */
  64. badgeIcon: { type: String, default: '' },
  65. })
  66. const slideName = computed(() => `guard-feed-${props.feedId}`)
  67. const farms = computed(() => props.farms)
  68. const farmIndex = ref(0)
  69. const imageIndexes = ref(props.farms.map(() => 0))
  70. const currentFarmImages = computed(() => farms.value[farmIndex.value]?.images ?? [])
  71. const currentImageIndex = computed(() => imageIndexes.value[farmIndex.value] ?? 0)
  72. function prevImage() {
  73. const idx = farmIndex.value
  74. if (imageIndexes.value[idx] > 0) imageIndexes.value[idx] -= 1
  75. }
  76. function nextImage() {
  77. const idx = farmIndex.value
  78. const max = (farms.value[idx]?.images.length ?? 1) - 1
  79. if (imageIndexes.value[idx] < max) imageIndexes.value[idx] += 1
  80. }
  81. </script>
  82. <style scoped lang="less">
  83. .image-feed-wrap {
  84. position: relative;
  85. height: 100%;
  86. width: 100%;
  87. }
  88. .farm-feed,
  89. .image-feed {
  90. height: 100%;
  91. width: 100%;
  92. background: #000;
  93. }
  94. .farm-feed {
  95. :deep(.feed-item) {
  96. position: relative;
  97. height: 100%;
  98. width: 100%;
  99. }
  100. }
  101. .image-feed {
  102. :deep(.feed-item) {
  103. position: relative;
  104. height: 100%;
  105. width: 100%;
  106. }
  107. }
  108. .info-board-layer {
  109. position: absolute;
  110. bottom: 0;
  111. z-index: 2;
  112. pointer-events: none;
  113. }
  114. .nav-btn {
  115. position: absolute;
  116. top: 50%;
  117. z-index: 4;
  118. transform: translateY(-50%);
  119. width: 40rem;
  120. height: 40rem;
  121. border: none;
  122. border-radius: 50%;
  123. display: flex;
  124. align-items: center;
  125. justify-content: center;
  126. background: rgba(0, 0, 0, 0.6);
  127. color: #fff;
  128. pointer-events: auto;
  129. svg {
  130. font-size: 32rem;
  131. }
  132. &.prev {
  133. left: 12rem;
  134. }
  135. &.next {
  136. right: 12rem;
  137. }
  138. &:disabled {
  139. opacity: 0.5;
  140. }
  141. }
  142. </style>