Commit bbb58a95a1b9e01802db9c301d4f966768c618ca

Authored by 王强
1 parent 6839c95f

购好物,首页,热销排行自动无限滚动,完成~

购好物,新版店铺首页,组件完成,等待对接接口~
src/components/marqueeCom/marqueeCom.vue
1 1 <template>
2 2 <div class="marquee-com" v-if="lists && lists.length">
3   - <div class="box" ref="marqueesBox">
  3 + <div class="box" ref="marqueeBox" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
4 4  
5 5 <!-- 判断是否有Clone数组 -->
6 6 <template v-if="listsClone && listsClone.length">
... ... @@ -70,6 +70,17 @@
70 70 props: ['lists', 'com', 'pid', 'options'],
71 71 data () {
72 72 return {
  73 + control: {
  74 + distance: 0, // 位移量
  75 + sub: null, // 追加第几个数组数据下标
  76 + timer: 30, // 检查配置选项,设置轮训时间
  77 + interval: null, // 轮训函数对象id
  78 + itemWidth: 0 // 滚动项目item宽度
  79 + },
  80 + targetTouches: {
  81 + ox: null, // 上一次的鼠标X位置
  82 + nx: null // 当前的鼠标X位置
  83 + },
73 84 listsClone: [] // lists深度拷贝对象
74 85 }
75 86 },
... ... @@ -84,47 +95,41 @@
84 95 /**
85 96 * 跑马灯无限循环移动函数
86 97 * @param {Object} itemWidth [元素宽度,用户处理添加/删除元素逻辑]
87   - * @param {Object} marqueesBox [列表元素DOM对象]
  98 + * @param {Object} marqueeBox [列表元素DOM对象]
88 99 */
89   - move(itemWidth, marqueesBox) {
90   -
91   - // 位移量
92   - let distance = 0;
93   -
94   - // 追加第几个数组数据下标
95   - let sub = null;
  100 + move(itemWidth, marqueeBox) {
96 101  
97 102 // 检查配置选项,设置轮训时间
98   - let timer = (this.options && this.options.timer && this.options.timer > 0) ? this.options.timer : 30;
  103 + this.$set(this.control, 'timer', (this.options && this.options.timer && this.options.timer > 0) ? this.options.timer : 30);
99 104  
100 105 // 设置位移
101   - let interval = setInterval(() => {
  106 + this.control.interval = setInterval(() => {
102 107  
103 108 // 计算位移量
104   - distance = distance - 1;
  109 + this.$set(this.control, 'distance', this.control.distance - 1);
105 110  
106   - // 向左移动
107   - marqueesBox.style.transform = 'translateX(' + distance + 'px)';
  111 + // 设置移动
  112 + marqueeBox.style.transform = 'translateX(' + this.control.distance + 'px)';
108 113  
109 114 // 判断添加/删除元素
110   - if (!(distance % itemWidth)) {
  115 + if (!(this.control.distance % itemWidth)) {
111 116  
112 117 // 判断应该追加第几个数组数据下标
113   - if (!sub) {
114   - sub = 0;
  118 + if (!this.control.sub) {
  119 + this.$set(this.control, 'sub', 0);
115 120 }
116 121  
117 122 // 追加数据到listsClone数组
118   - this.$set(this, 'listsClone', [...this.listsClone, ...[this.lists[sub]]]);
  123 + this.$set(this, 'listsClone', [...this.listsClone, ...[this.lists[this.control.sub]]]);
119 124  
120 125 // 判断数组下标是否越界
121   - if (sub < this.lists.length - 1) {
122   - sub++;
  126 + if (this.control.sub < this.lists.length - 1) {
  127 + this.$set(this.control, 'sub', this.control.sub + 1);
123 128 } else {
124   - sub = 0;
  129 + this.$set(this.control, 'sub', 0);
125 130 }
126 131 }
127   - }, timer);
  132 + }, this.control.timer);
128 133 },
129 134  
130 135 /**
... ... @@ -149,7 +154,92 @@
149 154 */
150 155 primary(item) {
151 156 this.$emit("primary", item);
152   - }
  157 + },
  158 +
  159 + /**
  160 + * touchstart事件
  161 + */
  162 + touchstart(e) {
  163 +
  164 + // 临时,屏蔽事件操作
  165 + return false;
  166 +
  167 + // 判断事件类型
  168 + if (e.type === 'touchstart') {
  169 +
  170 + // 暂停列表轮训
  171 + clearInterval(this.control.interval);
  172 + }
  173 + },
  174 +
  175 + /**
  176 + * touchmove事件
  177 + */
  178 + touchmove(e) {
  179 +
  180 + // 临时,屏蔽事件操作
  181 + return false;
  182 +
  183 + // 判断事件类型
  184 + if (e.type === 'touchmove') {
  185 +
  186 + // 判断是不是第一次touchmove
  187 + if (this.targetTouches.ox === null && this.targetTouches.nx === null) {
  188 +
  189 + // 初始化touchmove事件的X坐标
  190 + this.$set(this.targetTouches, 'ox', parseInt(e.targetTouches[0].clientX));
  191 + this.$set(this.targetTouches, 'nx', parseInt(e.targetTouches[0].clientX));
  192 + } else {
  193 +
  194 + // 设置新的nx坐标
  195 + this.$set(this.targetTouches, 'nx', parseInt(e.targetTouches[0].clientX));
  196 +
  197 + // 判断distance要 <= 0
  198 + if (this.control.distance <= 0) {
  199 +
  200 + // 坐标移动差值
  201 + let diff = this.targetTouches.nx - this.targetTouches.ox;
  202 +
  203 + // 执行向左移动
  204 + this.$set(this.control, 'distance', this.control.distance + diff);
  205 +
  206 + // 设置移动
  207 + this.$refs.marqueeBox.style.transform = 'translateX(' + this.control.distance + 'px)';
  208 + }
  209 +
  210 + // 更新ox坐标,将当前nx坐标赋值给ox坐标,并且清除nx坐标值
  211 + this.$set(this.targetTouches, 'ox', parseInt(e.targetTouches[0].clientX));
  212 + this.$set(this.targetTouches, 'nx', null);
  213 + }
  214 + }
  215 + },
  216 +
  217 + /**
  218 + * touchend事件
  219 + */
  220 + touchend(e) {
  221 +
  222 + // 临时,屏蔽事件操作
  223 + return false;
  224 +
  225 + // 判断事件类型
  226 + if (e.type === 'touchend') {
  227 +
  228 + // 设置touchstart初始坐标点
  229 + this.$set(this.targetTouches, 'ox', null);
  230 + this.$set(this.targetTouches, 'nx', null);
  231 +
  232 + // 暂停列表轮训
  233 + clearInterval(this.control.interval);
  234 +
  235 + // 延时两秒
  236 + setTimeout(() => {
  237 +
  238 + // 跑马灯无限循环移动函数
  239 + this.move(this.control.itemWidth, this.$refs.marqueeBox);
  240 + }, 2000);
  241 + }
  242 + }
153 243 },
154 244 mounted() {
155 245  
... ... @@ -177,16 +267,16 @@
177 267 this.$nextTick(() => {
178 268  
179 269 // 获取item宽度
180   - let itemWidth = this.$refs.items[0].clientWidth;
  270 + this.$set(this.control, 'itemWidth', this.$refs.items[0].clientWidth);
181 271  
182 272 // 判断是否获取到item宽度
183   - if (itemWidth) {
  273 + if (this.control.itemWidth) {
184 274  
185 275 // 延时2秒再开始滚动
186 276 setTimeout(() => {
187 277  
188 278 // 跑马灯无限循环移动函数
189   - this.move(itemWidth, this.$refs.marqueesBox);
  279 + this.move(this.control.itemWidth, this.$refs.marqueeBox);
190 280 }, 2000);
191 281 }
192 282 });
... ...
src/components/swiperBox/swiperBox.less 0 → 100644
  1 +@charset "UTF-8";
  2 +
  3 +.swiper-box {
  4 + position: relative;
  5 + width: 750px;
  6 + height: 380px;
  7 + overflow: hidden;
  8 + .swipe {
  9 + margin: 0 auto;
  10 + width: 690px;
  11 + height: 100%;
  12 + .swipe-item {
  13 + width: 100%;
  14 + height: 100%;
  15 + .item {
  16 + display: flex;
  17 + justify-content: center;
  18 + align-items: center;
  19 + width: 100%;
  20 + height: 100%;
  21 + border-radius: 16px;
  22 + overflow: hidden;
  23 + .img-box {
  24 + width: 100%;
  25 + height: 100%;
  26 + overflow: hidden;
  27 + .img {
  28 + display: block;
  29 + width: 100%;
  30 + height: 100%;
  31 + border-radius: 16px;
  32 + object-fit: cover;
  33 + overflow: hidden;
  34 + }
  35 + }
  36 + }
  37 + }
  38 + .custom-indicator {
  39 + width: 74px;
  40 + height: 38px;
  41 + line-height:38px;
  42 + background: #848484;
  43 + border-radius: 20px;
  44 + // opacity: 0.45;
  45 + position: absolute;
  46 + right: 30px;
  47 + bottom: 30px;
  48 + font-size: 22px;
  49 + color: #fff;
  50 + text-align: center;
  51 + }
  52 + /deep/ .van-swipe__indicators {
  53 + left: unset;
  54 + right: 26px;
  55 + bottom: 20px;
  56 + .van-swipe__indicator {
  57 + background-color: rgba(255, 255, 255, 0.5);
  58 + width: 8px;
  59 + height: 8px;
  60 + margin: 0 4px;
  61 + opacity: unset;
  62 + border-radius: 50%;
  63 + &.van-swipe__indicator--active {
  64 + background:rgba(252,21,65,1);
  65 + width: 16px;
  66 + border-radius: 4px;
  67 + }
  68 + }
  69 + }
  70 + .play-button-box {
  71 + position: absolute;
  72 + top: 0;
  73 + left: 0;
  74 + z-index: 1;
  75 + display: flex;
  76 + justify-content: center;
  77 + align-items: center;
  78 + width: 100%;
  79 + height: 100%;
  80 + .play-button-img {
  81 + display: block;
  82 + width: 100px;
  83 + height: 100px;
  84 + }
  85 + }
  86 + }
  87 +}
0 88 \ No newline at end of file
... ...
src/components/swiperBox/swiperBox.vue 0 → 100644
  1 +<template>
  2 + <div class="swiper-box" v-if="swipers && swipers.length">
  3 + <swipe class="swipe" :autoplay="0" :touchable="swipers.length === 1 ? false : true" :show-indicators="options && options.showIndicators !== undefined ? options.showIndicators : showIndicators" @change="onChange">
  4 + <swipe-item class="swipe-item" v-for="(item, index) in swipers" :key="index">
  5 + <div class="item" @click="jump(item)">
  6 + <div class="img-box">
  7 + <img v-lazy="item.simg + SIGN.BIG" class="img" />
  8 + </div>
  9 + </div>
  10 +
  11 + <!-- 展示播放按钮 -->
  12 + <div @click="play(item)" v-if="item.type == 22" class="play-button-box">
  13 + <img src="static/img/play_dark.png" class="play-button-img" />
  14 + </div>
  15 + </swipe-item>
  16 + </swipe>
  17 + </div>
  18 +</template>
  19 +
  20 +<script>
  21 +
  22 + // 引入组件
  23 + import { Swipe, SwipeItem } from "vant";
  24 +
  25 + export default {
  26 + name: 'swiperBox',
  27 + data () {
  28 + return {
  29 + showIndicators: true, // 是否显示定位图例
  30 + currentIndex: 0 // 当前展示的swiper索引
  31 + }
  32 + },
  33 + props: ['swipers', 'options'],
  34 + components: {
  35 + Swipe,
  36 + SwipeItem
  37 + },
  38 + methods: {
  39 +
  40 + /**
  41 + * 当swiper切换
  42 + * @param {Int} index [切换swiper索引]
  43 + */
  44 + onChange(index) {
  45 +
  46 + // 设置当前展示的swiper索引
  47 + this.$set(this, 'currentIndex', index);
  48 +
  49 + // 抛出事件
  50 + this.$emit("onChange", index);
  51 + },
  52 +
  53 + /**
  54 + * swiper跳转事件
  55 + */
  56 + jump(item) {
  57 +
  58 + // 抛出事件
  59 + this.$emit('jump', item);
  60 + },
  61 +
  62 + /**
  63 + * 播放视频
  64 + * @param {Object} item [数据对象]
  65 + */
  66 + play(item) {
  67 +
  68 + // 抛出事件
  69 + this.$emit('play', item);
  70 + }
  71 + }
  72 + }
  73 +</script>
  74 +
  75 +<!-- Add "scoped" attribute to limit CSS to this component only -->
  76 +<style rel="stylesheet/less" lang="less" scoped>
  77 + @import 'swiperBox';
  78 +</style>
0 79 \ No newline at end of file
... ...
src/pages/shopList/shopList.less 0 → 100644
  1 +@charset "UTF-8";
  2 +
  3 +.shop-list-page {
  4 + background-color: #ffffff;
  5 + min-height: 100vh;
  6 + .nav-box {
  7 + position: fixed;
  8 + top: 0;
  9 + z-index: 100;
  10 + background-color: #ffffff;
  11 + width: 100%;
  12 + height: auto;
  13 + .call-app-box {
  14 + width: 100%;
  15 + }
  16 + .back-box {
  17 + background-color: #ffffff;
  18 + width: 100%;
  19 + border-bottom: 1px solid #E5E5E5;
  20 + }
  21 + }
  22 + .pl {
  23 + background-color: transparent;
  24 + width: 100%;
  25 + &.is-open-app {
  26 + height: 122px;
  27 + }
  28 + &.is-cnlive {
  29 + height: 93px;
  30 + }
  31 + &.is-hide {
  32 + height: 0px;
  33 + }
  34 + &.tabsFixed {
  35 + &.is-open-app {
  36 + height: 212px;
  37 + }
  38 + &.is-cnlive {
  39 + height: 185px;
  40 + }
  41 + &.is-hide {
  42 + height: 90px;
  43 + }
  44 + }
  45 + }
  46 + .main-box {
  47 + .section {
  48 + width: 690px;
  49 + &.banner-box {
  50 + margin-top: 30px;
  51 + }
  52 + }
  53 + .tabs-box {
  54 + background-color: #ffffff;
  55 + width: 100%;
  56 + height: 90px;
  57 + // border-top: 1px solid transparent;
  58 + border-bottom: 1px solid transparent;
  59 + &.tabsFixed {
  60 + position: fixed;
  61 + top: 0;
  62 + z-index: 100;
  63 + // border-top: 1px solid #E5E5E5;
  64 + border-bottom: 1px solid #E5E5E5;
  65 + &.is-open-app {
  66 + top: 122px;
  67 + }
  68 + &.is-cnlive {
  69 + top: 95px;
  70 + }
  71 + &.is-hide {
  72 + top: 0;
  73 + }
  74 + }
  75 + }
  76 + .column-list-container {
  77 + padding-top: 30px;
  78 + .section {
  79 + // background-color: #ffffff;
  80 + margin: 0 auto 40px auto;
  81 + width: 690px;
  82 + height: auto;
  83 + border-radius: 24px;
  84 + overflow: hidden;
  85 + &:last-child {
  86 + margin-bottom: 0;
  87 + }
  88 + &.goods-list {
  89 + .column-list-box {
  90 + /deep/ .item-box {
  91 + border: 1px solid #F1F0F0;
  92 + border-radius: 16px;
  93 + }
  94 + }
  95 + .discount-flag {
  96 + position: absolute;
  97 + top: 10px;
  98 + left: 10px;
  99 + z-index: 1;
  100 + background: linear-gradient(90deg, #F9EE7A 0%, #FAD844 100%);
  101 + padding: 1px 8px;
  102 + color: #945A08;
  103 + font-size: 20px;
  104 + font-weight: 400;
  105 + border-radius: 6px;
  106 + }
  107 + }
  108 + }
  109 + /deep/ .van-list__loading {
  110 + .van-loading {
  111 + .van-loading__spinner {
  112 + width: 28px !important;
  113 + height: 28px !important;
  114 + }
  115 + .van-list__loading-text {
  116 + margin-bottom: 30px;
  117 + color: #333333;
  118 + font-size: 28px;
  119 + font-weight: 400;
  120 + }
  121 + }
  122 + }
  123 + /deep/ .van-list__finished-text {
  124 + margin-bottom: 30px;
  125 + color: #333333;
  126 + font-size: 28px;
  127 + font-weight: 400;
  128 + }
  129 + }
  130 + .bottom-pl {
  131 + background-color: transparent;
  132 + width: 100%;
  133 + // height: 100px;
  134 + height: 1px;
  135 + }
  136 + }
  137 +}
... ...
src/pages/shopList/shopList.vue 0 → 100644
  1 +<template>
  2 + <div class="shop-list-page" v-if="tabs.list[tabs.active]">
  3 +
  4 + <!-- 导航区域 -->
  5 + <div class="nav-box" :style="'padding-top: ' + $parent.top.value + 'px'" ref='navBox'>
  6 +
  7 + <!-- H5唤起App组件 -->
  8 + <call-app-box class="call-app-box" ref="callApp" v-if="!isCNLive.value && $parent.isOpenApp.value"
  9 + :callApps="$parent.callApps">
  10 + </call-app-box>
  11 +
  12 + <!-- 后退导航 -->
  13 + <back-box class="back-box" :class="{'hide': $route.query.hideBack == true}" v-if="isCNLive.value" @back="back"
  14 + :backs="backs">
  15 + </back-box>
  16 + </div>
  17 +
  18 + <!-- pl -->
  19 + <div class="pl" :class="{'is-cnlive': isCNLive.value, 'is-open-app': $parent.isOpenApp.value, 'is-hide': $route.query.hideBack == true, 'tabsFixed': tabs.fixed}" :style="'padding-top: ' + $parent.top.value + 'px'"></div>
  20 +
  21 + <!-- 主区域 -->
  22 + <div class="main-box">
  23 +
  24 + <!-- 轮播组件 -->
  25 + <div class="section banner-box" v-if="swipers && swipers.length">
  26 +
  27 + <!-- 轮播组件 -->
  28 + <swiper-box @jump="swiperJump"
  29 + :swipers="swipers"
  30 + :options="{
  31 + showIndicators: true
  32 + }">
  33 + </swiper-box>
  34 + </div>
  35 +
  36 + <!-- tab导航 -->
  37 + <div class="tabs-box" :class="{'is-cnlive': isCNLive.value, 'is-open-app': $parent.isOpenApp.value, 'is-hide': $route.query.hideBack == true, 'tabsFixed': tabs.fixed}" ref='tabsBox'>
  38 +
  39 + <!-- 横向滑动tabs导航组件 -->
  40 + <horiz-list v-if="tabs.list && tabs.list.length" @change="change"
  41 + :lists="tabs.list"
  42 + com="tab2"
  43 + :pid="tabs.active">
  44 + </horiz-list>
  45 + </div>
  46 +
  47 + <!-- 上拉加载 -->
  48 + <van-list class="column-list-container" v-model="tabs.list[tabs.active].loading" :finished="tabs.list[tabs.active].finished" finished-text="~没有更多了~" @load="loadMore">
  49 +
  50 + <!-- 商品列表 -->
  51 + <div class="section goods-list" v-if="lists[tabs.active] && lists[tabs.active].length">
  52 +
  53 + <!-- 商品样式 7 列表 -->
  54 + <column-list-box class="column-list-box" @jump="toDetail" @primary="primary"
  55 + :lists="lists[tabs.active]"
  56 + com='7'
  57 + goodType='2'
  58 + flag=''>
  59 + </column-list-box>
  60 + </div>
  61 + </van-list>
  62 +
  63 + <div class="bottom-pl"></div>
  64 + </div>
  65 +
  66 + <!-- 回到顶部按钮 -->
  67 + <goto-top
  68 + :top="parseInt(150)">
  69 + </goto-top>
  70 + </div>
  71 +</template>
  72 +
  73 +<script>
  74 +
  75 + // 引入组件
  76 + import Vue from "vue";
  77 + import { Toast, Indicator } from "mint-ui";
  78 + import { List } from "vant";
  79 + import callAppBox from "@/components/callAppBox/callAppBox";
  80 + import backBox from "@/components/backBox/backBox";
  81 + import swiperBox from "@/components/swiperBox/swiperBox";
  82 + import horizList from "@/components/horizList/horizList";
  83 + import columnListBox from "@/components/columnListBox/columnListBox";
  84 + import gotoTop from "@/components/gotoTop/gotoTop";
  85 +
  86 + Vue.use(List);
  87 +
  88 + export default {
  89 + name: 'shopListPage',
  90 + data () {
  91 + return {
  92 + title: '', // 标题
  93 + backs: null, // 后退按钮组
  94 + shareParams: {}, // 分享参数
  95 + CALL_STATUS: {
  96 + INIT: "init", // 初始化页面
  97 + LOAD_MORE: "loadMore" // 加载更多
  98 + },
  99 + swipers: null, // 主Banner
  100 + tabs: { // tabs设置
  101 + fixed: false, // 是否吸顶
  102 + offsetTop: 0, // 距离顶部距离
  103 + active: 0, // 活动项
  104 + init: false, // tabs数据是否初始化过,防止重复设置tabs列表
  105 + list: [ // tabs数组
  106 + {
  107 + id: 0,
  108 + title: '全部',
  109 + page: 1,
  110 + loading: false,
  111 + loadingNow: false,
  112 + finished: false
  113 + }
  114 + ]
  115 + },
  116 + lists: [] // 列表数据
  117 + }
  118 + },
  119 + components: {
  120 + callAppBox,
  121 + backBox,
  122 + swiperBox,
  123 + horizList,
  124 + columnListBox,
  125 + gotoTop
  126 + },
  127 + computed: {
  128 +
  129 + /**
  130 + * 获取输入数字的整数部分
  131 + */
  132 + getInteger () {
  133 + return (number) => {
  134 + return parseInt(number);
  135 + }
  136 + },
  137 +
  138 + /**
  139 + * 获取输入数字的小数部分
  140 + */
  141 + getDecimal () {
  142 + return (number) => {
  143 +
  144 + // 拆分数字
  145 + let numberArr = number.toString().split(".");
  146 +
  147 + // 判断是否有效数部分
  148 + if (numberArr.length === 2) {
  149 + return numberArr[1];
  150 + } else {
  151 + return "00";
  152 + }
  153 + }
  154 + }
  155 + },
  156 + created() {
  157 +
  158 + // 初始化页面
  159 + this.initPage();
  160 +
  161 + // 判断是否在App内
  162 + if (this.isCNLive.value) {
  163 +
  164 + // 添加后退按钮组“后退”和“分享”按钮
  165 + this.$set(this, 'backs', {
  166 + funcs: {
  167 + back: {
  168 + icon: "static/img/icon_back.png"
  169 + },
  170 + funcArray: [
  171 + {
  172 + icon: "static/img/icon_share.png",
  173 + func: this.toShare,
  174 + params: this.shareParams
  175 + }
  176 + ]
  177 + }
  178 + });
  179 + }
  180 +
  181 + // 获取页面头部数据
  182 + this.ghIndex();
  183 +
  184 + // 工会商品列表(初始化)
  185 + this.ghGoodsList(this.CALL_STATUS.INIT);
  186 + },
  187 + methods: {
  188 +
  189 + /**
  190 + * 初始化页面
  191 + */
  192 + initPage() {
  193 +
  194 + // 设置分享参数
  195 + this.$set(this, 'shareParams', {
  196 + type: 60,
  197 + id: this.$route.query.id || 1
  198 + });
  199 + },
  200 +
  201 + /**
  202 + * 加载更多
  203 + */
  204 + loadMore() {
  205 +
  206 + // 判断是否可加载
  207 + if (this.tabs.list[this.tabs.active].loadingNow === false || this.tabs.list[this.tabs.active].loadingNow === undefined) {
  208 +
  209 + // 设置加载中状态
  210 + this.$set(this.tabs.list[this.tabs.active], 'loadingNow', true);
  211 +
  212 + if (this.tabs.list[this.tabs.active].page == '1' || this.tabs.list[this.tabs.active].page === undefined) {
  213 +
  214 + // 设置初始化页码
  215 + this.$set(this.tabs.list[this.tabs.active], 'page', 1);
  216 +
  217 + // 工会商品列表
  218 + this.ghGoodsList(this.CALL_STATUS.INIT);
  219 + } else {
  220 +
  221 + // 判断页面是数字再执行
  222 + if (!isNaN(parseInt(this.tabs.list[this.tabs.active].page))) {
  223 +
  224 + // 工会商品列表
  225 + this.ghGoodsList(this.CALL_STATUS.LOAD_MORE);
  226 + }
  227 + }
  228 + }
  229 + },
  230 +
  231 + /**
  232 + * 切换tab
  233 + * @param {Object} item [对象数据]
  234 + * @param {Number} index [索引]
  235 + */
  236 + change(item, index) {
  237 +
  238 + // 切换前检查网络状态
  239 + if (this.$parent.onLine) {
  240 +
  241 + // 切换tab
  242 + this.$set(this.tabs, 'active', index);
  243 +
  244 + // 根据参数判断请求哪个接口,获取哪个列表
  245 + if (this.tabs.list[this.tabs.active].type == '1' && (!this.tabs.list[this.tabs.active].page || this.tabs.list[this.tabs.active].page == 1)) {
  246 +
  247 + // 工会商品列表(初始化)
  248 + this.ghGoodsList(this.CALL_STATUS.INIT);
  249 + }
  250 +
  251 + // 回到顶部
  252 + this.gotoTop();
  253 + } else {
  254 +
  255 + // 网络异常提醒
  256 + Toast("网络异常,请检查网络");
  257 + }
  258 + },
  259 +
  260 + /**
  261 + * 回到顶部
  262 + */
  263 + gotoTop() {
  264 + document.documentElement.scrollTop = document.body.scrollTop = 0;
  265 + },
  266 +
  267 + /**
  268 + * 获取页面头部数据
  269 + */
  270 + ghIndex() {
  271 +
  272 + // 判断必要参数
  273 + if (this.$route.query.ghId) {
  274 +
  275 + // 加载中提示
  276 + Indicator.open("加载中...");
  277 +
  278 + // 获取证书请求接口
  279 + this.http("/Api/" + (this.getApiVersion().index) + "/ghIndex", {
  280 + id: this.$route.query.ghId,
  281 + uid: (this.$route.query.uid || 0)
  282 + }, this.ghIndexCallback, {
  283 + method: "POST"
  284 + });
  285 + }
  286 + },
  287 +
  288 + /**
  289 + * 获取页面头部数据回调
  290 + * @param {Object} res [服务器返回数据]
  291 + * @param {Object} ext [扩展参数]
  292 + */
  293 + ghIndexCallback(res, ext) {
  294 +
  295 + // 加载完成
  296 + Indicator.close();
  297 +
  298 + // 返回处理
  299 + if (res.code == 200) {
  300 +
  301 + // 解构数据
  302 + let { data: datas } = res;
  303 +
  304 + /* 设置首页数据 开始 */
  305 +
  306 + // 判断是否为首次加载
  307 + if (!this.tabs.init) {
  308 +
  309 + // 设置标题
  310 + this.$set(this, 'title', datas.title);
  311 +
  312 + // 判断是否在App内
  313 + if (this.isCNLive.value) {
  314 +
  315 + // 设置nav标题
  316 + this.$set(this.backs, 'title', this.title);
  317 + }
  318 +
  319 + // 获取头部swiper的banner
  320 +
  321 +
  322 +
  323 + // 临时,测试swiper
  324 + this.$set(this, 'swipers', [
  325 + {
  326 + id: "251",
  327 + shop_type: "1",
  328 + simg: "https://wjjtest.ys2.cnliveimg.com/802/shop/img/2019/09/05/5d70b6ec83d77.jpg",
  329 + title: "123123321",
  330 + to: "https://wjjshop.cnlive.com/html/vaccines/2/#/pages/main/detail/detail?id=14",
  331 + type: "5"
  332 + },
  333 + {
  334 + id: "252",
  335 + shop_type: "",
  336 + simg: "https://wjjtest.ys2.cnliveimg.com/802/shop/img/2019/09/05/5d70b7546f20f.jpg",
  337 + title: "达人",
  338 + to: "daren_10455685",
  339 + type: "11"
  340 + }
  341 + ]);
  342 +
  343 +
  344 +
  345 +
  346 +
  347 +
  348 +
  349 +
  350 + // 获取tabs数据
  351 + if (datas.list && datas.list.length) {
  352 +
  353 + // 合并数组
  354 + this.$set(this.tabs, 'list', [...this.tabs.list, ...datas.list]);
  355 + }
  356 +
  357 + // 设置为已加载过,不重复加载重复数据
  358 + this.$set(this.tabs, 'init', true);
  359 + }
  360 +
  361 + /* 设置首页数据 结束 */
  362 +
  363 + } else if (res.code == 400) {
  364 +
  365 + // 获取数据失败
  366 + Toast(res.message);
  367 + } else {
  368 +
  369 + // 获取数据失败
  370 + Toast("获取数据失败");
  371 + }
  372 + },
  373 +
  374 + /**
  375 + * 工会商品列表
  376 + * @param {Object} CALL_STATUS [何处触发请求]
  377 + */
  378 + ghGoodsList(CALL_STATUS = this.CALL_STATUS.LOAD_MORE) {
  379 +
  380 + // 判断必要参数
  381 + if (this.$route.query.ghId && this.tabs.list[this.tabs.active].id >= 0) {
  382 +
  383 + // 设置开始加载
  384 + this.$set(this.tabs.list[this.tabs.active], "loading", true);
  385 +
  386 + // 加载中提示
  387 + Indicator.open('加载中...');
  388 +
  389 + // 请求接口
  390 + this.http("/Api/" + (this.getApiVersion().index) + "/ghGoodsList", {
  391 + id: this.$route.query.ghId,
  392 + group_id: this.tabs.list[this.tabs.active].id,
  393 + page: this.tabs.list[this.tabs.active].page
  394 + }, this.ghGoodsListCallback, {
  395 + method: "POST",
  396 + CALL_STATUS
  397 + });
  398 + }
  399 + },
  400 +
  401 + /**
  402 + * 工会商品列表回调
  403 + * @param {Object} res [服务器返回数据]
  404 + * @param {Object} ext [扩展参数]
  405 + */
  406 + ghGoodsListCallback(res, ext) {
  407 +
  408 + // 加载完成
  409 + Indicator.close();
  410 +
  411 + // 返回处理
  412 + if (res.code == 200) {
  413 +
  414 + // 解构数据
  415 + let {data: datas} = res;
  416 +
  417 + // 判断如果有数据
  418 + if (datas.list && datas.list.length) {
  419 +
  420 + /* 设置列表数据 开始 */
  421 + switch (ext.CALL_STATUS) {
  422 + case this.CALL_STATUS.INIT:
  423 +
  424 + // 设置推荐商品列表
  425 + this.$set(this.lists, this.tabs.active, datas.list);
  426 + break;
  427 + case this.CALL_STATUS.LOAD_MORE:
  428 +
  429 + // 添加数据到数组
  430 + this.$set(this.lists, this.tabs.active, [...this.lists[this.tabs.active], ...datas.list]);
  431 + break;
  432 + }
  433 +
  434 + // 设置页码
  435 + this.$set(this.tabs.list[this.tabs.active], 'page', ++this.tabs.list[this.tabs.active].page);
  436 +
  437 + // 取消Loading中状态,恢复LoadingMore功能
  438 + this.$set(this.tabs.list[this.tabs.active], 'loading', false);
  439 + this.$set(this.tabs.list[this.tabs.active], 'loadingNow', false);
  440 + this.$set(this.tabs.list[this.tabs.active], 'finished', false);
  441 + } else {
  442 +
  443 + // 取消Loading中状态,恢复LoadingMore功能
  444 + this.$set(this.tabs.list[this.tabs.active], 'loading', false);
  445 + this.$set(this.tabs.list[this.tabs.active], 'loadingNow', false);
  446 + this.$set(this.tabs.list[this.tabs.active], 'finished', true);
  447 + }
  448 +
  449 + /* 设置列表数据 结束 */
  450 +
  451 + } else if (res.code == 400) {
  452 +
  453 + // 获取商品出错
  454 + Toast(res.message);
  455 + } else {
  456 +
  457 + // 获取商品出错
  458 + Toast("获取商品出错");
  459 + }
  460 + },
  461 +
  462 + /**
  463 + * swiper跳转事件
  464 + * @param {Object} item [数据对象]
  465 + */
  466 + swiperJump(item) {
  467 +
  468 + // 判断在App内
  469 + if (this.isCNLive.value) {
  470 +
  471 + // 判断通用跳转函数是否存在
  472 + if (typeof this.$parent.gotoDetail === 'function') {
  473 +
  474 + // 跳转参数
  475 + let params = {
  476 + type: item.type,
  477 + to: item.id,
  478 + shop_type: item.shop_type
  479 + };
  480 +
  481 + // 通用跳转
  482 + this.$parent.gotoDetail(params);
  483 + }
  484 + } else {
  485 +
  486 + // App外,唤起App
  487 + this.openApp();
  488 + }
  489 + },
  490 +
  491 + /**
  492 + * 跳转商品详情
  493 + * @param {Object} item [数据对象]
  494 + */
  495 + toDetail(item) {
  496 +
  497 + // 判断在App内
  498 + if (this.isCNLive.value) {
  499 +
  500 + // 判断通用跳转函数是否存在
  501 + if (typeof this.$parent.gotoDetail === 'function') {
  502 +
  503 + // 跳转参数
  504 + let params = {
  505 + type: '2',
  506 + to: item.id,
  507 + shop_type: item.shop_type
  508 + };
  509 +
  510 + // 通用跳转
  511 + this.$parent.gotoDetail(params);
  512 + }
  513 + } else {
  514 +
  515 + // App外,唤起App
  516 + this.openApp();
  517 + }
  518 + },
  519 +
  520 + /**
  521 + * 项目按钮事件
  522 + * @param {Object} item [数据对象]
  523 + */
  524 + primary(item) {
  525 +
  526 + // 跳转商品详情
  527 + this.toDetail(item);
  528 + },
  529 +
  530 + /**
  531 + * 播放视频
  532 + * @param {String} videoUrl [数据对象]
  533 + */
  534 + playNow(videoUrl) {
  535 +
  536 + // 判断在App内
  537 + if (this.isCNLive.value) {
  538 +
  539 + // 判断通用跳转函数是否存在
  540 + if (typeof this.$parent.gotoDetail === 'function') {
  541 +
  542 + // 设置跳转参数
  543 + let params = {
  544 + type: '22',
  545 + to: item.to,
  546 + shop_type: ''
  547 + };
  548 +
  549 + // 通用跳转
  550 + this.$parent.gotoDetail(params);
  551 + }
  552 + } else {
  553 +
  554 + // App外,唤起App
  555 + this.openApp();
  556 + }
  557 + },
  558 +
  559 + /**
  560 + * 监听函数,设置滚动条new位置
  561 + */
  562 + scrollEventListenerFun() {
  563 +
  564 + // 下一周期执行
  565 + this.$nextTick(() => {
  566 +
  567 + // 判断组件是否存在
  568 + if (typeof this.$refs.tabsBox === 'object') {
  569 +
  570 + // 判断tabs的offsetTop是否设置
  571 + if (this.tabs.offsetTop == 0) {
  572 +
  573 + // 设置tabs原始位置
  574 + this.$set(this.tabs, 'offsetTop', this.$refs.tabsBox.offsetTop);
  575 + }
  576 +
  577 + // 判断是否有标题栏
  578 + if (typeof this.$refs.navBox === 'object') {
  579 +
  580 + // 设置tabsBox样式,如果window.scrollY >= tabsBox位置,tabsBox吸顶,否则不吸顶
  581 + this.$set(this.tabs, 'fixed', window.scrollY + (this.$refs.navBox.offsetHeight) >= this.tabs.offsetTop);
  582 + } else {
  583 +
  584 + // 设置tabsBox样式,如果window.scrollY >= tabsBox位置,tabsBox吸顶,否则不吸顶
  585 + this.$set(this.tabs, 'fixed', window.scrollY >= this.tabs.offsetTop);
  586 + }
  587 + }
  588 + });
  589 + },
  590 +
  591 + /**
  592 + * 设置滚动条监听
  593 + */
  594 + handleScroll() {
  595 + window.addEventListener("scroll", this.scrollEventListenerFun);
  596 + },
  597 +
  598 + /**
  599 + * 唤起App封装
  600 + */
  601 + openApp() {
  602 +
  603 + this.$nextTick(() => {
  604 +
  605 + // 唤起App
  606 + if (typeof this.$parent.callAppModal === 'function' && typeof this.$refs.callApp === 'object') {
  607 +
  608 + // 设置唤起参数
  609 + let options = {
  610 + success: this.$refs.callApp.callApp
  611 + }
  612 +
  613 + // 唤起App
  614 + this.$parent.callAppModal(options);
  615 + }
  616 + });
  617 + },
  618 +
  619 + /**
  620 + * 分享
  621 + */
  622 + toShare() {
  623 +
  624 + // 判断分享
  625 + if (typeof this.$parent.toShare === 'function' && typeof this.shareParams === 'object' && this.shareParams.type) {
  626 + this.$parent.toShare(this.shareParams.type, this.shareParams.id || 1);
  627 + }
  628 + },
  629 +
  630 + /**
  631 + * 后退
  632 + */
  633 + back() {
  634 +
  635 + // 判断是否有处理函数
  636 + if (typeof this.$parent.back === 'function') {
  637 +
  638 + // 后退
  639 + this.$parent.back();
  640 + } else {
  641 +
  642 + // 后退
  643 + window.history.go(-1);
  644 + }
  645 + }
  646 + },
  647 + head: {
  648 + title () {
  649 + return {
  650 + inner: this.title
  651 + }
  652 + }
  653 + },
  654 + watch: {
  655 +
  656 + // 设置页面标题
  657 + title(newVal) {
  658 + if (this.$store.state.browser.toLowerCase() == "qq" && this.$store.state.deviceType.toLowerCase() == "iphone") {
  659 + this.$iFrame.insertIFrame(newVal);
  660 + } else {
  661 + this.$emit("updateHead");
  662 + }
  663 + }
  664 + },
  665 + mounted() {
  666 +
  667 + // 如果是App外,设置唤起App和微信内分享参数
  668 + if (!this.isCNLive.value) {
  669 +
  670 + // 唤起详情页
  671 + if (typeof this.$parent.getOpenApp === "function" && typeof this.shareParams === 'object' && this.shareParams.type) {
  672 + this.$parent.getOpenApp({
  673 + type: this.shareParams.type,
  674 + id: this.shareParams.id || 1,
  675 + url: ""
  676 + });
  677 + }
  678 +
  679 + // 分享详情页
  680 + if (typeof this.$parent.getShareData === "function") {
  681 + this.$parent.getShareData({
  682 + type: this.shareParams.type,
  683 + id: this.shareParams.id || 1,
  684 + url: ""
  685 + });
  686 + }
  687 + }
  688 +
  689 + // 设置滚动条监听
  690 + this.handleScroll();
  691 +
  692 + // 页面切换可见状态监听
  693 + document.addEventListener('visibilitychange', () => {
  694 +
  695 + // 页面显示出来了
  696 + if (document.hidden === false && this.$route.name == 'shopList') {
  697 +
  698 + // 获取页面头部数据
  699 + // this.ghIndex();
  700 + }
  701 + });
  702 + }
  703 + }
  704 +</script>
  705 +
  706 +<!-- Add "scoped" attribute to limit CSS to this component only -->
  707 +<style rel="stylesheet/less" lang='less' scoped>
  708 + @import "shopList";
  709 +</style>
... ...
src/router/index.js
... ... @@ -142,6 +142,19 @@ const router = new Router({
142 142 }
143 143 },
144 144 {
  145 + // 新店铺首页(根据工会商品列表页改版)
  146 + path: "/shopList",
  147 + name: "shopList",
  148 + component: resolve => {
  149 + require([
  150 + "@/pages/shopList/shopList"
  151 + ], resolve);
  152 + },
  153 + meta: {
  154 + requireAuth: false
  155 + }
  156 + },
  157 + {
145 158 // 我的礼盒
146 159 path: "/myGiftBox",
147 160 name: "myGiftBox",
... ...