| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 | <!-- 装修基础组件:菜单导航(金刚区) --><template>  <!-- 包裹层 -->  <view    class="ui-swiper"    :class="[props.mode, props.bg, props.ui]"    :style="[{ 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',    },    bg: {      type: String,      default: 'bg-none',    },    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 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>
 |