503 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Vue
		
	
	
			
		
		
	
	
			503 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Vue
		
	
	
| <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';
 | |
| 
 | |
|   // 数据
 | |
|   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>
 |