فهرست منبع

Merge branch 'feature/yc-mobile-1020'

# Conflicts:
#	app.html
#	components/default/Header.vue
#	layouts/main.vue
#	pages/auth/login.vue
#	pages/index.vue
yangc 8 سال پیش
والد
کامیت
ead6437573
97فایلهای تغییر یافته به همراه4704 افزوده شده و 120 حذف شده
  1. 19 2
      app.html
  2. 0 8
      assets/README.md
  3. 192 0
      assets/scss/mobileCommon.scss
  4. 0 6
      components/README.md
  5. 5 2
      components/home/Carousel.vue
  6. 272 0
      components/mobile/Home.vue
  7. 118 0
      components/mobile/MobileFooter.vue
  8. 311 0
      components/mobile/MobileHeader.vue
  9. 213 0
      components/mobile/brand/BrandCenter.vue
  10. 548 0
      components/mobile/brand/BrandDetail.vue
  11. 494 0
      components/mobile/brand/ComponentDetail.vue
  12. 16 0
      components/mobile/common/Loading.vue
  13. 41 0
      components/mobile/common/LoginBox.vue
  14. 51 0
      components/mobile/common/PullDown.vue
  15. 48 0
      components/mobile/common/RemindBox.vue
  16. 5 0
      components/mobile/common/index.js
  17. 27 0
      components/mobile/help/HelpFooter.vue
  18. 39 0
      components/mobile/help/HelpHeader.vue
  19. 4 0
      components/mobile/help/index.js
  20. 5 0
      components/mobile/index.js
  21. 289 0
      components/mobile/search/MainSearch.vue
  22. 386 0
      components/mobile/store/StoreDetail.vue
  23. 54 21
      layouts/error.vue
  24. 54 21
      layouts/errorPage.vue
  25. 34 0
      layouts/help.vue
  26. 3 5
      layouts/main.vue
  27. 33 0
      layouts/mobile.vue
  28. 3 5
      nuxt.config.js
  29. 4 1
      package.json
  30. 0 7
      pages/README.md
  31. 6 2
      pages/auth/login.vue
  32. 30 18
      pages/index.vue
  33. 17 0
      pages/mobile/brand/_code.vue
  34. 20 0
      pages/mobile/brand/brandCenter/_initial.vue
  35. 30 0
      pages/mobile/brand/componentDetail/_uuid.vue
  36. 57 0
      pages/mobile/help/_id.vue
  37. 396 0
      pages/mobile/search/_keycode.vue
  38. 27 0
      pages/mobile/shop/_uuid.vue
  39. 314 0
      pages/mobile/shop/index.vue
  40. 412 0
      pages/mobile/user/index.vue
  41. 0 8
      plugins/README.md
  42. 0 11
      static/README.md
  43. BIN
      static/images/404-mobile.png
  44. BIN
      static/images/mobile/@2x/brand-bg.png
  45. BIN
      static/images/mobile/@2x/brand/brandWall.png
  46. BIN
      static/images/mobile/@2x/car@2x.png
  47. BIN
      static/images/mobile/@2x/empty-collect.png
  48. BIN
      static/images/mobile/@2x/home/background@2x.png
  49. BIN
      static/images/mobile/@2x/home/background_search@2x.png
  50. BIN
      static/images/mobile/@2x/home/bookbrand@2x.png
  51. BIN
      static/images/mobile/@2x/home/brand@2x.png
  52. BIN
      static/images/mobile/@2x/home/hot-search.png
  53. BIN
      static/images/mobile/@2x/home/modelbrand@2x.png
  54. BIN
      static/images/mobile/@2x/home/phonebrand@2x.png
  55. BIN
      static/images/mobile/@2x/home/shopbrand@2x.png
  56. BIN
      static/images/mobile/@2x/home/storebrand@2x.png
  57. BIN
      static/images/mobile/@2x/productDetail/component-desc-bg.png
  58. BIN
      static/images/mobile/@2x/productDetail/kind-narrow-down@2x.png
  59. BIN
      static/images/mobile/@2x/productDetail/pdf.png
  60. BIN
      static/images/mobile/@2x/productDetail/pdf@3x.png
  61. BIN
      static/images/mobile/@2x/productDetail/search@2x.png
  62. BIN
      static/images/mobile/@2x/productDetail/view@2x.png
  63. BIN
      static/images/mobile/@2x/search-empty.png
  64. BIN
      static/images/mobile/@2x/search/search-brand.png
  65. BIN
      static/images/mobile/@2x/share/QQ@2x.png
  66. BIN
      static/images/mobile/@2x/share/copylink@2x.png
  67. BIN
      static/images/mobile/@2x/share/download@2x.png
  68. BIN
      static/images/mobile/@2x/share/email@2x.png
  69. BIN
      static/images/mobile/@2x/share/flash@2x.png
  70. BIN
      static/images/mobile/@2x/share/message@2x.png
  71. BIN
      static/images/mobile/@2x/share/sina@2x.png
  72. BIN
      static/images/mobile/@2x/share/weChat@2x.png
  73. BIN
      static/images/mobile/@2x/shop/daili@2x.png
  74. BIN
      static/images/mobile/@2x/shop/jingxiao@2x.png
  75. BIN
      static/images/mobile/@2x/shop/yuanchang@2x.png
  76. BIN
      static/images/mobile/@2x/shoucangkongzhuangtai@2x.png
  77. BIN
      static/images/mobile/@2x/sousuokongzhuangtai@2x.png
  78. BIN
      static/images/mobile/@2x/title-line.png
  79. BIN
      static/images/zhongqiu/zq1.png
  80. BIN
      static/images/zhongqiu/zq10.png
  81. BIN
      static/images/zhongqiu/zq11.png
  82. BIN
      static/images/zhongqiu/zq12.png
  83. BIN
      static/images/zhongqiu/zq13.png
  84. BIN
      static/images/zhongqiu/zq2.png
  85. BIN
      static/images/zhongqiu/zq3.png
  86. BIN
      static/images/zhongqiu/zq4.png
  87. BIN
      static/images/zhongqiu/zq5.png
  88. BIN
      static/images/zhongqiu/zq6.png
  89. BIN
      static/images/zhongqiu/zq7.png
  90. BIN
      static/images/zhongqiu/zq8.png
  91. BIN
      static/images/zhongqiu/zq9.png
  92. 1 2
      store/index.js
  93. 34 1
      store/provider.js
  94. 49 0
      store/searchData.js
  95. 19 0
      store/searchData/searchHistory.js
  96. 10 0
      store/shop.js
  97. 14 0
      store/shop/storeInfo.js

+ 19 - 2
app.html

@@ -4,9 +4,9 @@
   <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"/>
   <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
   <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/element-ui/1.3.7/theme-default/index.css"/>
-  <link rel="stylesheet" type="text/css" href="https://at.alicdn.com/t/font_0d1jjt5tukcblnmi.css"/>
+  <!--<link rel="stylesheet" type="text/css" href="https://at.alicdn.com/t/font_0d1jjt5tukcblnmi.css"/>-->
+  <link rel="stylesheet" type="text/css" href="https://at.alicdn.com/t/font_452262_1eqwgrq76p2xzuxr.css">
   <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/Swiper/3.4.2/css/swiper.css"/>
-  <!--<link rel="stylesheet" type="text/css" href="https://at.alicdn.com/t/font_452262_1qlk0md3oua6ecdi.css"/>-->
   {{ HEAD }}
   <script>
     var _hmt = _hmt || [];
@@ -54,6 +54,23 @@
         var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
         g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
       <!-- End Piwik Code -->
+      if (/(iPhone|iPad|Opera Mini|Android.*Mobile|NetFront|PSP|BlackBerry|Windows Phone)/ig.test(w.navigator.userAgent)) {
+        var docEl = d.documentElement
+        var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize'
+        var recalc = function () {
+          var clientWidth = docEl.clientWidth
+          if (!clientWidth) return
+          if (clientWidth >= 750) {
+            docEl.style.fontSize = '100px'
+          } else {
+            docEl.style.fontSize = 100 * (clientWidth / 750) + 'px'
+          }
+        }
+//        recalc()
+        if (!d.addEventListener) return
+        w.addEventListener(resizeEvt, recalc, false)
+        d.addEventListener('DOMContentLoaded', recalc, false)
+      }
     })(window, document)
   </script>
   <script type="text/javascript">

+ 0 - 8
assets/README.md

@@ -1,8 +0,0 @@
-# ASSETS
-
-This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
-
-More information about the usage of this directory in the documentation:
-https://nuxtjs.org/guide/assets#webpacked
-
-**This directory is not required, you can delete it if you don't want to use it.**

+ 192 - 0
assets/scss/mobileCommon.scss

@@ -0,0 +1,192 @@
+html {
+  overflow-y: scroll;
+  -webkit-text-size-adjust: 100%;
+  -ms-text-size-adjust: 100%;
+}
+
+html * {
+  outline:none;
+  -webkit-text-size-adjust: none;
+  -webkit-tap-highlight-color:rgba(0,0,0,0);
+  -webkit-overflow-scrolling: touch;
+}
+
+/* 内外边距通常让各个浏览器样式的表现位置不同 */
+body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
+  margin: 0;
+  padding: 0;
+}
+
+input, select, textarea {
+  font-size: 100%;
+}
+
+/* 去掉各 Table  cell 的边距并让其边重合 */
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+/* 去除默认边框 */
+fieldset, img {
+  border: 0;
+}
+
+/* 去掉 firefox 下此元素的边框 */
+abbr, acronym {
+  border: 0;
+  font-variant: normal;
+}
+
+/* 一致的 del 样式 */
+del {
+  text-decoration: line-through;
+}
+
+address, caption, cite, code, dfn, em, th, var {
+  font-style: normal;
+  font-weight: 500;
+}
+
+/* 去掉列表前的标识, li 会继承 */
+ol, ul {
+  list-style: none;
+}
+
+/* 对齐是排版最重要的因素, 别让什么都居中 */
+caption, th {
+  text-align: left;
+}
+
+q:before, q:after {
+  content: '';
+}
+
+/* 统一上标和下标 */
+sub, sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sup {
+  top: -0.5em;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+/* 正常链接 未访问 */
+a:link {
+}
+
+/* 鼠标悬停 */
+a:hover {
+  text-decoration: underline;
+}
+
+/* 默认不显示下划线,保持页面简洁 */
+ins, a {
+  text-decoration: none;
+}
+.mobile-modal {
+  position: fixed;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 999;
+  background: rgba(0,0,0,.3);
+  .mobile-modal-box {
+    position: fixed;
+    width: 5.92rem;
+    font-size: .28rem;
+    top: 27%;
+    left: 11%;
+    z-index: 1000;
+    .mobile-modal-header {
+      line-height: .96rem;
+      background: rgb(65,141,246);
+      color: #fff;
+      text-align: center;
+      font-size: .32rem;
+      position: relative;
+      border-top-left-radius: .1rem;
+      border-top-right-radius: .1rem;
+      i {
+        position: absolute;
+        right: -.25rem;
+        font-size: .6rem;
+        bottom: .46rem;
+      }
+    }
+    .mobile-modal-content {
+      background: #fff;
+      color: #333;
+      padding: 0 .54rem;
+      border-bottom-left-radius: .1rem;
+      border-bottom-right-radius: .1rem;
+      div {
+        padding: .2rem 0;
+        line-height: .4rem;
+        border-bottom: .04rem solid rgb(183,213,254);
+        text-align: center;
+        &:last-child {
+          border-bottom: none;
+        }
+        &:first-child {
+          text-align: left;
+        }
+      }
+    }
+  }
+  .mobile-share-box {
+    position: fixed;
+    width: 100%;
+    font-size: .28rem;
+    bottom: 0;
+    left: 0;
+    z-index: 1000;
+    background: #fff;
+    color: #333;
+    .cancel-share {
+      height: .98rem;
+      line-height: .98rem;
+      font-size: .3rem;
+      text-align: center;
+      border-top: .04rem solid #cdcecf;
+    }
+    .share-area {
+      .share-item {
+        display: inline-block;
+        width: 1.5rem;
+        height: 1.52rem;
+        padding-top: .3rem;
+        i {
+          margin: 0 auto;
+          display: block;
+          font-size: .55rem;
+          width: .54rem;
+        }
+        span {
+          display: block;
+          text-align: center;
+          margin-top: .1rem;
+        }
+      }
+    }
+  }
+}
+.link-url {
+  color: #01a44e;
+}
+::-webkit-scrollbar {
+  opacity: 0;
+}
+
+input {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+}

+ 0 - 6
components/README.md

@@ -1,6 +0,0 @@
-# COMPONENTS
-
-The components directory contains your Vue.js Components.
-Nuxt.js doesn't supercharge these components.
-
-**This directory is not required, you can delete it if you don't want to use it.**

+ 5 - 2
components/home/Carousel.vue

@@ -50,8 +50,11 @@
             if (this.banners.data.length && swiper.activeIndex <= 0) {
               swiper.activeIndex = this.banners.data.length
             }
-            document.querySelector('.carousel').style.backgroundColor =
-              this.banners.data[swiper.activeIndex - 1].metadata['background-color']
+            let carousel = document.querySelector('.carousel')
+            if (carousel && carousel !== null) {
+              carousel.style.backgroundColor =
+                this.banners.data[swiper.activeIndex - 1].metadata['background-color']
+            }
           }
         }
       }

+ 272 - 0
components/mobile/Home.vue

@@ -0,0 +1,272 @@
+<template>
+  <div class="home">
+    <div class="mobile-modal" v-if="showStoreInfo">
+      <div class="mobile-modal-box">
+        <div class="mobile-modal-header">联系方式<i @click="showStoreInfo = false" class="icon-guanbi iconfont"></i></div>
+        <div class="mobile-modal-content">
+          <div>商家地址:深圳市南山区英唐大厦6楼</div>
+         <!-- <div class="content-line link-url">在线咨询</div>-->
+          <div>致电:<a href="tel:0755-96586323" class="content-line link-url">0755-96586323</a></div>
+          <div>邮件:<a href="mailto:yrsc@usoftchina.com" class="content-line link-url">yrsc@usoftchina.com</a></div>
+        </div>
+      </div>
+    </div>
+    <div v-if="!showMainSearch">
+      <div class="home-header" :style="'background:url(' + bgUrl + ')no-repeat center center/100% 6.14rem'">
+        <div class="home-search">
+          <!--<ul>-->
+          <!--<li :class="activeType=='model'?'active':''" @click="activeType='model'"><span>型号</span></li>-->
+          <!--<li :class="activeType=='brand'?'active':''" @click="activeType='brand'"><span>品牌</span></li>-->
+          <!--<li :class="activeType=='shops'?'active':''" @click="activeType='shops'"><span>商家</span></li>-->
+          <!--</ul>-->
+          <div class="home-input">
+            <input type="text" placeholder="请输入您要查找的型号或品牌"
+                   @click="onHomeSearchClick()"/>
+            <i class="iconfont icon-sousuo"></i>
+          </div>
+          <!--<p style="color:#e45803;line-height:.4rem;margin-top:.1rem;width:4.2rem;margin-left:1rem;">搜品牌、搜现货 、搜好店 、搜规格书 就上优软商城</p>-->
+        </div>
+      </div>
+      <div class="home-main">
+        <nuxt-link to="/mobile/shop" class="home-main-content">
+          <div>
+            <img src="/images/mobile/@2x/home/shopbrand@2x.png">
+          </div>
+          <p>店铺列表</p>
+        </nuxt-link>
+        <a @click="goCollect" class="home-main-content">
+          <div>
+            <img src="/images/mobile/@2x/home/storebrand@2x.png">
+          </div>
+          <p>我的收藏</p>
+        </a>
+        <nuxt-link to="/mobile/brand/brandCenter/ABCD" class="home-main-content">
+          <div>
+            <!--<i class="icon-pinpai iconfont"></i>-->
+            <img src="/images/mobile/@2x/home/brand@2x.png" alt="">
+          </div>
+          <!--<h2>
+            {{numbrand[0]}}
+          </h2>-->
+          <p>品牌列表</p>
+        </nuxt-link>
+        <a @click="showStoreInfo = true" class="home-main-content">
+          <div>
+            <img src="/images/mobile/@2x/home/phonebrand@2x.png">
+          </div>
+          <p>联系我们</p>
+        </a>
+        <!--<a class="home-main-content">
+          <div>
+            <i class="icon-xinghao iconfont"></i>
+          </div>
+          <h2>
+            {{numbrand[1]}}
+          </h2>
+          <p>型号</p>
+        </a>
+        <a class="home-main-content">
+          <div>
+            <i class="icon-biaoguigeshuomingshu iconfont"></i>
+          </div>
+          <h2>
+            {{numbrand[2]}}
+          </h2>
+          <p>规格书</p>
+        </a>-->
+      </div>
+    </div>
+    <main-search v-else @cancelSearchAction="onCancelSearch"></main-search>
+    <login-box @onLoginBoxClose="showLoginBox = false" v-if="showLoginBox"></login-box>
+  </div>
+</template>
+
+<script>
+  import MainSearch from '~/components/mobile/search/MainSearch.vue'
+  import {LoginBox} from '~components/mobile/common'
+  export default {
+    name: 'home',
+    data () {
+      return {
+        activeType: 'model',
+        showMainSearch: false,
+        showStoreInfo: false,
+        isMore: false,
+        isShow: false,
+        len: 0,
+        bgUrl: '/images/mobile/@2x/home/background@2x.png',
+        showLoginBox: false
+      }
+    },
+    components: {
+      MainSearch,
+      LoginBox
+    },
+    methods: {
+      onHomeSearchClick () {
+        this.showMainSearch = true
+        this.$store.dispatch('searchData/getSearchHistory')
+      },
+      matNumber (num) {
+        if (num > 99999999) {
+          this.isShow = true
+          let str2 = num.toString()
+          num = Math.floor(num / 100000000)
+          if (parseInt(str2.charAt(str2.length - 8)) > 8) {
+            num = num + 1
+          }
+          num += '亿'
+        }
+        if (num > 9999) {
+          this.isMore = true
+          let str = num.toString()
+          num = Math.floor(num / 10000)
+          if (parseInt(str.charAt(str.length - 4)) > 4) {
+            num = num + 1
+          }
+          num += '万'
+        } else {
+          num += '个'
+        }
+        return num
+      },
+      forNum (numbers) {
+        let num = []
+        for (let i = 0; i < numbers.length; i++) {
+          num.push(this.matNumber(numbers[i].count))
+        }
+        return num
+      },
+      onCancelSearch: function () {
+        this.showMainSearch = false
+      },
+      goCollect: function () {
+        if (this.user.logged) {
+          this.$router.push('/mobile/user')
+        } else {
+          this.showLoginBox = true
+        }
+      }
+    },
+    computed: {
+      numbrand () {
+        return this.forNum(this.counts)
+      },
+      counts () {
+        return this.$store.state.product.common.counts.data
+      },
+      user () {
+        return this.$store.state.option.user
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .home{
+    font-size: .28rem;
+    background: #f7fbff;
+    margin-bottom: .96rem;
+    .home-header{
+      width:100%;
+      height:6.12rem;
+      padding-bottom:1rem;
+      .home-search{
+        width:6rem;
+        line-height: .3rem;
+        margin:0 auto;
+        text-align: center;
+        padding-top: 1.74rem;
+        ul{
+          display:inline-flex;
+        li{
+          flex:1;
+          text-align:center;
+          >span{
+             display:inline-block;
+             width:.72rem;
+             line-height:.33rem;
+             height:.33rem;
+             background: #fff;
+             color:#000;
+             border-radius: .05rem .05rem 0 0 ;
+           }
+          }
+          li.active span{
+              background: #3c7cf5;
+              color:#fff;
+              cursor:pointer;
+          }
+        }
+        .home-input{
+          width: 6rem;
+          height: .61rem;
+          line-height: .61rem;
+          input{
+            width:5.17rem;
+            display: inline-block;
+            padding: 0 1rem 0 .16rem;
+            margin-right:-.83rem;
+            font-size:.24rem;
+            border:.04rem solid #3c7cf5;
+            border-radius:.05rem;
+          }
+          i{
+             display:inline-block;
+             text-align: center;
+             width:.83rem;
+             font-size:.33rem;
+             border-left:none;
+             color: #999;
+            vertical-align: middle;
+          }
+        }
+      }
+    }
+    }
+    .home-main{
+      width:100%;
+      text-align: center;
+      padding-top: .46rem;
+      height: 6.25rem;
+    }
+    .home-main a.home-main-content {
+      width:50%;
+      margin-bottom:.52rem;
+      display: inline-block;
+    }
+    .home-main .home-main-content div{
+      border-radius: .2rem;
+      width:1.14rem;
+      height:1.14rem;
+      margin:0 auto;
+    }
+    .home-main .home-main-content div>img{
+      width: 100%;
+      height:100%;
+    }
+  .home-main .home-main-content div>i {
+    font-size: .8rem;
+  }
+  .home-main .home-main-content:nth-child(3) div>i {
+    color: #ff3064;
+  }
+  /*.home-main .home-main-content:nth-child(5) div>i {
+    color: #fa6743;
+  }
+  .home-main .home-main-content:nth-child(6) div>i {
+    color: #fcb836;
+  }*/
+    .home-main .home-main-content p{
+      font-size:.28rem;
+      color:rgb(51,51,51);
+      line-height: .52rem;
+    }
+    .home-main .home-main-content h2{
+      font-size:.3rem;
+      color:#ff7800;
+      line-height: .32rem;
+      margin:0;
+      margin-top:.1rem;
+    }
+</style>

+ 118 - 0
components/mobile/MobileFooter.vue

@@ -0,0 +1,118 @@
+<template>
+  <div class="mobile-footer">
+    <span :class="activeType=='home'?'active':''">
+      <nuxt-link to="/">
+        <i :class="activeType=='home'?'iconfont icon-shouye':'iconfont icon-shouye1'"></i><p>首页</p>
+      </nuxt-link>
+    </span>
+    <span :class="activeType=='shops'?'active':''">
+      <nuxt-link to="/mobile/shop">
+        <i :class="activeType=='shops'?'iconfont icon-dianpu':'iconfont icon-dianpu1'"></i><p>店铺</p>
+      </nuxt-link>
+    </span>
+    <span :class="activeType=='user'?'active':''">
+      <a @click="goCollect">
+        <i :class="activeType=='user'?'iconfont icon-icon':'iconfont icon-wo'"></i><p>我</p>
+      </a>
+    </span>
+    <a @click="toTop" v-show="!hideToTop"><i class="iconfont icon-arrow-up icon-xlg"></i></a>
+    <login-box @onLoginBoxClose="showLoginBox = false" v-if="showLoginBox"></login-box>
+  </div>
+</template>
+<script>
+  import { scrollTo } from '~utils/scroll'
+  import {LoginBox} from '~components/mobile/common'
+  export default{
+    name: 'MobileFooter',
+    data () {
+      return {
+        hideToTop: true,
+        showLoginBox: false
+      }
+    },
+    components: {
+      LoginBox
+    },
+    computed: {
+      activeType () {
+        return this.$route.path === '/' ? 'home' : this.$route.path === '/mobile/shop' ? 'shops' : this.$route.path === '/mobile/user' ? 'user' : ''
+      },
+      user () {
+        return this.$store.state.option.user
+      }
+    },
+    mounted: function () {
+      this.$nextTick(function () {
+        window.addEventListener('scroll', this.onScroll)
+      })
+    },
+    methods: {
+      onScroll () {
+        let scrolled = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
+        if (scrolled > window.screen.availHeight) {
+          this.hideToTop = false
+        } else {
+          this.hideToTop = true
+        }
+      },
+      toTop () {
+        scrollTo('body', 300)
+      },
+      goCollect: function () {
+        if (this.user.logged) {
+          this.$router.push('/mobile/user')
+        } else {
+          this.showLoginBox = true
+        }
+      }
+    }
+  }
+</script>
+<style scoped>
+  .mobile-footer{
+    position:fixed;
+    bottom:0;
+    width:100%;
+    height:.98rem;
+    text-align: center;
+    border-top:.04rem solid #ccc;
+    background: #ffffff;
+    z-index: 10;
+  }
+  .mobile-footer span{
+    display: inline-block;
+    width: 2.5rem;
+    font-size:.32rem;
+    color:#b0b0b0;
+    padding-top:.1rem;
+  }
+
+  .mobile-footer span a{
+    color:#b0b0b0;
+  }
+
+  .mobile-footer span a i{
+    font-size:.45rem;
+  }
+  .mobile-footer span a p{
+    font-size:.22rem;
+  }
+
+  .mobile-footer span.active a{
+    color:#3976f4;
+  }
+  .mobile-footer >a {
+    position: absolute;
+    right: .1rem;
+    top: -1rem;
+    background: rgba(0,0,0,.4);
+    color: #fff;
+    width: .88rem;
+    height: .88rem;
+    line-height: .88rem;
+    border-radius: 100%;
+  }
+  .mobile-footer >a i{
+    font-size: .46rem;
+  }
+</style>

+ 311 - 0
components/mobile/MobileHeader.vue

@@ -0,0 +1,311 @@
+<template>
+  <div class="mobile-nav">
+   <!-- <div class="mobile-modal" v-if="showStoreInfo || showShare" @click="cancelModal">
+      <div class="mobile-modal-box" v-if="showStoreInfo" @click="stopPropagation($event)">
+        <div class="mobile-modal-header">联系方式<i @click="showStoreInfo = false" class="icon-guanbi iconfont"></i></div>
+        <div class="mobile-modal-content" v-if="showDefaultAddr">
+          <div>商家地址:深圳市南山区英唐大厦6楼</div>
+          <div class="content-line link-url">在线咨询</div>
+          <div>致电:<a href="tel:0755-96586323" class="content-line link-url">0755-96586323</a></div>
+          <div>邮件:<a href="mailto:yrsc@usoftchina.com" class="content-line link-url">yrsc@usoftchina.com</a></div>
+        </div>
+        <div class="mobile-modal-content" v-if="!showDefaultAddr">
+        <div>商家地址:{{store.enterprise.enAddress || store.enterprise.address}}</div>
+        <div class="content-line link-url">在线咨询</div>
+        <div>致电:<a :href="'tel:'+store.enterprise.enTel" class="content-line link-url">{{store.enterprise.enTel}}</a></div>
+          <div>邮件:<a :href="'mailto:'+store.enterprise.enEmail" class="content-line link-url">{{store.enterprise.enEmail}}</a></div>
+        </div>
+      </div>
+      <div class="mobile-share-box" v-if="showShare" @click="stopPropagation($event)">
+        <div class="share-area">
+          <ul>
+            <li class="share-item" @click="shareWeChat">
+             <i class="icon-weixin iconfont" style="color: #07af12;"></i>
+              <span>微信</span>
+            </li>
+            <li class="share-item" @click="shareQQ">
+              <i class="icon-qq1 iconfont" style="color: #5872f4;"></i>
+              <span>QQ</span>
+            </li>
+            <li class="share-item" @click="shareWeibo">
+              <i class="icon-ff0000 iconfont" style="color: #ff0000;"></i>
+              <span>微博</span>
+            </li>
+            <li class="share-item" @click="shareMessage">
+              <i class="icon-msnui-msg-invert iconfont" style="color: #25cdb7"></i>
+              <span>短信</span>
+              <a href="sms:" class="hide" id="share-sms"></a>
+            </li>
+            <li class="share-item" @click="shareEmail">
+              <i class="icon-youjian iconfont" style="height: .57rem;font-size: .41rem;color:#f18215;"></i>
+              <span>邮件</span>
+              <a href="mailto:" class="hide" id="share-mail"></a>
+            </li>
+            <li class="share-item" @click="flash">
+              <i class="icon-shuaxin iconfont" style="color: #2584cd;"></i>
+              <span>刷新</span>
+            </li>
+            <li class="share-item" @click="copyLink" id="copyLink"  :data-clipboard-text="url">
+              <i class="icon-lianjie iconfont" style="color: #73b0df;"></i>
+              <span>复制链接</span>
+            </li>
+          </ul>
+        </div>
+        <div class="cancel-share" @click="showShare=false">取消</div>
+      </div>
+    </div>-->
+    <div class="mobile-header" v-if="showHeader && !showMainSearch">
+      <!--<a class="iconfont icon-fanhui" @click="goLastPage">返回</a>-->
+      <p>{{title}}
+        <span @click="goMainSearch"><i class="icon-sousuo iconfont"></i>搜索</span>
+      </p>
+    </div>
+    <main-search v-if="showMainSearch" @cancelSearchAction="onCancelSearch"></main-search>
+<!--    <i v-show="rightIcon=='share'" class="iconfont icon-fenxiang" @click="showShare = true" @touchmove="onTouchMove($event)"></i>
+    <i v-show="rightIcon=='phone'" class="iconfont icon-dianhua" @click="showLink" @touchmove="onTouchMove($event)"></i>-->
+  </div>
+</template>
+<script>
+//  import Clipboard from 'clipboard'
+  import MainSearch from '~/components/mobile/search/MainSearch.vue'
+  export default {
+    data () {
+      return {
+//        showStoreInfo: false,
+//        showShare: false,
+//        rightIcon: 'phone',
+//        showDefaultAddr: true,
+//        url: '',
+//        clipboard: {},
+        showMainSearch: false,
+        title: '优软商城',
+        showHeader: false
+//        showSearch: false
+      }
+    },
+    components: {
+      MainSearch
+    },
+    watch: {
+      $route: function (val, oldVal) {
+        this.showMainSearch = false
+        this.title = this.initHeader(val.path)
+      }
+    },
+    created () {
+      this.title = this.initHeader(this.$route.path)
+    },
+    computed: {
+      brandDetail () {
+        return this.$store.state.brandDetail.detail.data
+      },
+      store () {
+        return this.$store.state.shop.storeInfo.store.data
+      },
+      component () {
+        return this.$store.state.componentDetail.detail.data
+      }
+//      showHeader () {
+//        return this.$route.path !== '/' || !this.$route.path || this.$route.path === ''
+//      },
+//      showSearch () {
+//        return this.$route.path !== '/' && !this.$route.path.startsWith('/mobile/search')
+//      }
+    },
+//    mounted () {
+//      let _this = this
+//      _this.url = window.location.href
+//      _this.clipboard = new Clipboard('#copyLink')
+//      _this.clipboard.on('success', e => {
+//        _this.clipboard.destroy()
+//        _this.showShare = false
+//      })
+//      _this.clipboard.on('error', e => {
+//        alert('浏览器不支持自动复制,请手动复制')
+//        _this.clipboard.destroy()
+//      })
+//    },
+    methods: {
+      goLastPage: function () {
+        window.history.back(-1)
+      },
+      initHeader: function (val) {
+//        if (val !== '/' || !val || val === '') {
+//          this.showHeader = true
+//          this.showSearch = !val.startsWith('/mobile/search')
+//        } else {
+//          this.showHeader = false
+//          this.showSearch = false
+//        }
+        this.showHeader = val !== '/' || !val || val === ''
+//        this.showSearch = val !== '/' && !this.startWith(val, '/mobile/search')
+        let title = '优软商城'
+        if (this.startWith(val, '/mobile/brand/componentDetail/')) {
+          title = this.component.code
+//          this.rightIcon = 'share'
+        } else if (this.startWith(val, '/mobile/brand/brandCenter')) {
+          title = '品牌墙'
+//          this.rightIcon = 'share'
+        } else if (this.startWith(val, '/mobile/brand/')) {
+          if (this.brandDetail.nameCn) {
+            if (this.brandDetail.nameCn !== this.brandDetail.nameEn) {
+              title = this.brandDetail.nameEn + '(' + this.brandDetail.nameCn + ')'
+            } else {
+              title = this.brandDetail.nameCn
+            }
+          } else {
+            if (this.component.brand.nameCn !== this.component.brand.nameEn) {
+              title = this.component.brand.nameEn + '(' + this.component.brand.nameCn + ')'
+            } else {
+              title = this.component.brand.nameCn
+            }
+          }
+//          this.rightIcon = 'share'
+        } else if (this.startWith(val, '/mobile/shop/')) {
+          title = this.store.storeName
+//          this.rightIcon = 'phone'
+        } else if (this.startWith(val, '/mobile/shop')) {
+          title = '店铺列表'
+//          this.rightIcon = 'phone'
+        } else if (this.startWith(val, '/mobile/user')) {
+          title = '我的收藏'
+//          this.rightIcon = 'phone'
+        } else if (this.startWith(val, '/mobile/search')) {
+          title = '搜索结果'
+//          this.rightIcon = 'share'
+        } else if (val === '' || val === '/' || !val) {
+          title = '优软商城'
+//          this.rightIcon = 'phone'
+        } else {
+          title = '优软商城'
+//          this.rightIcon = 'phone'
+        }
+        return title
+      },
+//      showLink: function () {
+//        this.showStoreInfo = true
+//        if (this.$route.path.startsWith('/mobile/shop/')) {
+//          this.showDefaultAddr = false
+//        } else {
+//          this.showDefaultAddr = true
+//        }
+//      },
+//      shareWeibo: function () {
+//        let _shareUrl = 'http://v.t.sina.com.cn/share/share.php?&appkey=895033136'     // 真实的appkey,必选参数
+//        _shareUrl += '&url=' + encodeURIComponent(document.location)     // 参数url设置分享的内容链接|默认当前页location,可选参数
+//        _shareUrl += '&title=' + encodeURIComponent(document.title)    // 参数title设置分享的标题|默认当前页标题,可选参数
+//        _shareUrl += '&source=' + encodeURIComponent('')
+//        _shareUrl += '&sourceUrl=' + encodeURIComponent('')
+//        _shareUrl += '&content=' + 'utf-8'   // 参数content设置页面编码gb2312|utf-8,可选参数
+//        _shareUrl += '&pic=' + encodeURIComponent('')  // 参数pic设置图片链接|默认为空,可选参数
+//        window.open(_shareUrl)
+//        this.showShare = false
+//      },
+//      shareWeChat: function () {
+//      },
+//      shareQQ: function () {
+//        let url = encodeURIComponent(document.location)
+//        let title = encodeURIComponent(document.title)
+//        let source = encodeURIComponent('')
+//        let desc = '优软商城'
+//        let pics = 'http://dfs.ubtob.com/group1/M00/4F/C3/CgpkyFnxWjOAMy5DAAlh1PrLlc8684.png'
+//        window.open('http://connect.qq.com/widget/shareqq/index.html?url=' +
+//          url + '&title=' + title + '&source=' + source + '&desc=' + desc + '&pics=' + pics)
+//        this.showShare = false
+//      },
+//      shareMessage: function () {
+//        document.getElementById('share-sms').click()
+//      },
+//      shareEmail: function () {
+//        document.getElementById('share-mail').click()
+//      },
+//      flash: function () {
+//        window.location.reload()
+//      },
+//      copyLink: function () {
+//        let _this = this
+//        _this.url = window.location.href
+//        _this.clipboard = new Clipboard('#copyLink')
+//      },
+//      onTouchMove: function (e) {
+//        let width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
+//        let height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
+//        let x = Math.min(width - 40, e.touches[0].clientX)
+//        let y = Math.min(height - 40, e.touches[0].clientY)
+//        x = Math.max(0, x)
+//        y = Math.max(0, y)
+//        e.preventDefault()
+//        e.target.style.left = x * 2 / 100.0 + 'rem'
+//        e.target.style.top = y * 2 / 100.0 + 'rem'
+//      },
+      onCancelSearch: function () {
+        this.showMainSearch = false
+      },
+//      cancelModal: function () {
+//        this.showStoreInfo = false
+//        this.showShare = false
+//      },
+      stopPropagation: function (event) {
+        event.stopPropagation()
+      },
+      goMainSearch: function () {
+        this.showMainSearch = true
+        this.$store.dispatch('searchData/getSearchHistory')
+      },
+      startWith: function (str, s) {
+        let reg = new RegExp('^' + s)
+        return reg.test(str)
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .mobile-header{
+    width:100%;
+    height:.88rem;
+    line-height: .88rem;
+    border-bottom:.04rem solid #ccc;
+    background: #3e82f5;
+    padding:0 .2rem 0 .1rem;
+    color:#fff;
+  }
+  .mobile-header p{
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    font-size:.36rem;
+    text-align: center;
+    margin: 0;
+    width: 6rem;
+    padding-left: 1rem;
+  }
+  .mobile-header a{
+    font-size:.28rem;
+    color:#fff;
+  }
+  .mobile-header p span {
+    position: absolute;
+    right: .4rem;
+    font-size: .28rem;
+  }
+  .mobile-header p span i {
+    font-size: .28rem;
+  }
+  .mobile-nav >i{
+    font-size: .4rem;
+    position: fixed;
+    right: .25rem;
+    top: .25rem;
+    z-index: 1000;
+    color: #fff;
+    background: rgba( 0, 0, 0, .251 );
+    width: .8rem;
+    height: .8rem;
+    line-height: .8rem;
+    border-radius: 100%;
+    padding-left: .2rem;
+  }
+  .hide {
+    display: none;
+  }
+</style>

+ 213 - 0
components/mobile/brand/BrandCenter.vue

@@ -0,0 +1,213 @@
+<template>
+  <div class="mobile-brand-center">
+    <div class="mobile-brand-wrap">
+      <div class="mobile-brand-header">
+        <img src="/images/mobile/@2x/brand/brandWall.png" alt="" v-show="!isScrolled">
+        <div class="mobile-brand-index" :class="{'scrolled': isScrolled}">
+          <p>索引:</p>
+          <nuxt-link :to="'/mobile/brand/brandCenter/' + item"
+                     :class="{'active': item == activeIndex}"
+                     :key="key" v-for="(item, key) in initArr">{{item}}</nuxt-link>
+        </div>
+      </div>
+      <div class="mobile-brand-list">
+        <div v-for="(brands, initial) in brandList">
+          <div class="brand-initial">
+            <p v-text="initial" :style="initial === '0~9' ? 'font-size: .28rem': 'font-size: .32rem'"></p>
+            <span>
+              {{initial}}开头共<span>{{brands.length || 0}}</span>个品牌
+            </span>
+          </div>
+          <div class="brand-items">
+            <nuxt-link :to="`/mobile/brand/${brand.uuid}/`" :key="key" v-for="(brand, key) in brands">
+              <div>{{brand.nameEn}}</div>
+              <div v-if="brand.nameCn != brand.nameEn">{{brand.nameCn}}</div>
+            </nuxt-link>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+  function sortList (letter) {
+    return function (a, b) {
+      var value1 = a[letter]
+      var value2 = b[letter]
+      if (value1 > value2) {
+        return 1
+      } else if (value1 < value2) {
+        return -1
+      } else {
+        return 0
+      }
+    }
+  }
+  export default {
+    name: 'brandList',
+    data () {
+      return {
+        initArr: [
+          'ABCD', 'EFGH', 'IJKL', 'MNOP', 'QRST', 'UVWX', 'YZ', '0~9'
+        ],
+        activeIndex: this.$route.params.initial,
+        isScrolled: false
+      }
+    },
+    mounted: function () {
+      this.$nextTick(function () {
+        window.addEventListener('scroll', this.onScroll)
+      })
+    },
+    watch: {
+      $route: function (val, oldVal) {
+        this.activeIndex = val.params.initial
+      }
+    },
+    computed: {
+      brandList () {
+        let brandsList = this.$store.state.product.brand.brandList.data
+        if (brandsList) {
+          for (let i in brandsList) {
+            brandsList[i] = brandsList[i].sort(sortList('nameEn'))
+          }
+        }
+        let temp = {}
+        let keys = []
+        for (let key in brandsList) {
+          keys.push(key)
+        }
+        keys = keys.sort()
+        for (let i = 0; i < keys.length; i++) {
+          temp[keys[i]] = brandsList[keys[i]]
+        }
+        return temp
+      }
+    },
+    methods: {
+      onScroll () {
+        if (this.startWith(this.$route.path, '/mobile/brand/brandCenter')) {
+          let scrolled = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
+          this.isScrolled = scrolled > 0
+        }
+      },
+      startWith: function (str, s) {
+        let reg = new RegExp('^' + s)
+        return reg.test(str)
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .mobile-brand-center {
+    width: 100%;
+    background: #f7f7f7;
+    padding-top: .24rem;
+    margin-bottom: .98rem;
+    .mobile-brand-wrap {
+      width: 6.96rem;
+      background: #fff;
+      margin: 0 auto;
+      padding: 0 .21rem;
+      border-radius: .1rem;
+      .mobile-brand-header {
+        text-align: center;
+        >img {
+          margin: .24rem auto .19rem;
+          width: 6.09rem;
+          height: .66rem;
+        }
+        .mobile-brand-index {
+          font-size: .3rem;
+          line-height: .62rem;
+          background: #f4fafd;
+          margin: .19rem 0 .25rem 0;
+          padding: 0 .07rem;
+          text-align: left;
+          p {
+            float: left;
+          }
+          a {
+            color: #666;
+            width: 1.1rem;
+            display: inline-block;
+            text-align: center;
+            &.active, &.hover, &.focus {
+              color: #418bf6;
+            }
+          }
+          &.scrolled {
+            position: fixed;
+            top: 0;
+            width: 100%;
+            background: #fff;
+            border-bottom: .04rem solid #ccc;
+            left: 0;
+            padding-left: .58rem;
+            margin-top: 0;
+          }
+        }
+      }
+      .mobile-brand-list {
+        font-size: .3rem;
+        .brand-initial {
+          border-bottom: .04rem solid #418bf6;
+          p {
+            width: .64rem;
+            height: .43rem;
+            line-height: .43rem;
+            margin: 0;
+            background: #418bf6;
+            color: #fff;
+            font-size: .32rem;
+            text-align: center;
+            display: inline-block;
+            border-top-left-radius: .05rem;
+            border-top-right-radius: .05rem;
+          }
+          >span {
+            font-size: .22rem;
+            color: #999;
+            >span {
+              color: #418bf6;
+            }
+          }
+        }
+        .brand-items {
+          overflow: hidden;
+          margin-bottom: .2rem;
+          a {
+            overflow: hidden;
+            display: inline-block;
+            color: #333;
+            border-radius: .05rem;
+            background: #fff;
+            margin: .18rem .42rem .12rem 0;
+            height: .78rem;
+            float: left;
+            &:nth-child(3n) {
+              margin-right: 0;
+            }
+            &:active {
+              color: #418bf6;
+            }
+            div {
+              width: 1.9rem;
+              height: .39rem;
+              line-height: .39rem;
+              text-align: left;
+              text-overflow: ellipsis;
+              white-space: nowrap;
+              overflow: hidden;
+              &:nth-child(2) {
+                font-size: .26rem;
+                color: #666;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+</style>

+ 548 - 0
components/mobile/brand/BrandDetail.vue

@@ -0,0 +1,548 @@
+<template>
+  <div class="brand-detail" @click="checkShowFilter()">
+    <div class="brand-logo">
+      <div class="brand-logo-box">
+        <img :src="brandDetail.logoUrl || '/images/component/default.png'" :alt="brandDetail.nameEn"/>
+      </div>
+    </div>
+    <div class="brand-switch-item">
+      <span :class="activeType=='detail'?'mobile-switch-btn active':'mobile-switch-btn'" @click="setActiveType('detail')">品牌</span>
+      <span :class="activeType=='product'?'mobile-switch-btn active':'mobile-switch-btn'" @click="setActiveType('product')">产品</span>
+    </div>
+    <div class="brand-param-list" v-if="activeType=='detail'">
+      <div class="brand-param-item" v-if="brandDetail.series">
+        <p class="remind-title">主营产品</p>
+        <img class="remind-tag" src="/images/mobile/@2x/title-line.png" alt="">
+        <div class="main-sell">{{brandDetail.series}}</div>
+      </div>
+      <div class="brand-param-item" v-if="applications.length>0">
+        <p class="remind-title">应用领域</p>
+        <img class="remind-tag" src="/images/mobile/@2x/title-line.png" alt="">
+        <div class="main-sell">
+          <span v-for="(item, index) in applications"><span>{{item}}</span><span v-show="index+1 < applications.length">|</span></span>
+        </div>
+      </div>
+      <div class="brand-param-item" v-if="brandDetail.brief">
+        <p class="remind-title">品牌介绍</p>
+        <img class="remind-tag" src="/images/mobile/@2x/title-line.png" alt="">
+        <div class="main-sell">{{brandDetail.brief | wordFilter}}</div>
+      </div>
+      <div class="brand-param-item" v-if="brandDetail.url">
+        <p class="remind-title">官网地址</p>
+        <img class="remind-tag" src="/images/mobile/@2x/title-line.png" alt="">
+        <a class="brand-url" :href="brandDetail.url" v-text="brandDetail.url"></a>
+      </div>
+    </div>
+    <div class="brand-product-list" v-if="activeType=='product'">
+      <!--{{showKindList}}-->
+      <div class="search-box" v-if="searchLists && searchLists.length > 0 || isSearch">
+        <div class="kind-selecter" @click="onListClick($event)">
+          <div @mouseenter="isInList = true" @mouseleave="isInList = false">
+            <span v-text="selectedKind.substring(0, 4)"></span>
+            <ul v-if="showKindList">
+              <li @click="selectKind({nameCn: '全部分类', id: ''}, $event)" v-show="selectedKind !== '全部分类'">全部分类</li>
+              <li v-for="kind in kindList" v-text="kind.nameCn" @click="selectKind(kind, $event)" v-show="selectedKind !== kind.nameCn"></li>
+            </ul>
+          </div>
+        </div>
+        <div class="kind-search">
+          <input type="text" v-model="keyword" placeholder="请输入型号">
+          <i @click="goodsSearch()" class="icon-sousuo iconfont"></i>
+        </div>
+      </div>
+      <ul class="product-list" v-if="productList.totalElements > 0">
+        <li v-for="product in searchLists">
+          <nuxt-link class="text-left" :to="'/mobile/brand/componentDetail/' + product.uuid">{{product.code}}</nuxt-link>
+          <a class="text-right" @click="toShowPdf(product.attach)">规格书 <i class="icon-chakan iconfont"></i></a>
+        </li>
+      </ul>
+      <div class="no-product" v-if="!productList.totalElements || productList.totalElements == 0">
+        <img :src="!isSearch?'/images/mobile/@2x/car@2x.png':'/images/mobile/@2x/search-empty.png'" alt="">
+        <div>抱歉,暂无产品信息</div>
+      </div>
+    </div>
+    <loading v-show="isSearchingMore"></loading>
+    <login-box @onLoginBoxClose="showLoginBox = false" v-if="showLoginBox"></login-box>
+  </div>
+</template>
+<script>
+  import { Loading, LoginBox } from '~components/mobile/common'
+  export default {
+    name: 'MobileBrandsDetail',
+    data () {
+      return {
+        applications: [],
+        activeType: 'detail',
+        keyword: '',
+        showKindList: false,
+        parentid: 0,
+        ids: null,
+        pageParams: {
+          page: 1,
+          count: 10,
+          filter: {}
+        },
+        selectedKind: '全部分类',
+        isInList: false,
+        isSearch: false,
+        isSearchingMore: false,
+        searchLists: [],
+        isChange: false,
+        isFilter: false,
+        showLoginBox: false
+      }
+    },
+    components: {
+      Loading,
+      LoginBox
+    },
+    filters: {
+      wordFilter: function (str) {
+        return str.length > 65 ? str.substring(0, 65) + '...' : str
+      }
+    },
+    mounted: function () {
+      let _this = this
+      _this.$nextTick(function () {
+        window.addEventListener('scroll', function () {
+          _this.scroll()
+        }, false)
+        document.addEventListener('click', _this.checkShowFilter)
+      })
+    },
+    watch: {
+      keyword: function (val, oldVal) {
+        this.isSearch = true
+      }
+    },
+    computed: {
+      brandDetail () {
+        let list = this.$store.state.brandDetail.detail.data
+        if (list.application && list.application !== '') {
+          this.applications = list.application.split(',')
+        }
+        this.pageParams.filter.brandid = list.id
+        return list
+      },
+      productList () {
+        let list = this.$store.state.brandComponent.component.data
+        if (this.isChange || this.isFilter) {
+          this.searchLists = []
+          this.pageParams.page = 1
+          this.isChange = false
+          this.isFilter = false
+        } else {
+          this.searchLists = this.searchLists.concat(list.content)
+          this.isSearchingMore = false
+        }
+        return list
+      },
+      allPage () {
+        return this.productList.totalPages || 0
+      },
+      kindList () {
+        let brands = this.$store.state.brandCategories.categories.data
+        if (!brands || brands.length === 0) {
+          return []
+        }
+        // 初始化去除重复数据
+        for (let i = 0; i < brands.length; i++) {
+          for (let j = 0; j < brands[i].length; j++) {
+            brands[i][j].children = []
+          }
+        }
+
+        // 处理第1层
+        if ((brands[0] && brands[0].length > 0) && (brands[1] && brands[1].length > 0)) {
+          for (let i = 0; i < brands[1].length; i++) {
+            for (let j = 0; j < brands[0].length; j++) {
+              if (brands[0][j].id === brands[1][i].parentid) {
+                if (!brands[0][j].children) {
+                  brands[0][j].children = []
+                }
+                brands[0][j].children.push(brands[1][i])
+                break
+              }
+            }
+          }
+        }
+
+        // 处理第2层
+        if ((brands[1] && brands[1].length > 0) && (brands[2] && brands[2].length > 0)) {
+          for (let i = 0; i < brands[2].length; i++) {
+            for (let j = 0; j < brands[1].length; j++) {
+              if (brands[1][j].id === brands[2][i].parentid) {
+                if (!brands[1][j].children) {
+                  brands[1][j].children = []
+                }
+                brands[1][j].children.push(brands[2][i])
+                break
+              }
+            }
+          }
+        }
+
+        // 处理第3层
+        if ((brands[2] && brands[2].length > 0) && (brands[3] && brands[3].length > 0)) {
+          for (let i = 0; i < brands[3].length; i++) {
+            for (let j = 0; j < brands[2].length; j++) {
+              if (brands[2][j].id === brands[3][i].parentid) {
+                if (!brands[2][j].children) {
+                  brands[2][j].children = []
+                }
+                brands[2][j].children.push(brands[3][i])
+                break
+              }
+            }
+          }
+        }
+        let kindList = []
+        if (brands[0]) {
+          for (let i = 0; i < brands[0].length; i++) {
+            this.getKinds(brands[0][i], kindList)
+          }
+        }
+        return kindList
+      },
+      user () {
+        return this.$store.state.option.user
+      }
+    },
+    methods: {
+      onListClick: function ($event) {
+        $event.stopPropagation()
+        this.showKindList = !this.showKindList
+//        alert(this.showKindList)
+      },
+      scroll: function () {
+        let scrolled = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
+        if (Math.ceil(scrolled + window.screen.availHeight) >= document.body.scrollHeight && !this.isSearchingMore && this.pageParams.page < this.allPage && this.activeType === 'product') {
+          this.getMoreProduct()
+        }
+      },
+      getMoreProduct: function () {
+        if (!this.isSearchingMore) {
+          this.pageParams.page++
+          this.isSearchingMore = true
+          this.pageCommodity(this.pageParams, this.ids)
+        }
+      },
+      getKinds: function (list, kindList) {
+        if (list.children && list.children.length > 0) {
+          for (let i = 0; i < list.children.length; i++) {
+            this.getKinds(list.children[i], kindList)
+          }
+        } else {
+          kindList.push(list)
+        }
+      },
+      selectKind: function (data, $event) {
+        $event.stopPropagation()
+        this.showKindList = false
+        this.selectedKind = data.nameCn
+        this.isFilter = true
+        if (this.parentid === data.id) {
+          this.parentid = 0
+          this.ids = null
+        } else {
+          if (data.level === 1) {
+            this.parentid = data.id
+          }
+        }
+        this.pageParams.page = 1
+        this.pageParams.filter.brandid = this.brandDetail.id
+        if (data.id !== '') {
+          this.pageParams.filter.kindid = data.id
+        } else {
+          delete this.pageParams.filter.kindid
+        }
+        this.pageCommodity(this.pageParams, this.ids)
+      },
+      goodsSearch () {
+        this.pageParams.page = 1
+        this.pageParams.filter.code = this.keyword
+        this.isFilter = true
+        this.pageCommodity(this.pageParams)
+      },
+      async pageCommodity (params) {
+        try {
+          let { data } = await this.$http.get('/api/product/component/list', { params })
+          this.$store.commit('brandComponent/GET_COMPONENT_SUCCESS', data)
+        } catch (err) {
+          this.$store.commit('brandComponent/GET_COMPONENT_FAILURE', err)
+        }
+      },
+      checkShowFilter: function () {
+        if (!this.isInList) {
+          this.showKindList = false
+        }
+      },
+      toShowPdf: function (url) {
+        if (this.user.logged) {
+          if (url && url !== '1') {
+            window.location.href = url
+          }
+        } else {
+          this.showLoginBox = true
+        }
+      },
+      setActiveType: function (type) {
+//        if (type === 'product' && (this.pageParams.page !== 1 || this.isFilter)) {
+//          this.pageParams = {
+//            page: 1,
+//            count: 10,
+//            filter: {brandid: this.brandDetail.id}
+//          }
+//          this.selectedKind = '全部分类'
+//          this.keyword = ''
+//          this.isChange = true
+//          this.$store.dispatch('loadBrandComponent', this.pageParams)
+//          this.pageCommodity(this.pageParams)
+//        }
+        this.activeType = type
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .brand-detail {
+    margin: 0 auto;
+    margin-bottom: 1.2rem;
+    text-align: center;
+    background: #f7f7f7;
+    .brand-logo {
+      height: 3.17rem;
+      width: 6.96rem;
+      display: inline-block;
+      margin: .28rem auto;
+      line-height: 2.13rem;
+      background: #fff;
+      text-align: center;
+      border-radius: .1rem;
+      background: url('/images/mobile/@2x/brand-bg.png')no-repeat;
+      background-size: 7.16rem 3.17rem;
+      background-position: -.1rem 0;
+      box-shadow: 0 0 .01rem .03rem #eee;
+      .brand-logo-box {
+        border: .04rem solid #c7e5fd;
+        border-radius: .1rem;
+        height: 2.21rem;
+        width: 3.73rem;
+        margin: .5rem auto 0;
+        background: #fff;
+        position: relative;
+        img {
+          max-height: 2.13rem;
+          max-width: 3.7rem;
+        }
+      }
+    }
+    .brand-switch-item {
+      text-align: center;
+      background: #fff;
+      margin-bottom: .28rem;
+      .mobile-switch-btn {
+        background: #fff;
+        color: #666;
+        display: inline-block;
+        height: .64rem;
+        font-size: .34rem;
+        line-height: .64rem;
+        width: 1.4rem;
+        &:first-child {
+          margin-right: 1.78rem;
+        }
+        &.active {
+          color: #fc5708;
+          border-bottom: .04rem solid #fc5708;
+        }
+      }
+    }
+    .brand-param-list {
+      text-align: left;
+      padding: .3rem .44rem .11rem;
+      margin-top: .28rem;
+      background: #fff;
+      .brand-param-item {
+        font-size: .3rem;
+        margin-bottom: .48rem;
+        .remind-tag {
+          width: 1.18rem;
+          float: left;
+          margin-top: .05rem;
+        }
+        .remind-title {
+          font-size: .3rem;
+          color: #418bf6;
+          margin: 0;
+        }
+        .main-sell {
+          color: #666;
+          line-height: .4rem;
+          max-height: 1.2rem;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          display: -webkit-box;
+          -webkit-box-orient: vertical;
+          -webkit-line-clamp: 3;
+          margin-top: .15rem;
+        }
+        .brand-url {
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          color: #01a44e;
+          margin-left: .28rem;
+          position: relative;
+          bottom: .32rem;
+          max-width: 5rem;
+          display: inline-block;
+          &:hover, &:active, &:focus, &:visited {
+            text-decoration: underline!important;
+          }
+        }
+      }
+    }
+    .brand-product-list {
+      font-size: .28rem;
+      background: #fff;
+      ul.product-list {
+        text-align: center;
+        li {
+          margin-left: .42rem;
+          width: 6.66rem;
+          height: .66rem;
+          line-height: .66rem;
+          border: {
+            bottom: .04rem solid rgb(230,228,228);
+          }
+          &:nth-child(even) {
+            background: #f9f9f9;
+          }
+          &:nth-child(1) {
+            border-top: .04rem solid rgb(230,228,228);
+          }
+          &:active, &:hover {
+            background: #eee;
+          }
+          .text-left {
+            float: left;
+            color: #333;
+            width: 4.45rem;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+          }
+          .text-right {
+            float: right;
+            color: #333;
+            i {
+              font-size: .55rem;
+              float: right;
+              margin-right: .27rem;
+              margin-left: .54rem;
+              color: #6fcafe;
+            }
+          }
+        }
+      }
+      .search-box {
+        margin-bottom: .28rem;
+        padding-top: .28rem;
+        .kind-selecter {
+          display: inline-block;
+          position: relative;
+          float: left;
+          margin-left: .72rem;
+          div {
+            display: inline-block;
+            span {
+              width: 1.64rem;
+              height: .6rem;
+              line-height: .6rem;
+              border: .04rem solid rgb(195,195,195);
+              border-radius: .05rem;
+              font-size: .28rem;
+              display: inline-block;
+              background: url('/images/mobile/@2x/productDetail/kind-narrow-down@2x.png')no-repeat;
+              padding-right: .15rem;
+              background-position: 1.35rem .25rem;
+              background-size: .14rem .1rem;
+              overflow: hidden;
+            }
+          }
+        }
+        .kind-search {
+          display: inline-block;
+          margin-right: .19rem;
+          width: 4.36rem;
+          height: .6rem;
+          line-height: .6rem;
+          vertical-align: middle;
+          input[type = "text"] {
+            display: inline-block;
+            width: 3.61rem;
+            height: .6rem;
+            border: .04rem solid rgb(195,195,195);
+            padding-left: .21rem;
+            font-size: .24rem;
+            float: left;
+            border-radius: .05rem;
+          }
+          i {
+            background: rgb(65,142,247);
+            width: .73rem;
+            height: .6rem;
+            line-height: .6rem;
+            font-size: .32rem;
+            color: #fff;
+            display: inline-block;
+            margin-left: -.04rem;
+          }
+        }
+        ul {
+          position: absolute;
+          top: 0.65rem;
+          max-height: 3.15rem;
+          overflow-y: auto;
+          z-index: 1;
+          &::-webkit-scrollbar
+          {
+            display: none;
+          }
+          li {
+            width: 1.64rem;
+            height: .83rem;
+            line-height: .83rem;
+            padding: 0 .08rem;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            background: rgba(0, 0, 0, 0.6);
+            color: #fff;
+          }
+        }
+      }
+      .no-product {
+        background: #fff;
+        padding-top: 1rem;
+        img {
+          display: block;
+          text-align: center;
+          margin: 0 auto;
+          margin-bottom: .45rem;
+          width: 4.11rem;
+          height: 2.5rem;
+        }
+        div {
+          width: 5.27rem;
+          margin: 0 auto;
+          text-align: center;
+          line-height: .4rem;
+          font-size: .32rem;
+          color: #999;
+        }
+      }
+    }
+  }
+</style>

+ 494 - 0
components/mobile/brand/ComponentDetail.vue

@@ -0,0 +1,494 @@
+<template>
+  <div class="component-detail">
+    <div class="base-detail">
+      <div class="base-detail-item" v-if="component.kind.nameCn">
+        <span>类&nbsp;&nbsp;&nbsp;&nbsp;目:</span>
+        <span>{{component.kind.nameCn}}</span>
+      </div>
+      <div class="base-detail-item" v-if="component.brand.nameCn">
+        <span>品&nbsp;&nbsp;&nbsp;&nbsp;牌:</span>
+        <span>{{component.brand.nameCn}}</span>
+      </div>
+      <div class="base-detail-item attach" @click="goAttach(component.attach)">
+        <span v-if="component.attach && component.attach != '1'">规格书:<img src="/images/mobile/@2x/productDetail/pdf.png" alt=""><span>查看</span></span>
+        <span v-else>规格书:-</span>
+      </div>
+      <div class="base-detail-item product-description" v-if="component.description">
+        <span class="description">产品描述:{{component.description}}</span>
+      </div>
+      <i class="iconfont icon-shoucang" :style="isCollect?'color:#ff7800':'color: #ddd'" @click="collectComponent"></i>
+    </div>
+    <div class="product-switch-item">
+      <span :class="activeType=='param'?'mobile-switch-btn active':'mobile-switch-btn'" @click="activeType='param'">参数</span>
+      <span :class="activeType=='store'?'mobile-switch-btn active':'mobile-switch-btn'" @click="activeType='store'">商家</span>
+    </div>
+    <div class="product-params" v-if="activeType == 'param'">
+      <div class="param-item" v-if="prop.value && prop.value!=''" v-for="prop in component.properties">
+        <span class="prop-name">{{prop.property.labelCn}}</span>
+        <span class="prop-value">{{prop.value}}</span>
+      </div>
+    </div>
+    <div class="product-store" v-if="activeType == 'store'">
+      <table v-if="searchLists&&searchLists.length > 0">
+        <thead>
+          <tr>
+            <th>商家</th>
+            <th>生产日期</th>
+            <th>数量<span>(PCS)</span></th>
+            <th>单价</th>
+            <th>交期(天)</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="store in searchLists">
+            <td class="store-name">
+              <div>
+                <nuxt-link :to="'/mobile/shop/' + store.storeid">
+                  {{store.storeName || '-' | storeNameFilter}}
+                </nuxt-link>
+              </div>
+            </td>
+            <td>
+              <div v-if="!store.packaging && !store.breakUp && !store.produceDate">-</div>
+              <div>{{store.produceDate}}</div>
+              <div>{{store.packaging}}</div>
+              <div>{{store.breakUp?'可拆卖':'不可拆卖'}}</div>
+            </td>
+            <td>
+              <div v-if="!store.prices || store.prices.length == 0">-</div>
+              <div v-for="price in store.prices">{{price.start}}+</div>
+            </td>
+            <td>
+              <div v-if="!store.prices || store.prices.length == 0">
+                <span>—</span>
+              </div>
+              <div v-for="price in store.prices" class="price-level">
+                <span v-if="store.currencyName.indexOf('RMB')!==-1">¥{{price.rMBPrice | currency}}</span>
+                <span v-if="store.currencyName.indexOf('USD')!==-1">${{price.uSDPrice | currency}}</span>
+              </div>
+            </td>
+            <td class="push-date">
+              <div v-if="store.b2cMinDelivery">
+                <span>{{store.b2cMinDelivery}}</span>
+                <span v-if="store.b2cMaxDelivery && store.b2cMaxDelivery !== store.b2cMinDelivery">-</span>
+                <span v-if="store.b2cMaxDelivery && store.b2cMaxDelivery !== store.b2cMinDelivery">{{store.b2cMaxDelivery}}</span>
+              </div>
+              <div v-if="store.minBuyQty"><span class="order-tag">订</span>{{store.minBuyQty}}起订</div>
+              <div v-if="!store.b2cMinDelivery">
+                <span>—</span>
+              </div>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <div v-if="(storeList.totalElements == 0 && activeType == 'store') || (component.properties && component.properties.length == 0 && activeType == 'param')" class="no-store">
+      <img src="/images/mobile/@2x/car@2x.png" alt="">
+      <div v-if="activeType == 'store'">抱歉,暂无商家出售此型号!</div>
+      <div v-if="activeType == 'store'">您可前往<strong>www.usoftmall.com</strong>网页版进行<strong>“发布求购”</strong>或<strong>“产品上架”</strong>操作!</div>
+      <div v-if="activeType == 'param'">抱歉,暂无参数信息!</div>
+    </div>
+    <remind-box :title="collectResult" :timeoutCount="timeoutCount"></remind-box>
+    <loading v-show="isSearchingMore"></loading>
+    <login-box @onLoginBoxClose="showLoginBox = false" v-if="showLoginBox"></login-box>
+  </div>
+</template>
+<script>
+  import {RemindBox, Loading, LoginBox} from '~components/mobile/common'
+  export default {
+    data () {
+      return {
+        activeType: 'param',
+        collectResult: '收藏成功',
+        timeoutCount: 0,
+        storeIds: [],
+        UmallExist: false,
+        storeExist: false,
+        params: {
+          count: 10,
+          page: 1,
+          sorting: {'minPriceRMB': 'ASC'},
+          filter: {
+            uuid: this.$route.params.uuid,
+            ignoreUMall: false,
+            ignoreStore: false,
+            storeIds: ''
+          }
+        },
+        isSearchingMore: false,
+        searchLists: [],
+        showLoginBox: false
+      }
+    },
+    components: {
+      RemindBox,
+      Loading,
+      LoginBox
+    },
+    mounted: function () {
+      let _this = this
+      _this.$nextTick(function () {
+        window.addEventListener('scroll', function () {
+          _this.scroll()
+        }, false)
+      })
+    },
+    computed: {
+      component () {
+        return this.$store.state.componentDetail.detail.data
+      },
+      storeList () {
+        let storeList = this.$store.state.componentInformation.information.data
+        let _self = this
+        if (storeList.content) {
+          storeList.content.forEach(function (item) {
+            _self.storeIds.push(item.storeid)
+          })
+        }
+        if (this.storeIds.length > 0) {
+          if (this.storeIds.indexOf(this.storeId) === -1) {
+            this.storeExist = true
+          } else {
+            this.storeIds.splice(this.storeIds.indexOf(this.storeId), 1)
+            if (this.storeIds.length > 0) {
+              this.storeExist = true
+            }
+            this.UmallExist = true
+          }
+        }
+        this.searchLists = this.searchLists.concat(storeList.content)
+        this.isSearchingMore = false
+        return storeList
+      },
+      allPage () {
+        return this.storeList.totalPages
+      },
+      colList () {
+        return this.$store.state.product.common.collectList.data
+      },
+      isCollect () {
+        let id = this.component.id
+        let store = this.colList
+        if (store) {
+          for (let i = 0; i < store.length; i++) {
+            if (store[i].componentid === id) {
+              return true
+            }
+          }
+        } else {
+          return false
+        }
+      },
+      user () {
+        return this.$store.state.option.user
+      }
+    },
+    filters: {
+      currency: function (num) {
+        if (typeof num === 'number') {
+          if (num <= 0.000001) {
+            num = 0.000001
+          } else {
+            if (num.toString().indexOf('.') === -1) {
+              num += '.00'
+            } else {
+              let inputStr = num.toString()
+              let arr = inputStr.split('.')
+              let floatNum = arr[1]
+              if (floatNum.length > 6) {
+                num = inputStr.substring(0, arr[0].length + 7)
+                if (Number(floatNum.charAt(6)) > 4) {
+                  num = (Number(num) * 1000000 + 1) / 1000000
+                }
+              } else if (floatNum.length === 1) {
+                num = num + '0'
+              }
+            }
+          }
+        }
+        return num
+      },
+      storeNameFilter: function (str) {
+        if (str === '') {
+          return str
+        }
+        let len = 0
+        let index = 0
+        for (let i = 0; i < str.length; i++) {
+          if (index === 0 && str.charAt(i).charCodeAt(0) > 255) {
+            len = len + 2
+          } else {
+            len++
+          }
+          if (len > 22) {
+            index = i
+            break
+          }
+        }
+        if (index > 0) {
+          return str.substring(0, index) + '...'
+        } else {
+          return str
+        }
+      }
+    },
+    methods: {
+      scroll: function () {
+        let scrolled = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
+        if (Math.ceil(scrolled + window.screen.availHeight) >= document.body.scrollHeight && !this.isSearchingMore && this.params.page < this.allPage) {
+          this.getMoreStore()
+        }
+      },
+      getMoreStore: function () {
+        if (!this.isSearchingMore) {
+          this.params.page++
+          this.isSearchingMore = true
+          this.$store.dispatch('loadComponentInformation', this.params)
+        }
+      },
+      goAttach: function (url) {
+        if (this.user.logged) {
+          if (url && url !== '1') {
+            window.location.href = url
+          }
+        } else {
+          this.showLoginBox = true
+        }
+      },
+      collectComponent: function () {
+        if (this.user.logged) {
+          if (!this.isCollect) {
+            this.$store.dispatch('product/saveEntity', {componentid: this.component.id, kind: 2})
+            this.collectResult = '收藏成功'
+            this.timeoutCount++
+          } else {
+            this.$http.post('/trade/collection/delete/cmpId', [this.component.id]).then(response => {
+              this.collectResult = '取消成功'
+              this.timeoutCount++
+              this.$store.dispatch('product/saveStores')
+            })
+          }
+        } else {
+          this.showLoginBox = true
+        }
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .component-detail {
+    font-size: .28rem;
+    margin-bottom: 1.2rem;
+    background: #f7f7f7;
+    padding-top: .2rem;
+    .base-detail {
+      margin: 0 .27rem .2rem .27rem;
+      padding: .18rem .36rem 0 .36rem;
+      border-radius: .1rem;
+      background: url('/images/mobile/@2x/productDetail/component-desc-bg.png')no-repeat;
+      background-size: cover;
+      max-height: 3.17rem;
+      position: relative;
+      .base-detail-item {
+        margin-top: .2rem;
+        position: relative;
+        color: #fff;
+        &:nth-child(1) {
+          margin-top: 0;
+        }
+        &:nth-last-child(1) {
+          color: #999;
+        }
+        &.attach {
+          display: inline-block;
+          img {
+            background-color: #fff;
+            width: .36rem;
+            height: .4rem;
+            position: relative;
+            bottom: .05rem;
+          }
+          >span >span {
+            margin-left: .1rem;
+            color: #418bf6;
+          }
+        }
+        &:last-child {
+          margin-bottom: 0;
+        }
+        &.product-description {
+          height: 1.58rem;
+        }
+        .description {
+          line-height: .4rem;
+          max-height: 1.2rem;
+          word-break: break-all;
+          overflow : hidden;
+          text-overflow: ellipsis;
+          display: -webkit-box;
+          -webkit-line-clamp: 3;
+          -webkit-box-orient: vertical;
+          color: #999;
+        }
+      }
+      >i {
+        position: absolute;
+        font-size: .4rem;
+        background: #fff;
+        width: .6rem;
+        height: .6rem;
+        line-height: .6rem;
+        border-radius: 100%;
+        box-shadow: 0 0 .05rem #aaa;
+        right: .28rem;
+        top: .55rem;
+        text-align: center;
+      }
+    }
+    .product-switch-item {
+      text-align: center;
+      background: #fff;
+      .mobile-switch-btn {
+        background: #fff;
+        color: #666;
+        display: inline-block;
+        height: .64rem;
+        line-height: .64rem;
+        font-size: .34rem;
+        width: 1.4rem;
+        &:first-child {
+          margin-right: 1.78rem;
+        }
+        &.active {
+          color: #fc5708;
+          border-bottom: .04rem solid #fc5708;
+        }
+      }
+    }
+    .product-params {
+      line-height: .28rem;
+      margin-top: .2rem;
+      .param-item {
+        padding: .19rem .4rem;
+        border-bottom: 0.04rem solid #eee;
+        &:nth-child(1) {
+          border-top: 0.04rem solid #eee;
+        }
+        &:nth-child(even) {
+          background: #f9f9f9;
+        }
+        &:nth-child(odd) {
+          background: #fff;
+        }
+        .prop-name {
+          width: 3.72rem;
+          display: inline-block;
+          text-overflow: ellipsis;
+          overflow: hidden;
+          white-space: nowrap;
+        }
+        .prop-value {
+          text-overflow: ellipsis;
+          overflow: hidden;
+          white-space: nowrap;
+          display: inline-block;
+          width: 2.69rem;
+          float: right;
+          text-align: right;
+        }
+      }
+    }
+    .product-store {
+      margin: .2rem 0;
+      table {
+        width: 100%;
+        font-size: .28rem;
+        thead {
+          background: #d5e5fb;
+          tr {
+            th {
+              font-weight: bold;
+              text-align: center;
+              height: .78rem;
+              line-height: .78rem;
+              >span {
+               font-size: .22rem;
+              }
+            }
+          }
+        }
+        tbody {
+          background: #fff;
+          tr {
+            border-bottom: 0.2rem solid #f7f7f7;
+            td {
+              padding: .2rem .1rem;
+              &.store-name {
+                color: #418bf6;
+                div {
+                  padding: 0;
+                  width: 1.2rem;
+                  overflow: hidden;
+                  margin-left: .16rem;
+                }
+              }
+              div {
+                margin-bottom: .2rem;
+                text-align: left;
+                &:last-child {
+                  margin-bottom: 0;
+                }
+              }
+              &.push-date {
+              text-align: center;
+                div {
+                  text-align: center;
+                }
+            }
+              .price-level:last-child {
+                color: #fc5708;
+              }
+              .order-tag {
+                display: inline-block;
+                font-size: .18rem;
+                color: #fff;
+                font-weight: bold;
+                background: #ee1717;
+                height: .27rem;
+                width: .27rem;
+                line-height: .27rem;
+                text-align: center;
+                border-radius: .05rem;
+                position: relative;
+                top: -.03rem;
+              }
+            }
+          }
+        }
+      }
+    }
+    .no-store {
+      background: #fff;
+      padding-top: 1rem;
+      img {
+        display: block;
+        text-align: center;
+        margin: 0 auto;
+        margin-bottom: .45rem;
+        width: 3.31rem;
+        height: 2.13rem;
+      }
+      div {
+        width: 5.27rem;
+        margin: 0 auto;
+        text-align: center;
+        line-height: .4rem;
+        color: #999;
+        .link-url {
+          color: #01a44e;
+        }
+      }
+    }
+  }
+
+</style>

+ 16 - 0
components/mobile/common/Loading.vue

@@ -0,0 +1,16 @@
+<template>
+  <div class="loading">
+    <img src="/images/all/loading.gif" alt="">
+  </div>
+</template>
+<style lang="scss" scoped>
+  .loading {
+    text-align: center;
+    background: #fff;
+    >img {
+      width: .64rem;
+      height: .64rem;
+      margin: .2rem 0;
+    }
+  }
+</style>

+ 41 - 0
components/mobile/common/LoginBox.vue

@@ -0,0 +1,41 @@
+<template>
+  <div class="mobile-modal">
+    <div class="mobile-modal-box">
+      <div class="mobile-modal-header">请登录后再操作<i @click="close" class="icon-guanbi iconfont"></i></div>
+      <div class="mobile-modal-content">
+        <span @click="close">暂不登录</span><span @click="goLogin">马上登录</span>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+  export default {
+    methods: {
+      close: function () {
+        this.$emit('onLoginBoxClose')
+      },
+      goLogin: function () {
+        this.$router.push('/auth/login')
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .mobile-modal-content {
+    padding: .54rem !important;
+    text-align: center;
+    span {
+      display: inline-block;
+      width: 1.5rem;
+      height: .6rem;
+      line-height: .6rem;
+      text-align: center;
+      background: #418df6;
+      color: #fff;
+      border-radius: .1rem;
+      &:first-child {
+        margin-right: .5rem;
+      }
+    }
+  }
+</style>

+ 51 - 0
components/mobile/common/PullDown.vue

@@ -0,0 +1,51 @@
+<template>
+  <div class="loading" v-show="isSearchingMore">
+    <img src="/images/all/loading.gif" alt="">
+  </div>
+</template>
+<script>
+  export default {
+    data () {
+      return {
+        isSearchingMore: false,
+        searchLists: [],
+        page: 1
+      }
+    },
+    props: ['searchMore', 'allPage', 'count'],
+    mounted: function () {
+      let _this = this
+      _this.$nextTick(function () {
+        window.addEventListener('scroll', function () {
+          _this.scroll()
+        }, false)
+      })
+    },
+    methods: {
+      scroll: function () {
+        let scrolled = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
+        if (Math.ceil(scrolled + window.screen.availHeight) >= document.body.scrollHeight && !this.isSearchingMore && this.page < this.allPage) {
+          this.getMore()
+        }
+      },
+      getMore: function () {
+        if (!this.isSearchingMore) {
+          this.page++
+          this.isSearchingMore = true
+          this.searchMore(this.page, this.count)
+        }
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .loading {
+    text-align: center;
+    background: #fff;
+    >img {
+      width: .64rem;
+      height: .64rem;
+      margin: .2rem 0;
+    }
+  }
+</style>

+ 48 - 0
components/mobile/common/RemindBox.vue

@@ -0,0 +1,48 @@
+<template>
+  <div class="com-remind-box" v-show="showBox">{{title}}</div>
+</template>
+<script>
+  export default {
+    props: ['title', 'timeoutCount'],
+    data () {
+      return {
+        showBox: false,
+        timer: ''
+      }
+    },
+    watch: {
+      timeoutCount: function (val, oldVal) {
+        if (val > 0) {
+          clearTimeout(this.timer)
+          this.setTimer()
+        } else {
+          this.showBox = false
+        }
+      }
+    },
+    methods: {
+      setTimer: function () {
+        let _this = this
+        _this.showBox = true
+        _this.timer = setTimeout(function () {
+          _this.showBox = false
+        }, 1000)
+      }
+    }
+  }
+</script>
+<style>
+  .com-remind-box{
+    position: fixed;
+    top: 50%;
+    left: 50%;
+    margin-left: -1.07rem;
+    margin-top: -.6rem;
+    z-index: 100;
+    background: rgba(0,0,0,.6);
+    color: #fff;
+    font-size: .28rem;
+    padding: .44rem .51rem;
+    border-radius: .1rem;
+  }
+</style>

+ 5 - 0
components/mobile/common/index.js

@@ -0,0 +1,5 @@
+import Loading from './Loading.vue'
+import RemindBox from './RemindBox.vue'
+import LoginBox from './LoginBox.vue'
+
+export { Loading, RemindBox, LoginBox }

+ 27 - 0
components/mobile/help/HelpFooter.vue

@@ -0,0 +1,27 @@
+<template>
+  <footer>
+      <div class=" text-center">
+        <p>
+          &copy; 2016 深圳市优软科技有限公司
+        </p>
+      </div>
+  </footer>
+</template>
+<script>
+  export default{
+    name: 'HelpFooter'
+  }
+</script>
+<style scoped>
+footer{
+  position: fixed;
+  bottom: 0;
+  background: #fff;
+  height: 40px;
+  line-height: 40px;
+}
+footer p{
+  font-size: 14px;
+  color: #000;
+}
+</style>

+ 39 - 0
components/mobile/help/HelpHeader.vue

@@ -0,0 +1,39 @@
+<template>
+  <nav id="nav" class="navbar navbar-default">
+    <div class="container-fluid">
+      <div class="navbar-header">
+        <a class="navbar-brand pull-left" href="/" style="color: #fff; font-size: 16px;">返回首页</a>
+      </div>
+    </div>
+  </nav>
+</template>
+<script>
+  export default {
+  }
+</script>
+<style lang="scss" scoped>
+  @import '~assets/scss/mobileCommon';
+  /* nav */
+  #nav {
+    background-color: #474443;
+    border-color: #474443;
+  }
+  .navbar {
+    margin-bottom: 0;
+    min-height: 40px;
+  }
+  .navbar-toggle {
+    padding: 4px 5px;
+  }
+  .navbar-default .navbar-collapse, .navbar-default .navbar-form {
+    border-color: #666666;
+  }
+  .navbar-default .navbar-nav>li>a {
+    color: #dddddd;
+  }
+  .navbar-brand {
+    padding: 10px 15px;
+    height: 40px;
+  }
+
+</style>

+ 4 - 0
components/mobile/help/index.js

@@ -0,0 +1,4 @@
+import HelpHeader from './HelpHeader.vue'
+import HelpFooter from './HelpFooter.vue'
+
+export { HelpHeader, HelpFooter }

+ 5 - 0
components/mobile/index.js

@@ -0,0 +1,5 @@
+import MobileHeader from './MobileHeader.vue'
+import MobileFooter from './MobileFooter.vue'
+import Home from './Home.vue'
+
+export { MobileFooter, MobileHeader, Home }

+ 289 - 0
components/mobile/search/MainSearch.vue

@@ -0,0 +1,289 @@
+<template>
+  <div class="main-search" @touchstart="cancelFocus">
+    <div class="main-search-header">
+      <input type="text" id="search-box" v-model="keyword" placeholder="请输入您要查找的型号或品牌" @keyup.13="onSearch()">
+      <span @click="onSearch()">搜索</span>
+      <a @click="cancelSearch">取消</a>
+    </div>
+    <ul class="associate-list" v-show="associate.show">
+      <li @click="onAssociateClick(similar)" v-for="similar in similarKeywords.all">
+        <i class="icon-sousuo iconfont"></i>
+        <span>{{similar}}</span>
+      </li>
+      <li @click="onAssociateClick(keyword)">查找“{{keyword}}”</li>
+    </ul>
+    <div class="hot-history" v-show="!associate.show">
+      <div class="search-history" v-if="searchHistory && searchHistory.length > 0">
+        <p>历史搜索<i class="iconfont icon-lajitong" @click="deleteHistory"></i></p>
+        <ul>
+          <li v-for="item in searchHistory" @click="onSearch(item.keyword)">
+            <a>{{item.keyword}}</a>
+          </li>
+        </ul>
+      </div>
+      <div class="search-hot">
+        <img src="/images/mobile/@2x/home/hot-search.png" alt="">
+        <ul>
+          <li v-for="hotword in hotwords">
+            <nuxt-link :to="hotword.url" v-text="hotword.name"></nuxt-link>
+          </li>
+        </ul>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+  export default {
+    name: 'home',
+    data () {
+      return {
+        keyword: '',
+        associate: {
+          show: false
+        }
+      }
+    },
+    props: {
+      hotwords: {
+        type: Array,
+        default () {
+          return [
+            {name: 'DSP1-DC5V-F', url: '/mobile/brand/componentDetail/0900300200000669'},
+            {name: 'Vishay', url: '/mobile/brand/30327265e42a871be050007f01003d96'},
+            {name: 'Panasonic', url: '/mobile/brand/30327265e47d871be050007f01003d96'},
+            {name: 'Taiyo Yuden', url: '/mobile/brand/30327265e4be871be050007f01003d96'},
+            {name: 'AE3ZZP332', url: '/mobile/brand/componentDetail/0100100100000003'}
+          ]
+        }
+      }
+    },
+//    filters: {
+//      similarFilter: function ([key, keyword]) {
+//        console.log(keyword)
+//        let index = key.indexOf(keyword)
+//        if (index !== -1) {
+//          key = key.substring(0, index) + '<strong>' + key.substr(index, keyword.length) + '</strong>' + key.substring(index + keyword.length, key.length)
+//        }
+//        return key
+//      }
+//    },
+    methods: {
+      onSearch (key) {
+        if (key && key !== '') {
+          this.keyword = key
+        }
+        if (this.keyword) {
+          this.$router.push({path: '/mobile/search?w=' + encodeURIComponent(this.keyword)})
+        }
+      },
+      onChange () {
+        if (!this.keyword) {
+          this.associate.show = false
+          this.$store.dispatch('resetSearchKeywords')
+        } else {
+          this.searchKeywords()
+        }
+        if (this.click_flag) {
+          this.associate.show = false
+        }
+      },
+      searchKeywords () {
+        this.$store.dispatch('searchKeywords', { keyword: this.keyword })
+        this.associate.show = true
+      },
+      onAssociateClick (word) {
+        this.keyword = word
+        this.onSearch()
+      },
+      cancelSearch: function () {
+        this.$emit('cancelSearchAction')
+      },
+      cancelFocus: function () {
+        document.getElementById('search-box').blur()
+      },
+      deleteHistory () {
+        this.$http.delete('/search/searchHistory').then(response => {
+          this.$store.dispatch('searchData/getSearchHistory')
+        })
+      }
+    },
+    created () {
+      this.$store.dispatch('resetSearchKeywords')
+    },
+    mounted () {
+      document.getElementById('search-box').focus()
+    },
+    watch: {
+      'keyword': function (val, oldVal) {
+        let keywords = this.similarKeywords.data
+        if (!keywords || !keywords.length) {
+          this.onChange()
+        }
+      }
+    },
+    computed: {
+      similarKeywords () {
+        return this.$store.state.search.keywords.data
+      },
+      searchHistory () {
+        return this.$store.state.searchData.searchHistory.searchHistory.data
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .main-search {
+    background: #fff;
+    height: -webkit-fill-available;
+    width: -webkit-fill-available;
+    position: fixed;
+    z-index: 1000;
+    top: 0;
+    bottom: 0;
+    .main-search-header {
+      height: .88rem;
+      background: #3e82f5;
+      padding-left: .5rem;
+      line-height: .88rem;
+      input {
+        width: 4.78rem;
+        height: .62rem;
+        line-height: .62rem;
+        font-size: .28rem;
+        color: #999;
+        padding-left: .2rem;
+        border: .04rem solid #fff;
+        background: #fff;
+        outline: none;
+        border-radius: 0;
+        float: left;
+        margin-top: .12rem;
+        -webkit-appearance: none;
+        border-top-left-radius: .05rem;
+        border-bottom-left-radius: .05rem;
+      }
+      span {
+        display: inline-block;
+        width: 1.02rem;
+        text-align: center;
+        height: .62rem;
+        line-height: .62rem;
+        color: #366df3;
+        font-size: .28rem;
+        margin-left: .02rem;
+        border-top-right-radius: .05rem;
+        border-bottom-right-radius: .05rem;
+        background: #fff;
+        float: left;
+        margin-top: .12rem;
+      }
+      a {
+        font-size: .28rem;
+        color: #fff;
+        margin-left: .2rem;
+      }
+    }
+    .associate-list {
+      background: #fff;
+      li {
+        height: 0.7rem;
+        line-height: .9rem;
+        margin: 0 .45rem;
+        border-bottom: .04rem solid #f1f0f0;
+        i {
+          font-size: .36rem;
+          margin-right: .24rem;
+          color: #ddd;
+        }
+        span {
+          color: #999;
+          font-size: .28rem;
+          line-height: .58rem;
+          height: .58rem;
+          display: inline-block;
+        }
+        &:active, &:hover {
+          background: #eee;
+        }
+        &:last-child {
+          text-align: center;
+          font-size: .3rem;
+          color: #3976f4;
+          border-bottom: none;
+          &:active, &:hover {
+            background: #fff;
+          }
+        }
+      }
+    }
+    .hot-history {
+      .search-history {
+        padding-left: .51rem;
+        padding-top: .38rem;
+        >p {
+          font-size: .3rem;
+          color: #333;
+          i {
+            font-size: .3rem;
+            float: right;
+            margin-right: 0.4rem;
+          }
+        }
+        ul {
+          text-align: left;
+          margin-top: .26rem;
+          li {
+            display: inline-block;
+            max-width: 2.83rem;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            margin: 0 .1rem .1rem 0;
+            background: #f2f6ff;
+            height: .56rem;
+            line-height: .56rem;
+            padding: 0 .12rem;
+            a {
+              font-size: .3rem;
+              color: #666;
+            }
+          }
+        }
+      }
+      .search-hot {
+        text-align: center;
+        margin-top: .3rem;
+        >img {
+          width: 2.56rem;
+          height: .67rem;
+        }
+        ul {
+          text-align: left;
+          padding-left: .51rem;
+          margin-top: .31rem;
+          li {
+            display: inline-block;
+            max-width: 2.83rem;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            margin: 0 .1rem .1rem 0;
+            background: #fef1eb;
+            height: .56rem;
+            line-height: .56rem;
+            padding: 0 .12rem;
+            a {
+              font-size: .3rem;
+              color: #666;
+            }
+            &:nth-child(1) {
+              a {
+                color: #fc5708;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+</style>

+ 386 - 0
components/mobile/store/StoreDetail.vue

@@ -0,0 +1,386 @@
+<template>
+  <div class="store-detail">
+    <div class="store-logo">
+      <div class="store-logo-box">
+        <img :src="store.logoUrl || '/images/component/default.png'"/>
+        <i class="iconfont icon-shoucang" :style="isFocus === 'true'?'color:#ff7800':'color: #ddd'" @click="collectStore"></i>
+      </div>
+    </div>
+    <div class="store-switch-item">
+      <span :class="activeType=='detail'?'mobile-switch-btn active':'mobile-switch-btn'" @click="activeType='detail'">介绍</span>
+      <span :class="activeType=='product'?'mobile-switch-btn active':'mobile-switch-btn'" @click="activeType='product'">产品</span>
+    </div>
+    <div class="store-description" v-if="activeType=='detail'">
+      <p>
+        {{store.description}}
+      </p>
+    </div>
+    <div class="product-store" v-if="activeType == 'product'">
+      <table v-if="commodities.content&&commodities.content.length > 0">
+        <thead>
+        <tr>
+          <th>型号/品牌</th>
+          <th>包装</th>
+          <th>数量</th>
+          <th>单价</th>
+          <th>交期(天)</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="commodity in searchLists" @click="goProductDetail(commodity.uuid)">
+          <td class="store-name">
+            <div>{{commodity.code}}</div>
+            <div>{{commodity.brandNameCn}}</div>
+          </td>
+          <td>
+            <div v-if="!commodity.packaging && !commodity.breakUp && !commodity.produceDate">-</div>
+            <div>{{commodity.packaging}}</div>
+            <div>{{commodity.breakUp?'可拆卖':'不可拆卖'}}</div>
+            <div>{{commodity.produceDate}}</div>
+          </td>
+          <td>
+            <div v-if="!commodity.prices || commodity.prices.length == 0">-</div>
+            <div v-for="price in commodity.prices">{{price.start}}+</div>
+          </td>
+          <td>
+            <div v-if="!commodity.prices || commodity.prices.length == 0">
+              <span>—</span>
+            </div>
+            <div v-for="price in commodity.prices" class="price-level">
+              <span v-if="commodity.currencyName.indexOf('RMB')!==-1">¥{{price.rMBPrice | currency}}</span>
+              <span v-if="commodity.currencyName.indexOf('USD')!==-1">${{price.uSDPrice | currency}}</span>
+            </div>
+          </td>
+          <td>
+            <div v-if="commodity.b2cMinDelivery">
+              <span>{{commodity.b2cMinDelivery}}</span>
+              <span v-if="commodity.b2cMaxDelivery && commodity.b2cMaxDelivery !== commodity.b2cMinDelivery">-</span>
+              <span v-if="commodity.b2cMaxDelivery && commodity.b2cMaxDelivery !== commodity.b2cMinDelivery">{{commodity.b2cMaxDelivery}}</span>
+            </div>
+            <div v-if="commodity.minBuyQty"><span class="order-tag">订</span>{{commodity.minBuyQty}}起订</div>
+            <div v-if="!commodity.b2cMinDelivery">
+              <span>—</span>
+            </div>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+      <div v-if="!commodities.content || commodities.content.length == 0" class="no-product">
+        <img src="/images/mobile/@2x/car@2x.png" alt="">
+        <div>抱歉,暂无产品信息</div>
+      </div>
+    </div>
+    <remind-box :title="collectResult" :timeoutCount="timeoutCount"></remind-box>
+    <loading v-show="isSearchingMore"></loading>
+    <login-box @onLoginBoxClose="showLoginBox = false" v-if="showLoginBox"></login-box>
+  </div>
+</template>
+<script>
+  import {RemindBox, Loading, LoginBox} from '~components/mobile/common'
+  export default {
+    data () {
+      return {
+        activeType: 'detail',
+        collectResult: '收藏成功',
+        timeoutCount: 0,
+        isSearchingMore: false,
+        searchLists: [],
+        page: 1,
+        showLoginBox: false
+      }
+    },
+    components: {
+      RemindBox,
+      Loading,
+      LoginBox
+    },
+    mounted: function () {
+      let _this = this
+      _this.$nextTick(function () {
+        window.addEventListener('scroll', function () {
+          _this.scroll()
+        }, false)
+      })
+    },
+    filters: {
+      currency: function (num) {
+        if (typeof num === 'number') {
+          if (num <= 0.000001) {
+            num = 0.000001
+          } else {
+            if (num.toString().indexOf('.') === -1) {
+              num += '.00'
+            } else {
+              let inputStr = num.toString()
+              let arr = inputStr.split('.')
+              let floatNum = arr[1]
+              if (floatNum.length > 6) {
+                num = inputStr.substring(0, arr[0].length + 7)
+                if (Number(floatNum.charAt(6)) > 4) {
+                  num = (Number(num) * 1000000 + 1) / 1000000
+                }
+              } else if (floatNum.length === 1) {
+                num = num + '0'
+              }
+            }
+          }
+        }
+        return num
+      }
+    },
+    computed: {
+      store () {
+        return this.$store.state.shop.storeInfo.store.data
+      },
+      commodities () {
+        let list = this.$store.state.shop.storeInfo.storeCommodity.data
+        this.searchLists = this.searchLists.concat(list.content)
+        this.isSearchingMore = false
+        return list
+      },
+      allPage () {
+        return this.commodities.totalPages
+      },
+      isFocus () {
+        return this.$store.state.shop.storeInfo.focusList.data
+      },
+      user () {
+        return this.$store.state.option.user
+      }
+    },
+    methods: {
+      scroll: function () {
+        let scrolled = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
+        if (Math.ceil(scrolled + window.screen.availHeight) >= document.body.scrollHeight && !this.isSearchingMore && this.page < this.allPage && this.activeType === 'product') {
+          this.getMoreCom()
+        }
+      },
+      getMoreCom: function () {
+        if (!this.isSearchingMore) {
+          this.page++
+          this.isSearchingMore = true
+          console.log(this.page)
+          this.pageCommodity({ page: this.page, count: 6 })
+        }
+      },
+      async pageCommodity (pageParams, kindId, keyword) {
+        let params = { storeid: this.$route.params.uuid, origin: 'store', kindUuid: kindId, code: keyword }
+        params.page = pageParams.page
+        params.count = pageParams.count
+        try {
+          let { data } = await this.$http.get('/api/commodity/commodities', { params })
+          this.$store.commit('shop/storeInfo/GET_STORE_COMMODITY_SUCCESS', data)
+        } catch (err) {
+          this.$store.commit('shop/storeInfo/GET_STORE_COMMODITY_FAILURE', err)
+        }
+      },
+      goProductDetail: function (uuid) {
+        this.$router.push('/mobile/brand/componentDetail/' + uuid)
+      },
+      collectStore: function () {
+        if (this.user.logged) {
+          if (this.isFocus === 'false') {
+            this.$store.dispatch('shop/StoreFocus', {storeName: this.store.storeName, storeid: this.store.id})
+              .then(response => {
+                this.$store.dispatch('shop/StoreFocusList', {id: this.store.id})
+                this.collectResult = '收藏成功'
+                this.timeoutCount++
+              })
+          } else if (this.isFocus === 'true') {
+            this.$http.post('/trade/storeFocus/delete/storeId', [this.store.id])
+              .then(response => {
+                this.$store.dispatch('shop/StoreFocusList', {id: this.store.id})
+                this.collectResult = '取消成功'
+                this.timeoutCount++
+              })
+          }
+        } else {
+          this.showLoginBox = true
+        }
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .store-detail {
+    margin: 0 auto;
+    margin-bottom: 1.2rem;
+    text-align: center;
+    background: #f7f7f7;
+    height: 100%;
+    .store-logo {
+      height: 3.17rem;
+      width: 6.96rem;
+      display: inline-block;
+      margin: .2rem auto;
+      line-height: 2.13rem;
+      background: #fff;
+      text-align: center;
+      border-radius: .1rem;
+      background: url('/images/mobile/@2x/brand-bg.png') no-repeat;
+      background-size: cover;
+      .store-logo-box {
+        border: .04rem solid #c7e5fd;
+        border-radius: .1rem;
+        height: 2.21rem;
+        width: 3.73rem;
+        margin: .5rem auto 0;
+        background: #fff;
+        position: relative;
+        img {
+          max-height: 2.1rem;
+          max-width: 3.71rem;
+        }
+        >i {
+          position: absolute;
+          font-size: .4rem;
+          background: #fff;
+          width: .6rem;
+          height: .6rem;
+          line-height: .6rem;
+          border-radius: 100%;
+          box-shadow: 0 0 .05rem #aaa;
+          right: -1.44rem;
+          top: .75rem;
+          text-align: center;
+        }
+      }
+    }
+    .store-switch-item {
+      text-align: center;
+      background: #fff;
+      .mobile-switch-btn {
+        background: #fff;
+        color: #666;
+        display: inline-block;
+        height: .64rem;
+        font-size: .34rem;
+        line-height: .64rem;
+        width: 1.4rem;
+        &:first-child {
+          margin-right: 1.78rem;
+        }
+        &.active {
+          color: #fc5708;
+          border-bottom: .04rem solid #fc5708;
+        }
+      }
+    }
+    .store-description {
+      background: #f7f7f7;
+      width: 100%;
+      p {
+        background: #fff;
+        margin: .2rem auto 0;
+        padding: .4rem .34rem;
+        width: 100%;
+        font-size: .3rem;
+        color: #666;
+        text-align: left;
+        height: 95%;
+        box-shadow: 0 .03rem .01rem 0 #cdcbcb96;
+        line-height: .5rem;
+      }
+    }
+    .product-store {
+      margin: .2rem 0 0 0;
+      table {
+        width: 100%;
+        font-size: .28rem;
+        thead {
+          background: #d5e5fb;
+          tr {
+            th {
+              font-weight: bold;
+              text-align: center;
+              height: .78rem;
+              line-height: .78rem;
+            }
+          }
+        }
+        tbody {
+          tr {
+            background: #fff;
+            border-bottom: 0.2rem solid #f7f7f7;
+            td {
+              padding: .2rem .1rem;
+              text-align: left;
+              div {
+                overflow: hidden;
+                text-overflow: ellipsis;
+                white-space: nowrap;
+                margin-bottom: .2rem;
+                max-width: 1.58rem;
+                &:last-child {
+                  margin-bottom: 0;
+                }
+              }
+              .price-level:last-child {
+                color: #fc5708;
+              }
+              .order-tag {
+                display: inline-block;
+                font-size: .18rem;
+                color: #fff;
+                font-weight: bold;
+                background: #ee1717;
+                height: .27rem;
+                width: .27rem;
+                line-height: .27rem;
+                text-align: center;
+                border-radius: .05rem;
+                position: relative;
+                top: -.03rem;
+              }
+            }
+            &:active {
+              background: #e1e1e1;
+            }
+          }
+        }
+      }
+      .no-store {
+        background: #fff;
+        padding-top: 1rem;
+        img {
+          display: block;
+          text-align: center;
+          margin: 0 auto;
+          margin-bottom: .45rem;
+          width: 3.31rem;
+          height: 2.13rem;
+        }
+        div {
+          width: 5.27rem;
+          margin: 0 auto;
+          text-align: center;
+          line-height: .4rem;
+          color: #999;
+          .link-url {
+            color: #01a44e;
+          }
+        }
+      }
+    }
+    .no-product {
+      background: #fff;
+      padding-top: 1rem;
+      img {
+        display: block;
+        text-align: center;
+        margin: 0 auto;
+        margin-bottom: .45rem;
+        width: 3.31rem;
+        height: 2.13rem;
+      }
+      div {
+        width: 5.27rem;
+        margin: 0 auto;
+        text-align: center;
+        line-height: .4rem;
+        font-size: .32rem;
+        color: #999;
+      }
+    }
+  }
+</style>

+ 54 - 21
layouts/error.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="error">
-    <div class="section">
+    <div class="section" :class="{'mobile-page': isMobile}">
       <div class="container">
         <div class="btn-click">
           <a href="/"><i class="fa fa-home" style="margin-right:5px;"></i>返回首页</a>
@@ -13,7 +13,18 @@
 
 <script>
   export default {
-    props: ['error']
+    props: ['error'],
+    layout (context) {
+      return context.store.state.option.isMobile ? 'login' : 'default'
+    },
+    computed: {
+      isMobile () {
+        return this.$store.state.option.isMobile
+      }
+//      background () {
+//        return this.isMobile ? '#ebfdff url("/images/404-mobile.png")no-repeat center center' : '#ebfdff url("/images/404.png")no-repeat center center'
+//      }
+    }
   }
 </script>
 
@@ -22,24 +33,46 @@
     background: #ebfdff url('/images/404.png')no-repeat center center;
     height:900px;
     position:relative;
-  }
-  .section .btn-click{
-    position:absolute;
-    top:54%;
-    left:50%;
-    margin-left:-150px;
-  }
-  .section .btn-click a{
-    display:inline-block;
-    width:130px;
-    height:33px;
-    font-size: 16px;
-    line-height: 33px;
-    color:#fff;
-    background: #f87c29;
-    text-align: center;
-    margin:0 10px;
-    border-radius: 2px;
-    text-decoration: none;
+    .btn-click{
+      position:absolute;
+      top:54%;
+      left:50%;
+      margin-left:-150px;
+      a{
+        display:inline-block;
+        width:130px;
+        height:33px;
+        font-size: 16px;
+        line-height: 33px;
+        color:#fff;
+        background: #f87c29;
+        text-align: center;
+        margin:0 10px;
+        border-radius: 2px;
+        text-decoration: none;
+      }
+    }
+    &.mobile-page {
+      background: #ebfdff url("/images/404-mobile.png")no-repeat center center;
+      position: fixed;
+      top: 0;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      height: auto;
+      .btn-click{
+        top: 66%;
+        left: 1.2rem;
+        margin-left: 0;
+        a{
+          width: 2.3rem;
+          height: .63rem;
+          font-size: .32rem;
+          line-height: .63rem;
+          margin: 0 .1rem;
+          border-radius: .04rem;
+        }
+      }
+    }
   }
 </style>

+ 54 - 21
layouts/errorPage.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="error">
-    <div class="section">
+    <div class="section" :class="{'mobile-page': isMobile}">
       <div class="container">
         <div class="btn-click">
           <a href="/"><i class="fa fa-home" style="margin-right:5px;"></i>返回首页</a>
@@ -13,7 +13,18 @@
 
 <script>
   export default {
-    props: ['error']
+    props: ['error'],
+    layout (context) {
+      return context.store.state.option.isMobile ? 'login' : 'default'
+    },
+    computed: {
+      isMobile () {
+        return this.$store.state.option.isMobile
+      }
+//      background () {
+//        return this.isMobile ? '#ebfdff url("/images/404-mobile.png")no-repeat center center' : '#ebfdff url("/images/404.png")no-repeat center center'
+//      }
+    }
   }
 </script>
 
@@ -22,24 +33,46 @@
     background: #ebfdff url('/images/404.png')no-repeat center center;
     height:900px;
     position:relative;
-  }
-  .section .btn-click{
-    position:absolute;
-    top:54%;
-    left:50%;
-    margin-left:-150px;
-  }
-  .section .btn-click a{
-    display:inline-block;
-    width:130px;
-    height:33px;
-    font-size: 16px;
-    line-height: 33px;
-    color:#fff;
-    background: #f87c29;
-    text-align: center;
-    margin:0 10px;
-    border-radius: 2px;
-    text-decoration: none;
+    .btn-click{
+      position:absolute;
+      top:54%;
+      left:50%;
+      margin-left:-150px;
+      a{
+        display:inline-block;
+        width:130px;
+        height:33px;
+        font-size: 16px;
+        line-height: 33px;
+        color:#fff;
+        background: #f87c29;
+        text-align: center;
+        margin:0 10px;
+        border-radius: 2px;
+        text-decoration: none;
+      }
+    }
+    &.mobile-page {
+      background: #ebfdff url("/images/404-mobile.png")no-repeat center center;
+      position: fixed;
+      top: 0;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      height: auto;
+      .btn-click{
+        top: 66%;
+        left: 1.2rem;
+        margin-left: 0;
+        a{
+          width: 2.3rem;
+          height: .63rem;
+          font-size: .32rem;
+          line-height: .63rem;
+          margin: 0 .1rem;
+          border-radius: .04rem;
+        }
+      }
+    }
   }
 </style>

+ 34 - 0
layouts/help.vue

@@ -0,0 +1,34 @@
+<template>
+  <div id="main">
+    <header-view v-if="!isMobile"></header-view>
+    <main-header v-if="!isMobile"></main-header>
+    <main-nav v-if="!isMobile"></main-nav>
+    <help-header v-if="isMobile"></help-header>
+    <nuxt/>
+    <footer-view v-if="!isMobile"></footer-view>
+    <right-bar v-if="!isMobile"></right-bar>
+    <help-footer v-else></help-footer>
+  </div>
+</template>
+<script>
+  import { Header, Footer, RightBar } from '~components/default'
+  import { MainHeader, MainNav } from '~components/main'
+  import { HelpHeader, HelpFooter } from '~components/mobile/help'
+  export default {
+    name: 'help',
+    components: {
+      HeaderView: Header,
+      FooterView: Footer,
+      RightBar,
+      MainHeader,
+      MainNav,
+      HelpHeader,
+      HelpFooter
+    },
+    computed: {
+      isMobile: function () {
+        return this.$store.state.option.isMobile
+      }
+    }
+  }
+</script>

+ 3 - 5
layouts/main.vue

@@ -20,11 +20,6 @@
       MainHeader,
       MainNav
     },
-//    data () {
-//      return {
-//        isInFrame: false
-//      }
-//    },
     head () {
       return {
         title: this.title,
@@ -35,6 +30,9 @@
       }
     },
     computed: {
+      isMobile: function () {
+        return this.$store.state.option.isMobile
+      },
       isInFrame () {
         let cookies = this.$store.state.option.cookies
         let cookieArr = cookies.split(';')

+ 33 - 0
layouts/mobile.vue

@@ -0,0 +1,33 @@
+<template>
+  <div id="mobile">
+    <mobile-header></mobile-header>
+    <nuxt/>
+    <mobile-footer></mobile-footer>
+  </div>
+</template>
+<script>
+  import { MobileHeader, MobileFooter } from '~components/mobile'
+  export default {
+    name: 'mobile',
+    components: {
+      MobileHeader,
+      MobileFooter
+    },
+//    middleware: 'authenticated',
+    head () {
+      return {
+        meta: [
+          { name: 'apple-mobile-web-app-capable', content: 'yes' },
+          { name: 'MobileOptimized', content: '320' },
+          { name: 'HandheldFriendly', content: 'true' },
+          { name: 'viewport', content: 'width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no' },
+          { 'http-equiv': 'Cache-Control', content: 'no-siteapp' },
+          { name: 'format-detection', content: 'telephone=no, email=no' }
+        ]
+      }
+    }
+  }
+</script>
+<style lang="scss">
+  @import '~assets/scss/mobileCommon';
+</style>

+ 3 - 5
nuxt.config.js

@@ -1,6 +1,6 @@
 const path = require('path')
 const isProdMode = Object.is(process.env.NODE_ENV, 'production')
-const baseUrl = process.env.BASE_URL || (isProdMode ? 'http://10.10.100.107:8081/platform-b2c/' : 'http://192.168.253.60:9090/platform-b2c/')
+const baseUrl = process.env.BASE_URL || (isProdMode ? 'http://10.10.100.107:8081/platform-b2c/' : 'http://192.168.253.123:8888/homepage/')
 
 module.exports = {
   router: {
@@ -36,7 +36,7 @@ module.exports = {
     extend (config, { dev, isClient, isServer }) {
       config.resolve.alias['~utils'] = path.join(__dirname, 'utils')
       config.module.rules.push({
-        test: /\.scss$/,
+        test: /\.(scss|css)$/,
         loader: 'vue-style-loader!css-loader!sass-loader'
       })
       config.module.rules.push({
@@ -72,9 +72,7 @@ module.exports = {
     ]
   },
   css: [
-    /* {
-    src: 'font-awesome/css/font-awesome.css'
-  }, */{src: '~assets/scss/app.scss', lang: 'scss'}
+     { src: '~assets/scss/app.scss', lang: 'scss' }
     /* {
       src: 'swiper/dist/css/swiper.css'
     } */ /* , {

+ 4 - 1
package.json

@@ -8,6 +8,7 @@
     "axios": "^0.15.3",
     "bezier-easing": "^2.0.3",
     "bootstrap": "^3.3.7",
+    "clipboard": "^1.7.1",
     "cookiejar": "^2.1.1",
     "cross-env": "^3.1.4",
     "element-ui": "^1.3.7",
@@ -39,6 +40,7 @@
     "babel-plugin-transform-runtime": "^6.23.0",
     "babel-preset-es2015": "^6.24.1",
     "babel-preset-stage-2": "^6.24.1",
+    "css-loader": "^0.28.7",
     "eslint": "^3.15.0",
     "eslint-config-standard": "^6.2.1",
     "eslint-loader": "^1.6.1",
@@ -46,6 +48,7 @@
     "eslint-plugin-promise": "^3.4.1",
     "eslint-plugin-standard": "^2.0.1",
     "node-sass": "^4.5.3",
-    "sass-loader": "^6.0.6"
+    "sass-loader": "^6.0.6",
+    "vue-loader": "^13.3.0"
   }
 }

+ 0 - 7
pages/README.md

@@ -1,7 +0,0 @@
-# PAGES
-
-This directory contains your Application Views and Routes.
-The framework reads all the .vue files inside this directory and create the router of your application.
-
-More information about the usage of this directory in the documentation:
-https://nuxtjs.org/guide/routing

+ 6 - 2
pages/auth/login.vue

@@ -1,11 +1,15 @@
 <template>
-
+  <div>
+    <!--登录代理页面-->
+  </div>
 </template>
 <script>
   export default {
+    name: 'login',
     layout: 'login',
     mounted () {
-      this.$http.get('/login/page', {params: {returnUrl: window.location.protocol + '//' + window.location.host}}).then(response => {
+      let uri = window.location.protocol + '//' + window.location.host + '/'
+      this.$http.get('/login/page', {params: {returnUrl: uri}}).then(response => {
         if (response.data) {
           window.location.href = response.data.content + '&baseUrl=' + encodeURIComponent(window.location.protocol + '//' + window.location.host + response.data.baseUrl)
         }

+ 30 - 18
pages/index.vue

@@ -1,36 +1,36 @@
 <template>
   <div class="index">
-    <christmas v-if="isOpen" @listenopen="listenOpen" :hasNewYear="hasNewYear"></christmas>
-    <carousel>
-      <kind-category @loadchild="loadProductKinds"></kind-category>
-    </carousel>
-    <advert></advert>
-    <floor-list></floor-list>
-    <news></news>
-    <partner></partner>
+    <div v-if="isMobile">
+      <Home></Home>
+    </div>
+    <div v-if="!isMobile">
+      <christmas v-if="isOpen" @listenopen="listenOpen" :hasNewYear="hasNewYear"></christmas>
+      <carousel>
+        <kind-category @loadchild="loadProductKinds"></kind-category>
+      </carousel>
+      <advert></advert>
+      <floor-list></floor-list>
+      <news></news>
+      <partner></partner>
+    </div>
   </div>
 </template>
 <script>
   import { KindCategory, Carousel, Advert, FloorList, Partner, News } from '~components/home'
   import { Christmas } from '~components/default'
+  import { Home } from '~components/mobile'
 
   export default {
     name: 'index',
-    layout: 'main',
+    layout (context) {
+      return context.store.state.option.isMobile ? 'mobile' : 'main'
+    },
     data () {
       return {
         isOpen: false,
         hasNewYear: false
       }
     },
-    fetch ({ store }) {
-      return Promise.all([
-        store.dispatch('loadFloors'),
-        store.dispatch('loadBanners', {type: 'home'}),
-        store.dispatch('loadProductKinds', { id: 0 }),
-        store.dispatch('loadNewsSnapshot', { page: 1, pageSize: 10 })
-      ])
-    },
     components: {
       KindCategory,
       Carousel,
@@ -38,7 +38,16 @@
       FloorList,
       Partner,
       News,
-      Christmas
+      Christmas,
+      Home
+    },
+    fetch ({store}) {
+      return !store.state.option.isMobile ? Promise.all([
+        store.dispatch('loadFloors'),
+        store.dispatch('loadBanners', {type: 'home'}),
+        store.dispatch('loadProductKinds', { id: 0 }),
+        store.dispatch('loadNewsSnapshot', { page: 1, pageSize: 10 })
+      ]) : []
     },
     mounted () {
       let user = this.user.logged
@@ -78,6 +87,9 @@
     computed: {
       user () {
         return this.$store.state.option.user
+      },
+      isMobile: function () {
+        return this.$store.state.option.isMobile
       }
     },
     methods: {

+ 17 - 0
pages/mobile/brand/_code.vue

@@ -0,0 +1,17 @@
+<template>
+  <brand-detail></brand-detail>
+</template>
+<script>
+  import BrandDetail from '~components/mobile/brand/BrandDetail.vue'
+  export default {
+    layout: 'mobile',
+    components: {
+      BrandDetail
+    },
+    fetch ({ store, params }) {
+      return Promise.all([
+        store.dispatch('loadBrandDetail', { id: params.code })
+      ])
+    }
+  }
+</script>

+ 20 - 0
pages/mobile/brand/brandCenter/_initial.vue

@@ -0,0 +1,20 @@
+<template>
+  <brand-center></brand-center>
+</template>
+<script>
+  import BrandCenter from '~components/mobile/brand/BrandCenter.vue'
+  export default {
+    layout: 'mobile',
+    components: {
+      BrandCenter
+    },
+    fetch ({store, route}) {
+      return Promise.all([
+        store.dispatch('product/loadBrands', {'keyword': route.params.initial})
+      ])
+    }
+  }
+</script>
+<style>
+
+</style>

+ 30 - 0
pages/mobile/brand/componentDetail/_uuid.vue

@@ -0,0 +1,30 @@
+<template>
+  <component-detail></component-detail>
+</template>
+<script>
+  import ComponentDetail from '~components/mobile/brand/ComponentDetail.vue'
+  export default {
+    layout: 'mobile',
+    components: {
+      ComponentDetail
+    },
+    fetch ({ store, params }) {
+      return Promise.all([
+        store.dispatch('loadComponentDetail', {id: params.uuid}),
+//        store.dispatch('loadComponentStore', {uuid: params.uuid}),
+        store.dispatch('loadComponentInformation',
+          {
+            count: 10,
+            page: 1,
+            sorting: {'minPriceRMB': 'ASC'},
+            filter: {
+              uuid: params.uuid,
+              ignoreUMall: false,
+              ignoreStore: false,
+              storeIds: ''
+            }}),
+        store.dispatch('product/saveStores')
+      ])
+    }
+  }
+</script>

+ 57 - 0
pages/mobile/help/_id.vue

@@ -0,0 +1,57 @@
+<template>
+  <!--文章详情-->
+  <div class="helper-center-details" style="color: #000">
+    <div class="ql-container ql-snow" v-html="helpDetail.article"></div>
+    <div v-if="helpDetail.length == 0" style="color: #999;">暂无数据!</div>
+  </div>
+</template>
+<script>
+  export default {
+    layout: 'help',
+    fetch ({ store, route }) {
+      return Promise.all([
+        store.dispatch('loadHelpDetail', route.params)
+      ])
+    },
+    computed: {
+      helpDetail () {
+        return this.$store.state.help.detail.data
+      }
+    }
+  }
+</script>
+<style>
+ .helper-center-details{
+   width: 100%;
+   margin-bottom: 50px;
+}
+ .helper-center-details .ql-container{
+   margin: 0 auto;
+   width: 95%;
+ }
+ .helper-center-details .ql-container .ql-size-20{
+   display: block;
+   margin-top: 15px;
+   font-weight: bold;
+ }
+ .helper-center-details .ql-container strong{
+   display: block;
+   margin-top: 15px;
+   font-size: 16px;
+ }
+ .helper-center-details p, .helper-center-details h3{
+   margin: 0 auto;
+   width: 100%;
+ }
+ .helper-center-details span{
+   font-size: 16px;
+ }
+ .helper-center-details span.ql-size-16 img{
+   width: 5%;
+ }
+ .helper-center-details img{
+   margin: 0 auto;
+   width: 100%;
+   text-align: center;
+ }
+</style>

+ 396 - 0
pages/mobile/search/_keycode.vue

@@ -0,0 +1,396 @@
+<template>
+  <div class="search-list">
+    <div class="search-item" v-if="productList.expose > 0 || productList.brands">
+      <span :class="activeType=='store'?'active':''" @click="clickType('store')">所有器件</span>
+      <span :class="activeType=='support'?'active':''" @click="clickType('support')">仅看有货</span>
+    </div>
+
+    <div class="brand-list-content" v-if="(!productList.brands && brandList && brandList.length > 0) && productList.expose > 0">
+      <div class="brand-list-top">
+        <span>品牌墙</span>
+        <span class="row-switch" @click="onclick()" v-if="brandList.length > 8">{{!isShow?'收起':'展开'}}<i :class="{'iconfont icon-arrow-down':isShow,'icon-icon-shang iconfont':!isShow}"></i></span>
+      </div>
+      <div class="brand-list-item" >
+        <div v-for="item in isShow?brandList.slice(0, 8):brandList">
+          <nuxt-link :to="'/mobile/brand/'+item.br_uuid">
+            <img :src="item.logoUrl ||'/images/component/default.png'"/>
+          </nuxt-link>
+        </div>
+      </div>
+    </div>
+
+    <div class="detail-brand-content" v-if="productList.brands" @click="goBrand(list.uuid)">
+      <h4>主营产品 <img src="/images/mobile/@2x/search/search-brand.png" alt=""></h4>
+      <div class="brand-list">
+        <div class="list-left">
+          <img :src="list.logoUrl || '/images/component/default.png'" :alt="list.nameEn"/>
+          <span>{{list.nameCn}}</span>
+        </div>
+        <p>{{list.series | productDescFilter}}</p>
+      </div>
+    </div>
+
+    <div class="detail-brand" v-for="(item, index) in searchLists" :style="index == 0 ? 'padding-top: .2rem;' : ''" v-if="searchLists.length > 0">
+      <div class="brand-item" @click="goComponent(item.uuid)">
+        <p>型号:<span>{{item.code}}</span></p>
+        <p>品牌:<span>{{item.brandEn || item.brand.nameCn}}</span></p>
+        <p>产品描述:<span>{{item.description || '-'}}</span></p>
+        <i class="iconfont icon-shoucang" :style="(item.isFocus)?'color:#ff7800':'color:#bbb'" @click="collect(item, $event)"></i>
+      </div>
+    </div>
+
+    <div class="none-state" v-if="!productList.components || productList.components.length === 0">
+      <img :src="brandList && brandList.length > 0 ? '/images/mobile/@2x/car@2x.png':'/images/mobile/@2x/search-empty.png'">
+      <p v-text="brandList && brandList.length > 0 ? '抱歉,暂无器件信息' : '抱歉,暂无搜索结果'"></p>
+      <a @click="goLastPage">返回上一页</a>
+    </div>
+    <remind-box :title="collectResult" :timeoutCount="timeoutCount"></remind-box>
+    <loading v-show="isSearchingMore"></loading>
+    <login-box @onLoginBoxClose="showLoginBox = false" v-if="showLoginBox"></login-box>
+  </div>
+</template>
+
+<script>
+  import {RemindBox, Loading, LoginBox} from '~components/mobile/common'
+  export default {
+    layout: 'mobile',
+    data () {
+      return {
+        activeType: 'store',
+        count: '',
+        filter: {},
+        page: 1,
+        sorting: {},
+        isShow: true,
+        isMove: '',
+        isFocus: false,
+        isClickCollect: false,
+        collectResult: '收藏成功',
+        timeoutCount: 0,
+        searchLists: [],
+        isSearchingMore: false,
+        showLoginBox: false
+      }
+    },
+    components: {
+      RemindBox,
+      Loading,
+      LoginBox
+    },
+    mounted: function () {
+      let _this = this
+      _this.$nextTick(function () {
+        window.addEventListener('scroll', function () {
+          _this.scroll()
+        }, false)
+      })
+    },
+    fetch ({store, route}) {
+      return Promise.all([
+        store.dispatch('searchData/searchForListInMobile', {count: 15, filter: {}, keyword: route.query.w, page: 1, sorting: {}}),
+        store.dispatch('searchData/searchForBrands', {collectList: 'goods_brand', keyword: route.query.w, paramJSON: {}})
+      ])
+    },
+    filters: {
+      productDescFilter: function (str) {
+        return str.length > 50 ? str.substring(0, 50) + '...' : str
+      }
+    },
+    computed: {
+      productList () {
+        let list = this.$store.state.searchData.searchList.lists.data
+        if (this.isSearchingMore) {
+          this.searchLists = this.searchLists.concat(list.components)
+          this.isSearchingMore = false
+        } else {
+          this.searchLists = list.components
+        }
+        return list
+      },
+      allPage () {
+        return Math.floor(this.productList.total / this.productList.size) + Math.floor(this.productList.total % this.productList.size > 0 ? 1 : 0)
+      },
+      brandList () {
+        return this.$store.state.searchData.searchBrands.brands.data
+      },
+      brandDetail () {
+        return this.$store.state.searchData.searchList.lists.data.brands
+      },
+      list () {
+        let list = this.$store.state.searchData.searchDetail.detail.data
+        if (list.application && list.application !== '') {
+          this.applications = list.application.split(',')
+        }
+        return list
+      },
+      user () {
+        return this.$store.state.option.user
+      }
+    },
+    methods: {
+      onclick () {
+        this.isShow = !this.isShow
+      },
+      clickType (type) {
+//        this.searchLists = []
+        if (type === 'store') {
+          this.activeType = 'store'
+          this.$store.dispatch('searchData/searchForListInMobile', {count: 15, filter: {}, keyword: this.$route.query.w, page: 1, sorting: {}})
+        } else if (type === 'support') {
+          this.activeType = 'support'
+          this.$store.dispatch('searchData/searchForListInMobile', {count: 15, filter: {'goods_status': 601}, keyword: this.$route.query.w, page: 1, sorting: {'RESERVE': 'DESC'}})
+        }
+      },
+      goBrand: function (uuid) {
+        this.$router.push('/mobile/brand/' + uuid)
+      },
+      collect: function (item, $event) {
+        this.isClickCollect = true
+        if (this.user.logged) {
+          if (!item.isFocus) {
+            this.$http.post('/trade/collection/save', {componentid: item.cmpId, kind: 2})
+              .then(response => {
+                item.isFocus = true
+                this.collectResult = '收藏成功'
+                this.timeoutCount++
+              })
+          } else {
+            this.$http.post('/trade/collection/delete/cmpId', [item.cmpId]).then(response => {
+              item.isFocus = false
+              this.collectResult = '取消成功'
+              this.timeoutCount++
+            })
+          }
+        } else {
+          this.showLoginBox = true
+        }
+      },
+      goComponent: function (uuid) {
+        if (!this.isClickCollect) {
+          this.$router.push('/mobile/brand/componentDetail/' + uuid)
+        } else {
+          this.isClickCollect = false
+        }
+      },
+      goLastPage: function () {
+        window.history.back(-1)
+      },
+      getMoreSearch: function () {
+        this.page++
+        this.isSearchingMore = true
+        this.$store.dispatch('searchData/searchForListInMobile', {count: 15, filter: {}, keyword: this.$route.query.w, page: this.page, sorting: {'GO_RESERVE': 'DESC', 'GO_SEARCH': 'DESC'}})
+      },
+      scroll: function () {
+        let scrolled = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
+        if (Math.ceil(scrolled + window.screen.availHeight) >= document.body.scrollHeight && !this.isSearchingMore && this.page < this.allPage) {
+          this.getMoreSearch()
+        }
+      }
+    }
+  }
+</script>
+
+<style scoped lang="scss">
+  .search-list{
+    width:100%;
+    padding-bottom: 1rem;
+    .none-state{
+      text-align: center;
+      margin-top:2rem;
+      width:100%;
+      img{
+        margin:0 auto;
+        width: 3.31rem;
+        height: 2.13rem;
+      }
+      p {
+        font-size: .32rem;
+        color: #999;
+        margin: 1.19rem 0 0 0;
+      }
+      a {
+        display: block;
+        font-size: .28rem;
+        color: #fff;
+        width: 1.88rem;
+        height: .54rem;
+        line-height: .54rem;
+        background: #418bf6;
+        margin: .7rem auto 0;
+        border-radius: .05rem;
+      }
+    }
+    .search-item{
+      text-align: center;
+      span{
+        display:inline-block;
+        width:1.41rem;
+        line-height: .76rem;
+        font-size:.32rem;
+        color:#666;
+        &:first-child {
+          margin-right: 2rem;
+        }
+      }
+      span.active{
+        color:#3976f4;
+        border-bottom:.04rem solid #3976f4;
+      }
+    }
+
+    .brand-list-content{
+      margin:0 auto;
+      border-top:.04rem solid #dedfdf;
+      border-bottom:.04rem solid #dedfdf;
+      width:7.1rem;
+      min-height:1.51rem;
+      overflow: hidden;
+      text-align: left;
+      padding-top:.33rem;
+      padding-bottom:.33rem;
+      .brand-list-top{
+        span:first-child{
+          font-size:.32rem;
+          float: left;
+          margin: 0 0 .1rem .2rem;
+        }
+        span.row-switch{
+          font-size:.28rem;
+          color:#53a0f7;
+          float: right;
+          margin: 0 .2rem 0 0;
+          i {
+            font-size: .16rem;
+          }
+        }
+      }
+      .brand-list-item{
+        overflow: hidden;
+        margin: .1rem .2rem 0;
+        text-align: center;
+        clear: both;
+        >div {
+          display: inline-block;
+          margin-right: .14rem;
+          float: left;
+          &:nth-child(4n) {
+            margin-right: 0;
+          }
+          a {
+            width: 1.57rem;
+            height: .77rem;
+            display: inline-block;
+            margin: .1rem 0;
+            border: .04rem solid #53a0f7;
+            border-radius: .1rem;
+            line-height: .77rem;
+            img{
+              max-width:1.07rem;
+              max-height:.57rem;
+            }
+          }
+        }
+      }
+    }
+
+    .detail-brand-content{
+      margin:0 auto;
+      border-top:.04rem solid #dedfdf;
+      border-bottom:.04rem solid #dedfdf;
+      width:7.1rem;
+      height:3.02rem;
+      padding-top:.18rem;
+      h4{
+        font-size:.32rem;
+        line-height: .6rem;
+        margin:0 0 0 3.97rem;
+        position: relative;
+        img {
+          position: absolute;
+          left: -1.28rem;
+          top: .24rem;
+          width: 3.8rem;
+          height: .11rem;
+        }
+      }
+      .brand-list{
+        margin:0 .15rem;
+        .list-left{
+          border:.02rem solid #418ef7;
+          border-radius: .05rem;
+          width:2.03rem;
+          height:1.73rem;
+          display: inline-block;
+          img{
+            display:block;
+            width:100%;
+            height:1.25rem;
+          }
+          span{
+            display: block;
+            font-size: .24rem;
+            color:#fff;
+            text-align: center;
+            width:100%;
+            background: #418ef7;
+            line-height: .45rem;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+          }
+        }
+        p{
+          width:4.3rem;
+          font-size:.28rem;
+          line-height: .4rem;
+          padding:.12rem .46rem 0 .05rem;
+          float: right;
+        }
+      }
+    }
+
+    .detail-brand{
+      background: #f8fcff;
+      width:100%;
+      min-height:1.5rem;
+      padding-bottom: .2rem;
+      .brand-item{
+        width:7rem;
+        margin:0 auto;
+        border-radius:.1rem;
+        background: #fff;
+        padding:.2rem;
+        position:relative;
+        -webkit-box-shadow: 0 .03rem .01rem 0 #cdcbcb96;
+        -moz-box-shadow: 0 .03rem .01rem 0 #cdcbcb96;
+        box-shadow: 0 .03rem .01rem 0 #cdcbcb96;
+        &:active{
+          background: #e1e1e1;
+        }
+        p{
+          font-size:.28rem;
+          line-height:.4rem;
+          color:#333;
+          margin:0;
+          span{}
+        }
+        i{
+          display:block;
+          position:absolute;
+          top:.1rem;
+          right:.22rem;
+          font-size:.5rem;
+          color:#ff7800;
+          width: .6rem;
+          height: .6rem;
+          line-height: .6rem;
+          text-align: center;
+        }
+      }
+      div.active{
+        background: #d4d;
+      }
+    }
+  }
+
+</style>

+ 27 - 0
pages/mobile/shop/_uuid.vue

@@ -0,0 +1,27 @@
+<template>
+  <div>
+    <store-detail></store-detail>
+  </div>
+</template>
+<script>
+  import StoreDetail from '~components/mobile/store/StoreDetail.vue'
+  export default {
+    layout: 'mobile',
+    fetch ({ store, params }) {
+      return Promise.all([
+        store.dispatch('shop/findStoreInfoFromUuid', params),
+        store.dispatch('shop/pageCommoditiesOfStore', params.uuid, { page: 1, count: 10 })
+      ])
+    },
+    computed: {
+      description () {
+        return this.$store.state.shop.storeInfo.store.data
+      }
+    },
+    components: {
+      StoreDetail
+    }
+  }
+</script>
+<style scoped>
+</style>

+ 314 - 0
pages/mobile/shop/index.vue

@@ -0,0 +1,314 @@
+<template>
+  <div class="shop">
+    <div class="shop-top">
+      <p><i class="iconfont icon-dianpu1"></i><span>{{list.totalElements || 0}}</span>家店铺</p>
+      <span @click="onClick()">{{downName}} <i class="iconfont icon-arrow-down"></i></span>
+      <ul class="supdown" v-if="down">
+        <li @click="onDown('ORIGINAL_FACTORY-DISTRIBUTION-AGENCY' )" v-show="downName !== '全部'">全部</li>
+        <li @click="onDown('ORIGINAL_FACTORY')" v-show="downName !== '原厂'">原厂</li>
+        <li @click="onDown('AGENCY')" v-show="downName !== '代理'">代理</li>
+        <li @click="onDown('DISTRIBUTION')" v-show="downName !== '经销'">经销</li>
+      </ul>
+    </div>
+    <div class="shop-list" v-for="item in searchLists" @click="goStoreDetail(item.uuid)" v-if="item">
+      <h3>{{item.storeName}}</h3>
+      <div class="list-item">
+        <div class="item-img">
+          <i :style="'background:url(' + isType(item.type) + ')no-repeat 0 0/.65rem .33rem;'"></i>
+          <img :src="item.logoUrl || '/images/component/default.png'">
+        </div>
+        <div class="list-item-phone">
+          <p>电话:<span>{{item.enterprise.enTel}}</span></p>
+          <p>传真:<span>{{item.enterprise.enFax}}</span></p>
+          <!--<p>商家介绍: <nuxt-link :to="'/mobile/merchantDescription/'+item.uuid">点击查看</nuxt-link></p>-->
+          <p>联系商家:<a @click="selectStoreInfo(item, $event)">点击查看</a></p>
+          <i class="iconfont icon-shoucang" :style="item.isFocus=='true'?'color:#ff7800':'color:#bbb'" @click="focusStore(item, $event)"></i>
+        </div>
+      </div>
+    </div>
+    <remind-box :title="collectResult" :timeoutCount="timeoutCount"></remind-box>
+    <loading v-show="isSearchingMore"></loading>
+    <div class="mobile-modal" v-if="showStoreInfo">
+      <div class="mobile-modal-box">
+        <div class="mobile-modal-header">联系方式<i @click="showStoreInfo = false" class="icon-guanbi iconfont"></i></div>
+        <div class="mobile-modal-content">
+          <div v-if="checkInfo(enterpriseInfo.enAddress || enterpriseInfo.address)">商家地址:{{enterpriseInfo.enAddress || enterpriseInfo.address}}</div>
+          <!--<div class="content-line link-url">在线咨询</div>-->
+          <div v-if="checkInfo(enterpriseInfo.enTel)">致电:<a :href="'tel:' + enterpriseInfo.enTel" class="content-line link-url">{{enterpriseInfo.enTel}}</a></div>
+          <div v-if="checkInfo(enterpriseInfo.enEmail)">邮件:<a :href="'mailto:' + enterpriseInfo.enEmail" class="content-line link-url">{{enterpriseInfo.enEmail}}</a></div>
+        </div>
+      </div>
+    </div>
+    <login-box @onLoginBoxClose="showLoginBox = false" v-if="showLoginBox"></login-box>
+  </div>
+</template>
+
+<script>
+  import {RemindBox, Loading, LoginBox} from '~components/mobile/common'
+  export default {
+    layout: 'mobile',
+    data () {
+      return {
+        page: 1,
+        count: 10,
+        types: 'ORIGINAL_FACTORY-DISTRIBUTION-AGENCY',
+        down: false,
+        downName: '全部',
+        isFocus: true,
+        collectResult: '收藏成功',
+        timeoutCount: 0,
+        isSearchingMore: false,
+        searchLists: [],
+        isChange: false,
+        showStoreInfo: false,
+        enterpriseInfo: {},
+        showLoginBox: false
+      }
+    },
+    components: {
+      RemindBox,
+      Loading,
+      LoginBox
+    },
+    fetch ({ store }) {
+      return Promise.all([
+        store.dispatch('provider/findStoreListInMobil', { page: 1, count: 10, types: 'ORIGINAL_FACTORY-DISTRIBUTION-AGENCY' })
+      ])
+    },
+    computed: {
+      list () {
+        let list = this.$store.state.provider.stores.storeList.data
+        if (this.isChange) {
+          this.searchLists = []
+          this.page = 1
+          this.isChange = false
+        } else {
+          this.searchLists = this.searchLists.concat(list.content)
+          this.isSearchingMore = false
+        }
+        return list
+      },
+      allPage () {
+        return this.list.totalPages
+      },
+      user () {
+        return this.$store.state.option.user
+      }
+    },
+    mounted: function () {
+      let _this = this
+      _this.$nextTick(function () {
+        window.addEventListener('scroll', function () {
+          _this.scroll()
+        }, false)
+      })
+    },
+    methods: {
+      scroll: function () {
+        let scrolled = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
+        if (Math.ceil(scrolled + window.screen.availHeight) >= document.body.scrollHeight && !this.isSearchingMore && this.page < this.allPage) {
+          this.getMoreStore()
+        }
+      },
+      getMoreStore: function () {
+        if (!this.isSearchingMore) {
+          this.page++
+          this.isSearchingMore = true
+          this.$store.dispatch('provider/findStoreListInMobil', { page: this.page, count: this.count, types: this.types })
+        }
+      },
+      isType (type) {
+        let bgurl = ''
+        if (type === 'ORIGINAL_FACTORY') {
+          bgurl = '/images/mobile/@2x/shop/yuanchang@2x.png'
+        }
+        if (type === 'DISTRIBUTION') {
+          bgurl = '/images/mobile/@2x/shop/jingxiao@2x.png'
+        }
+        if (type === 'AGENCY') {
+          bgurl = '/images/mobile/@2x/shop/daili@2x.png'
+        }
+        return bgurl
+      },
+      onClick () {
+        this.down = !this.down
+      },
+      onDown (type) {
+        this.isChange = true
+        this.down = !this.down
+        this.types = type
+        if (type === 'ORIGINAL_FACTORY') {
+          this.downName = '原厂'
+        } else if (type === 'DISTRIBUTION') {
+          this.downName = '经销'
+        } else if (type === 'AGENCY') {
+          this.downName = '代理'
+        } else if (type === 'ORIGINAL_FACTORY-DISTRIBUTION-AGENCY') {
+          this.downName = '全部'
+        }
+        this.$store.dispatch('provider/findStoreListInMobil', { page: 1, count: 10, types: type })
+      },
+      focusStore: function (item, $event) {
+//        item.isFocus = item.isFocus === 'false' ? 'true' : 'false'
+        $event.stopPropagation()
+        if (this.user.logged) {
+          if (item.isFocus === 'false') {
+            this.$http.post('/trade/storeFocus/save', {storeName: item.storeName, storeid: item.id})
+              .then(response => {
+                item.isFocus = 'true'
+                this.collectResult = '收藏成功'
+                this.timeoutCount++
+              })
+          } else {
+            this.$http.post('/trade/storeFocus/delete/storeId', [item.id])
+              .then(response => {
+                item.isFocus = 'false'
+                this.collectResult = '取消成功'
+                this.timeoutCount++
+              })
+          }
+        } else {
+          this.showLoginBox = true
+        }
+      },
+      goStoreDetail: function (uuid) {
+        this.$router.push('/mobile/shop/' + uuid)
+      },
+      selectStoreInfo: function (store, $event) {
+        $event.stopPropagation()
+        this.enterpriseInfo = store.enterprise || {}
+        this.showStoreInfo = true
+      },
+      checkInfo: function (str) {
+        return str && str.trim() !== ''
+      }
+    }
+  }
+</script>
+
+<style scoped lang="scss">
+  .shop{
+    margin-bottom: .98rem;
+    min-height: 10rem;
+    background: #e2e4e6;
+  .shop-top{
+    width:100%;
+    height:1.14rem;
+    line-height: 1.14rem;
+    padding:0 .3rem;
+    position:relative;
+    background:#fff;
+    .supdown{
+      position:absolute;
+      top:.8rem;
+      right:.2rem;
+      z-index:1;
+      background:#616264;
+      border-radius:.1rem;
+      li{
+        font-size: .28rem;
+        color:#ffffff;
+        height: .32rem;
+        line-height: .32rem;
+        margin: .4rem .3rem;
+      }
+    }
+    p{
+      font-size:.24rem;
+      color:#000;
+      display: inline-block;
+      i{
+        font-size: .53rem;
+        color:#418ef7;
+      }
+      span{
+        font-size:.3rem;
+        color:#f94f28;
+        margin:0 .1rem;
+      }
+    }
+    >span{
+      font-size:.28rem;
+      color:#53a0f7;
+      float: right;
+    }
+  }
+  .shop-list {
+    background:#fff;
+    margin-top:.1rem;
+    padding-bottom:.1rem;
+    box-shadow: 0 .03rem .01rem 0 #cdcbcb96;
+    h3{
+      font-size: .32rem;
+      line-height: .4rem;
+      margin: 0 0 0 .27rem;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      padding-top: .1rem;
+      position: relative;
+      top: .1rem;
+    }
+    .list-item{
+      width:6.77rem;
+      margin-left:.39rem;
+      .item-img{
+        position:relative;
+        width:2.42rem;
+        height:1.69rem;
+        display: inline-block;
+        vertical-align: middle;
+        i{
+          display:block;
+          position:absolute;
+          width:.65rem;
+          height:.33rem;
+        }
+        img{
+          display:inline-block;
+          width:100%;
+          height:100%;
+          border:.04rem solid #eee;
+          background-color: #fff;
+        }
+      }
+      .list-item-phone{
+        width:3.95rem;
+        padding:.18rem 0;
+        position:relative;
+        display: inline-block;
+        vertical-align: middle;
+        margin-left: .2rem;
+        p{
+          font-size:.28rem;
+          line-height: .67rem;
+          margin:0;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          width: 3.2rem;
+        }
+        i{
+          display:block;
+          position:absolute;
+          top: -.06rem;
+          right: -.06rem;
+          font-size:.5rem;
+          color:#ff7800;
+          width: .6rem;
+          height: .6rem;
+          text-align: center;
+          line-height: .6rem;
+        }
+        i.active{
+          color:#333;
+        }
+      }
+    }
+    &:active {
+      background: #e1e1e1;
+    }
+    }
+  }
+
+</style>

+ 412 - 0
pages/mobile/user/index.vue

@@ -0,0 +1,412 @@
+<template>
+  <div class="user-content">
+    <div class="user-name">
+      <img src="/images/component/default.png"/>
+      <div class="user-info">
+        <p v-text="userInfo.data.userName"></p>
+        <p v-text="enterpriseInfo.enName"></p>
+      </div>
+      <span @click="onclick()">{{listName}}<i class="iconfont icon-arrow-down"></i></span>
+      <ul class="supdown" v-if="down">
+        <li @click="onDown('1')" v-show="!isDevice || !isShop">全部收藏</li>
+        <li @click="onDown('-1')" v-show="isDevice || !isShop">店铺关注</li>
+        <li @click="onDown('0')" v-show="!isDevice || isShop">器件收藏</li>
+      </ul>
+    </div>
+    <div class="collect-list-type" v-if="focusPage.totalElements > 0 && isShop">
+      <p>店铺</p>
+    </div>
+    <div class="shop-list" v-if="isShop" v-for="item in focusPage.content" @click="goStoreDetail(item.storeInfo.uuid)">
+      <h3>{{item.storeName}}</h3>
+      <div class="list-item">
+        <div class="item-img">
+          <img :src="getBackground(item.storeInfo.type)" />
+          <img :src="item.storeInfo.logoUrl || '/images/component/default.png'">
+        </div>
+        <div class="list-item-phone">
+          <p>电话:<span>{{item.storeInfo.enterprise.enTel}}</span></p>
+          <p>传真:<span>{{item.storeInfo.enterprise.enFax}}</span></p>
+          <p>联系商家:<a @click="selectStoreInfo(item, $event)">点击查看</a></p>
+          <i class="iconfont icon-shoucang" @click="cancelFocus('store', item, $event)"></i>
+        </div>
+      </div>
+    </div>
+    <div class="collect-list-type" v-if="collectSave.totalElements > 0 && isDevice">
+      <p>器件</p>
+    </div>
+    <div class="detail-brand" v-for="(item, index) in collectSave.content" v-if="isDevice" @click="goComponentDetail(item.componentinfo.uuid)">
+      <a>
+        <div class="brand-item">
+          <p>型号:<span>{{item.componentinfo.code}}</span></p>
+          <p>品牌:<span>{{item.componentinfo.brand.nameCn}}</span></p>
+          <p>产品描述:<span>{{item.componentinfo.kind.nameCn}}</span></p>
+          <i class="iconfont icon-shoucang" @click="cancelFocus('product', item, $event)"></i>
+        </div>
+      </a>
+    </div>
+    <div class="none-state" v-if="(collectSave.totalElements == 0 && !isShop) || (focusPage.totalElements == 0 && !isDevice) || (collectSave.totalElements == 0 && focusPage.totalElements == 0)">
+      <img src="/images/mobile/@2x/empty-collect.png">
+      <p v-text="getRemindText()"></p>
+      <nuxt-link to="/">返回首页</nuxt-link>
+    </div>
+    <remind-box :title="collectResult" :timeoutCount="timeoutCount"></remind-box>
+    <div class="mobile-modal" v-if="showStoreInfo">
+      <div class="mobile-modal-box">
+        <div class="mobile-modal-header">联系方式<i @click="showStoreInfo = false" class="icon-guanbi iconfont"></i></div>
+        <div class="mobile-modal-content">
+          <div v-if="checkInfo(storeInfo.enAddress || storeInfo.address)">商家地址:{{storeInfo.enAddress || storeInfo.address}}</div>
+          <!--<div class="content-line link-url">在线咨询</div>-->
+          <div v-if="checkInfo(storeInfo.enTel)">致电:<a :href="'tel:' + storeInfo.enTel" class="content-line link-url">{{storeInfo.enTel}}</a></div>
+          <div v-if="checkInfo(storeInfo.enEmail)">邮件:<a :href="'mailto:' + storeInfo.enEmail" class="content-line link-url">{{storeInfo.enEmail}}</a></div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import RemindBox from '~components/mobile/common/RemindBox.vue'
+  export default {
+    layout: 'mobile',
+    data () {
+      return {
+        userName: '',
+        down: false,
+        count: '',
+        page: '',
+        type: '',
+        listName: '全部收藏',
+        isShop: true,
+        isDevice: true,
+        collectResult: '取消成功',
+        timeoutCount: 0,
+        showStoreInfo: false,
+        storeInfo: {}
+      }
+    },
+    components: {
+      RemindBox
+    },
+    fetch ({ store }) {
+      return Promise.all([
+        store.dispatch('product/saveStores', { count: 100, page: 1, type: 'component' }),
+        store.dispatch('shop/StoreFocusPage', { count: 100, page: 1 })
+      ])
+    },
+    methods: {
+      onclick () {
+        this.down = !this.down
+      },
+      onDown (type) {
+        if (type === '-1') {
+          this.listName = '店铺关注'
+          this.isShop = true
+          this.isDevice = false
+          this.down = false
+        }
+        if (type === '0') {
+          this.listName = '器件收藏'
+          this.isDevice = true
+          this.isShop = false
+          this.down = false
+        }
+        if (type === '1') {
+          this.listName = '全部收藏'
+          this.isDevice = true
+          this.isShop = true
+          this.down = false
+        }
+      },
+      cancelFocus: function (type, item, event) {
+        event.stopPropagation()
+        if (type === 'store') {
+          this.$http.post('/trade/storeFocus/delete/storeId', [item.storeid])
+            .then(response => {
+              this.$store.dispatch('shop/StoreFocusPage', { count: 100, page: 1 })
+              this.timeoutCount++
+            })
+        } else {
+          // /trade/collection/
+          this.$http.delete('/trade/collection/' + item.id)
+            .then(response => {
+              this.$store.dispatch('product/saveStores', { count: 100, page: 1, type: 'component' })
+              this.timeoutCount++
+            })
+        }
+      },
+      getBackground: function (type) {
+        let url = ''
+        if (type === 'AGENCY') {
+          url += '/images/mobile/@2x/shop/daili@2x.png'
+        } else if (type === 'DISTRIBUTION') {
+          url += '/images/mobile/@2x/shop/jingxiao@2x.png'
+        } else if (type === 'ORIGINAL_FACTORY') {
+          url += '/images/mobile/@2x/shop/yuanchang@2x.png'
+        }
+        return url
+      },
+      goStoreDetail: function (uuid) {
+        this.$router.push('/mobile/shop/' + uuid)
+      },
+      goComponentDetail: function (uuid) {
+        this.$router.push('/mobile/brand/componentDetail/' + uuid)
+      },
+      getRemindText: function () {
+        if (this.isDevice && !this.isShop) {
+          return '抱歉,暂无器件收藏'
+        } else if (!this.isDevice && this.isShop) {
+          return '抱歉,暂无店铺关注'
+        } else {
+          return '抱歉,暂无收藏记录'
+        }
+      },
+      selectStoreInfo: function (store, event) {
+        event.stopPropagation()
+        this.storeInfo = store.storeInfo.enterprise || {}
+        this.showStoreInfo = true
+      },
+      checkInfo: function (str) {
+        return str && str.trim() !== ''
+      }
+    },
+    computed: {
+      collectSave () {
+        return this.$store.state.product.common.collectList.data
+      },
+      userInfo () {
+        return this.$store.state.option.user
+      },
+      enterpriseInfo () {
+        let ens = this.userInfo.data.enterprises
+//        if (ens && ens.length) {
+//          return ens.find(item => item.current) || {enName: this.userInfo.data.userName + '(个人账户)'}
+//        } else {
+//          return {enName: this.userInfo.data.userName + '(个人账户)'}
+//        }
+        if (ens && ens.length) {
+          for (let i = 0; i < ens.length; i++) {
+            if (ens[i].current) {
+              return ens[i]
+            }
+          }
+        }
+        return {enName: this.userInfo.data.userName + '(个人账户)'}
+      },
+      focusPage () {
+        return this.$store.state.shop.storeInfo.focusPage.data
+      }
+    }
+  }
+</script>
+
+<style scoped lang="scss">
+  .user-content{
+    margin-bottom: .98rem;
+    background: #dfe2e4;
+
+    .none-state{
+      text-align: center;
+      padding:1.5rem 0;
+      background: #fff;
+      margin-top:.1rem;
+      width:100%;
+      img{
+        margin:0 auto;
+        width: 4.08rem;
+        height: 2.62rem;
+      }
+      p {
+        font-size: .32rem;
+        color: #999;
+        margin: 1.19rem 0 0 0;
+      }
+      a {
+        display: block;
+        font-size: .28rem;
+        color: #fff;
+        width: 1.88rem;
+        height: .54rem;
+        line-height: .54rem;
+        background: #418bf6;
+        margin: .7rem auto 0;
+        border-radius: .05rem;
+      }
+    }
+    .user-name{
+      padding:.14rem 0 .2rem .34rem;
+      background:#fff;
+      width:100%;
+      position:relative;
+      border-bottom: .1rem solid #dfe2e4;
+      .supdown{
+        position:absolute;
+        top:.8rem;
+        right:.3rem;
+        z-index:10;
+        width:1.7rem;
+        background:#616264;
+        border-radius:.1rem;
+        li{
+          font-size: .28rem;
+          color:#ffffff;
+          height: .32rem;
+          line-height: .32rem;
+          margin: .4rem 0;
+          text-align: center;
+        }
+      }
+      img{
+        display: inline-block;
+        width:1.25rem;
+        height:1.25rem;
+        border:.04rem solid #c5dbfc;
+        border-radius: .05rem;
+        vertical-align: middle;
+      }
+      .user-info {
+        margin-left:.25rem;
+        display: inline-block;
+        vertical-align: middle;
+        p{
+          font-size:.3rem;
+          margin:0;
+          font-weight: bold;
+          display: block;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          width: 3.92rem;
+          &:nth-child(2) {
+            font-weight: normal;
+            margin-top: .3rem;
+          }
+        }
+      }
+      span{
+        font-size:.28rem;
+        color:#53a0f7;
+        position: relative;
+        bottom: .3rem;
+      }
+    }
+    .shop-list {
+      background:#fff;
+      border-bottom: .1rem solid #dfe2e4;
+      padding-bottom:.28rem;
+      h3{
+        font-size:.32rem;
+        line-height: .8rem;
+        margin:0;
+        margin-left:.27rem;
+        margin-bottom:.14rem;
+      }
+      .list-item{
+        width:6.77rem;
+        margin-left:.39rem;
+        .item-img{
+          width:2.4rem;
+          vertical-align: middle;
+          display: inline-block;
+          img{
+            &:nth-child(2) {
+              width:2.4rem;
+              height:1.69rem;
+              border: .04rem solid #eee;
+            }
+            &:nth-child(1) {
+              position:absolute;
+              width:.65rem;
+              height:.33rem;
+            }
+          }
+        }
+        .list-item-phone{
+          width:3.95rem;
+          padding:.18rem 0;
+          position:relative;
+          display: inline-block;
+          vertical-align: middle;
+          margin-left: .26rem;
+          p{
+            font-size:.28rem;
+            line-height: .67rem;
+            margin:0;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            width: 3.2rem;
+          }
+          i{
+            display:block;
+            position:absolute;
+            top: -.06rem;
+            right: -.18rem;
+            font-size:.5rem;
+            color:#ff7800;
+            width: .6rem;
+            height: .6rem;
+            text-align: center;
+            line-height: .6rem;
+          }
+        }
+      }
+      &:active {
+        background: #e1e1e1;
+      }
+    }
+    .detail-brand{
+      background: #fff;
+      width:100%;
+      min-height:1.5rem;
+      padding:.2rem 0;
+      border-bottom: .1rem solid #dfe2e4;
+      &:nth-child(1) {
+        margin-top:.1rem;
+      }
+      .brand-item{
+        width:7rem;
+        margin:0 auto;
+        border-radius:.1rem;
+        background: #fff;
+        padding:.2rem;
+        position:relative;
+        &:active{
+           background: #e1e1e1;
+         }
+        p{
+          font-size:.28rem;
+          line-height:.4rem;
+          color:#333;
+          margin:0;
+        }
+        i{
+          display:block;
+          position:absolute;
+          top:.2rem;
+          right:.1rem;
+          font-size:.5rem;
+          color:#ff7800;
+          width: .6rem;
+          height: .6rem;
+          line-height: .6rem;
+          text-align: center;
+        }
+      }
+      div.active{
+        background: #d4d;
+      }
+    }
+    .collect-list-type {
+      background: #fff;
+      border-bottom: .04rem solid #acacac;
+      p {
+        font-size: .32rem;
+        margin: 0 0 0 .13rem;
+        width: .92rem;
+        text-align: center;
+        line-height: .5rem;
+        border-bottom: .06rem solid #418bf6;
+      }
+    }
+  }
+</style>

+ 0 - 8
plugins/README.md

@@ -1,8 +0,0 @@
-# PLUGINS
-
-This directory contains your Javascript plugins that you want to run before instantiating the root vue.js application.
-
-More information about the usage of this directory in the documentation:
-https://nuxtjs.org/guide/plugins
-
-**This directory is not required, you can delete it if you don't want to use it.**

+ 0 - 11
static/README.md

@@ -1,11 +0,0 @@
-# STATIC
-
-This directory contains your static files.
-Each file inside this directory is mapped to /.
-
-Example: /static/robots.txt is mapped as /robots.txt.
-
-More information about the usage of this directory in the documentation:
-https://nuxtjs.org/guide/assets#static
-
-**This directory is not required, you can delete it if you don't want to use it.**

BIN
static/images/404-mobile.png


BIN
static/images/mobile/@2x/brand-bg.png


BIN
static/images/mobile/@2x/brand/brandWall.png


BIN
static/images/mobile/@2x/car@2x.png


BIN
static/images/mobile/@2x/empty-collect.png


BIN
static/images/mobile/@2x/home/background@2x.png


BIN
static/images/mobile/@2x/home/background_search@2x.png


BIN
static/images/mobile/@2x/home/bookbrand@2x.png


BIN
static/images/mobile/@2x/home/brand@2x.png


BIN
static/images/mobile/@2x/home/hot-search.png


BIN
static/images/mobile/@2x/home/modelbrand@2x.png


BIN
static/images/mobile/@2x/home/phonebrand@2x.png


BIN
static/images/mobile/@2x/home/shopbrand@2x.png


BIN
static/images/mobile/@2x/home/storebrand@2x.png


BIN
static/images/mobile/@2x/productDetail/component-desc-bg.png


BIN
static/images/mobile/@2x/productDetail/kind-narrow-down@2x.png


BIN
static/images/mobile/@2x/productDetail/pdf.png


BIN
static/images/mobile/@2x/productDetail/pdf@3x.png


BIN
static/images/mobile/@2x/productDetail/search@2x.png


BIN
static/images/mobile/@2x/productDetail/view@2x.png


BIN
static/images/mobile/@2x/search-empty.png


BIN
static/images/mobile/@2x/search/search-brand.png


BIN
static/images/mobile/@2x/share/QQ@2x.png


BIN
static/images/mobile/@2x/share/copylink@2x.png


BIN
static/images/mobile/@2x/share/download@2x.png


BIN
static/images/mobile/@2x/share/email@2x.png


BIN
static/images/mobile/@2x/share/flash@2x.png


BIN
static/images/mobile/@2x/share/message@2x.png


BIN
static/images/mobile/@2x/share/sina@2x.png


BIN
static/images/mobile/@2x/share/weChat@2x.png


BIN
static/images/mobile/@2x/shop/daili@2x.png


BIN
static/images/mobile/@2x/shop/jingxiao@2x.png


BIN
static/images/mobile/@2x/shop/yuanchang@2x.png


BIN
static/images/mobile/@2x/shoucangkongzhuangtai@2x.png


BIN
static/images/mobile/@2x/sousuokongzhuangtai@2x.png


BIN
static/images/mobile/@2x/title-line.png


BIN
static/images/zhongqiu/zq1.png


BIN
static/images/zhongqiu/zq10.png


BIN
static/images/zhongqiu/zq11.png


BIN
static/images/zhongqiu/zq12.png


BIN
static/images/zhongqiu/zq13.png


BIN
static/images/zhongqiu/zq2.png


BIN
static/images/zhongqiu/zq3.png


BIN
static/images/zhongqiu/zq4.png


BIN
static/images/zhongqiu/zq5.png


BIN
static/images/zhongqiu/zq6.png


BIN
static/images/zhongqiu/zq7.png


BIN
static/images/zhongqiu/zq8.png


BIN
static/images/zhongqiu/zq9.png


+ 1 - 2
store/index.js

@@ -28,8 +28,7 @@ export const actions = {
   nuxtServerInit (store, { params, route, isDev, isServer, req }) {
     // 检查设备类型
     const userAgent = isServer ? req.headers['user-agent'] : navigator.userAgent
-    const isMobile = /(iPhone|iPod|Opera Mini|Android.*Mobile|NetFront|PSP|BlackBerry|Windows Phone)/ig.test(userAgent)
-
+    const isMobile = /(iPhone|iPad|Opera Mini|Android.*Mobile|NetFront|PSP|BlackBerry|Windows Phone)/ig.test(userAgent)
     const cookie = isServer ? req.headers['cookie'] : null
     store.commit('option/SET_MOBILE_LAYOUT', isMobile)
     store.commit('option/SET_USER_AGENT', userAgent)

+ 34 - 1
store/provider.js

@@ -8,7 +8,9 @@ import axios from '~plugins/axios'
 function countStoreOrderCount (store) {
   return axios.get('/api/provider/order/storeid/' + store.uuid + '/count')
 }
-
+function findStoreFocusInMobil (store) {
+  return axios.get('/trade/storeFocus/ifFocus?storeid=' + store.id)
+}
 export const actions = {
   // 获取销售排行榜信息
   loadSalesStore ({ commit }, params = {isOriginal: false}) {
@@ -102,5 +104,36 @@ export const actions = {
       }, err => {
         commit('stores/GET_STORE_LIST_FAILURE', err)
       })
+  },
+  findStoreListInMobil ({ commit }, params = {}) {
+    params.op = 'pageByType'
+    commit('stores/REQUEST_STORE_LIST')
+    return axios.get('/api/store-service/stores', { params })
+      .then(response => {
+        let listData = response.data
+        let focusData = []
+        for (let i = 0; i < listData.content.length; i++) {
+          let str = findStoreFocusInMobil({id: listData.content[i].id})
+          focusData.push(str)
+        }
+        // 合并请求,获取店铺关注信息
+        return Promise.all(focusData)
+          .then(result => {
+            if (result) {
+              for (let i = 0; i < result.length; i++) {
+                listData.content[i].isFocus = result[i] ? result[i].data : 'false'
+              }
+            }
+            commit('stores/GET_STORE_LIST_SUCCESS', listData)
+          }, err => {
+            console.log(err)
+            for (let i = 0; i < listData.content.length; i++) {
+              listData.content[i].isFocus = 'false'
+            }
+            commit('stores/GET_STORE_LIST_SUCCESS', listData)
+          })
+      }, err => {
+        commit('stores/GET_STORE_LIST_FAILURE', err)
+      })
   }
 }

+ 49 - 0
store/searchData.js

@@ -1,5 +1,22 @@
 import axios from '~plugins/axios'
 
+function reloadListData ({ commit }, listData) {
+  axios.get('/trade/collection/list').then(response => {
+    let focusData = response.data
+    for (let i = 0; i < listData.components.length; i++) {
+      for (let j = 0; j < focusData.length; j++) {
+        listData.components[i].isFocus = listData.components[i].cmpId === focusData[j].componentid
+        if (listData.components[i].isFocus) {
+          break
+        }
+      }
+    }
+    commit('searchList/GET_LIST_SUCCESS', listData)
+  }, err => {
+    console.log(err)
+    commit('searchList/GET_LIST_SUCCESS', listData)
+  })
+}
 export const actions = {
   // 获取搜索kind
   searchForKinds ({ commit }, params = {}) {
@@ -50,6 +67,28 @@ export const actions = {
         commit('searchList/GET_LIST_FAILURE', err)
       })
   },
+  // 获取手机端搜索list
+  searchForListInMobile ({ commit }, params = {}) {
+    commit('searchList/REQUEST_LIST', params)
+    return axios.get(`/api/product/component/search/compGoods`, {params})
+      .then(response => {
+        let listData = response.data
+        if (response.data.brands && response.data.brands.uuid) {
+          commit('searchDetail/REQUEST_DETAIL', params)
+          return axios.get(`/api/product/brand/${response.data.brands.uuid}`)
+            .then(response => {
+              reloadListData({ commit }, listData)
+              commit('searchDetail/GET_DETAIL_SUCCESS', response.data)
+            }, err => {
+              commit('searchDetail/GET_DETAIL_FAILURE', err)
+            })
+        } else {
+          reloadListData({ commit }, listData)
+        }
+      }, err => {
+        commit('searchList/GET_LIST_FAILURE', err)
+      })
+  },
   // 获取搜索货源
   searchForStoreType ({ commit }, params = {}) {
     commit('searchStoreType/REQUEST_STORETYPE', params)
@@ -69,6 +108,16 @@ export const actions = {
       }, err => {
         commit('searchCrname/GET_CRNAME_FAILURE', err)
       })
+  },
+  // 获取搜索历史
+  getSearchHistory ({ commit }, params = {}) {
+    commit('searchHistory/REQUEST_SEARCH_HISTORY', params)
+    return axios.get(`/search/searchHistory`, {params})
+      .then(response => {
+        commit('searchHistory/GET_SEARCH_HISTORY_SUCCESS', response.data)
+      }, err => {
+        commit('searchHistory/GET_SEARCH_HISTORY_FAILURE', err)
+      })
   }
 }
 

+ 19 - 0
store/searchData/searchHistory.js

@@ -0,0 +1,19 @@
+export const state = () => ({
+  searchHistory: {
+    fetching: false,
+    data: []
+  }
+})
+
+export const mutations = {
+  REQUEST_SEARCH_HISTORY (state) {
+    state.searchHistory.fetching = true
+  },
+  GET_SEARCH_HISTORY_FAILURE (state) {
+    state.searchHistory.fetching = false
+  },
+  GET_SEARCH_HISTORY_SUCCESS (state, result) {
+    state.searchHistory.fetching = false
+    state.searchHistory.data = result
+  }
+}

+ 10 - 0
store/shop.js

@@ -112,5 +112,15 @@ export const actions = {
       }, err => {
         commit('storeInfo/GET_FOCUS_FAILURE', err)
       })
+  },
+  // 根据UUID获取收藏店铺信息
+  StoreFocusPage ({commit}, params = {}) {
+    commit('storeInfo/REQUEST_FOCUSPAGE')
+    return axios.get(`/trade/storeFocus/page`, {params})
+      .then(response => {
+        commit('storeInfo/GET_FOCUSPAGE_SUCCESS', response.data)
+      }, err => {
+        commit('storeInfo/GET_FOCUSPAGE_FAILURE', err)
+      })
   }
 }

+ 14 - 0
store/shop/storeInfo.js

@@ -29,6 +29,10 @@ export const state = () => ({
   focusList: {
     fetching: false,
     data: {}
+  },
+  focusPage: {
+    fetching: false,
+    data: {}
   }
 })
 
@@ -102,5 +106,15 @@ export const mutations = {
   GET_FOCUSLIST_SUCCESS (state, result) {
     state.focusList.fetching = false
     state.focusList.data = result
+  },
+  REQUEST_FOCUSPAGE (state) {
+    state.focusPage.fetching = true
+  },
+  GET_FOCUSPAGE_SUCCESS (state, result) {
+    state.focusPage.fetching = false
+    state.focusPage.data = result
+  },
+  GET_FOCUSPAGE_FAILURE (state) {
+    state.focusPage.fetching = false
   }
 }