| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 | <template>  <view>    <view class="ui-swiper" :class="[props.mode, props.bg, props.ui]">      <swiper        :circular="props.circular"        :current="state.cur"        :autoplay="props.autoplay && !state.videoPlaySataus"        :interval="props.interval"        :duration="props.duration"        @transition="transition"        @animationfinish="animationfinish"        :style="customStyle"        @change="swiperChange"      >        <swiper-item          class="swiper-item"          v-for="(item, index) in props.list"          :key="index"          :class="{ cur: state.cur == index }"          @tap="onSwiperItem(item)"        >          <view class="ui-swiper-main">            <image              v-if="item.type === 'image'"              class="swiper-image"              :mode="props.imageMode"              :src="item.src"              width="100%"              height="100%"              @load="onImgLoad"            ></image>            <su-video              v-else              :ref="(el) => (refs.videoRef[`video_${index}`] = el)"              :poster="sheep.$url.cdn(item.poster)"              :src="sheep.$url.cdn(item.src)"              :index="index"              :moveX="state.moveX"              :initialTime="item.currentTime || 0"              :height="seizeHeight"              @videoTimeupdate="videoTimeupdate"            ></su-video>          </view>        </swiper-item>      </swiper>      <template v-if="!state.videoPlaySataus">        <view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle != 'tag'">          <view            class="line-box"            v-for="(item, index) in props.list"            :key="index"            :class="[state.cur == index ? 'cur' : '', props.dotCur]"          ></view>        </view>        <view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle == 'tag'">          <view            class="ui-tag radius-lg"            :class="[props.dotCur]"            style="pointer-events: none; padding: 0 10rpx"          >            <view style="transform: scale(0.7)">{{ state.cur + 1 }} / {{ props.list.length }}</view>          </view>        </view>      </template>    </view>  </view></template><script setup>  /**   * 轮播组件   *   * @property {Boolean} circular = false  	- 是否采用衔接滑动,即播放到末尾后重新回到开头   * @property {Boolean} autoplay = true  	- 是否自动切换   * @property {Number} interval = 5000  		- 自动切换时间间隔   * @property {Number} duration = 500  		- 滑动动画时长,app-nvue不支持   * @property {Array} list = [] 				- 轮播数据   * @property {String} ui = ''  				- 样式class   * @property {String} mode  				- 模式   * @property {String} dotStyle  			- 指示点样式   * @property {String} dotCur= 'ui-BG-Main' 	- 当前指示点样式,默认主题色   * @property {String} bg  					- 背景   * @property {String} height = 300  		- 组件高度   * @property {String} imgHeight = 300   	- 图片高度   *   * @example list = [{url:'跳转路径',urlType:'跳转方式',type:'轮播类型',src:'轮播内容地址',poster:'视频必传'}]   */  import { reactive, computed } from 'vue';  import sheep from '@/sheep';  import { clone } from 'lodash-es';  // 数据  const state = reactive({    imgHeight: 0,    cur: 0,    moveX: 0,    videoPlaySataus: false,    heightList: [],  });  const refs = reactive({    videoRef: {},  });  // 接收参数  const props = defineProps({    circular: {      type: Boolean,      default: true,    },    autoplay: {      type: Boolean,      default: false,    },    interval: {      type: Number,      default: 3000,    },    duration: {      type: Number,      default: 500,    },    mode: {      type: String,      default: 'default',    },    imageMode: {      type: String,      default: 'scaleToFill',    },    list: {      type: Array,      default() {        return [];      },    },    dotStyle: {      type: String,      default: 'long', //default long tag    },    dotCur: {      type: String,      default: 'ss-bg-opactity-block',    },    bg: {      type: String,      default: 'bg-none',    },    height: {      type: Number,      default: 0,    },    imgHeight: {      type: Number,      default: 0,    },    imgTopRadius: {      type: Number,      default: 0,    },    imgBottomRadius: {      type: Number,      default: 0,    },    isPreview: {      type: Boolean,      default: false,    },    seizeHeight: {      type: Number,      default: 200,    },  });  // current 改变时会触发 change 事件  const swiperChange = (e) => {    if (e.detail.source !== 'touch' && e.detail.source !== 'autoplay') return;    state.cur = e.detail.current;    state.videoPlaySataus = false;    if (props.list[state.cur].type === 'video') {      refs.videoRef[`video_${state.cur}`].pausePlay();    }  };  // 点击轮播组件  const onSwiperItem = (item) => {    if (item.type === 'video') {      state.videoPlaySataus = true;    } else {      sheep.$router.go(item.url);      onPreview();    }  };  const onPreview = () => {    if (!props.isPreview) return;    let previewImage = clone(props.list);    previewImage.forEach((item, index) => {      if (item.type === 'video') {        previewImage.splice(index, 1);      }    });    uni.previewImage({      urls:        previewImage.length < 1          ? [props.src]          : previewImage.reduce((pre, cur) => {              pre.push(cur.src);              return pre;            }, []),      current: state.cur,      // longPressActions: {      //   itemList: ['发送给朋友', '保存图片', '收藏'],      //   success: function (data) {      //     console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');      //   },      //   fail: function (err) {      //     console.log(err.errMsg);      //   },      // },    });  };  //  // swiper-item 的位置发生改变时会触发 transition  const transition = (e) => {    // #ifdef APP-PLUS    state.moveX = e.detail.dx;    // #endif  };  // 动画结束时会触发 animationfinish  const animationfinish = (e) => {    state.moveX = 0;  };  const videoTimeupdate = (e) => {    props.list[state.cur].currentTime = e.detail.currentTime;  };  // 自动计算高度  const customStyle = computed(() => {    let height;    // 固定高度情况    if (props.height !== 0) {      height = props.height;    }    // 自动高度情况    if (props.height === 0) {      // 图片预加载占位高度      if (state.imgHeight !== 0) {        height = state.imgHeight;      } else if (props.seizeHeight !== 0) {        height = props.seizeHeight;      }    }    return {      height: height + 'rpx',    };  });  // 计算轮播图片最大高度  function onImgLoad(e) {    if (props.height === 0) {      let newHeight = (e.detail.height / e.detail.width) * 750;      if (state.imgHeight < newHeight) {        state.imgHeight = newHeight;      }    }  }</script><style lang="scss" scoped>  .ui-swiper {    position: relative;    .ui-swiper-main {      width: 100%;      height: 100%;    }    .ui-swiper-main .swiper-image {      width: 100%;      height: 100%;    }    .ui-swiper-dot {      position: absolute;      width: 100%;      bottom: 20rpx;      height: 30rpx;      display: flex;      align-items: center;      justify-content: center;      &.default .line-box {        display: inline-flex;        border-radius: 50rpx;        width: 6px;        height: 6px;        border: 2px solid transparent;        margin: 0 10rpx;        opacity: 0.3;        position: relative;        justify-content: center;        align-items: center;        &.cur {          width: 8px;          height: 8px;          opacity: 1;          border: 0px solid transparent;        }        &.cur::after {          content: '';          border-radius: 50rpx;          width: 4px;          height: 4px;          background-color: #fff;        }      }      &.long .line-box {        display: inline-block;        border-radius: 100rpx;        width: 6px;        height: 6px;        margin: 0 10rpx;        opacity: 0.3;        position: relative;        &.cur {          width: 24rpx;          opacity: 1;        }        &.cur::after {        }      }      &.line {        bottom: 20rpx;        .line-box {          display: inline-block;          width: 30px;          height: 3px;          opacity: 0.3;          position: relative;          &.cur {            opacity: 1;          }        }      }      &.tag {        justify-content: flex-end;        position: absolute;        bottom: 20rpx;        right: 20rpx;      }    }    &.card {      .swiper-item {        width: 610rpx !important;        left: 70rpx;        box-sizing: border-box;        padding: 20rpx 0rpx 60rpx;        overflow: initial;      }      .swiper-item .ui-swiper-main {        width: 100%;        display: block;        height: 100%;        transform: scale(0.9);        transition: all 0.2s ease-in 0s;        position: relative;        background-size: cover;        .swiper-image {          height: 100%;        }      }      .swiper-item .ui-swiper-main::before {        content: '';        display: block;        background: inherit;        filter: blur(5px);        position: absolute;        width: 100%;        height: 100%;        top: 10rpx;        left: 10rpx;        z-index: -1;        opacity: 0.3;        transform-origin: 0 0;        transform: scale(1, 1);      }      .swiper-item.cur .ui-swiper-main {        transform: scale(1);        transition: all 0.2s ease-in 0s;      }      .ui-swiper-dot.tag {        position: absolute;        bottom: 85rpx;        right: 75rpx;      }    }    &.hotelCard {      .swiper-item {        width: 650rpx !important;        left: 30rpx;        box-sizing: border-box;        padding: 0rpx 0rpx 50rpx;        overflow: initial;      }      .swiper-item .ui-swiper-main {        width: 100%;        display: block;        height: 100%;        transform: scale(0.9);        opacity: 0.8;        transition: all 0.2s ease-in 0s;        position: relative;        background-size: cover;        .swiper-image {          width: 100%;          height: 400rpx;        }      }      .swiper-item .ui-swiper-main::before {        content: '';        display: block;        background: inherit;        filter: blur(5px);        position: absolute;        width: 100%;        height: 100%;        top: 10rpx;        left: 10rpx;        z-index: -1;        opacity: 0.3;        transform-origin: 0 0;        transform: scale(1, 1);      }      .swiper-item.cur .ui-swiper-main {        transform: scale(1);        transition: all 0.2s ease-in 0s;        opacity: 1;      }      .ui-swiper-dot {        display: none;      }    }    &.hotelDetail {      .swiper-item {        width: 690rpx !important;        left: 30rpx;        box-sizing: border-box;        padding: 20rpx 0rpx;        overflow: initial;      }      .swiper-item .ui-swiper-main {        width: 100%;        display: block;        height: 100%;        transform: scale(0.96);        transition: all 0.2s ease-in 0s;        position: relative;        background-size: cover;        .swiper-image {          height: 100%;        }      }      .swiper-item.cur .ui-swiper-main {        transform: scale(0.96);        transition: all 0.2s ease-in 0s;      }    }  }</style>
 |