tremble vor 5 Jahren
Ursprung
Commit
c31e408615
72 geänderte Dateien mit 1288 neuen und 124 gelöschten Zeilen
  1. 1 0
      mobile/package.json
  2. 10 6
      mobile/src/assets/font/iconfont.css
  3. 24 3
      mobile/src/components/citySelect/city.js
  4. 6 4
      mobile/src/components/citySelect/index.vue
  5. 24 3
      mobile/src/components/createInvoice/city.js
  6. 6 5
      mobile/src/components/createInvoice/index.vue
  7. 24 3
      mobile/src/components/editInvoice/city.js
  8. 118 0
      mobile/src/components/videoSwiper/index.vue
  9. 1 1
      mobile/src/main.js
  10. 5 1
      mobile/src/pages/account/codeLogin/index.vue
  11. 7 3
      mobile/src/pages/account/forget/index.vue
  12. 3 1
      mobile/src/pages/account/login/index.vue
  13. 4 2
      mobile/src/pages/account/manage/change/index.vue
  14. 12 0
      mobile/src/pages/account/manage/consumption/index.vue
  15. 2 6
      mobile/src/pages/account/manage/myscene/index.vue
  16. 6 0
      mobile/src/pages/account/manage/order/index.vue
  17. 3 1
      mobile/src/pages/account/manage/payselect/index.vue
  18. 1 1
      mobile/src/pages/account/manage/submit/index.vue
  19. 7 3
      mobile/src/pages/account/register/index.vue
  20. 11 2
      mobile/src/pages/layout/header.vue
  21. 10 2
      mobile/src/pages/layout/style.scss
  22. 5 4
      mobile/src/pages/payresult/index.vue
  23. 1 1
      mobile/src/pages/purchasezhijia/index.vue
  24. 134 0
      mobile/src/pages/video/index.vue
  25. 5 0
      mobile/src/router/index.js
  26. 1 0
      mobile/src/store/language/cn/manage.js
  27. 1 1
      mobile/src/store/language/cn/purchase.js
  28. 1 0
      mobile/src/store/language/en/manage.js
  29. 62 1
      mobile/src/store/language/home_cn.js
  30. 63 1
      mobile/src/store/language/home_en.js
  31. 3 1
      mobile/src/util/browser.js
  32. 1 1
      mobile/src/util/http.js
  33. 26 0
      mobile/src/util/index.js
  34. 1 0
      pc/package.json
  35. 10 2
      pc/src/App.vue
  36. 10 6
      pc/src/assets/font/iconfont.css
  37. 48 0
      pc/src/components/browseVideo/index.vue
  38. 24 3
      pc/src/components/citySelect/city.js
  39. 11 6
      pc/src/components/citySelect/index.vue
  40. 116 0
      pc/src/components/lselect/perfect-scrollbar.css
  41. 2 2
      pc/src/components/toast/capacityRecharge.vue
  42. 4 1
      pc/src/components/toast/index.vue
  43. 1 1
      pc/src/main.js
  44. 7 2
      pc/src/page/layout/aside/temp/ltemp/forget.vue
  45. 8 2
      pc/src/page/layout/aside/temp/ltemp/login.vue
  46. 9 4
      pc/src/page/layout/aside/temp/ltemp/register.vue
  47. 5 1
      pc/src/page/layout/footer.vue
  48. 4 1
      pc/src/page/layout/header/index.vue
  49. 1 0
      pc/src/page/manage/style.scss
  50. 3 1
      pc/src/page/manage/temp/change.vue
  51. 10 1
      pc/src/page/manage/temp/consumption.vue
  52. 1 1
      pc/src/page/manage/temp/device.vue
  53. 1 1
      pc/src/page/manage/temp/iconsumption.js
  54. 3 3
      pc/src/page/manage/temp/information.vue
  55. 7 0
      pc/src/page/manage/temp/order.vue
  56. 2 6
      pc/src/page/manage/temp/scene.vue
  57. 1 1
      pc/src/page/purchasezhijia/index.vue
  58. 50 0
      pc/src/page/service/plugin/ff-scrollbar.vue
  59. 22 5
      pc/src/page/service/temp/clause.vue
  60. 24 5
      pc/src/page/service/temp/qa.vue
  61. 70 6
      pc/src/page/service/temp/use.vue
  62. 83 0
      pc/src/page/service/video.vue
  63. 5 0
      pc/src/router/index.js
  64. 1 0
      pc/src/store/language/cn/manage.js
  65. 1 1
      pc/src/store/language/cn/purchase.js
  66. 1 1
      pc/src/store/language/cn/toast.js
  67. 1 0
      pc/src/store/language/en/manage.js
  68. 1 1
      pc/src/store/language/en/toast.js
  69. 62 1
      pc/src/store/language/home_cn.js
  70. 61 0
      pc/src/store/language/home_en.js
  71. 3 1
      pc/src/util/browser.js
  72. 26 1
      pc/src/util/index.js

+ 1 - 0
mobile/package.json

@@ -17,6 +17,7 @@
     "axios": "^0.18.0",
     "gsap": "^2.1.2",
     "i": "^0.3.6",
+    "js-base64": "^2.5.2",
     "js-cookie": "^2.2.0",
     "luxy.js": "^0.1.0",
     "qqsdk": "^0.1.5",

+ 10 - 6
mobile/src/assets/font/iconfont.css

@@ -1,11 +1,11 @@
 @font-face {
   font-family: 'iconfont';  /* project id 941679 */
-  src: url('//at.alicdn.com/t/font_941679_nydb6f32r2j.eot');
-  src: url('//at.alicdn.com/t/font_941679_nydb6f32r2j.eot?#iefix') format('embedded-opentype'),
-  url('//at.alicdn.com/t/font_941679_nydb6f32r2j.woff2') format('woff2'),
-  url('//at.alicdn.com/t/font_941679_nydb6f32r2j.woff') format('woff'),
-  url('//at.alicdn.com/t/font_941679_nydb6f32r2j.ttf') format('truetype'),
-  url('//at.alicdn.com/t/font_941679_nydb6f32r2j.svg#iconfont') format('svg');
+  src: url('//at.alicdn.com/t/font_941679_any402rn3j.eot');
+  src: url('//at.alicdn.com/t/font_941679_any402rn3j.eot?#iefix') format('embedded-opentype'),
+  url('//at.alicdn.com/t/font_941679_any402rn3j.woff2') format('woff2'),
+  url('//at.alicdn.com/t/font_941679_any402rn3j.woff') format('woff'),
+  url('//at.alicdn.com/t/font_941679_any402rn3j.ttf') format('truetype'),
+  url('//at.alicdn.com/t/font_941679_any402rn3j.svg#iconfont') format('svg');
 }
 
 .iconfont {
@@ -16,6 +16,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-teaching:before {
+  content: "\e6bb";
+}
+
 .icon-xinlang:before {
   content: "\e60a";
 }

+ 24 - 3
mobile/src/components/citySelect/city.js

@@ -3005,9 +3005,30 @@ let citylist = [
       {
         n: '中山市',
         a: [
-          {
-            s: '中山市'
-          }
+          {s: '黄圃镇'},
+          {s: '南头镇'},
+          {s: '东凤镇'},
+          {s: '阜沙镇'},
+          {s: '小榄镇'},
+          {s: '东升镇'},
+          {s: '古镇镇'},
+          {s: '横栏镇'},
+          {s: '三角镇'},
+          {s: '民众镇'},
+          {s: '南朗镇'},
+          {s: '港口镇'},
+          {s: '大涌镇'},
+          {s: '沙溪镇'},
+          {s: '三乡镇'},
+          {s: '板芙镇'},
+          {s: '神湾镇'},
+          {s: '坦洲镇'},
+          {s: '石岐街道'},
+          {s: '东区街道'},
+          {s: '西区街道'},
+          {s: '南区街道'},
+          {s: '五桂山街道'},
+          {s: '火炬开发区街道'}
         ]
       },
       {

+ 6 - 4
mobile/src/components/citySelect/index.vue

@@ -202,10 +202,12 @@ export default {
       this.handleChange()
     },
     handleChange () {
-      let prov = this.citylist[this.currentPID]
-      let city = prov.c[this.currentCID]
-      let dist = city.a[this.currentSID]
-      this.areaPath = [prov.p, city.n, dist.s]
+      setTimeout(() => {
+        let prov = this.citylist[this.currentPID]
+        let city = prov.c[this.currentCID]
+        let dist = city.a[this.currentSID]
+        this.areaPath = [prov.p, city.n, dist.s]
+      })
     }
   },
   mounted () {

+ 24 - 3
mobile/src/components/createInvoice/city.js

@@ -3005,9 +3005,30 @@ let citylist = [
       {
         n: '中山市',
         a: [
-          {
-            s: '中山市'
-          }
+          {s: '黄圃镇'},
+          {s: '南头镇'},
+          {s: '东凤镇'},
+          {s: '阜沙镇'},
+          {s: '小榄镇'},
+          {s: '东升镇'},
+          {s: '古镇镇'},
+          {s: '横栏镇'},
+          {s: '三角镇'},
+          {s: '民众镇'},
+          {s: '南朗镇'},
+          {s: '港口镇'},
+          {s: '大涌镇'},
+          {s: '沙溪镇'},
+          {s: '三乡镇'},
+          {s: '板芙镇'},
+          {s: '神湾镇'},
+          {s: '坦洲镇'},
+          {s: '石岐街道'},
+          {s: '东区街道'},
+          {s: '西区街道'},
+          {s: '南区街道'},
+          {s: '五桂山街道'},
+          {s: '火炬开发区街道'}
         ]
       },
       {

+ 6 - 5
mobile/src/components/createInvoice/index.vue

@@ -329,11 +329,12 @@ export default {
       this.handleChange()
     },
     handleChange () {
-      let prov = this.citylist[this.currentPID]
-      let city = prov.c[this.currentCID]
-      let dist = city.a[this.currentSID]
-      this.areaPath = [prov.p, city.n, dist.s]
-      console.log(this.areaPath)
+      setTimeout(() => {
+        let prov = this.citylist[this.currentPID]
+        let city = prov.c[this.currentCID]
+        let dist = city.a[this.currentSID]
+        this.areaPath = [prov.p, city.n, dist.s]
+      })
     },
     async saveInvoice () {
       if (!this.amount) {

+ 24 - 3
mobile/src/components/editInvoice/city.js

@@ -3005,9 +3005,30 @@ let citylist = [
       {
         n: '中山市',
         a: [
-          {
-            s: '中山市'
-          }
+          {s: '黄圃镇'},
+          {s: '南头镇'},
+          {s: '东凤镇'},
+          {s: '阜沙镇'},
+          {s: '小榄镇'},
+          {s: '东升镇'},
+          {s: '古镇镇'},
+          {s: '横栏镇'},
+          {s: '三角镇'},
+          {s: '民众镇'},
+          {s: '南朗镇'},
+          {s: '港口镇'},
+          {s: '大涌镇'},
+          {s: '沙溪镇'},
+          {s: '三乡镇'},
+          {s: '板芙镇'},
+          {s: '神湾镇'},
+          {s: '坦洲镇'},
+          {s: '石岐街道'},
+          {s: '东区街道'},
+          {s: '西区街道'},
+          {s: '南区街道'},
+          {s: '五桂山街道'},
+          {s: '火炬开发区街道'}
         ]
       },
       {

+ 118 - 0
mobile/src/components/videoSwiper/index.vue

@@ -0,0 +1,118 @@
+<template>
+  <div class="media-layout">
+    <p class="b-text2">{{langVideoCourse.name}}</p>
+    <p class="b-text3">{{langVideoCourse.sub}}</p>
+    <swiper class="swiper-wrapper" :options="swiperOption">
+      <swiper-slide
+        class="swiper-slide"
+        v-for="(item,i) in langVideoCourse.items"
+        :key="i"
+      >
+        <div class="video-item" @click="handleItem(item)">
+          <img class="li-img" :src="`${$cdn}course/img/${item.code}.jpg`" alt="">
+          <img class="bofang" :src="`${$cdn}course/img/video_play@2x.png`" alt="">
+          <!-- <video controls="controls" playsinline="playsinline" muted="muted" :poster="`${$cdn}course/img/${item.code}.jpg`">
+            <source :src="`${$cdn}course/videos/${item.code}.mp4`" type="video/mp4">
+          </video> -->
+          <h3 class="btom-text" v-html="item.name"></h3>
+        </div>
+      </swiper-slide>
+    </swiper>
+  </div>
+</template>
+
+<script>
+import 'swiper/dist/css/swiper.css'
+import { swiper, swiperSlide } from 'vue-awesome-swiper'
+import { mapState } from 'vuex'
+
+export default {
+  computed: {
+    ...mapState({
+      langVideoCourse: state => state.language.home.videoCourse.content[0]
+    })
+  },
+  components: {
+    swiper,
+    swiperSlide
+  },
+  methods: {
+    handleItem (item) {
+      this.$emit('handleVideo', item)
+    }
+  },
+  data () {
+    return {
+      swiperOption: {
+        slidesPerView: 1.1,
+        spaceBetween: 10,
+        freeMode: true,
+        pagination: {
+          el: '.swiper-pagination',
+          clickable: true
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.media-layout {
+  .b-text2,.b-text3{
+    text-align: left;
+    margin-left: 0;
+    margin-right: 0;
+  }
+  .swiper-container {
+    width: 100%;
+  }
+
+  .swiper-wrapper {
+    margin-top: 10px;
+    padding: 10px 0 20px;
+  }
+
+  .swiper-slide {
+    width: 100%;
+    transform-style: preserve-3d;
+    .tpo-l{
+      width: 25%;
+      margin-bottom: 0;
+    }
+    .video-item{
+      width: 100%;
+      border-radius: 4px;
+      overflow: hidden;
+      position: relative;
+      .li-img{
+        width: 100%;
+        cursor: pointer;
+        border-radius: 4px;
+        overflow: hidden;
+      }
+      .bofang{
+        width: 30px;
+        height: 30px;
+        position: absolute;
+        left: 20px;
+        bottom: 50px;
+      }
+
+    }
+    .btom-text{
+      font-size: 14px;
+      color: rgba(0, 0, 0, 0.7);
+      line-height: 20px;
+      font-weight: bold;
+      margin-top: 10px;
+    }
+  }
+
+  .swiper-slide .main-img {
+    width: 100%;
+    margin: 0 auto;
+    display: block;
+  }
+}
+</style>

+ 1 - 1
mobile/src/main.js

@@ -17,7 +17,7 @@ let axios = require('./util/http.js').default
 let router = require('./router').default
 
 Vue.prototype.$http = axios
-Vue.prototype.$serverName = 'https://pro.4dkankan.com/'
+Vue.prototype.$serverName = 'https://www.4dkankan.com/'
 // Vue.prototype.$serverName = ''
 
 Vue.config.productionTip = false

+ 5 - 1
mobile/src/pages/account/codeLogin/index.vue

@@ -23,7 +23,7 @@
       <div class="code-con">
         <div class="input-con" :class="{inputActive:inputActive==='code'}">
           <img :src="`${$cdn}images/icon/icon-code@2x.png`" style="width:23px;" alt="">
-          <input v-model="authCode" @focus="inputActive='code'" @blur="inputActive=''" style="padding-left:6px;" type="text" :placeholder="langLogin.code.placeholder">
+          <input v-model="authCode" @focus="inputActive='code'" @blur="inputActive=''" style="padding-left:6px;" type="text" oninput="value=value.replace(/[^\d]/g,'')" maxlength='6' :placeholder="langLogin.code.placeholder">
         </div>
         <div v-if="!jishi" class="btns" @click="getAuthCode">{{langLogin.code.txt}}</div>
         <span class="btns" v-else>{{language==='en'?`Resend after ${interTime}s`:`${interTime}s后重新发送`}}</span>
@@ -44,6 +44,7 @@
 <script>
 import selectCall from '../country.js'
 import {mapState} from 'vuex'
+import { reg } from '@/util'
 
 export default {
   data () {
@@ -84,6 +85,9 @@ export default {
       this.codeActive = item
     },
     async getAuthCode () {
+      if (!reg.phone.test(this.phone)) {
+        return
+      }
       let res = await this.$store.dispatch('getAuthCode', {
         phone: this.phone,
         code: Number(this.codeActive[1].substr(1))

+ 7 - 3
mobile/src/pages/account/forget/index.vue

@@ -24,7 +24,7 @@
       <div class="code-con">
         <div class="input-con" :class="{inputActive:inputActive==='code'}">
           <img :src="`${$cdn}images/icon/icon-code@2x.png`" style="width:23px;" alt="">
-          <input v-model="authCode" @focus="inputActive='code'" @blur="inputActive=''" style="padding-left:6px;" type="text" :placeholder="langLogin.code.placeholder">
+          <input v-model="authCode" @focus="inputActive='code'" @blur="inputActive=''" style="padding-left:6px;" type="text" oninput="value=value.replace(/[^\d]/g,'')" maxlength='6' :placeholder="langLogin.code.placeholder">
         </div>
         <div v-if="!jishi" class="btns" @click="getAuthCode">{{langLogin.code.txt}}</div>
         <span class="btns" v-else>{{language==='en'?`Resend after ${interTime}s`:`${interTime}s后重新发送`}}</span>
@@ -50,7 +50,8 @@
 <script>
 import {mapState} from 'vuex'
 import selectCall from '../country.js'
-
+import { reg, encodeStr } from '@/util'
+import { Base64 } from 'js-base64'
 export default {
   computed: {
     ...mapState({
@@ -90,6 +91,9 @@ export default {
     },
 
     async getAuthCode () {
+      if (!reg.phone.test(this.phone)) {
+        return
+      }
       let res = await this.$store.dispatch('getAuthCode', {
         phone: this.phone,
         code: Number(this.codeActive[1].substr(1))
@@ -148,7 +152,7 @@ export default {
       }
 
       let params = {
-        password: this.password,
+        password: encodeStr(Base64.encode(this.password)),
         phoneNum: this.phone,
         confirmPwd: this.confirmpass,
         msgAuthCode: this.authCode

+ 3 - 1
mobile/src/pages/account/login/index.vue

@@ -30,6 +30,8 @@
 
 <script>
 import {mapState} from 'vuex'
+import { Base64 } from 'js-base64'
+import {encodeStr} from '@/util/index.js'
 
 export default {
   computed: {
@@ -74,7 +76,7 @@ export default {
       }
       let params = {
         phoneNum: this.phone,
-        password: this.password
+        password: encodeStr(Base64.encode(this.password))
       }
       await this.$store.dispatch('login', params)
       if (this.token) {

+ 4 - 2
mobile/src/pages/account/manage/change/index.vue

@@ -12,7 +12,7 @@
           <div class="order-sub">
             <div class="top-title">{{langModify.code.txt}}</div>
             <div class="code-con">
-              <div class="ant-input" >
+              <div class="ant-input">
                <input v-model="code" type="text" :placeholder="langModify.code.placeholder" />
               </div>
                 <button type="submit" class="ant-btn ant-btn-primary code">
@@ -48,6 +48,8 @@
 
 <script>
 import {mapState} from 'vuex'
+import { Base64 } from 'js-base64'
+import { encodeStr } from '@/util'
 
 export default {
   computed: {
@@ -128,7 +130,7 @@ export default {
         return this.$toast.show('warn', this.langToast['31'])
       }
       let params = {
-        password: this.password,
+        password: encodeStr(Base64.encode(this.password)),
         phoneNum: this.info.userName,
         confirmPwd: this.confirmpassword,
         msgAuthCode: this.code

+ 12 - 0
mobile/src/pages/account/manage/consumption/index.vue

@@ -48,6 +48,12 @@ let PAYSSTREN = {
   2: 'paypal'
 }
 
+let AMOUNTSTR = {
+  0: '¥',
+  1: '¥',
+  2: '$'
+}
+
 let methodStr = {
   0: 'getConsumpList',
   1: 'getChargeList',
@@ -174,6 +180,7 @@ export default {
   },
   methods: {
     toDetail (item) {
+      console.log(item)
       this.$router.push({
         name: 'consumpdetail'
       })
@@ -220,6 +227,7 @@ export default {
       this.data = this.myinvoicelist.list
       this.data.forEach(item => {
         item['detail'] = '详细'
+        item['money'] = '¥' + item['money']
         item['type'] = invoiceType[item['type']]
         item['finish'] = invoceStatusType[item['finish']]
       })
@@ -245,6 +253,8 @@ export default {
         item['statusStr'] = this.useStatus[item['status']]
         item['unitSize1'] = item['unitSize'] + item['unit'] + '/' + this.shixian[item['month']]
         item['channel'] = this.channelType[item['status']]
+        item['amount'] = AMOUNTSTR[item['payType']] + item['amount']
+
         item['payTypeStr'] = this.language === 'en' ? PAYSSTREN[item['payType']] : PAYSSTR[item['payType']]
       })
     }
@@ -271,6 +281,8 @@ export default {
           item['statusStr'] = this.useStatus[item['status']]
           item['unitSize1'] = item['unitSize'] + item['unit'] + '/' + this.shixian[item['month']]
           item['channel'] = this.channelType[item['status']]
+          item['amount'] = AMOUNTSTR[item['payType']] + item['amount']
+
           item['payTypeStr'] = newVal === 'en' ? PAYSSTREN[item['payType']] : PAYSSTR[item['payType']]
           data.push(item)
         })

+ 2 - 6
mobile/src/pages/account/manage/myscene/index.vue

@@ -103,7 +103,7 @@ export default {
       currentPage: 1,
       showShare: false,
       imgs,
-      url: 'https://pro.4dkankan.com/showProPC.html?m=KcMeJlOr8',
+      url: 'https://www.4dkankan.com/showProPC.html?m=KcMeJlOr8',
       total: 0,
       item: ''
     }
@@ -220,11 +220,7 @@ export default {
     gotoEdit (item) {
       let url = item.webSite
       let temp = ''
-      if (item.sceneScheme < 4) {
-        temp = url.replace('show', 'editApp')
-      } else {
-        temp = url.replace('show', 'edit')
-      }
+      temp = url.replace('show', 'edit')
       // let temp1 = temp.replace('//pro', '//test')
       let temp1 = temp.replace('showProPC.html', 'showProMobile.html')
       // console.log(temp1)

+ 6 - 0
mobile/src/pages/account/manage/order/index.vue

@@ -52,6 +52,9 @@
               <template v-else-if="getStatus(item) === 'partShipped'">
                 <span class="expreeNum">{{langOrders.partShipped}}</span>
               </template>
+              <template v-else-if="getStatus(item) === 'received'">
+                <span class="expreeNum">{{langOrders.received}}:{{item.orderItems[0].expressNum}}</span>
+              </template>
               <template v-else>
                 <span class="expreeNum">{{langOrders.hasCancal}}</span>
               </template>
@@ -237,6 +240,9 @@ export default {
           case 'shipped':
             temp = 'shipped'
             break
+          case 'received':
+            temp = 'received'
+            break
           default:
             break
         }

+ 3 - 1
mobile/src/pages/account/manage/payselect/index.vue

@@ -81,7 +81,9 @@ export default {
 
       if (PAYS[item.id] !== 'paypal') {
         if (this.isWeixin) {
-          return location.replace(`https://open.weixin.qq.com/connect/oauth2/authorize?redirect_uri=http%3a%2f%2fpro.4dkankan.com%2fapi%2forder%2fpay%2fwechatPreJsPay%3forderId=${orderId}%26spaceId=${spaceId}%26orderType=${Number(orderType)}&appid=wx779dbafb46bab697&response_type=code&scope=snsapi_base&state=1&connect_redirect=1#wechat_redirect`)
+          let href = spaceId ? `https://open.weixin.qq.com/connect/oauth2/authorize?redirect_uri=http%3a%2f%2fwww.4dkankan.com%2fapi%2forder%2fpay%2fwechatPreJsPay%3forderId=${orderId}%26spaceId=${spaceId || null}%26orderType=${Number(orderType)}&appid=wx779dbafb46bab697&response_type=code&scope=snsapi_base&state=1&connect_redirect=1#wechat_redirect`
+            : `https://open.weixin.qq.com/connect/oauth2/authorize?redirect_uri=http%3a%2f%2fwww.4dkankan.com%2fapi%2forder%2fpay%2fwechatPreJsPay%3forderId=${orderId}%26orderType=${Number(orderType)}&appid=wx779dbafb46bab697&response_type=code&scope=snsapi_base&state=1&connect_redirect=1#wechat_redirect`
+          return location.replace(href)
         }
         let response = await this.$http
           .post(`/order/pay/${PAYS[item.id]}`, params, {

+ 1 - 1
mobile/src/pages/account/manage/submit/index.vue

@@ -217,7 +217,7 @@ export default {
 
       if (Number(payType) !== 2) {
         if (this.isWeixin) {
-          return location.replace(`https://open.weixin.qq.com/connect/oauth2/authorize?redirect_uri=http%3a%2f%2fpro.4dkankan.com%2fapi%2forder%2fpay%2fwechatPreJsPay%3forderId=${this.orderId}%26orderType=${Number(this.orderType)}&appid=wx779dbafb46bab697&response_type=code&scope=snsapi_base&state=1&connect_redirect=1#wechat_redirect`)
+          return location.replace(`https://open.weixin.qq.com/connect/oauth2/authorize?redirect_uri=http%3a%2f%2fwww.4dkankan.com%2fapi%2forder%2fpay%2fwechatPreJsPay%3forderId=${this.orderId}%26orderType=${Number(this.orderType)}&appid=wx779dbafb46bab697&response_type=code&scope=snsapi_base&state=1&connect_redirect=1#wechat_redirect`)
         }
         let response = await this.$http
           .post(`order/pay/${paytypes[payType]}`, data, {

+ 7 - 3
mobile/src/pages/account/register/index.vue

@@ -28,7 +28,7 @@
       <div class="code-con">
         <div class="input-con" :class="{inputActive:inputActive==='code'}">
           <img :src="`${$cdn}images/icon/icon-code@2x.png`" style="width:23px;" alt="">
-          <input v-model="authCode" @focus="inputActive='code'" @blur="inputActive=''" style="padding-left:6px;" type="text" :placeholder="langLogin.code.placeholder">
+          <input v-model="authCode" @focus="inputActive='code'" @blur="inputActive=''" oninput="value=value.replace(/[^\d]/g,'')" maxlength='6' style="padding-left:6px;" type="text" :placeholder="langLogin.code.placeholder">
         </div>
         <div v-if="!jishi" class="btns" @click="getAuthCode">{{langLogin.code.txt}}</div>
         <span class="btns" v-else>{{language==='en'?`Resend after ${interTime}s`:`${interTime}s后重新发送`}}</span>
@@ -62,7 +62,8 @@
 <script>
 import selectCall from '../country.js'
 import {mapState} from 'vuex'
-
+import { reg, encodeStr } from '@/util'
+import { Base64 } from 'js-base64'
 export default {
   computed: {
     ...mapState({
@@ -104,6 +105,9 @@ export default {
       this.codeActive = item
     },
     async getAuthCode () {
+      if (!reg.phone.test(this.phone)) {
+        return
+      }
       let res = await this.$store.dispatch('getAuthCode', {
         phone: this.phone,
         code: Number(this.codeActive[1].substr(1))
@@ -174,7 +178,7 @@ export default {
       let country = Number(this.codeActive[1].substr(1))
 
       let params = {
-        password: this.password,
+        password: encodeStr(Base64.encode(this.password)),
         phoneNum: this.phone,
         msgAuthCode: this.authCode,
         nickName: this.nickname,

+ 11 - 2
mobile/src/pages/layout/header.vue

@@ -31,7 +31,8 @@
     </div>
     <div class="search-active" :style="{maxHeight:searchActive?'140px':'0'}">
       <input type="text" ref="searchInput" v-model="searchTxt" :placeholder="langHeader.placeholder">
-      <i @click="()=>{searchActive = false;searchTxt=''}" class="iconfont icon-cuowu"></i>
+      <i @click="()=>{searchActive = false;searchTxt='';showCount=false}" class="iconfont icon-cuowu"></i>
+      <div v-if="showCount" class="noLeng">{{searchTxt?total:0}}</div>
       <div v-if="scene.length>0&&searchActive" class="logo"></div>
     </div>
     <div class="search-result" id="search-result" @touchmove.stop :style="{maxHeight:scene.length>0&&searchActive?'calc(100vh - 140px)':'0'}">
@@ -85,6 +86,9 @@
 import vcenter from '@/components/vcenter/'
 import { mapState } from 'vuex'
 import Paging from '@/components/Paging'
+
+import browser from '@/util/browser'
+
 let hasBack = [
   'introduce',
   'introtow',
@@ -210,7 +214,9 @@ export default {
       pageSize: 10,
       currentPage: 1,
       scene: [],
-      userList: false
+      showCount: false,
+      userList: false,
+      browserLang: browser.language
     }
   },
   watch: {
@@ -237,6 +243,8 @@ export default {
     }
   },
   mounted () {
+    let lang = ~this.browserLang.indexOf('zh') ? '中' : 'en'
+    this.$store.commit('change_language', lang)
     document.addEventListener('click', (e) => {
       if (this.$refs.userList) {
         if (!this.$refs.userList.contains(e.target)) {
@@ -350,6 +358,7 @@ export default {
       let data = result.data.data
       this.scene = data.list
       this.total = data.total
+      this.showCount = true
     }
   },
   components: {

+ 10 - 2
mobile/src/pages/layout/style.scss

@@ -167,16 +167,17 @@ $bannerHeight:60px;
       position: absolute;
       left: 50%;
       top: 50%;
+      min-width: 80%;
       transform: translate(-50%, -50%);
       appearance: none;
       background: #2b2b2b;
       text-align: center;
       caret-color: #1fe4dc;
-      font-size: 25px;
+      font-size: 18px;
       border: none;
       height: 30px;
       line-height: 30px;
-      color: #fff;
+      color: #999;
     }
     .icon-cuowu {
       right: 20px;
@@ -184,6 +185,13 @@ $bannerHeight:60px;
       font-size: 20px;
       position: absolute;
     }
+    .noLeng{
+      position: absolute;
+      bottom: 18px;
+      font-size: 30px;
+      left: 50%;
+      transform: translateX(-50%);
+    }
     .logo{
       width: 0;
       height: 0;

+ 5 - 4
mobile/src/pages/payresult/index.vue

@@ -4,21 +4,21 @@
       <div class="pay-result">
         <img :src="result==='success'?`${$cdn}images/paysuccess.png`:`${$cdn}images/payfail.png`" alt>
         <div class="pay-txt">
-          <div class="main-title" :style="{color:result==='success'?'#13d533':'#ea0b0b'}">{{result==='success'?'恭喜您,支付成功!':'支付失败!请重新支付'}}</div>
+          <div class="main-title" :style="{color:result==='success'?'#13d533':'#ea0b0b'}">{{result==='success'?(language==='en'?'Congratulations! The payment was successful.':'恭喜您,支付成功!'):(language==='en'?'Your payment failed, please try again.':'支付失败!请重新支付')}}</div>
         </div>
       </div>
     </div>
     <div v-if="orderinfo['orderSn']" class="pay-bottom">
       <div class="pay-info">
-        <span>订单号:</span>
+        <span>{{language==='en'?'Order number: ':'订单号:'}}</span>
         <span>{{orderinfo.orderSn||'*******'}}</span>
       </div>
       <div class="pay-info">
-        <span>商品总价:</span>
+        <span>{{language==='en'?'Item price: ':'商品总价:'}}</span>
         <span>{{orderinfo.country===1?'USD ':'¥'}}{{orderinfo.price||'*****'}}</span>
       </div>
     </div>
-    <div class="btn-submit" @click="backto">{{orderinfo.orderType===0?'我的订单':'我的相机'}}</div>
+    <div class="btn-submit" @click="backto">{{orderinfo.orderType===0?(language==='en'?'My Order':'我的订单'):language==='en'?'My Cameras':'我的相机'}}</div>
   </div>
 </template>
 
@@ -37,6 +37,7 @@ export default {
   computed: {
     ...mapState({
       token: state => state.user.token,
+      language: state => state.language.current,
       info: state => state.user.info,
       orderinfo: state => {
         let type = Object.prototype.toString.call(state.user.orderinfo)

+ 1 - 1
mobile/src/pages/purchasezhijia/index.vue

@@ -48,7 +48,7 @@
       </div>
       <div class="p2-pramas">
         <div class="p2-name">{{langzhijia.parmas.name}}</div>
-        <div v-for="(item,i) in langzhijia.parmas.detail" :key="i">
+        <div v-for="(item,i) in langzhijia.parmas.detail" :key="i+1">
           <p class="title">{{item.label}}</p>
           <div class="name">{{item.name}}</div>
           <div class="detail" >

+ 134 - 0
mobile/src/pages/video/index.vue

@@ -0,0 +1,134 @@
+<template>
+  <div class="container">
+    <div class="popup-player" v-if="showVideo">
+      <div class="mask" @click="()=>{currenVideo='';showVideo=false}"></div>
+      <video autoplay controls :src="currenVideo" class="video-fr"></video>
+    </div>
+    <mediaSwiper @handleVideo="itemHandle" class="media-con"/>
+
+    <div class="setion">
+      <p class="b-text2">{{langVideoCourse.name}}</p>
+      <p class="b-text3">{{langVideoCourse.sub}}</p>
+      <ul class="i-ul">
+        <li @click="itemHandle(sub)" v-for="(sub,idx) in langVideoCourse.items" :key="idx">
+          <img class="li-img" :src="`${$cdn}course/img/${sub.code}.jpg`" alt="">
+          <img class="bofang" :src="`${$cdn}course/img/video_play@2x.png`" alt="">
+          <h3 class="btom-text">{{sub.name}}</h3>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+import mediaSwiper from '@/components/videoSwiper'
+
+export default {
+  components: {
+    mediaSwiper
+  },
+  computed: {
+    ...mapState({
+      langVideoCourse: state => state.language.home.videoCourse.content[1]
+    })
+  },
+  data () {
+    return {
+      currenVideo: '',
+      showVideo: false
+    }
+  },
+  methods: {
+    itemHandle (item) {
+      this.showVideo = true
+      this.currenVideo = `${this.$cdn}course/videos/${item.code}.mp4`
+    },
+    itemHandles (item) {
+      console.log(item)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.container{
+  width: 100%;
+  .media-con{
+    width: 90%;
+    margin: 20px auto;
+  }
+  .popup-player {
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 99999;
+    .mask {
+      width: 100%;
+      height: 100%;
+      position: absolute;
+      left: 0;
+      top: 0;
+      background: rgba(0, 0, 0, 0.9);
+    }
+    .video-fr {
+      width: 100%;
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+    }
+  }
+  .setion{
+    width: 90%;
+    margin: 20px auto;
+    .b-text2,.b-text3{
+      text-align: left;
+      margin-left: 0;
+      margin-right: 0;
+    }
+    .i-ul{
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      flex-wrap: wrap;
+      width: 100%;
+      li{
+        position: relative;
+        max-width: 49%;
+
+        min-height: 160px;
+        margin-top: 20px;
+        .btom-text{
+          font-size: 14px;
+          color: rgba(0, 0, 0, 0.7);
+          line-height: 20px;
+          font-weight: bold;
+          margin-top: 10px;
+        }
+        .li-img{
+          width: 100%;
+          cursor: pointer;
+          border-radius: 4px;
+          overflow: hidden;
+        }
+        .bofang{
+          width: 20px;
+          height: 20px;
+          position: absolute;
+          left: 20px;
+          top: 70px;
+        }
+        .li-p{
+          font-size: 16px;
+          color: #2d2d2d;
+          margin-top: 10px;
+        }
+      }
+    }
+  }
+}
+
+</style>

+ 5 - 0
mobile/src/router/index.js

@@ -56,6 +56,11 @@ let router = new Router({
       component: resolve => require(['@/pages/about'], resolve)
     },
     {
+      path: '/video-course',
+      name: 'video-course',
+      component: resolve => require(['@/pages/video'], resolve)
+    },
+    {
       path: '/pay',
       name: 'pay',
       component: resolve => require(['@/pages/pay'], resolve)

+ 1 - 0
mobile/src/store/language/cn/manage.js

@@ -167,6 +167,7 @@ export default{
     wlNum: '物流单号:',
     unshipped: '等待商家发货',
     finish: '已完成',
+    received: '已收货',
     partShipped: '部分发货',
     hasCancal: '已取消',
     edit: '修改',

+ 1 - 1
mobile/src/store/language/cn/purchase.js

@@ -173,7 +173,7 @@ export default{
         `答:原套餐的剩余时间将换算为新套餐的时间,体现为延期时间。<br/>
         计算公式为:以每月129元的20G套餐为准,其中10G为基础容量不计算,换算的单位价值约为:0.42元/天/GB。<br/>
         如您原套餐为20G套餐(月),还有10天到期,则计算剩余为42元。<br/>
-        当天您升级套餐为50G套餐(月),其费用为520元/月(单天价值为16元)。<br/>
+        当天您升级套餐为50G套餐(月),其费用为509元/月(单天价值为16.4元)。<br/>
         原套餐剩余价值约为新套餐单天价值的2.6倍,则新套餐延期时间为3天。<br/>
         (延期计费方式采用进一法:1.01~1.99倍数都视作2倍。)`
     },

+ 1 - 0
mobile/src/store/language/en/manage.js

@@ -170,6 +170,7 @@ export default{
     unshipped: 'Waiting for shipment',
     finish: 'Finished',
     partShipped: 'Partly dispatched',
+    received: 'Received',
     hasCancal: 'Cancelled',
     edit: 'Edit',
     editInfo: 'Only within 7 days after delivery',

+ 62 - 1
mobile/src/store/language/home_cn.js

@@ -105,6 +105,11 @@ export default {
             icon: 'icon-baoxiutiaokuan',
             name: '售后服务',
             to: {name: 'service', params: {id: 'clause'}}
+          },
+          {
+            icon: 'icon-teaching',
+            name: '视频教程',
+            to: {name: 'video-course'}
           }
         ]
       },
@@ -162,6 +167,62 @@ export default {
     txt: '抱歉,您访问的页面出现了错误,请重新加载',
     btn: '返回首页'
   },
+  videoCourse: {
+    content: [
+      {
+        name: '视频教程',
+        sub: '快速上手以及详细教学',
+        items: [
+          {
+            code: 'img_guide01@2x',
+            name: '快速上手指南'
+          },
+          {
+            code: 'img_guide02@2x',
+            name: '完整版操作指南'
+          }
+        ]
+      },
+      {
+        name: '分段教学',
+        sub: '功能模块拆解分段学习',
+        items: [
+          {
+            code: 'img_01getready@2x',
+            name: '使用前准备'
+          },
+          {
+            code: 'img_03shotstep@2x',
+            name: '拍摄流程'
+          },
+          {
+            code: 'img_08shotmode@2x',
+            name: '拍摄方式'
+          },
+          {
+            code: 'img_05point@2x',
+            name: '点位调整功能'
+          },
+          {
+            code: 'img_06video@2x',
+            name: '球幕视频功能'
+          },
+          {
+            code: 'img_02editmb@2x',
+            name: '编辑模型-手机APP端模型编辑'
+          },
+          {
+            code: 'img_04editpc@2x',
+            name: '编辑模型-PC端模型编辑'
+          },
+          {
+            code: 'img_07share@2x',
+            name: '上传分享'
+          }
+        ]
+      }
+    ]
+  },
   purchase,
   purchasetow,
   purchasezhijia,
@@ -273,7 +334,7 @@ export default {
       },
       {
         name: '联系电话',
-        content: '0756-6996790-800'
+        content: '400-669-8025'
       }
     ],
     links: [{

+ 63 - 1
mobile/src/store/language/home_en.js

@@ -105,7 +105,13 @@ export default {
           icon: 'icon-baoxiutiaokuan',
           name: 'Warranty Information',
           to: {name: 'service', params: {id: 'clause'}}
-        }]
+        },
+        {
+          icon: 'icon-teaching',
+          name: 'Video tutorial',
+          to: {name: 'video-course'}
+        }
+        ]
       },
       {
         title: 'Technologies',
@@ -161,6 +167,62 @@ export default {
     txt: 'Error,Please reload.',
     btn: 'Go Back'
   },
+  videoCourse: {
+    content: [
+      {
+        name: '视频教程',
+        sub: '快速上手以及详细教学',
+        items: [
+          {
+            code: 'img_guide01@2x',
+            name: '快速上手指南'
+          },
+          {
+            code: 'img_guide02@2x',
+            name: '完整版操作指南'
+          }
+        ]
+      },
+      {
+        name: '分段教学',
+        sub: '功能模块拆解分段学习',
+        items: [
+          {
+            code: 'img_01getready@2x',
+            name: '使用前准备'
+          },
+          {
+            code: 'img_03shotstep@2x',
+            name: '拍摄流程'
+          },
+          {
+            code: 'img_08shotmode@2x',
+            name: '拍摄方式'
+          },
+          {
+            code: 'img_05point@2x',
+            name: '点位调整功能'
+          },
+          {
+            code: 'img_06video@2x',
+            name: '球幕视频功能'
+          },
+          {
+            code: 'img_02editmb@2x',
+            name: '编辑模型-手机APP端模型编辑'
+          },
+          {
+            code: 'img_04editpc@2x',
+            name: '编辑模型-PC端模型编辑'
+          },
+          {
+            code: 'img_07share@2x',
+            name: '上传分享'
+          }
+        ]
+      }
+    ]
+  },
   home,
   eight,
   purchase,

+ 3 - 1
mobile/src/util/browser.js

@@ -22,7 +22,9 @@ function versions () {
     // 是否为web应用程序,没有头部与底部
     webApp: u.indexOf('Safari') === -1,
     // 是否为微信浏览器
-    weixin: ~u.indexOf('MicroMessenger')
+    weixin: ~u.indexOf('MicroMessenger'),
+    // 获取浏览器语言
+    language: (navigator.browserLanguage || navigator.language).toLowerCase()
   }
 }
 

+ 1 - 1
mobile/src/util/http.js

@@ -8,7 +8,7 @@ let vue = new Vue()
 const exceptUrls = ['/sso/user/logout', '/sso/user/sendUserInfo', '/user/checkToken']
 
 // axios.defaults.baseURL = process.env.NODE_ENV === 'development' ? 'http://120.79.15.136:8086/api' : '/api'
-axios.defaults.baseURL = process.env.NODE_ENV === 'development' ? 'https://www.4dkankan.com/api' : '/api'
+axios.defaults.baseURL = process.env.NODE_ENV === 'development' ? 'https://test.4dkankan.com/api' : '/api'
 
 // 请求超时时限
 axios.defaults.timeout = 15000

+ 26 - 0
mobile/src/util/index.js

@@ -1,3 +1,18 @@
+function randomWord (randomFlag, min, max) {
+  let str = ''
+  let range = min
+  let arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
+  // 随机产生
+  if (randomFlag) {
+    range = Math.round(Math.random() * (max - min)) + min
+  }
+  for (var i = 0; i < range; i++) {
+    let pos = Math.round(Math.random() * (arr.length - 1))
+    str += arr[pos]
+  }
+  return str
+}
+
 module.exports = {
   getPosition: function (dom, parent) {
     var ret = {
@@ -37,5 +52,16 @@ module.exports = {
       (String(day).length > 1 ? day : '0' + day)
       // + ' ' + (String(hour).length > 1 ? hour : '0' + hour) + ':' + (String(minute).length > 1 ? minute : '0' + minute) +
       //  ':' + (String(second).length > 1 ? second : '0' + second)
+  },
+  encodeStr: function (str) {
+    const NUM = 2
+    const front = randomWord(false, 8)
+    const middle = randomWord(false, 8)
+    const end = randomWord(false, 8)
+
+    let str1 = str.substring(0, NUM)
+    let str2 = str.substring(NUM)
+
+    return front + str2 + middle + str1 + end
   }
 }

+ 1 - 0
pc/package.json

@@ -18,6 +18,7 @@
     "detect-zoom": "^1.0.2",
     "gsap": "^2.1.2",
     "i": "^0.3.6",
+    "js-base64": "^2.5.2",
     "js-cookie": "^2.2.0",
     "luxy.js": "^0.1.0",
     "three": "^0.102.1",

+ 10 - 2
pc/src/App.vue

@@ -1,5 +1,6 @@
 <template>
   <div id="app" :style="{transform:scale!==1 && gre150? `scale(${scale})`:'none','paddingTop':deviceLogin?'0px':'68px'}">
+    <browseVideo v-if="showVideo" :url="showVideo.url"/>
     <div class="big-nav" :class="{'hidden-nav':deviceLogin}">
       <iheader>
         <inavs slot-scope="{cp}" :cp="cp" />
@@ -18,6 +19,7 @@
 import '@/assets/style/reset.scss'
 import '@/assets/style/public.scss'
 import '@/assets/font/iconfont.css'
+import browseVideo from '@/components/browseVideo'
 import header from '@/page/layout/header'
 import footer from '@/page/layout/footer'
 import left from '@/page/layout/left'
@@ -39,7 +41,8 @@ export default {
       isNotChorme: browser.firefox || browser.edge,
       isIE: browser.trident,
       isPad: browser.iPad,
-      gre150: false
+      gre150: false,
+      showVideo: false
     }
   },
   watch: {
@@ -70,7 +73,8 @@ export default {
     iaside: aside,
     inavs: navs,
     ichat: chat,
-    ileft: left
+    ileft: left,
+    browseVideo
   },
   computed: {
     ...mapState({
@@ -96,6 +100,10 @@ export default {
       }
     })()
 
+    this.$bus.$on('toggleVideo', (status) => {
+      this.showVideo = status
+    })
+
     this.$bus.$on('showMask', (index) => {
       clearTimeout(this.timeout)
       this.maskZIndex = index || 99

+ 10 - 6
pc/src/assets/font/iconfont.css

@@ -1,11 +1,11 @@
 @font-face {
   font-family: 'iconfont';  /* project id 941679 */
-  src: url('//at.alicdn.com/t/font_941679_nydb6f32r2j.eot');
-  src: url('//at.alicdn.com/t/font_941679_nydb6f32r2j.eot?#iefix') format('embedded-opentype'),
-  url('//at.alicdn.com/t/font_941679_nydb6f32r2j.woff2') format('woff2'),
-  url('//at.alicdn.com/t/font_941679_nydb6f32r2j.woff') format('woff'),
-  url('//at.alicdn.com/t/font_941679_nydb6f32r2j.ttf') format('truetype'),
-  url('//at.alicdn.com/t/font_941679_nydb6f32r2j.svg#iconfont') format('svg');
+  src: url('//at.alicdn.com/t/font_941679_any402rn3j.eot');
+  src: url('//at.alicdn.com/t/font_941679_any402rn3j.eot?#iefix') format('embedded-opentype'),
+  url('//at.alicdn.com/t/font_941679_any402rn3j.woff2') format('woff2'),
+  url('//at.alicdn.com/t/font_941679_any402rn3j.woff') format('woff'),
+  url('//at.alicdn.com/t/font_941679_any402rn3j.ttf') format('truetype'),
+  url('//at.alicdn.com/t/font_941679_any402rn3j.svg#iconfont') format('svg');
 }
 
 .iconfont {
@@ -16,6 +16,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-teaching:before {
+  content: "\e6bb";
+}
+
 .icon-xinlang:before {
   content: "\e60a";
 }

+ 48 - 0
pc/src/components/browseVideo/index.vue

@@ -0,0 +1,48 @@
+<template>
+  <div class="brow-layout" @click="closeVideo">
+    <div class="con" @click.stop>
+        <video :src="`${$cdn}course/videos/${url}.mp4`" autoplay controls loop></video>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: ['url'],
+  data () {
+    return {
+
+    }
+  },
+  methods: {
+    closeVideo () {
+      this.$bus.$emit('toggleVideo', false)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.brow-layout{
+  width: 100%;
+  height: 100%;
+  position: fixed;
+  left: 0;
+  top: 0;
+  bottom: 0;
+  right: 0;
+  background-color: rgba(0,0,0,0.4);
+  z-index: 9999;
+  .con{
+    position: fixed;
+    width: 960px;
+    height: auto;
+    transform: translate(-50%,-50%);
+    top: 50%;
+    left: 50%;
+    video{
+      width: 100%;
+    }
+  }
+}
+</style>

+ 24 - 3
pc/src/components/citySelect/city.js

@@ -3005,9 +3005,30 @@ let citylist = [
       {
         n: '中山市',
         a: [
-          {
-            s: '中山市'
-          }
+          {s: '黄圃镇'},
+          {s: '南头镇'},
+          {s: '东凤镇'},
+          {s: '阜沙镇'},
+          {s: '小榄镇'},
+          {s: '东升镇'},
+          {s: '古镇镇'},
+          {s: '横栏镇'},
+          {s: '三角镇'},
+          {s: '民众镇'},
+          {s: '南朗镇'},
+          {s: '港口镇'},
+          {s: '大涌镇'},
+          {s: '沙溪镇'},
+          {s: '三乡镇'},
+          {s: '板芙镇'},
+          {s: '神湾镇'},
+          {s: '坦洲镇'},
+          {s: '石岐街道'},
+          {s: '东区街道'},
+          {s: '西区街道'},
+          {s: '南区街道'},
+          {s: '五桂山街道'},
+          {s: '火炬开发区街道'}
         ]
       },
       {

+ 11 - 6
pc/src/components/citySelect/index.vue

@@ -68,12 +68,17 @@ export default {
   },
   methods: {
     handleChange () {
-      let prov = this.citylist[this.currentPID]
-      let city = prov.c[this.currentCID]
-      let dist = city.a[this.currentSID]
-      this.$emit('currentVal', [
-        prov.p, city.n, dist.s
-      ])
+      setTimeout(() => {
+        let prov = this.citylist[this.currentPID]
+        let city = prov.c[this.currentCID]
+        let dist = city.a[this.currentSID]
+        console.log([
+          prov.p, city.n, dist.s
+        ])
+        this.$emit('currentVal', [
+          prov.p, city.n, dist.s
+        ])
+      })
     }
   },
   mounted () {

+ 116 - 0
pc/src/components/lselect/perfect-scrollbar.css

@@ -0,0 +1,116 @@
+/*
+ * Container style
+ */
+.ps {
+  overflow: hidden !important;
+  overflow-anchor: none;
+  -ms-overflow-style: none;
+  touch-action: auto;
+  -ms-touch-action: auto;
+}
+
+/*
+ * Scrollbar rail styles
+ */
+.ps__rail-x {
+  display: none;
+  opacity: 0;
+  transition: background-color .2s linear, opacity .2s linear;
+  -webkit-transition: background-color .2s linear, opacity .2s linear;
+  height: 15px;
+  /* there must be 'bottom' or 'top' for ps__rail-x */
+  bottom: 0px;
+  /* please don't change 'position' */
+  position: absolute;
+}
+
+.ps__rail-y {
+  display: none;
+  opacity: 0;
+  transition: background-color .2s linear, opacity .2s linear;
+  -webkit-transition: background-color .2s linear, opacity .2s linear;
+  width: 15px;
+  /* there must be 'right' or 'left' for ps__rail-y */
+  right: 0;
+  /* please don't change 'position' */
+  position: absolute;
+}
+
+.ps--active-x > .ps__rail-x,
+.ps--active-y > .ps__rail-y {
+  display: block;
+  background-color: transparent;
+}
+
+.ps:hover > .ps__rail-x,
+.ps:hover > .ps__rail-y,
+.ps--focus > .ps__rail-x,
+.ps--focus > .ps__rail-y,
+.ps--scrolling-x > .ps__rail-x,
+.ps--scrolling-y > .ps__rail-y {
+  opacity: 0.6;
+}
+
+.ps .ps__rail-x:hover,
+.ps .ps__rail-y:hover,
+.ps .ps__rail-x:focus,
+.ps .ps__rail-y:focus,
+.ps .ps__rail-x.ps--clicking,
+.ps .ps__rail-y.ps--clicking {
+  background-color: #eee;
+  opacity: 0.9;
+}
+
+/*
+ * Scrollbar thumb styles
+ */
+.ps__thumb-x {
+  background-color: #aaa;
+  border-radius: 6px;
+  transition: background-color .2s linear, height .2s ease-in-out;
+  -webkit-transition: background-color .2s linear, height .2s ease-in-out;
+  height: 6px;
+  /* there must be 'bottom' for ps__thumb-x */
+  bottom: 2px;
+  /* please don't change 'position' */
+  position: absolute;
+}
+
+.ps__thumb-y {
+  background-color: #aaa;
+  border-radius: 6px;
+  transition: background-color .2s linear, width .2s ease-in-out;
+  -webkit-transition: background-color .2s linear, width .2s ease-in-out;
+  width: 6px;
+  /* there must be 'right' for ps__thumb-y */
+  right: 2px;
+  /* please don't change 'position' */
+  position: absolute;
+}
+
+.ps__rail-x:hover > .ps__thumb-x,
+.ps__rail-x:focus > .ps__thumb-x,
+.ps__rail-x.ps--clicking .ps__thumb-x {
+  background-color: #999;
+  height: 11px;
+}
+
+.ps__rail-y:hover > .ps__thumb-y,
+.ps__rail-y:focus > .ps__thumb-y,
+.ps__rail-y.ps--clicking .ps__thumb-y {
+  background-color: #999;
+  width: 11px;
+}
+
+/* MS supports */
+@supports (-ms-overflow-style: none) {
+  .ps {
+    overflow: auto !important;
+  }
+}
+
+@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+  .ps {
+    overflow: auto !important;
+  }
+}

+ 2 - 2
pc/src/components/toast/capacityRecharge.vue

@@ -199,7 +199,7 @@ let qixianArr = [
   }
 ]
 export default {
-  props: ['visible', 'type', 'capacityitem', 'deadline', 'capacitydetail', 'capacitycountry', 'capacitychildName'],
+  props: ['visible', 'capacitycount', 'type', 'capacityitem', 'deadline', 'capacitydetail', 'capacitycountry', 'capacitychildName'],
   computed: {
     deadlineTime () {
       return formatDate(this.delay * ONEDAY + this.deadline)
@@ -228,7 +228,7 @@ export default {
       }
     },
     activeItem (newVal) {
-      if (newVal) {
+      if (newVal && this.capacitycount > 0) {
         this.pay()
       }
     },

+ 4 - 1
pc/src/components/toast/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <pointRecharge :pointid='pointid' :pointitem='pointitem' :pointchildName='pointchildName' :visible='ponintVisible' @closePoint="pointshandle"/>
-    <capacityRecharge @qixian='qixianHandle' @renewItem='renewhandle' :capacitydetail='capacitydetail' :capacityitem='capacityitem' :capacitycountry='capacitycountry' :capacitychildName='capacitychildName' :deadline='capacitydeadline' :type='capacitytype' :visible='capacityvisible' @closePoint="capacityhandle"/>
+    <capacityRecharge @qixian='qixianHandle' :capacitycount='capacitycount' @renewItem='renewhandle' :capacitydetail='capacitydetail' :capacityitem='capacityitem' :capacitycountry='capacitycountry' :capacitychildName='capacitychildName' :deadline='capacitydeadline' :type='capacitytype' :visible='capacityvisible' @closePoint="capacityhandle"/>
     <editInvoice :edititem=editItem :visible='editVisible' @closePoint="invoicehandle"/>
     <showInvoice :showitem=showItem :visible='showVisible' @closePoint="()=>{showVisible = false}"/>
     <binding :btype="bindingType" :visible='bindingVisible' @closePoint="()=>{bindingVisible = false,emitCallback()}"/>
@@ -84,6 +84,7 @@ export default {
       showItem: '',
       capacitydetail: '',
       capacitycountry: 0,
+      capacitycount: 0,
       capacityitem: '',
       capacitychildName: '',
       capacitydeadline: '',
@@ -129,6 +130,7 @@ export default {
     },
     renewhandle (data) {
       this.capacityitem = data.item
+      this.capacitycount++
       this.capacitydeadline = data.deadLine
     },
     capacityhandle (data) {
@@ -136,6 +138,7 @@ export default {
         this.callback()
       }
       this.capacityitem = ''
+      this.capacitycount = 0
       this.capacityvisible = false
     },
 

+ 1 - 1
pc/src/main.js

@@ -17,7 +17,7 @@ let router = require('./router').default
 
 Vue.prototype.$http = axios
 // Vue.prototype.$serverName = 'http://192.168.0.10:8080/'
-// Vue.prototype.$serverName = 'https://pro.4dkankan.com/'
+// Vue.prototype.$serverName = 'https://www.4dkankan.com/'
 
 Vue.prototype.$serverName = ''
 

+ 7 - 2
pc/src/page/layout/aside/temp/ltemp/forget.vue

@@ -13,7 +13,7 @@
       <input autocomplete="off" :placeholder="languagelAside.phone.placeholder" oninput="value=value.replace(/[^\d]/g,'')" maxlength='11' v-model="phone" type="text">
     </div>
     <div class="verification input">
-      <input autocomplete="off" :placeholder="languagelAside.code.placeholder" v-model="authCode" type="text">
+      <input autocomplete="off" :placeholder="languagelAside.code.placeholder" v-model="authCode"  oninput="value=value.replace(/[^\d]/g,'')" maxlength='6'>
       <span v-if="!jishi" @click="getAuthCode">{{languagelAside.code.txt}}</span>
       <span v-else>{{language==='en'?`Resend after ${interTime}s`:`${interTime}s后重新发送`}}</span>
     </div>
@@ -31,6 +31,8 @@
 <script>
 import {mapState} from 'vuex'
 import selectCall from '@/util/country.js'
+import { reg, encodeStr } from '@/util'
+import { Base64 } from 'js-base64'
 
 export default {
   computed: {
@@ -65,6 +67,9 @@ export default {
   },
   methods: {
     async getAuthCode () {
+      if (!reg.phone.test(this.phone)) {
+        return
+      }
       let res = await this.$store.dispatch('getAuthCode', {
         phone: this.phone,
         code: this.codeActive[1].substr(1)
@@ -126,7 +131,7 @@ export default {
         return this.$toast.show('warn', this.langToast['31'])
       }
       let params = {
-        password: this.password,
+        password: encodeStr(Base64.encode(this.password)),
         phoneNum: this.phone,
         confirmPwd: this.confirmpass,
         msgAuthCode: this.authCode

+ 8 - 2
pc/src/page/layout/aside/temp/ltemp/login.vue

@@ -19,7 +19,7 @@
           <input :key="'codelogin'" readonly onfocus="this.removeAttribute('readonly')"  autocomplete="off" v-model="phone" oninput="value=value.replace(/[^\d]/g,'')" maxlength='11' :placeholder="languagelAside.phone.placeholder" type="text">
         </div>
         <div class="verification input codeLogin">
-          <input :key="'authCode'" v-model="authCode" autocomplete="off" :placeholder="languagelAside.code.placeholder" type="text">
+          <input :key="'authCode'" v-model="authCode" autocomplete="off" :placeholder="languagelAside.code.placeholder"  oninput="value=value.replace(/[^\d]/g,'')" maxlength='6'>
           <span v-if="!jishi" @click="getAuthCode">{{languagelAside.code.txt}}</span>
           <span v-else>{{language==='en'?`Resend after ${interTime}s`:`${interTime}s后重新发送`}}</span>
         </div>
@@ -60,6 +60,9 @@
 import {mapState} from 'vuex'
 import selectCall from '@/util/country.js'
 
+import { reg, encodeStr } from '@/util'
+import { Base64 } from 'js-base64'
+
 export default {
   props: ['active', 'current'],
   computed: {
@@ -144,6 +147,9 @@ export default {
       }
     },
     async getAuthCode () {
+      if (!reg.phone.test(this.phone)) {
+        return
+      }
       let res = await this.$store.dispatch('getAuthCode', {
         phone: this.phone,
         code: Number(this.codeActive[1].substr(1))
@@ -202,7 +208,7 @@ export default {
 
         params = {
           phoneNum: this.phone,
-          password: this.password,
+          password: encodeStr(Base64.encode(this.password)),
           randomcode: '1234',
           rememberMe: Boolean(this.rememberMe)
         }

+ 9 - 4
pc/src/page/layout/aside/temp/ltemp/register.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="iregister-layout">
-    <div class="input"><input autocomplete="off" v-model="nickname" :placeholder="languagelAside.userName.placeholder" type="text"></div>
+    <div class="input"><input autocomplete="off" v-model="nickname" :placeholder="languagelAside.userName.placeholder" maxlength='11' type="text"></div>
     <div class="input phone-select" ref="quhaoMenu" >
       <div @click="showSelect=!showSelect">
         <span>{{codeActive[1]}}</span>
@@ -14,7 +14,7 @@
       <input readonly onfocus="this.removeAttribute('readonly')"  autocomplete="off" v-model="phone" oninput="value=value.replace(/[^\d]/g,'')" maxlength='11' :placeholder="languagelAside.phone.placeholder" type="text">
     </div>
     <div class="verification input">
-      <input v-model="authCode" :placeholder="languagelAside.code.placeholder" type="text">
+      <input v-model="authCode"  oninput="value=value.replace(/[^\d]/g,'')" maxlength='6' :placeholder="languagelAside.code.placeholder" >
       <span v-if="!jishi" @click="getAuthCode">{{languagelAside.code.txt}}</span>
       <span v-else>{{language==='en'?`Resend after ${interTime}s`:`${interTime}s后重新发送`}}</span>
     </div>
@@ -41,6 +41,8 @@
 <script>
 import { mapState } from 'vuex'
 import selectCall from '@/util/country.js'
+import { reg, encodeStr } from '@/util'
+import { Base64 } from 'js-base64'
 
 export default {
   data () {
@@ -93,6 +95,9 @@ export default {
       this.codeActive = item
     },
     async getAuthCode () {
+      if (!reg.phone.test(this.phone)) {
+        return
+      }
       let res = await this.$store.dispatch('getAuthCode', {
         phone: this.phone,
         code: Number(this.codeActive[1].substr(1))
@@ -161,7 +166,7 @@ export default {
 
       let country = Number(this.codeActive[1].substr(1))
       let params = {
-        password: this.password,
+        password: encodeStr(Base64.encode(this.password)),
         phoneNum: this.phone,
         msgAuthCode: this.authCode,
         nickName: this.nickname,
@@ -180,7 +185,7 @@ export default {
       this.$toast.showConfirm('success', this.langToast['23'], async () => {
         let params1 = {
           phoneNum: this.phone,
-          password: this.password,
+          password: encodeStr(Base64.encode(this.password)),
           randomcode: '1234',
           rememberMe: false
         }

+ 5 - 1
pc/src/page/layout/footer.vue

@@ -38,7 +38,7 @@
           <a href="http://www.4dmodel.com/" target="_blank">{{langFooter.model}}</a>
         </div>
         <p>Copyright © 2018 4DAGE Co., Ltd. All rights reserved. </p>
-        <p>粤ICP备14078495号-1</p>
+        <p><a class="a_class" href="http://www.beian.miit.gov.cn" target="_blank">粤ICP备14078495号-3</a></p>
       </div>
     </div>
   </div>
@@ -62,6 +62,10 @@ export default {
 </script>
 
 <style lang="scss" scoped>
+  .a_class,.a_class:hover,.a_class:active{
+    color: #fff;
+    text-decoration: none;
+  }
   .footer {
     padding-top: 40px;
     padding-bottom: 50px;

+ 4 - 1
pc/src/page/layout/header/index.vue

@@ -48,7 +48,7 @@
       </a>
       <a class="user cart" :style="{width:language==='en'?'273px':'192px'}" v-else>
         <div class="cart-user" @click="$router.push({name:deviceLogin?'scene':'information'})">
-          <div class="user-img" :style="{background:`url(${info.head}) 50% 50%/cover no-repeat`}"></div>
+          <div class="user-img" :style="{background:info.head?`url(${info.head}) 50% 50%/cover no-repeat`:`#e6e6e6 50% 50%/cover no-repeat`}"></div>
           <ul class="user-hover">
             <li class="item" @click="$router.push({name:deviceLogin?'scene':'information'})">{{langHeader.myaccount}}</li>
             <li class="item" @click.stop="handleLogout">{{langHeader.logout}}</li>
@@ -79,6 +79,7 @@ export default {
       overb: false,
       active: true,
       ismobile: browser.mobile,
+      browserLang: browser.language,
       isWide,
       count: 0
     }
@@ -140,6 +141,8 @@ export default {
     })
   },
   mounted () {
+    let lang = ~this.browserLang.indexOf('zh') ? '中' : 'en'
+    this.$store.commit('change_language', lang)
     if (this.isLogin && !this.deviceLogin) {
       let cart = JSON.parse(this.$store.state.user.cart)
       let count = 0

+ 1 - 0
pc/src/page/manage/style.scss

@@ -44,6 +44,7 @@
               display: inline-block;
               position: relative;
               cursor: pointer;
+              min-width: 120px;
               ul{
                 background-color: #fff;
                 position: absolute;

+ 3 - 1
pc/src/page/manage/temp/change.vue

@@ -25,6 +25,8 @@
 
 <script>
 import {mapState} from 'vuex'
+import { encodeStr } from '@/util'
+import { Base64 } from 'js-base64'
 
 export default {
   computed: {
@@ -107,7 +109,7 @@ export default {
       }
 
       let params = {
-        password: this.password,
+        password: encodeStr(Base64.encode(this.password)),
         phoneNum: this.info.userName,
         confirmPwd: this.confirmpass,
         msgAuthCode: this.code

+ 10 - 1
pc/src/page/manage/temp/consumption.vue

@@ -71,6 +71,12 @@ let PAYSSTREN = {
   2: 'paypal'
 }
 
+let AMOUNTSTR = {
+  0: '¥',
+  1: '¥',
+  2: '$'
+}
+
 let methodStr = {
   0: 'getConsumpList',
   1: 'getChargeList',
@@ -256,6 +262,7 @@ export default {
         item['validDateStr'] = condition
         item['unitSize1'] = item['unitSize'] + item['unit'] + '/' + this.shixian[item['month']]
         item['channel'] = this.channelType[item['status']]
+        item['amount1'] = AMOUNTSTR[item['payType']] + item['amount']
         item['payTypeStr'] = newVal === 'en' ? PAYSSTREN[item['payType']] : PAYSSTR[item['payType']]
       })
     },
@@ -349,8 +356,9 @@ export default {
         item['validDateStr'] = condition
         item['statusStr'] = this.useStatus[item['status']]
         item['unitSize1'] = item['unitSize'] + item['unit'] + '/' + this.shixian[item['month']]
-
         item['channel'] = this.channelType[item['status']]
+        item['amount1'] = AMOUNTSTR[item['payType']] + item['amount']
+
         item['payTypeStr'] = this.language === 'en' ? PAYSSTREN[item['payType']] : PAYSSTR[item['payType']]
       })
     },
@@ -416,6 +424,7 @@ export default {
       this.data = this.myinvoicelist.list
       this.data.forEach(item => {
         item['detail'] = '详细'
+        item['money'] = '¥' + item['money']
         item['finish'] = invoceStatusType[item['finish']]
         item['type'] = invoiceType[item['type']]
       })

+ 1 - 1
pc/src/page/manage/temp/device.vue

@@ -30,7 +30,7 @@
           </template>
           <div class="i-left" :style="{marginTop:tabActive===4?'15px':'25px'}">
             <template v-if="tabActive===4">
-              <p class="d-id">S/N: {{item.snCode||(item.childName&&item.childName.replace('4DKKPRO_',''))||'--'}} </p>
+              <p class="d-id" :title="item.snCode||(item.childName&&item.childName.replace('4DKKPRO_',''))||'--'">S/N: {{item.snCode||(item.childName&&item.childName.replace('4DKKPRO_',''))||'--'}} </p>
               <!-- item.spaceEndTime|| -->
               <p class="p-sub" style="padding-left: 26px;" :title="`${item.usedSpaceStr} / ${item.totalSpaceStr}`">
                 <img :src="`${$cdn}images/icon-cloud.png`" alt="">

+ 1 - 1
pc/src/page/manage/temp/iconsumption.js

@@ -22,7 +22,7 @@ let capacity = [
     en: 'Method of Payment',
     name: '支付类型'
   }, {
-    key: 'amount',
+    key: 'amount1',
     en: 'Paid amount',
     name: '实付金额'
   },

+ 3 - 3
pc/src/page/manage/temp/information.vue

@@ -9,7 +9,7 @@
       <div class="info" :class="{'info-en':language==='en'}" slot="show">
         <div class="s-tx">
           <p>{{langAccount.avatar}}</p>
-          <div class="card-img avatar" :style="{backgroundImage: `url(${info.head})`}"></div>
+          <div class="card-img avatar" :style="{backgroundImage: info.head? `url(${info.head})`:`#e6e6e6`}"></div>
         </div>
         <p><span>{{langAccount.account}}</span><span>{{info.userName||'--'}}</span></p>
         <p><span>{{langAccount.nickname}}</span><span>{{info.nickName||'--'}}</span></p>
@@ -20,7 +20,7 @@
           <div class="i-tx">
             <p>{{langAccount.avatar}}</p>
             <div class="itx-con">
-            <div class="card-img avatar" :style="{backgroundImage: `url(${info.head})`}"></div>
+            <div class="card-img avatar" :style="{backgroundImage: info.head?`url(${info.head})`:'#e6e6e6'}"></div>
               <div class="btn choose">
                 <input class="el-upload" ref="uploadInput" name="file" type="file" @change="update" alt="sadasdasd">
                 <span>{{langAccount.select}}</span>
@@ -363,7 +363,7 @@ export default {
           val: shipMobile
         },
         {
-          name: 'Name',
+          name: '姓名',
           En: 'Name',
           val: shipName
         }

+ 7 - 0
pc/src/page/manage/temp/order.vue

@@ -50,6 +50,10 @@
             <template v-else-if="getStatus(item) === 'partShipped'">
               <span class="expreeNum">{{langOrders.partShipped}}</span>
             </template>
+            <template v-else-if="getStatus(item) === 'received'">
+              <span class="expreeNum">{{langOrders.received}}:{{item.orderItems[0].expressNum}}</span>
+            </template>
+
             <template v-else>
               <span class="expreeNum">{{langOrders.hasCancal}}</span>
             </template>
@@ -218,6 +222,9 @@ export default {
           case 'shipped':
             temp = 'shipped'
             break
+          case 'received':
+            temp = 'received'
+            break
           default:
             break
         }

+ 2 - 6
pc/src/page/manage/temp/scene.vue

@@ -133,7 +133,7 @@ export default {
       rnd: Math.random(),
       lwidth: 853,
       lheight: 480,
-      url: 'https://pro.4dkankan.com/showProPC.html?m=KcMeJlOr8',
+      url: 'https://www.4dkankan.com/showProPC.html?m=KcMeJlOr8',
       item: ''
     }
   },
@@ -258,11 +258,7 @@ export default {
     gotoEdit (item) {
       let url = item.webSite
       let temp = ''
-      if (item.sceneScheme < 4) {
-        temp = url.replace('show', 'editPC')
-      } else {
-        temp = url.replace('show', 'edit')
-      }
+      temp = url.replace('show', 'edit')
       window.open(temp.replace('http://', 'https://') + (this.language === 'en' ? '&lang=en' : ''), '_blank')
     },
     async del (item) {

+ 1 - 1
pc/src/page/purchasezhijia/index.vue

@@ -40,7 +40,7 @@
                   <span>{{langPurchase.cart}}</span>
                 </vcenter>
               </div>
-              <div class="button"  @click="pay">
+              <div class="button" @click="pay">
                 <vcenter>
                   <span>{{langPurchase.buy}}</span>
                 </vcenter>

+ 50 - 0
pc/src/page/service/plugin/ff-scrollbar.vue

@@ -0,0 +1,50 @@
+<template>
+  <div class="outer">
+    <slot name="con"></slot>
+    <div class="ff-scrollbar">
+      <div ref="thumb" class="ff-scroll-thumb" :style="{top:scorlltop}"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    scorlltop: {
+      default: () => [],
+      type: String
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  $lw: 220px;
+  $cw: 430px;
+  $encw: 430px;
+  .outer{
+    overflow: hidden;
+    max-height: 100%;
+    height: calc(100vh - 380px);
+    width: $cw - 20px;
+    text-align: center;
+    position: relative;
+  }
+  .ff-scrollbar{
+    height: calc(100vh - 380px);
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 10px;
+    background: none;
+  }
+  .ff-scroll-thumb{
+    height: 50px;
+    width: 5px;
+    background-color: rgba(221, 221, 221, 0.2);
+    border-radius: 5px;
+    position: absolute;
+    top: 0;
+    right: 0;
+  }
+</style>

+ 22 - 5
pc/src/page/service/temp/clause.vue

@@ -1,15 +1,28 @@
 <template>
   <div class="use-layout" :class="language">
     <lselect :options="navs" :selected="navActive" class="select" :class="{oy: navs.length > 15}" @change="handleChange" />
-    <div class="use-con" ref="dcon" id="dcon">
-      <img class="img" v-for="(item,i) in current.size" :src="`${$cdn}images/clause/${current.id}/${language==='en'?'en':'zh'}/${i+1}.jpg`" :key="i" :alt="i+1">
-    </div>
+
+    <template v-if="isFireFox" >
+      <scrollbar :scorlltop='scorlltop'>
+        <div slot="con" class="use-con" ref="dcon" id="dcon">
+          <img class="img" v-for="(item,i) in current.size" :src="`${$cdn}images/clause/${current.id}/${language==='en'?'en':'zh'}/${i+1}.jpg`" :key="i" :alt="i+1">
+        </div>
+      </scrollbar>
+    </template>
+
+     <template v-else>
+      <div slot="con" style="position:static" class="use-con" ref="dcon" id="dcon">
+        <img class="img" v-for="(item,i) in current.size" :src="`${$cdn}images/clause/${current.id}/${language==='en'?'en':'zh'}/${i+1}.jpg`" :key="i" :alt="i+1">
+      </div>
+    </template>
   </div>
 </template>
 
 <script>
 import lselect from '@/components/lselect'
 import {mapState} from 'vuex'
+import scrollbar from '../plugin/ff-scrollbar'
+import browser from '@/util/browser'
 
 export default {
   props: ['data'],
@@ -48,6 +61,9 @@ export default {
           active = item.alt
         }
       })
+
+      let fixTop = Math.round(sTop / this.$refs.dcon.scrollHeight * 100)
+      this.scorlltop = fixTop + '%'
       this.navs.forEach(sub => {
         if (String(active) === String(sub.cover)) {
           nav = sub
@@ -77,7 +93,8 @@ export default {
       active: '',
       navs: this.data.data,
       current: this.data.detail,
-      navActive: this.data.data[0]
+      navActive: this.data.data[0],
+      isFireFox: browser.firefox
     }
   },
   mounted () {
@@ -85,7 +102,7 @@ export default {
       this.$refs.dcon.addEventListener('scroll', this.listen)
     }, 0)
   },
-  components: {lselect}
+  components: {lselect, scrollbar}
 }
 </script>
 

+ 24 - 5
pc/src/page/service/temp/qa.vue

@@ -1,15 +1,29 @@
 <template>
   <div class="use-layout" :class="language">
     <lselect :options="navs" :selected="navActive" class="select" :class="{oy: navs.length > 15}" @change="handleChange" />
-    <div class="use-con" ref="dcon" id="dcon">
-      <img class="img" v-for="(item,i) in current.size" :src="`${$cdn}images/qa/${current.id}/${language==='en'?'en':'zh'}/${i+1}.jpg`" :key="i" :alt="i+1">
-    </div>
+
+    <template v-if="isFireFox" >
+      <scrollbar :scorlltop='scorlltop'>
+        <div slot="con" class="use-con" ref="dcon" id="dcon">
+          <img class="img" v-for="(item,i) in current.size" :src="`${$cdn}images/qa/${current.id}/${language==='en'?'en':'zh'}/${i+1}.jpg`" :key="i" :alt="i+1">
+        </div>
+      </scrollbar>
+    </template>
+
+    <template v-else>
+      <div class="use-con"  style="position:static" ref="dcon" id="dcon">
+        <img class="img" v-for="(item,i) in current.size" :src="`${$cdn}images/qa/${current.id}/${language==='en'?'en':'zh'}/${i+1}.jpg`" :key="i" :alt="i+1">
+      </div>
+    </template>
+
   </div>
 </template>
 
 <script>
 import lselect from '@/components/lselect'
 import {mapState} from 'vuex'
+import scrollbar from '../plugin/ff-scrollbar'
+import browser from '@/util/browser'
 
 export default {
   props: ['data'],
@@ -48,6 +62,9 @@ export default {
           active = item.alt
         }
       })
+
+      let fixTop = Math.round(sTop / this.$refs.dcon.scrollHeight * 100)
+      this.scorlltop = fixTop + '%'
       this.navs.forEach(sub => {
         if (String(active) === String(sub.cover)) {
           nav = sub
@@ -77,7 +94,9 @@ export default {
       active: '',
       navs: this.data.data,
       current: this.data.detail,
-      navActive: this.data.data[0]
+      navActive: this.data.data[0],
+      isFireFox: browser.firefox,
+      scorlltop: 0
     }
   },
   mounted () {
@@ -85,7 +104,7 @@ export default {
       this.$refs.dcon.addEventListener('scroll', this.listen)
     }, 0)
   },
-  components: {lselect}
+  components: {lselect, scrollbar}
 }
 </script>
 

+ 70 - 6
pc/src/page/service/temp/use.vue

@@ -1,14 +1,35 @@
 <template>
   <div class="use-layout" :class="language">
-    <lselect :options="navs" :selected="navActive" class="select" :class="{oy: navs.length > 15}" @change="handleChange" />
-    <div class="use-con" ref="dcon" id="dcon">
-      <img class="img" v-for="(item,i) in current.size" :src="`${$cdn}images/use/${current.id}/${language==='en'?'en':'zh'}/${i+1}.jpg`" :key="i" :alt="i+1">
-    </div>
+    <template v-if="isFireFox" >
+      <div class="ls-outer">
+        <lselect :options="navs" :selected="navActive" class="select firefox-select" :class="{oy: navs.length > 15}" @change="handleChange" />
+        <div class="ls-scrollbar">
+          <div ref="thumb" class="ls-scroll-thumb" :style="{top:scorlltop}"></div>
+        </div>
+      </div>
+
+      <scrollbar :scorlltop='scorlltop'>
+        <div slot="con" class="use-con" ref="dcon" id="dcon">
+          <img class="img" v-for="(item,i) in current.size" :src="`${$cdn}images/use/${current.id}/${language==='en'?'en':'zh'}/${i+1}.jpg`" :key="i" :alt="i+1">
+        </div>
+      </scrollbar>
+    </template>
+
+    <template v-else>
+      <lselect :options="navs" :selected="navActive" class="select" :class="{oy: navs.length > 15}" @change="handleChange" />
+      <div class="use-con" style="position:static" ref="dcon" id="dcon">
+        <img class="img" v-for="(item,i) in current.size" :src="`${$cdn}images/use/${current.id}/${language==='en'?'en':'zh'}/${i+1}.jpg`" :key="i" :alt="i+1">
+      </div>
+    </template>
+
   </div>
 </template>
 
 <script>
 import lselect from '@/components/lselect'
+import scrollbar from '../plugin/ff-scrollbar'
+import browser from '@/util/browser'
+
 import {mapState} from 'vuex'
 
 export default {
@@ -33,6 +54,9 @@ export default {
 
         this.$refs.dcon.removeEventListener('scroll', this.listen)
         document.querySelector('#dcon').scrollTop = offTop
+        let fixTop = Math.round(offTop / this.$refs.dcon.scrollHeight * 100)
+        this.scorlltop = fixTop + '%'
+        console.log(offTop)
         setTimeout(() => {
           this.$refs.dcon.addEventListener('scroll', this.listen)
         }, 500)
@@ -49,6 +73,8 @@ export default {
         }
       })
 
+      let fixTop = Math.round(sTop / this.$refs.dcon.scrollHeight * 100)
+      this.scorlltop = fixTop + '%'
       this.navs.forEach(sub => {
         if (String(active) === String(sub.cover)) {
           nav = sub
@@ -78,7 +104,9 @@ export default {
       active: '',
       navs: this.data.data,
       current: this.data.detail,
-      navActive: this.data.data[0]
+      navActive: this.data.data[0],
+      scorlltop: '0',
+      isFireFox: browser.firefox
     }
   },
   mounted () {
@@ -86,7 +114,7 @@ export default {
       this.$refs.dcon.addEventListener('scroll', this.listen)
     }, 0)
   },
-  components: {lselect}
+  components: {lselect, scrollbar}
 }
 </script>
 
@@ -94,6 +122,34 @@ export default {
   $lw: 220px;
   $cw: 430px;
   $encw: 430px;
+  .ls-outer{
+      min-width: 220px;
+      position: absolute;
+      top: 0;
+      left: 0;
+      max-height: 100%;
+      overflow: hidden;
+      height: 100%;
+  }
+
+  .ls-scrollbar{
+    height: calc(100vh - 380px);
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 10px;
+    border-right: 1px solid #dcdcdc;
+  }
+  .ls-scroll-thumb{
+    height: 50px;
+    width: 5px;
+    background-color: #ddd;
+    border-radius: 5px;
+    position: absolute;
+    top: 0;
+    right: 0;
+  }
+
 .use-layout {
   position: relative;
   max-width: 800px;
@@ -111,12 +167,19 @@ export default {
     overflow-y: auto;
     height: 100%;
   }
+
+  .firefox-select{
+    min-width: $lw + 20px;
+  }
+
   .img{
     margin-bottom: 20px;
     width: 100%;
     text-align: center;
   }
+
   .use-con{
+    position: absolute;
     max-height: 100%;
     height: calc(100vh - 380px);
     overflow-y: auto;
@@ -146,6 +209,7 @@ export default {
       -webkit-border-radius: 4px;
     }
   }
+
 }
 .en{
   max-width: 900px;

+ 83 - 0
pc/src/page/service/video.vue

@@ -0,0 +1,83 @@
+<template>
+  <div class="container">
+    <div class="setion" v-for="(item,i) in langVideoCourse" :key="i">
+      <p class="b-title">{{item.name}}</p>
+      <p class="b-label">{{item.sub}}</p>
+      <ul class="i-ul">
+        <li :style="{width:`${90/item.items.length*2}%`}" @click="itemHandle(sub)" v-for="(sub,idx) in item.items" :key="idx">
+          <img class="li-img" :src="`${$cdn}course/img/${sub.code}.jpg`" alt="">
+          <img class="bofang" :src="`${$cdn}course/img/video_play@2x.png`" alt="">
+          <p class="li-p">{{sub.name}}</p>
+        </li>
+      </ul>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+
+export default {
+  computed: {
+    ...mapState({
+      langVideoCourse: state => state.language.home.videoCourse.content
+    })
+  },
+  data () {
+    return {
+
+    }
+  },
+  methods: {
+    itemHandle (item) {
+      this.$bus.$emit('toggleVideo', {
+        status: true,
+        url: item.code
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.container{
+  width: 1200px;
+  margin: 50px auto;
+  .setion{
+    margin: 20px 0;
+    .b-title{
+      font-size: 38px;
+    }
+    .i-ul{
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      flex-wrap: wrap;
+      width: 100%;
+      margin-top: 20px;
+      li{
+        position: relative;
+        max-width: 49%;
+        margin-top: 20px;
+        .li-img{
+          width: 100%;
+          cursor: pointer;
+        }
+        .bofang{
+          width: 30px;
+          height: 30px;
+          position: absolute;
+          left: 20px;
+          bottom: 50px;
+        }
+        .li-p{
+          font-size: 16px;
+          color: #2d2d2d;
+          margin-top: 10px;
+        }
+      }
+    }
+  }
+}
+
+</style>

+ 5 - 0
pc/src/router/index.js

@@ -19,6 +19,11 @@ let router = new Router({
       component: resolve => require(['@/page/videos'], resolve)
     },
     {
+      path: '/video-course',
+      name: 'video-course',
+      component: resolve => require(['@/page/service/video'], resolve)
+    },
+    {
       path: '/capacity',
       name: 'capacity',
       component: resolve => require(['@/page/capacity'], resolve)

+ 1 - 0
pc/src/store/language/cn/manage.js

@@ -152,6 +152,7 @@ export default{
     unshipped: '等待商家发货',
     finish: '已完成',
     partShipped: '部分发货',
+    received: '已收货',
     hasCancal: '已取消',
     edit: '修改(发货后7天内可修改)',
     dontneed: '发票类型:不需要发票',

+ 1 - 1
pc/src/store/language/cn/purchase.js

@@ -178,7 +178,7 @@ export default{
         `答:原套餐的剩余时间将换算为新套餐的时间,体现为延期时间。<br/>
         计算公式为:以每月129元的20G套餐为准,其中10G为基础容量不计算,换算的单位价值约为:0.42元/天/GB。<br/>
         如您原套餐为20G套餐(月),还有10天到期,则计算剩余为42元。<br/>
-        当天您升级套餐为50G套餐(月),其费用为520元/月(单天价值为16元)。<br/>
+        当天您升级套餐为50G套餐(月),其费用为509元/月(单天价值为16.4元)。<br/>
         原套餐剩余价值约为新套餐单天价值的2.6倍,则新套餐延期时间为3天。<br/>
         (延期计费方式采用进一法:1.01~1.99倍数都视作2倍。)`
     },

+ 1 - 1
pc/src/store/language/cn/toast.js

@@ -65,7 +65,7 @@ export default{
   '3007': '昵称已存在',
   '3008': '该手机已被注册',
   '3009': '两次输入的密码不一致',
-  '3010': '昵称长度错误',
+  '3010': '昵称长度在2-11位之间',
   '3011': '密码必须包含英文字母和数字,长度在8-16位之间',
   '3012': '昵称包含敏感词',
   '3013': '手机号码格式错误',

+ 1 - 0
pc/src/store/language/en/manage.js

@@ -154,6 +154,7 @@ export default{
     unshipped: 'Waiting for shipment',
     finish: 'Finished',
     partShipped: 'Partly dispatched',
+    received: 'Received',
     hasCancal: 'Cancelled',
     edit: 'Edit (Only within 7 days after delivery)',
     dontneed: 'Invoice type: No invoice required',

+ 1 - 1
pc/src/store/language/en/toast.js

@@ -65,7 +65,7 @@ export default{
   '3007': 'User name already exists.',
   '3008': 'The mobile number is already registered.',
   '3009': 'The password you typed don\'t match.',
-  '3010': 'Incorrect user name length.',
+  '3010': 'Incorrect user name length. (2-11 characters)',
   '3011': 'Passoword must contain letters and numbers, 8-16 characters.',
   '3012': 'User name contains sensitive words.',
   '3013': 'Incorrect format of mobile number.',

+ 62 - 1
pc/src/store/language/home_cn.js

@@ -76,6 +76,11 @@ export default {
           icon: 'icon-baoxiutiaokuan',
           name: '保修条款',
           to: {name: 'service_list', params: {id: 'clause', active: 'pro'}}
+        },
+        {
+          icon: 'icon-teaching',
+          name: '视频教程',
+          to: {name: 'video-course'}
         }
       ],
       'case_overview': [
@@ -202,7 +207,7 @@ export default {
         val: 'pr@4dage.com'
       }, {
         key: '联系电话',
-        val: '0756-6996790-800'
+        val: '400-669-8025'
       }
     ]
   },
@@ -245,6 +250,62 @@ export default {
     txt: '抱歉,您访问的页面出现了错误,请重新加载',
     btn: '返回首页'
   },
+  videoCourse: {
+    content: [
+      {
+        name: '视频教程',
+        sub: '快速上手以及详细教学',
+        items: [
+          {
+            code: 'img_guide01@2x',
+            name: '快速上手指南'
+          },
+          {
+            code: 'img_guide02@2x',
+            name: '完整版操作指南'
+          }
+        ]
+      },
+      {
+        name: '分段教学',
+        sub: '功能模块拆解分段学习',
+        items: [
+          {
+            code: 'img_01getready@2x',
+            name: '使用前准备'
+          },
+          {
+            code: 'img_03shotstep@2x',
+            name: '拍摄流程'
+          },
+          {
+            code: 'img_08shotmode@2x',
+            name: '拍摄方式'
+          },
+          {
+            code: 'img_05point@2x',
+            name: '点位调整功能'
+          },
+          {
+            code: 'img_06video@2x',
+            name: '球幕视频功能'
+          },
+          {
+            code: 'img_02editmb@2x',
+            name: '编辑模型-手机APP端模型编辑'
+          },
+          {
+            code: 'img_04editpc@2x',
+            name: '编辑模型-PC端模型编辑'
+          },
+          {
+            code: 'img_07share@2x',
+            name: '上传分享'
+          }
+        ]
+      }
+    ]
+  },
   home,
   binocular,
   eight,

+ 61 - 0
pc/src/store/language/home_en.js

@@ -77,6 +77,11 @@ export default {
           icon: 'icon-baoxiutiaokuan',
           name: 'Warranty <br/>Information',
           to: {name: 'service_list', params: {id: 'clause', active: 'pro'}}
+        },
+        {
+          icon: 'icon-teaching',
+          name: 'Video <br/>tutorial',
+          to: {name: 'video-course'}
         }
       ],
       'case_overview': [
@@ -246,6 +251,62 @@ export default {
     txt: 'Error,Please reload.',
     btn: 'Go Back'
   },
+  videoCourse: {
+    content: [
+      {
+        name: '视频教程',
+        sub: '快速上手以及详细教学',
+        items: [
+          {
+            code: 'img_guide01@2x',
+            name: '快速上手指南'
+          },
+          {
+            code: 'img_guide02@2x',
+            name: '完整版操作指南'
+          }
+        ]
+      },
+      {
+        name: '分段教学',
+        sub: '功能模块拆解分段学习',
+        items: [
+          {
+            code: 'img_01getready@2x',
+            name: '使用前准备'
+          },
+          {
+            code: 'img_03shotstep@2x',
+            name: '拍摄流程'
+          },
+          {
+            code: 'img_08shotmode@2x',
+            name: '拍摄方式'
+          },
+          {
+            code: 'img_05point@2x',
+            name: '点位调整功能'
+          },
+          {
+            code: 'img_06video@2x',
+            name: '球幕视频功能'
+          },
+          {
+            code: 'img_02editmb@2x',
+            name: '编辑模型-手机APP端模型编辑'
+          },
+          {
+            code: 'img_04editpc@2x',
+            name: '编辑模型-PC端模型编辑'
+          },
+          {
+            code: 'img_07share@2x',
+            name: '上传分享'
+          }
+        ]
+      }
+    ]
+  },
   home,
   binocular,
   eight,

+ 3 - 1
pc/src/util/browser.js

@@ -26,7 +26,9 @@ function versions () {
     // 是否为web应用程序,没有头部与底部
     webApp: u.indexOf('Safari') === -1,
     // 是否为微信浏览器
-    weixin: ~u.indexOf('MicroMessenger')
+    weixin: ~u.indexOf('MicroMessenger'),
+    // 获取浏览器语言
+    language: (navigator.browserLanguage || navigator.language).toLowerCase()
   }
 }
 

+ 26 - 1
pc/src/util/index.js

@@ -1,5 +1,18 @@
 const detectZoom = require('detect-zoom')
-
+function randomWord (randomFlag, min, max) {
+  let str = ''
+  let range = min
+  let arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
+  // 随机产生
+  if (randomFlag) {
+    range = Math.round(Math.random() * (max - min)) + min
+  }
+  for (var i = 0; i < range; i++) {
+    let pos = Math.round(Math.random() * (arr.length - 1))
+    str += arr[pos]
+  }
+  return str
+}
 module.exports = {
   getPosition: function (dom, parent) {
     var ret = {
@@ -57,5 +70,17 @@ module.exports = {
       (String(day).length > 1 ? day : '0' + day)
       // + ' ' + (String(hour).length > 1 ? hour : '0' + hour) + ':' + (String(minute).length > 1 ? minute : '0' + minute) +
       //  ':' + (String(second).length > 1 ? second : '0' + second)
+  },
+
+  encodeStr: function (str) {
+    const NUM = 2
+    const front = randomWord(false, 8)
+    const middle = randomWord(false, 8)
+    const end = randomWord(false, 8)
+
+    let str1 = str.substring(0, NUM)
+    let str2 = str.substring(NUM)
+
+    return front + str2 + middle + str1 + end
   }
 }