BannerSwiper.vue 5.12 KB
<!--
 * @Date: 2025-01-20 10:00:00
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-07-29 12:41:42
 * @FilePath: /jgdl/src/components/BannerSwiper.vue
 * @Description: 轮播图组件
-->
<template>
  <view class="px-4 pt-4" style="background: linear-gradient( 180deg, #fb923c 0%, rgba(255,203,53,0) 61%);">
    <nut-swiper :init-page="0" :pagination-visible="true" pagination-color="#ffffff" auto-play="3000"
      class="rounded-lg overflow-hidden" height="160">
      <nut-swiper-item v-for="(item, index) in bannerList" :key="index" @click="onBannerClick(item)">
        <image :src="item.image" mode="aspectFill" class="w-full h-40 object-cover" />
      </nut-swiper-item>
    </nut-swiper>
  </view>

  <!-- 图片预览组件 -->
  <nut-image-preview
    v-model:show="showImagePreview"
    :images="previewImages"
    :init-no="currentImageIndex"
    :show-index="false"
  />

  <!-- 文章弹框组件 -->
  <nut-popup
    v-model:visible="showArticleModal"
    position="bottom"
    :style="{ height: '100vh' }"
    close-icon-position="bottom-right"
    :safe-area-inset-bottom="true"
  >
    <view class="article-modal">
      <view class="article-header p-4 border-b border-gray-300" style="text-align: center;">
        <text class="text-lg font-bold">{{ currentArticle.title }}</text>
      </view>
      <scroll-view :scroll-y="true" :catch-move="true" class="article-content" style="height: calc(100vh - 300rpx);">
        <view class="p-4">
          <rich-text :nodes="currentArticle.content"></rich-text>
        </view>
      </scroll-view>
      <view class="article-footer p-4">
        <nut-button type="primary" block color="#fb923c" @click="closeArticleModal">
          关闭
        </nut-button>
      </view>
    </view>
  </nut-popup>
</template>

<script setup>
import Taro from '@tarojs/taro'
import { ref } from 'vue'

// 定义props
defineProps({
  bannerList: {
    type: Array,
    default: () => []
  }
})

// 定义emits
const emit = defineEmits(['bannerClick'])

// 响应式数据
const showImagePreview = ref(false)
const previewImages = ref([])
const currentImageIndex = ref(0)
const showArticleModal = ref(false)
const currentArticle = ref({
  title: '',
  content: ''
})

/**
 * 点击轮播图处理函数
 */
const onBannerClick = (item) => {
  switch (item.type) {
    case 'icon':
      // 只显示封面图 - 显示图片预览
      previewImages.value = [{ src: item.image }]
      currentImageIndex.value = 0
      showImagePreview.value = true
      break
    case 'content':
      // 只显示文章详情 - 显示文章弹框
      currentArticle.value = {
        title: item.title,
        content: processRichTextContent(item.content)
      }
      showArticleModal.value = true
      break
    case 'vehicle':
      // 只进入车辆详情页 - 跳转到车辆详情页
      Taro.navigateTo({
        url: `/pages/productDetail/index?id=${item.id}`
      })
      break
    default:
      console.warn('未知的轮播图类型:', item.type)
  }

  // 触发父组件事件
  emit('bannerClick', item)
}

/**
 * 处理富文本内容,让图片适配容器宽度
 * @param {string} content - 富文本内容
 * @returns {string} 处理后的富文本内容
 */
const processRichTextContent = (content) => {
  if (!content) return ''

  // 移除img标签中的width和height属性,并添加style属性强制设置宽度为100%
  let processedContent = content
    // 移除width属性
    .replace(/<img([^>]*?)\s+width\s*=\s*["'][^"']*["']([^>]*?)>/gi, '<img$1$2>')
    // 移除height属性
    .replace(/<img([^>]*?)\s+height\s*=\s*["'][^"']*["']([^>]*?)>/gi, '<img$1$2>')
    // 移除现有的style属性中的width和height
    .replace(/<img([^>]*?)\s+style\s*=\s*["']([^"']*?)["']([^>]*?)>/gi, (match, before, styleContent, after) => {
      const cleanedStyle = styleContent
        .replace(/width\s*:\s*[^;]+;?/gi, '')
        .replace(/height\s*:\s*[^;]+;?/gi, '')
        .replace(/;\s*;/g, ';')
        .replace(/^\s*;|;\s*$/g, '')
      return `<img${before} style="${cleanedStyle}"${after}>`
    })

  // 为所有img标签添加或更新style属性,设置宽度为100%
  processedContent = processedContent.replace(/<img([^>]*?)>/gi, (match, attributes) => {
    if (attributes.includes('style=')) {
      // 如果已有style属性,在其中添加width: 100%
      return match.replace(/style\s*=\s*["']([^"']*?)["']/gi, (styleMatch, styleContent) => {
        const newStyle = styleContent ? `${styleContent}; width: 100%; height: auto;` : 'width: 100%; height: auto;'
        return `style="${newStyle}"`
      })
    } else {
      // 如果没有style属性,添加一个
      return `<img${attributes} style="width: 100%; height: auto;">`
    }
  })

  return processedContent
}

/**
 * 关闭文章弹框
 */
const closeArticleModal = () => {
  showArticleModal.value = false
}
</script>

<style lang="less">
.article-modal {
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.article-content {
  overflow: auto;
}

.article-footer {
  // position: sticky;
  bottom: 0;
  left: 0;
  right: 0;
  background: white;
  // border-top: 1px solid #eee;
  padding: 16px;
  z-index: 1000;
  // margin-top: auto;
}
</style>