| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 | <!-- 装修基础组件:菜单导航(金刚区) --><template>	<!-- 包裹层 -->	<view class="ui-swiper" :class="[props.mode, props.ui]"		:style="[bgStyle, { height: swiperHeight + (menuList.length > 1 ? 50 : 0) + 'rpx' }]">		<!-- 轮播 -->		<swiper :circular="props.circular" :current="state.cur" :autoplay="props.autoplay" :interval="props.interval"			:duration="props.duration" :style="[{ height: swiperHeight + 'rpx' }]" @change="swiperChange">			<swiper-item v-for="(arr, index) in menuList" :key="index" :class="{ cur: state.cur == index }">				<!-- 宫格 -->				<view class="grid-wrap">					<view v-for="(item, index) in arr" :key="index"						class="grid-item ss-flex ss-flex-col ss-col-center ss-row-center"						:style="[{ width: `${100 * (1 / data.column)}%`, height: '200rpx' }]" hover-class="ss-hover-btn"						@tap="sheep.$router.go(item.url)">						<view class="menu-box ss-flex ss-flex-col ss-col-center ss-row-center">							<view v-if="item.badge.show" class="tag-box"								:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]">								{{ item.badge.text }}							</view>							<image v-if="item.iconUrl" class="menu-icon" :style="[                  {                    width: props.iconSize + 'rpx',                    height: props.iconSize + 'rpx',                  },                ]" :src="sheep.$url.cdn(item.iconUrl)" mode="aspectFill"></image>							<view v-if="data.layout === 'iconText'" class="menu-title"								:style="[{ color: item.titleColor }]">								{{ item.title }}							</view>						</view>					</view>				</view>			</swiper-item>		</swiper>		<!-- 指示点 -->		<template v-if="menuList.length > 1">			<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle != 'tag'">				<view class="line-box" v-for="(item, index) in menuList.length" :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" :class="[props.dotCur]" style="pointer-events: none">					<view style="transform: scale(0.7)">{{ state.cur + 1 }} / {{ menuList.length }}</view>				</view>			</view>		</template>	</view></template><script setup>	/**	 * 轮播menu	 *	 * @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|Number} col = 4  			- 一行数量	 * @property {String|Number} row = 1 			- 几行	 * @property {String} hasBorder 				- 是否有边框	 * @property {String} borderColor 				- 边框颜色	 * @property {String} background		  		- 背景	 * @property {String} hoverClass 				- 按压样式类	 * @property {String} hoverStayTime 		  	- 动画时间	 *	 * @property {Array} list 		  				- 导航列表	 * @property {Number} iconSize 		  			- 图标大小	 * @property {String} color 		  			- 标题颜色	 *	 */	import {		reactive,		computed	} from 'vue';	import sheep from '@/sheep';	// 数据	const state = reactive({		cur: 0,	});	// 接收参数	const props = defineProps({		// 装修数据		data: {			type: Object,			default: () => ({}),		},		// 装修样式		styles: {			type: Object,			default: () => ({}),		},		circular: {			type: Boolean,			default: true,		},		autoplay: {			type: Boolean,			default: false,		},		interval: {			type: Number,			default: 5000,		},		duration: {			type: Number,			default: 500,		},		ui: {			type: String,			default: '',		},		mode: {			//default			type: String,			default: 'default',		},		dotStyle: {			type: String,			default: 'long', //default long tag		},		dotCur: {			type: String,			default: 'ui-BG-Main',		},		height: {			type: Number,			default: 300,		},		// 是否有边框		hasBorder: {			type: Boolean,			default: true,		},		// 边框颜色		borderColor: {			type: String,			default: 'red',		},		background: {			type: String,			default: 'blue',		},		hoverClass: {			type: String,			default: 'ss-hover-class', //'none'为没有hover效果		},		// 一排宫格数		col: {			type: [Number, String],			default: 3,		},		iconSize: {			type: Number,			default: 80,		},		color: {			type: String,			default: '#000',		},	});	// 设置背景样式	const bgStyle = computed(() => {		// 直接从 props.styles 解构		const {			bgType,			bgImg,			bgColor		} = props.styles;		// 根据 bgType 返回相应的样式		return {			background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor		};	});	// 生成数据	const menuList = computed(() => splitData(props.data.list, props.data.row * props.data.column));	const swiperHeight = computed(() => props.data.row * (props.data.layout === 'iconText' ? 200 : 180));	const windowWidth = sheep.$platform.device.windowWidth;	// current 改变时会触发 change 事件	const swiperChange = (e) => {		state.cur = e.detail.current;	};	// 重组数据	const splitData = (oArr = [], length = 1) => {		let arr = [];		let minArr = [];		oArr.forEach((c) => {			if (minArr.length === length) {				minArr = [];			}			if (minArr.length === 0) {				arr.push(minArr);			}			minArr.push(c);		});		return arr;	};</script><style lang="scss" scoped>	.grid-wrap {		width: 100%;		display: flex;		position: relative;		box-sizing: border-box;		overflow: hidden;		flex-wrap: wrap;		align-items: center;	}	.menu-box {		position: relative;		z-index: 1;		transform: translate(0, 0);		.tag-box {			position: absolute;			z-index: 2;			top: 0;			right: -6rpx;			font-size: 2em;			line-height: 1;			padding: 0.4em 0.6em 0.3em;			transform: scale(0.4) translateX(0.5em) translatey(-0.6em);			transform-origin: 100% 0;			border-radius: 200rpx;			white-space: nowrap;		}		.menu-icon {			transform: translate(0, 0);			width: 80rpx;			height: 80rpx;			padding-bottom: 10rpx;		}		.menu-title {			font-size: 24rpx;			color: #333;		}	}	::v-deep(.ui-swiper) {		position: relative;		z-index: 1;		.ui-swiper-dot {			position: absolute;			width: 100%;			bottom: 20rpx;			height: 30rpx;			display: flex;			align-items: center;			justify-content: center;			z-index: 2;			&.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;			}		}	}</style>
 |