Преглед на файлове

Merge branch 'cps' into test

panyong преди 2 години
родител
ревизия
5ba9d06dde

+ 8 - 0
htmldev/cps/README.md

@@ -22,3 +22,11 @@ npm run lint
 
 ### Customize configuration
 See [Configuration Reference](https://cli.vuejs.org/config/).
+
+### todo list
+- [ ] 商家logo默认/选中2中状态
+- [ ] 测试省钱页跳转微信小程序
+- [x] 搜索页
+- [ ] 分享测试:默认、带有分享人信息、分享赚钱、商品列表及详情
+- [x] 商品详情页:淘宝复制口令
+- [x] 底部导航:新增购物

+ 3 - 1
htmldev/cps/src/App.vue

@@ -10,6 +10,7 @@
       v-model="active"
       v-if="$route.meta.showTabbar">
       <van-tabbar-item replace to="/" icon="wap-home-o" name="MarketingSave">首页</van-tabbar-item>
+      <van-tabbar-item to="/category" icon="shop-o" name="CategoryIndex">购物</van-tabbar-item>
       <van-tabbar-item replace to="/invite" icon="gold-coin-o" name="Invite">分享赚钱</van-tabbar-item>
       <van-tabbar-item replace to="/mine" icon="user-o" name="Mine">我的</van-tabbar-item>
     </van-tabbar>
@@ -36,7 +37,7 @@ export default {
   watch: {
     '$route.name': {
       handler: function (newVal) {
-        const condition = ['PaymentCode', 'ShareMiddle', 'CategoryIndex', 'CategoryDetail'].findIndex(item => item === newVal) === -1
+        const condition = ['PaymentCode', 'ShareMiddle', 'CategoryIndex', 'CategoryDetail', 'Search'].findIndex(item => item === newVal) === -1
         if (!newVal) {
           return
         }
@@ -63,6 +64,7 @@ export default {
 
 <style lang="scss">
 @import "./assets/styles/reset";
+@import "./assets/styles/shop.css";
 
 body {
   background: #F7F6F9;

+ 1 - 1
htmldev/cps/src/api/request.js

@@ -55,7 +55,7 @@ request.interceptors.request.use(request => {
 
   // 因为微信开发者工具重复授权,本地开发时写死
   if (/^(0|192|10|localhost)/.test(domain)) {
-    request.headers.wechatToken = '1ba0a97c52bcd02b03ab8e4d55e14740'
+    request.headers.wechatToken = '22aff98dec057448c59427e42065d3df'
   } else {
     request.headers.wechatToken = getCookieValue('fanbutingwechatToken')
   }

BIN
htmldev/cps/src/assets/fonts/shop.ttf


+ 41 - 0
htmldev/cps/src/assets/styles/shop.css

@@ -0,0 +1,41 @@
+@font-face {
+  font-family: "shopfont"; /* Project id  */
+  src: url('../fonts/shop.ttf?t=1637031976812') format('truetype');
+}
+
+.shopfont {
+  font-family: "shopfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.shopfont-shuangliebiao:before {
+  content: "\e6bb";
+}
+
+.shopfont-liebiao:before {
+  content: "\ec6b";
+}
+
+.shopfont-jingdong-:before {
+  content: "\e643";
+}
+
+.shopfont-ziyuan-:before {
+  content: "\e6a8";
+}
+
+.shopfont-pinduoduo1:before {
+  content: "\e602";
+}
+
+.shopfont-taobao:before {
+  content: "\e615";
+}
+
+.shopfont-kaolahaigou:before {
+  content: "\e627";
+}
+

+ 25 - 0
htmldev/cps/src/mixin/fbtClipboard.js

@@ -0,0 +1,25 @@
+import { Toast } from 'vant'
+import Clipboard from 'clipboard'
+
+export default {
+  directives: {
+    copy: {
+      inserted (el) {
+        const { clipboardToast, clipboardHide } = el.dataset
+        const obj = new Clipboard(el)
+        obj.on('success', () => {
+          if (clipboardHide === '1') {
+            return
+          }
+          Toast(clipboardToast || '复制成功')
+        })
+        obj.on('error', () => {
+          if (clipboardHide === '1') {
+            return
+          }
+          Toast('复制失败,请手动选择复制')
+        })
+      }
+    }
+  }
+}

+ 10 - 0
htmldev/cps/src/router/index.js

@@ -152,6 +152,7 @@ const routes = [
         name: 'CategoryIndex',
         component: _import('views/category/index/index'),
         meta: {
+          showTabbar: true,
           isUseCache: false,
           keepAlive: true
         }
@@ -166,6 +167,15 @@ const routes = [
         })
       }
     ]
+  },
+  {
+    path: '/search', // 搜索
+    name: 'Search',
+    component: _import('views/search/index'),
+    meta: {
+      isUseCache: false,
+      keepAlive: true
+    }
   }
 ]
 

+ 70 - 6
htmldev/cps/src/views/category/detail/index.vue

@@ -58,7 +58,18 @@
       </a>
       <a
         href="javascript:;"
-        @click="jumpShops">
+        data-clipboard-hide="1"
+        :data-clipboard-text="goodsInfo.url"
+        v-copy
+        @click="jumpShops"
+        v-if="source === 'taobao'">
+        <span>自购省</span>
+        <span>券¥{{ couponInfo.fav }} + ¥{{ goodsInfo.commission }}</span>
+      </a>
+      <a
+        href="javascript:;"
+        @click="jumpShops"
+        v-else>
         <span>自购省</span>
         <span>券¥{{ couponInfo.fav }} + ¥{{ goodsInfo.commission }}</span>
       </a>
@@ -66,6 +77,20 @@
     <!--弹窗:分享提示-->
     <ShareTipInWechat
       ref="fbtShareTipInWechat"/>
+    <van-popup
+      class="fbt-van-copy"
+      v-model="copyPopup"
+      round
+      closeable
+      position="bottom"
+      :style="{ height: '40%', background: '#f6f6f6' }">
+      <p class="title">淘口令已复制</p>
+      <p class="content">{{ goodsInfo.url }}</p>
+      <van-button
+        round
+        type="primary">去淘宝粘贴打开
+      </van-button>
+    </van-popup>
   </div>
 </template>
 
@@ -73,16 +98,19 @@
 import funWxShare from '@/utils/wxShare0.0'
 import Banner from './child/banner'
 import ShareTipInWechat from '../../common/shareTipInWechat'
-import { Button, Toast } from 'vant'
+import fbtClipboard from '../../../mixin/fbtClipboard'
+import { Button, Toast, Popup } from 'vant'
 import { apiGoodsDetail } from './api/api'
 import { platform } from '../../../utils/platform'
 
 export default {
   name: 'index',
+  mixins: [fbtClipboard],
   components: {
     Banner,
     ShareTipInWechat,
-    'van-button': Button
+    'van-button': Button,
+    'van-popup': Popup
   },
   props: {
     source: {
@@ -99,7 +127,8 @@ export default {
       goodsInfo: {},
       couponInfo: {}, // 优惠券相关
       goodsCarouselPictures: [], // 商品轮播图
-      goodsDetailPictures: [] // 商品详情图片
+      goodsDetailPictures: [], // 商品详情图片
+      copyPopup: false // 淘口令复制提示
     }
   },
   created () {
@@ -142,6 +171,10 @@ export default {
         Toast('数据加载中,稍后重试')
         return
       }
+      if (this.source === 'taobao') {
+        this.copyPopup = true
+        return
+      }
       top.location.href = this.goodsInfo.url
     }
   }
@@ -208,11 +241,10 @@ export default {
       font-size: 11px;
       font-weight: bold;
       color: #EA483F;
-      line-height: 18px;
+      line-height: 24px;
     }
 
     &:nth-of-type(2) {
-      padding-top: 2px;
       font-size: 14px;
       font-weight: 500;
       color: #EA483F;
@@ -347,4 +379,36 @@ export default {
     margin: 0 auto;
   }
 }
+
+.fbt-van-copy {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+
+  .title {
+    width: 100%;
+    padding: 30px 0 20px;
+    font-size: 18px;
+    font-weight: bold;
+    color: #333333;
+    line-height: 28px;
+    text-align: center;
+  }
+
+  .content {
+    width: 350px;
+    padding: 18px 15px;
+    border-radius: 18px;
+    font-size: 14px;
+    color: #959595;
+    line-height: 20px;
+    word-break: break-all;
+    background: #fff;
+  }
+
+  ::v-deep .van-button {
+    width: 350px;
+    margin-top: 20px;
+  }
+}
 </style>

+ 23 - 19
htmldev/cps/src/views/category/index/child/main.vue

@@ -21,7 +21,7 @@
       <li
         v-for="(item, index) in list"
         :key="index"
-        class="list-item border-bottom-1px"
+        class="list-item"
         @click="getOrderDetail(item.goods_id)">
         <div class="photo">
           <img
@@ -47,21 +47,23 @@
             <!--<span>0</span>-->
             <!--</div>-->
           </div>
-          <div class="card" v-show="item.discount > 0 && item.commission > 0">
+          <div class="card" v-show="item.discount > 0 || item.commission > 0">
             <p class="coupon" v-show="item.discount > 0">
               <span>券</span>
               <span>¥{{ item.discount }}</span>
             </p>
             <p class="profit" v-show="item.commission > 0">
               <span>分享赚</span>
-              <span>¥&nbsp;{{ item.commission | fen2Yuan }}</span>
+              <span>¥&nbsp;{{ item.commission }}</span>
             </p>
           </div>
           <!--暂时不用这个字段-->
-          <!--<div class="shop">-->
-          <!--<img src="" alt="">-->
-          <!--<p>店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名店名</p>-->
-          <!--</div>-->
+          <div class="shop">
+            <van-icon
+              size="20"
+              name="shop-o"/>
+            <p>{{ item.shop_name }}</p>
+          </div>
         </div>
       </li>
       <li class="pullup-wrapper">
@@ -82,12 +84,13 @@
 </template>
 <script>
 import BScroll from 'better-scroll'
-import { Toast, Loading } from 'vant'
+import { Toast, Loading, Icon } from 'vant'
 import { apiGoodsList } from '../api/api'
 
 export default {
   components: {
-    'van-loading': Loading
+    'van-loading': Loading,
+    'van-icon': Icon
   },
   props: {
     source: {
@@ -107,7 +110,8 @@ export default {
       pagenum: 0,
       pagesize: 20,
       list: [],
-      scroll: null
+      scroll: null,
+      screenHeight: Math.round(window.screen.height / 2)
     }
   },
   methods: {
@@ -169,6 +173,7 @@ export default {
             this.$nextTick(() => {
               if (!this.scroll) {
                 this.scroll = new BScroll(this.$refs.returnWrapper, {
+                  probeType: 1,
                   click: true,
                   pullDownRefresh: {
                     threshold: 50, // 顶部下拉的距离
@@ -187,6 +192,10 @@ export default {
                 this.scroll.on('pullingUp', () => {
                   this.getList()
                 })
+
+                this.scroll.on('scroll', ({ y }) => {
+                  this.$emit('setShowHeader', Math.abs(y) < this.screenHeight)
+                })
               } else {
                 this.scroll.finishPullDown()
                 this.scroll.finishPullUp()
@@ -221,7 +230,7 @@ export default {
 .wrapper {
   position: absolute;
   left: 0;
-  top: 95px;
+  top: 0;
   right: 0;
   bottom: 0;
   width: 100%;
@@ -232,7 +241,7 @@ export default {
     flex-direction: column;
     align-items: center;
     width: 100%;
-    padding: 0 0 102px;
+    padding: 136px 0 102px;
   }
 }
 
@@ -410,14 +419,9 @@ export default {
   align-items: center;
   margin-top: 8px;
 
-  img {
-    width: 12px;
-    height: 11px;
-    margin-right: 6px;
-  }
-
   p {
-    width: calc(100% - 18px);
+    width: calc(100% - 24px);
+    margin-left: 4px;
     font-size: 12px;
     font-weight: 500;
     color: #999999;

+ 140 - 60
htmldev/cps/src/views/category/index/index.vue

@@ -1,73 +1,95 @@
 <template>
   <div
     class="category-container">
-    <!--商家-->
-    <div class="business-list">
-      <van-tabs
-        v-model="businessValue"
-        :color="'#ee0a24'"
-        :title-active-color="'#FFF'"
-        :title-inactive-color="'#8A8A8A'"
-        :background="'#fff'"
-        @click="fetchCateList"
-        ref="fbtBusinessList">
-        <van-tab
-          :name="item.value"
-          v-for="item in sourceList"
-          :key="item.value">
-          <template #title>
-            <i>{{ item.name }}</i>
-          </template>
-        </van-tab>
-      </van-tabs>
-    </div>
-    <!--分类-->
-    <div
-      class="cate-header">
-      <van-tabs
-        v-model="catId"
-        :color="'#F09E38'"
-        :title-active-color="'#333333'"
-        :title-inactive-color="'#515151'"
-        :background="'#fff'"
-        @click="selectCateId"
-        ref="fbtCateList">
-        <van-tab
-          :name="item.id"
-          v-for="item in cateList"
-          :key="item.id">
-          <template #title>{{ item.name }}</template>
-        </van-tab>
-      </van-tabs>
-      <van-dropdown-menu
-        class="fbt-van-dropdown-menu">
-        <van-dropdown-item
-          ref="fbtCateDropdownMenu">
-          <template #title>
-            <van-icon name="arrow-down"/>
-          </template>
-          <ul>
-            <li
-              :class="{'active': item.id === catId}"
+    <transition name="fade">
+      <div
+        class="header-wrap"
+        v-show="showHeader">
+        <!--模拟搜索-->
+        <div class="jump-search">
+          <van-search
+            :shape="'round'"
+            v-model="searchValue"
+            placeholder="搜索商品标题 领优惠券拿返现"/>
+          <router-link
+            :to="{name: 'Search'}">去搜索页
+          </router-link>
+        </div>
+        <!--商家-->
+        <div class="business-list">
+          <van-tabs
+            v-model="businessValue"
+            :color="'#ee0a24'"
+            :title-active-color="'#FFF'"
+            :title-inactive-color="'#8A8A8A'"
+            :background="'#fff'"
+            @click="fetchCateList"
+            ref="fbtBusinessList">
+            <van-tab
+              :name="item.value"
+              v-for="item in sourceList"
+              :key="item.value">
+              <template #title>
+                <i>{{ item.name }}</i>
+              </template>
+            </van-tab>
+          </van-tabs>
+        </div>
+        <!--分类-->
+        <div
+          class="cate-header">
+          <van-tabs
+            v-model="catId"
+            :color="'#F09E38'"
+            :title-active-color="'#333333'"
+            :title-inactive-color="'#515151'"
+            :background="'#fff'"
+            @click="selectCateId"
+            ref="fbtCateList">
+            <van-tab
+              :name="item.id"
               v-for="item in cateList"
-              :key="item.id"
-              @click="onConfirm(item)">{{ item.name }}
-            </li>
-          </ul>
-        </van-dropdown-item>
-      </van-dropdown-menu>
-    </div>
+              :key="item.id">
+              <template #title>{{ item.name }}</template>
+            </van-tab>
+          </van-tabs>
+          <van-dropdown-menu
+            class="fbt-van-dropdown-menu">
+            <van-dropdown-item
+              ref="fbtCateDropdownMenu">
+              <template #title>
+                <van-icon name="arrow-down"/>
+              </template>
+              <ul>
+                <li
+                  :class="{'active': item.id === catId}"
+                  v-for="item in cateList"
+                  :key="item.id"
+                  @click="onConfirm(item)">{{ item.name }}
+                </li>
+              </ul>
+            </van-dropdown-item>
+          </van-dropdown-menu>
+        </div>
+      </div>
+    </transition>
     <!--商品列表-->
     <Main
       :source="this.businessValue"
       :catId="catId"
+      @setShowHeader="setShowHeader"
       ref="myMain"/>
+    <!--回到顶部-->
+    <BSScrollTop
+      @handleScrollTop="handleScrollTop"
+      v-show="!showHeader"/>
   </div>
 </template>
 
 <script>
-import { Tab, Tabs, DropdownMenu, DropdownItem, Icon, Toast } from 'vant'
+import { Tab, Tabs, DropdownMenu, DropdownItem, Icon, Toast, Search } from 'vant'
 import Main from './child/main'
+import BSScrollTop from '../../common/BSScrollTop'
 import { apiCateList } from './api/api'
 
 export default {
@@ -78,10 +100,13 @@ export default {
     'van-dropdown-menu': DropdownMenu,
     'van-dropdown-item': DropdownItem,
     'van-icon': Icon,
-    Main
+    'van-search': Search,
+    Main,
+    BSScrollTop
   },
   data () {
     return {
+      searchValue: '',
       sourceList: [
         {
           name: '京东',
@@ -107,14 +132,17 @@ export default {
       businessValue: 'jd',
       catId: '',
       cateList: [], // 分类列表
-      numPositionY: 0 // BS纵轴坐标
+      numPositionY: 0, // BS纵轴坐标
+      showHeader: true // 是否展示顶部模块:搜索、商家筛选、类别筛选
     }
   },
   activated () {
     if (!this.$route.meta.isUseCache) {
+      this.searchValue = ''
       this.businessValue = this.sourceList[0].value
       this.catId = ''
       this.numPositionY = 0
+      this.showHeader = true
       this.fetchCateList()
     } else {
       this.$nextTick(() => {
@@ -156,10 +184,16 @@ export default {
         this.$refs.fbtCateDropdownMenu.toggle()
         this.$refs.myMain.init()
       })
+    },
+    setShowHeader (val) {
+      this.showHeader = val
+    },
+    handleScrollTop () {
+      this.$refs.myMain.scroll && this.$refs.myMain.scroll.scrollTo(0, 0)
     }
   },
   beforeRouteLeave (to, from, next) {
-    if (['CategoryDetail'].findIndex(item => item === to.name) > -1) {
+    if (['MarketingSave', 'Invite', 'Mine', 'Search', 'CategoryDetail'].findIndex(item => item === to.name) > -1) {
       from.meta.isUseCache = true
     }
     this.numPositionY = this.$refs.myMain.scroll ? this.$refs.myMain.scroll.y : 0
@@ -174,10 +208,56 @@ export default {
   left: 0;
   top: 0;
   right: 0;
-  bottom: 0;
+  bottom: 50px;
   width: 100%;
 }
 
+.fade-enter-active,
+.fade-leave-active {
+  transition: all 0.6s;
+}
+
+.fade-enter,
+.fade-leave-to {
+  transform: translate3d(0, -136px, 0);
+}
+
+.header-wrap {
+  position: absolute;
+  left: 0;
+  top: 0;
+  z-index: 10;
+  width: 100%;
+}
+
+.jump-search {
+  ::v-deep .van-search {
+    padding-bottom: 0;
+
+    input {
+      &::-webkit-input-placeholder {
+        -webkit-text-fill-color: #999;
+        opacity: 1;
+        color: #999;
+      }
+    }
+  }
+
+  position: relative;
+  left: 0;
+  top: 0;
+  overflow: hidden;
+
+  a {
+    position: absolute;
+    left: 12px;
+    top: 10px;
+    right: 12px;
+    bottom: 0;
+    opacity: 0;
+  }
+}
+
 .business-list {
   position: relative;
   left: 0;

+ 42 - 0
htmldev/cps/src/views/common/BSScrollTop/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <a
+    class="scroll-top"
+    href="javascript:;"
+    @click="$emit('handleScrollTop')">
+    <van-icon
+      :color="'#333'"
+      name="arrow-up"/>
+  </a>
+</template>
+
+<script>
+import { Icon } from 'vant'
+
+export default {
+  name: 'index',
+  components: {
+    'van-icon': Icon
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.scroll-top {
+  position: absolute;
+  bottom: 20px;
+  right: 10px;
+  z-index: 10;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 50px;
+  height: 50px;
+  border-radius: 50%;
+  background: #fff;
+  box-shadow: 0 0 10px 0 #ccc;
+
+  ::v-deep .van-icon {
+    font-size: 30px;
+  }
+}
+</style>

+ 1 - 2
htmldev/cps/src/views/common/openWxMiniProgram/index.vue

@@ -11,8 +11,7 @@
         @click="funJumpIndex">取消</a>
       <div>
         <a
-          href="javascript:;"
-          @click="funJumpIndex">允许</a>
+          href="javascript:;">允许</a>
         <wx-open-launch-weapp
           ref="launchBtn"
           :username="accountId"

+ 11 - 0
htmldev/cps/src/views/search/api/index.js

@@ -0,0 +1,11 @@
+import request from '@/api/request'
+
+/**
+ * 商品搜索接口
+ * @param obj
+ */
+export const apiGoodsSearch = (obj) => request({
+  method: 'GET',
+  url: '/api/buy/goods/search',
+  params: obj
+})

+ 433 - 0
htmldev/cps/src/views/search/child/main.vue

@@ -0,0 +1,433 @@
+<template>
+  <div
+    class="wrapper"
+    ref="returnWrapper">
+    <ul>
+      <li
+        :class="{'static': isRefresh}"
+        class="pulldown-wrapper">
+        <van-loading
+          v-show="isRefresh"
+          size="24px"
+          type="spinner">加载中...
+        </van-loading>
+        <div
+          v-show="!isRefresh"
+          class="van-loading">
+          <span
+            class="van-loading__text">下拉刷新</span>
+        </div>
+      </li>
+      <li
+        v-for="(item, index) in list"
+        :key="index"
+        class="list-item"
+        @click="getOrderDetail(item.goods_id)">
+        <div class="photo">
+          <img
+            :src="item.goods_thumb_url"
+            alt="">
+        </div>
+        <div class="info">
+          <div
+            class="top">
+            <p>{{ item.goods_name }}</p>
+          </div>
+          <div class="middle">
+            <div class="price-wrap">
+              <p class="price">
+                <span>¥</span>
+                <span>{{ item.price && (item.price * 1).toFixed(2) }}</span>
+              </p>
+              <p class="origin">¥{{ item.market_price && (item.market_price * 1).toFixed(2) }}</p>
+            </div>
+            <!--暂时不用这个字段-->
+            <!--<div class="sale-count">-->
+            <!--<span>已售</span>-->
+            <!--<span>0</span>-->
+            <!--</div>-->
+          </div>
+          <div class="card" v-show="item.discount > 0 || item.commission > 0">
+            <p class="coupon" v-show="item.discount > 0">
+              <span>券</span>
+              <span>¥{{ item.discount }}</span>
+            </p>
+            <p class="profit" v-show="item.commission > 0">
+              <span>分享赚</span>
+              <span>¥&nbsp;{{ item.commission }}</span>
+            </p>
+          </div>
+          <!--暂时不用这个字段-->
+          <div class="shop">
+            <van-icon
+              size="20"
+              name="shop-o"/>
+            <p>{{ item.shop_name }}</p>
+          </div>
+        </div>
+      </li>
+      <li class="pullup-wrapper">
+        <van-loading
+          v-show="!finished"
+          size="24px"
+          type="spinner">加载中...
+        </van-loading>
+        <div
+          v-show="finished"
+          class="van-loading">
+          <span
+            class="van-loading__text">没有更多了</span>
+        </div>
+      </li>
+    </ul>
+  </div>
+</template>
+<script>
+import BScroll from 'better-scroll'
+import { Toast, Loading, Icon } from 'vant'
+import { apiGoodsSearch } from '../api'
+
+export default {
+  components: {
+    'van-loading': Loading,
+    'van-icon': Icon
+  },
+  props: {
+    keyword: {
+      type: String,
+      default: ''
+    },
+    source: {
+      type: String,
+      default: ''
+    },
+    sort: {
+      type: [String, Number],
+      default: 1
+    }
+  },
+  data () {
+    return {
+      finished: false, // 所有数据是否加载完
+      isRefresh: false, // 是否下拉刷新
+      isFetchLock: false, // 接口调用加锁
+      pagenum: 0,
+      pagesize: 20,
+      list: [],
+      scroll: null
+    }
+  },
+  methods: {
+    init () {
+      this.finished = false
+      this.isRefresh = false
+      this.isFetchLock = false
+      this.pagenum = 0
+      this.pagesize = 20
+      this.list = []
+      if (this.scroll) {
+        this.scroll.scrollTo(0, 0)
+      }
+      this.getList()
+    },
+    onRefresh () {
+      this.pagenum = 0
+      this.pagesize = 20
+      this.finished = false
+      this.isRefresh = true
+      this.getList()
+    },
+    async getList () {
+      if (this.finished) {
+        return
+      }
+      if (this.isFetchLock) {
+        return
+      }
+      this.isFetchLock = true
+      this.pagenum++
+      try {
+        const { status, data, msg } = await apiGoodsSearch({
+          page: this.pagenum,
+          page_size: this.pagesize,
+          source: this.source,
+          keyword: this.keyword,
+          sort: this.sort
+        })
+        if (status) {
+          const { list } = data
+          // 下拉刷新数据清空
+          if (this.isRefresh) {
+            this.isRefresh = false
+            this.list = []
+          }
+
+          // 没有数据返回了
+          if (Array.isArray(list) && !list.length) {
+            this.finished = true
+          }
+
+          if (Array.isArray(list) && list.length) {
+            // 总页数小于等于1页时
+            if (list.length < this.pagesize) {
+              this.finished = true
+            }
+            this.list = this.list.concat(list)
+
+            this.$nextTick(() => {
+              if (!this.scroll) {
+                this.scroll = new BScroll(this.$refs.returnWrapper, {
+                  click: true,
+                  pullDownRefresh: {
+                    threshold: 50, // 顶部下拉的距离
+                    stop: 20 // 回弹停留的距离
+                  },
+                  pullUpLoad: {
+                    threshold: -20
+                  },
+                  scrollbar: true
+                })
+
+                this.scroll.on('pullingDown', () => {
+                  this.onRefresh()
+                })
+
+                this.scroll.on('pullingUp', () => {
+                  this.getList()
+                })
+              } else {
+                this.scroll.finishPullDown()
+                this.scroll.finishPullUp()
+                this.scroll.refresh()
+              }
+            })
+          }
+        } else {
+          Toast(msg)
+        }
+        this.isFetchLock = false
+      } catch (err) {
+        this.isFetchLock = false
+      }
+    },
+    getOrderDetail (id) {
+      this.$router.push({
+        name: 'CategoryDetail',
+        params: {
+          source: this.source,
+          goodsId: id
+        }
+      })
+    }
+  },
+  beforeDestroy () {
+    this.scroll && this.scroll.destroy()
+  }
+}
+</script>
+<style lang="scss" scoped>
+.wrapper {
+  position: absolute;
+  left: 0;
+  top: 136px;
+  right: 0;
+  bottom: 0;
+  width: 100%;
+  overflow: hidden;
+
+  ul {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    width: 100%;
+    padding: 0 0 102px;
+  }
+}
+
+.pulldown-wrapper {
+  position: absolute;
+  left: 0;
+  top: -43px;
+  width: 100%;
+  text-align: center;
+  margin-bottom: 10px;
+
+  &.static {
+    position: static;
+    left: 0;
+    top: 0;
+  }
+}
+
+.pullup-wrapper {
+  width: 100%;
+  text-align: center;
+
+  .van-loading {
+    margin-top: 16px;
+  }
+}
+
+.list-item {
+  display: flex;
+  width: 355px;
+  min-height: 138px;
+  background: #FEFEFE;
+  border-radius: 5px;
+  margin-bottom: 10px;
+  padding: 9px;
+}
+
+.photo {
+  width: 121px;
+  height: 121px;
+  margin-right: 10px;
+  border-radius: 6px;
+  overflow: hidden;
+
+  img {
+    display: block;
+    width: 100%;
+  }
+}
+
+.info {
+  width: 206px;
+  padding-top: 6px;
+}
+
+.top {
+  p {
+    display: -webkit-box;
+    font-size: 14px;
+    font-weight: bold;
+    color: #333333;
+    line-height: 18px;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+  }
+}
+
+.middle {
+  display: flex;
+  align-items: flex-end;
+  justify-content: space-between;
+  margin-top: 8px;
+}
+
+.price-wrap {
+  display: flex;
+  align-items: flex-end;
+}
+
+.price {
+  margin-right: 8px;
+  font-size: 0;
+
+  span {
+    &:nth-of-type(1) {
+      margin-right: 4px;
+      font-size: 12px;
+      font-weight: bold;
+      color: #EA483F;
+      line-height: 18px;
+    }
+
+    &:nth-of-type(2) {
+      font-size: 16px;
+      font-weight: bolder;
+      color: #EA483F;
+      line-height: 18px;
+    }
+  }
+}
+
+.origin {
+  font-size: 10px;
+  text-decoration: line-through;
+  color: #999999;
+  line-height: 18px;
+}
+
+.sale-count {
+  font-size: 0;
+
+  span {
+    font-size: 10px;
+    color: #999999;
+    line-height: 18px;
+
+    &:nth-of-type(1) {
+      margin-right: 4px;
+    }
+  }
+}
+
+.card {
+  display: flex;
+  align-items: center;
+  margin-top: 4px;
+}
+
+.coupon {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-width: 57px;
+  height: 18px;
+  background: url("../image/ic_coupon.png") center center/100% 100% no-repeat;
+  margin-right: 11px;
+
+  span {
+    font-size: 11px;
+    font-weight: bold;
+    color: #FFFFFF;
+    line-height: 18px;
+
+    &:nth-of-type(1) {
+      margin-right: 2px;
+    }
+  }
+}
+
+.profit {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-width: 82px;
+  height: 19px;
+  background: #FBF4F4;
+  padding: 0 8px;
+  border-radius: 3px;
+
+  span {
+    font-size: 10px;
+    font-weight: 500;
+    color: #D64136;
+    line-height: 18px;
+
+    &:nth-of-type(1) {
+      margin-right: 2px;
+    }
+  }
+}
+
+.shop {
+  display: flex;
+  align-items: center;
+  margin-top: 8px;
+
+  p {
+    width: calc(100% - 24px);
+    margin-left: 4px;
+    font-size: 12px;
+    font-weight: 500;
+    color: #999999;
+    line-height: 22px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+}
+</style>

BIN
htmldev/cps/src/views/search/image/ic_coupon.png


+ 252 - 0
htmldev/cps/src/views/search/index.vue

@@ -0,0 +1,252 @@
+<template>
+  <div>
+    <form
+      class="fbt-form"
+      action="/">
+      <van-search
+        v-model.trim="keyword"
+        show-action
+        :shape="'round'"
+        placeholder="搜索商品标题 领优惠券拿返现"
+        @search="onSearch">
+        <template #action>
+          <div
+            class="btn"
+            @click="onSearch">搜索
+          </div>
+        </template>
+      </van-search>
+    </form>
+    <!--商家-->
+    <div
+      class="business-list">
+      <van-tabs
+        v-model="source"
+        :color="'#F09E38'"
+        :title-active-color="'#333333'"
+        :title-inactive-color="'#515151'"
+        :background="'#fff'"
+        @click="fetchGoodsList"
+        ref="fbtCateList">
+        <van-tab
+          :name="item.value"
+          v-for="item in sourceList"
+          :key="item.value">
+          <template #title>{{ item.name }}</template>
+        </van-tab>
+      </van-tabs>
+    </div>
+    <ul
+      class="filter-wrap border-top-1px"
+      v-show="booGoodsWrap">
+      <li
+        :class="{'active': sort === item.value}"
+        v-for="item in filterList"
+        :key="item.value"
+        @click="selectSortType(item)">{{ item.name }}
+      </li>
+    </ul>
+    <!--商品列表-->
+    <Main
+      :keyword="keyword"
+      :source="source"
+      :sort="sort"
+      ref="myMain"
+      v-show="booGoodsWrap"/>
+  </div>
+</template>
+
+<script>
+import { Search, Tabs, Tab, Toast } from 'vant'
+import Main from './child/main'
+
+export default {
+  name: 'index',
+  components: {
+    'van-search': Search,
+    'van-tabs': Tabs,
+    'van-tab': Tab,
+    Main
+  },
+  data () {
+    return {
+      keyword: '',
+      sourceList: [
+        {
+          name: '京东',
+          value: 'jd'
+        },
+        {
+          name: '唯品会',
+          value: 'vip'
+        },
+        {
+          name: '拼多多',
+          value: 'pdd'
+        },
+        {
+          name: '考拉',
+          value: 'kaola'
+        },
+        {
+          name: '淘宝',
+          value: 'taobao'
+        }
+      ], // 商家列表
+      // 1-综合排序,2-价格升序,3-销量降序 默认1
+      filterList: [
+        {
+          name: '综合',
+          value: 1
+        },
+        {
+          name: '销量',
+          value: 2
+        },
+        {
+          name: '价格',
+          value: 3
+        }
+      ],
+      source: 'jd',
+      sort: 1,
+      booGoodsWrap: false,
+      numPositionY: 0
+    }
+  },
+  activated () {
+    if (!this.$route.meta.isUseCache) {
+      this.keyword = ''
+      this.source = 'jd'
+      this.sort = 1
+      this.booGoodsWrap = false
+      this.numPositionY = 0
+    } else {
+      this.$nextTick(() => {
+        if (this.$refs.myMain.scroll) {
+          this.$refs.myMain.scroll.refresh()
+          this.$refs.myMain.scroll.scrollTo(0, this.numPositionY)
+        }
+      })
+    }
+    setTimeout(() => {
+      this.$refs.fbtCateList.resize()
+    }, 500)
+    this.$route.meta.isUseCache = false
+  },
+  methods: {
+    onSearch () {
+      if (!this.keyword) {
+        Toast('搜索内容不能为空')
+        return
+      }
+      if (!this.booGoodsWrap) {
+        this.booGoodsWrap = true
+      }
+      this.$nextTick(() => {
+        this.$refs.myMain.init()
+      })
+    },
+    fetchGoodsList () {
+      if (!this.keyword) {
+        return
+      }
+      this.$nextTick(() => {
+        this.$refs.myMain.init()
+      })
+    },
+    selectSortType (item) {
+      this.sort = item.value
+      if (!this.keyword) {
+        return
+      }
+      this.$nextTick(() => {
+        this.$refs.myMain.init()
+      })
+    }
+  },
+  beforeRouteLeave (to, from, next) {
+    if (['CategoryDetail'].findIndex(item => item === to.name) > -1) {
+      from.meta.isUseCache = true
+    }
+    this.numPositionY = this.$refs.myMain.scroll ? this.$refs.myMain.scroll.y : 0
+    next()
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.fbt-form {
+  ::v-deep .van-search {
+    padding-bottom: 0;
+
+    .van-cell {
+      align-items: center;
+
+      input {
+        color: #333;
+        -webkit-text-fill-color: #333;
+        opacity: 1;
+
+        &::-webkit-input-placeholder {
+          -webkit-text-fill-color: #999;
+          opacity: 1;
+          color: #999;
+        }
+      }
+    }
+  }
+
+  .btn {
+    font-size: 16px;
+    color: #333;
+  }
+}
+
+.cate-header {
+  position: relative;
+  left: 0;
+  top: 0;
+  width: 100%;
+
+  ::v-deep .van-tabs {
+    width: calc(100% - 42px);
+
+    .van-tabs__nav--line {
+      .van-tab {
+        &--active {
+
+          .van-tab__text {
+            font-size: 16px !important;
+            font-weight: bold;
+          }
+        }
+
+        .van-tab__text {
+          font-size: 13px;
+        }
+      }
+    }
+  }
+}
+
+.filter-wrap {
+  display: flex;
+  align-items: center;
+  background: #fff;
+  @include border-top-1px(#eee);
+
+  li {
+    flex: 1;
+    line-height: 40px;
+    font-size: 14px;
+    color: #999;
+    text-align: center;
+
+    &.active {
+      font-weight: bolder;
+      color: #333;
+    }
+  }
+}
+</style>