Bladeren bron

bulid: 初始化项目结构

zhangyuhan 5 jaren geleden
bovenliggende
commit
ed0d6f1002
67 gewijzigde bestanden met toevoegingen van 3217 en 142 verwijderingen
  1. 2 1
      .browserslistrc
  2. 125 0
      .drone.yml
  3. 3 0
      .env.development
  4. 3 0
      .env.production
  5. 1 0
      .eslintignore
  6. 4 1
      .eslintrc.js
  7. 3 1
      README.md
  8. 9 0
      package.json
  9. BIN
      public/favicon.ico
  10. 44 12
      public/index.html
  11. 518 0
      public/js/commonForVue.js
  12. 46 25
      src/App.vue
  13. 19 0
      src/api/demo.ts
  14. 53 0
      src/api/index.ts
  15. BIN
      src/assets/image/empty.png
  16. BIN
      src/assets/image/icon-ali-pay.png
  17. BIN
      src/assets/image/icon-check.png
  18. BIN
      src/assets/image/icon-checkbox-choose.png
  19. BIN
      src/assets/image/icon-duihao.png
  20. BIN
      src/assets/image/icon-wx-pay.png
  21. BIN
      src/assets/image/img-404.jpg
  22. BIN
      src/assets/logo.png
  23. 0 61
      src/components/HelloWorld.vue
  24. 84 0
      src/components/common/CountDownButton.vue
  25. 84 0
      src/components/common/Empty.vue
  26. 39 0
      src/components/common/Image.vue
  27. 209 0
      src/components/layout.vue
  28. 8 0
      src/main.ts
  29. 90 15
      src/router/index.ts
  30. 18 0
      src/router/modules/auth.ts
  31. 10 0
      src/router/modules/demo.ts
  32. 34 0
      src/router/modules/main.ts
  33. 18 0
      src/router/modules/record.ts
  34. 26 0
      src/router/modules/share.ts
  35. 18 0
      src/router/modules/withdrawal.ts
  36. 66 5
      src/store/index.ts
  37. 40 0
      src/store/modules/demo.ts
  38. 208 0
      src/style/_mixin.scss
  39. 7 0
      src/style/_variables.scss
  40. 257 0
      src/style/base.scss
  41. 140 0
      src/style/common.scss
  42. 25 0
      src/style/pic-icon.scss
  43. 82 0
      src/style/vant-reset.scss
  44. 22 0
      src/utils/globalDirectives.ts
  45. 40 0
      src/utils/globalFilters.ts
  46. 253 0
      src/utils/globalFunctions.ts
  47. 19 0
      src/utils/globalFunctionsForApp.ts
  48. 17 0
      src/utils/index.ts
  49. 35 0
      src/views/404.vue
  50. 196 0
      src/views/Demo.vue
  51. 1 1
      src/views/Help.vue
  52. 2 15
      src/views/Home.vue
  53. 5 0
      src/views/Logout.vue
  54. 5 0
      src/views/Readme.vue
  55. 5 0
      src/views/Result.vue
  56. 5 0
      src/views/auth/Enterprise.vue
  57. 5 0
      src/views/auth/Everyman.vue
  58. 5 0
      src/views/record/Details.vue
  59. 5 0
      src/views/record/List.vue
  60. 5 0
      src/views/share/Details.vue
  61. 5 0
      src/views/share/List.vue
  62. 5 0
      src/views/share/Share.vue
  63. 5 0
      src/views/withdrawal/Enterprise.vue
  64. 5 0
      src/views/withdrawal/Everyman.vue
  65. 19 0
      src/vue_bus.ts
  66. 179 0
      vue.config.js
  67. 81 5
      yarn.lock

+ 2 - 1
.browserslistrc

@@ -1,3 +1,4 @@
 > 1%
 > 1%
+Android >= 4.0
+iOS >= 7
 last 2 versions
 last 2 versions
-not dead

+ 125 - 0
.drone.yml

@@ -0,0 +1,125 @@
+kind: pipeline
+name: 完整的使用示例
+
+steps:
+  # 环境依赖相关
+  - name: restore-cache
+    pull: if-not-exists
+    image: drillster/drone-volume-cache
+    volumes:
+      - name: cache
+        path: /cache
+    settings:
+      restore: true
+      mount:
+        - ./node_modules
+        - ./yarn-cache
+        - ./node_cache
+  - name: npm-install
+    pull: if-not-exists
+    image: node
+    commands:
+      - echo Use yarn management node_modules
+      - npm config set cache ./node_cache
+      - npm config set registry https://registry.npm.taobao.org --global
+      - npm config set disturl https://npm.taobao.org/dist --global
+      - npm config set sass_binary_site https://npm.taobao.org/mirrors/node-sass
+      - npm install yarn --prefer-offline --no-audit
+      - yarn config set registry https://registry.npm.taobao.org --global
+      - yarn config set disturl https://npm.taobao.org/dist --global
+      - yarn config set sass_binary_site https://npm.taobao.org/mirrors/node-sass
+      - yarn install --prefer-offline --cache-folder ./yarn-cache
+  - name: rebuild-cache
+    pull: if-not-exists
+    image: drillster/drone-volume-cache
+    volumes:
+      - name: cache
+        path: /cache
+    settings:
+      rebuild: true
+      mount:
+        - ./node_modules
+        - ./yarn-cache
+        - ./node_cache
+  # 质量相关
+  - name: lint-code
+    pull: if-not-exists
+    image: node
+    failure: ignore
+    commands:
+      - npm run lint
+  - name: code-analysis
+    pull: if-not-exists
+    image: aosapps/drone-sonar-plugin
+    settings:
+      sonar_host:
+        from_secret: sonar_host
+      sonar_token:
+        from_secret: sonar_token
+  # 编译相关
+  - name: build-code
+    pull: if-not-exists
+    image: node
+    commands:
+      - npm run build
+  # 部署相关
+  # scp 示例
+  - name: scp-code
+    pull: if-not-exists
+    image: appleboy/drone-scp
+    settings:
+      host:
+        from_secret: ssh_host
+      username:
+        from_secret: ssh_user
+      port: 22
+      password:
+        from_secret: ssh_password
+      target:
+        from_secret: ssh_remote_dir
+      source:
+        - dist/*
+  # 通知相关
+  - name: notification-success
+    pull: if-not-exists
+    image: lddsb/drone-dingtalk-message
+    settings:
+      token:
+        from_secret: ding-groupbot-token
+      secret:
+        from_secret: ding-secret-for-generate-sign
+      type: markdown
+      tpl: ./ding.md
+      tips_title: '滴滴,部署成功'
+      success_color: "008000"
+      success_pic: "https://raw.githubusercontent.com/Ethan-Liuu/picture/master/success.png"
+    when:
+      status:
+        - success
+  - name: notification-failure
+    pull: if-not-exists
+    image: lddsb/drone-dingtalk-message
+    settings:
+      token:
+        from_secret: ding-groupbot-token
+      secret:
+        from_secret: ding-secret-for-generate-sign
+      type: markdown
+      tpl: ./ding.md
+      tips_title: '滴滴, 有一条错误待处理'
+      success_color: "FF0000"
+      success_pic: "https://raw.githubusercontent.com/Ethan-Liuu/picture/master/failed.png"
+    when:
+      status:
+        - failure
+
+volumes:
+  - name: cache
+    host:
+      path: /opt/efe-zyh/ci-cd/node-cache
+
+trigger:
+  branch:
+    exclude:
+      - master
+      - feature/*

+ 3 - 0
.env.development

@@ -0,0 +1,3 @@
+NODE_ENV=development
+VUE_APP_BASE_API = '/dev/api'
+BASE_URL = '/dev/page/'

+ 3 - 0
.env.production

@@ -0,0 +1,3 @@
+NODE_ENV=production
+VUE_APP_BASE_API = '/online/api'
+BASE_URL = '/online/page/'

+ 1 - 0
.eslintignore

@@ -0,0 +1 @@
+/vue.config.js

+ 4 - 1
.eslintrc.js

@@ -13,6 +13,9 @@ module.exports = {
   },
   },
   rules: {
   rules: {
     'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
     'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
-    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+    'no-any': [0, { 'ignore-rest-args': 0 }],
+    '@typescript-eslint/no-explicit-any': ['off'],
+    '@typescript-eslint/camelcase': ['off']
   }
   }
 }
 }

+ 3 - 1
README.md

@@ -1,4 +1,6 @@
-# jy_partner
+# jy_partner 剑鱼伙伴
+
+# p20
 
 
 ## Project setup
 ## Project setup
 ```
 ```

+ 9 - 0
package.json

@@ -8,7 +8,11 @@
     "lint": "vue-cli-service lint"
     "lint": "vue-cli-service lint"
   },
   },
   "dependencies": {
   "dependencies": {
+    "axios": "^0.19.2",
     "core-js": "^3.6.5",
     "core-js": "^3.6.5",
+    "js-cookie": "^2.2.1",
+    "moment": "^2.24.0",
+    "vant": "^2.8.2",
     "vue": "^2.6.11",
     "vue": "^2.6.11",
     "vue-class-component": "^7.2.3",
     "vue-class-component": "^7.2.3",
     "vue-property-decorator": "^8.4.2",
     "vue-property-decorator": "^8.4.2",
@@ -16,6 +20,8 @@
     "vuex": "^3.4.0"
     "vuex": "^3.4.0"
   },
   },
   "devDependencies": {
   "devDependencies": {
+    "@types/js-cookie": "^2.2.6",
+    "@types/qs": "^6.9.4",
     "@typescript-eslint/eslint-plugin": "^2.33.0",
     "@typescript-eslint/eslint-plugin": "^2.33.0",
     "@typescript-eslint/parser": "^2.33.0",
     "@typescript-eslint/parser": "^2.33.0",
     "@vue/cli-plugin-babel": "~4.4.0",
     "@vue/cli-plugin-babel": "~4.4.0",
@@ -26,14 +32,17 @@
     "@vue/cli-service": "~4.4.0",
     "@vue/cli-service": "~4.4.0",
     "@vue/eslint-config-standard": "^5.1.2",
     "@vue/eslint-config-standard": "^5.1.2",
     "@vue/eslint-config-typescript": "^5.0.2",
     "@vue/eslint-config-typescript": "^5.0.2",
+    "autoprefixer": "^9.8.5",
     "eslint": "^6.7.2",
     "eslint": "^6.7.2",
     "eslint-plugin-import": "^2.20.2",
     "eslint-plugin-import": "^2.20.2",
     "eslint-plugin-node": "^11.1.0",
     "eslint-plugin-node": "^11.1.0",
     "eslint-plugin-promise": "^4.2.1",
     "eslint-plugin-promise": "^4.2.1",
     "eslint-plugin-standard": "^4.0.0",
     "eslint-plugin-standard": "^4.0.0",
     "eslint-plugin-vue": "^6.2.2",
     "eslint-plugin-vue": "^6.2.2",
+    "postcss-px-to-viewport": "^1.1.1",
     "sass": "^1.26.5",
     "sass": "^1.26.5",
     "sass-loader": "^8.0.2",
     "sass-loader": "^8.0.2",
+    "ts-import-plugin": "^1.6.6",
     "typescript": "~3.9.3",
     "typescript": "~3.9.3",
     "vue-template-compiler": "^2.6.11"
     "vue-template-compiler": "^2.6.11"
   }
   }

BIN
public/favicon.ico


+ 44 - 12
public/index.html

@@ -1,17 +1,49 @@
 <!DOCTYPE html>
 <!DOCTYPE html>
-<html lang="en">
-  <head>
+<html lang="zh-CN">
+<head>
     <meta charset="utf-8">
     <meta charset="utf-8">
+    <meta name="keywords" content="剑鱼标讯,伙伴计划,新基建">
+    <meta name="description" content="剑鱼标讯,伙伴计划,新基建">
+    <meta name="author" content="剑鱼标讯">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, viewport-fit=cover">
+    <meta name="browsermode" content="application">
+    <meta name="x5-orientation" content="portrait">
+    <meta name="screen-orientation" content="portrait">
+    <meta name="x5-page-mode" content="app">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black">
+    <meta name="format-detection" content="telephone=no">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
-    <title><%= htmlWebpackPlugin.options.title %></title>
-  </head>
-  <body>
-    <noscript>
-      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
-    </noscript>
-    <div id="app"></div>
-    <!-- built files will be auto injected -->
-  </body>
+    <!-- <title><%= htmlWebpackPlugin.options.title %></title> -->
+    <title>伙伴计划</title>
+    <!-- 使用CDN的CSS文件 -->
+    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
+    <link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
+    <% } %>
+    <!-- 使用CDN的JS文件 -->
+    <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
+    <script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
+    <% } %>
+</head>
+<body>
+<noscript>
+    <strong>JavaScript enabled. Please enable it to continue.</strong>
+</noscript>
+<div id="app"></div>
+<!-- built files will be auto injected -->
+<script src="./js/commonForVue.js?v=<%= htmlWebpackPlugin.options.version %>"></script>
+<!-- vConsole debug -->
+<!-- <script type="text/javascript" src="//cdn.bootcdn.net/ajax/libs/vConsole/3.3.4/vconsole.min.js"></script>
+<script>new VConsole()</script> -->
+<script>
+    var _hmt = _hmt || [];
+    (function() {
+        var hm = document.createElement("script");
+        hm.src = "https://hm.baidu.com/hm.js?72331746d85dcac3dac65202d103e5d9";
+        var s = document.getElementsByTagName("script")[0];
+        s.parentNode.insertBefore(hm, s);
+    })();
+</script>
+</body>
 </html>
 </html>

+ 518 - 0
public/js/commonForVue.js

@@ -0,0 +1,518 @@
+window.afterClickBack = function () {
+  console.log('afterClickBack', location.href)
+}
+
+var vTools = {
+  // 公共前缀url
+  baseUrl: '',
+  errorCodeMap: {
+    1001: '需要重新登录',
+    1002: '缺失参数',
+    1003: '没有权限',
+  },
+  $env: {},
+  // 传入你要获取的参数的名字
+  getParam: function (name) {
+    var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
+    var r = window.location.search.substr(1).match(reg); //获取url中'?'符后的字符串并正则匹配
+
+    var context = '';
+    if (r != null) context = r[2];
+    // 释放变量
+    reg = null;
+    r = null;
+    return context == null || context == '' || context == 'undefined' ? '' : context;
+  },
+  // 数组对象根据某一个值进行冒泡排序
+  // arr 数组
+  // value 字符串
+  bSort: function (arr, value) {
+    var len = arr.length;
+    for (var i = 0; i < len - 1; i++) {
+      for (var j = 0; j < len - 1 - i; j++) {
+        // 相邻元素两两对比,元素交换,大的元素交换到后面
+        if (arr[j][value] > arr[j + 1][value]) {
+          var temp = arr[j];
+          arr[j] = arr[j + 1];
+          arr[j + 1] = temp;
+        }
+      }
+    }
+    return arr;
+  },
+  // 解决ios返回不刷新页面的问题
+  iosBackRefresh: function () {
+    var isPageHide = false;
+    window.addEventListener('pageshow', function () {
+      if (isPageHide) {
+        location.reload();
+      }
+    });
+    window.addEventListener('pagehide', function () {
+      isPageHide = true;
+    });
+  },
+  // 通过userAgent获取用户手机操作系统类型
+  androidOrIOS: function () {
+    var u = navigator.userAgent.toLowerCase();
+    var app = navigator.appVersion;
+    var agent = null;
+    if (/iphone|ipod|ipad|ios/.test(u)) {
+      agent = 'ios'
+    } else {
+      agent = 'android'
+    }
+    return agent
+  },
+  // 取[m, n]随机数
+  getRandomNumber: function (min, max) {
+    return Math.floor(Math.random() * (max - min + 1) + min);
+  },
+  // 获取唯一的uuid
+  // https://www.kancloud.cn/ifeng/js100/622666
+  getRandomUuid: function () {
+    var s = [];
+    var hexDigits = "0123456789abcdef";
+    for (var i = 0; i < 36; i++) {
+      s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
+    }
+    s[14] = "4";  // bits 12-15 of the time_hi_and_version field to 0010
+    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01
+    s[8] = s[13] = s[18] = s[23] = "-";
+
+    var uuid = s.join("");
+    return uuid;
+  },
+  // 获取随机字符串
+  // 不传参数则获取长度不固定的字符串
+  getRandomString: function (len) {
+    var randomString = '';
+    if (len) {
+      /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
+      var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
+      var maxPos = $chars.length;
+      for (i = 0; i < len; i++) {
+        randomString += $chars.charAt(Math.floor(Math.random() * maxPos));
+      }
+    } else {
+      // Math.random()  生成随机数字, eg: 0.123456
+      // .toString(36)  转化成36进制 : "0.4fzyo82mvyr"
+      // .substring(2)  去掉前面两位 : "yo82mvyr"
+      // .slice(-8)  截取最后八位 : "yo82mvyr"
+      randomString = Math.random().toString(36).substring(2)
+    }
+    return randomString;
+  },
+  // FROM: https://www.jianshu.com/p/90ed8b728975
+  // 比较两个兑现是否相等
+  // 返回true为相等,返回false为不相等
+  deepCompare: function (x, y) {
+    var i, l, leftChain, rightChain;
+    function compare2Objects(x, y) {
+      var p;
+      // remember that NaN === NaN returns false
+      // and isNaN(undefined) returns true
+      if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
+        return true;
+      }
+
+      // Compare primitives and functions.
+      // Check if both arguments link to the same object.
+      // Especially useful on the step where we compare prototypes
+      if (x === y) {
+        return true;
+      }
+
+      // Works in case when functions are created in constructor.
+      // Comparing dates is a common scenario. Another built-ins?
+      // We can even handle functions passed across iframes
+      if ((typeof x === 'function' && typeof y === 'function') ||
+        (x instanceof Date && y instanceof Date) ||
+        (x instanceof RegExp && y instanceof RegExp) ||
+        (x instanceof String && y instanceof String) ||
+        (x instanceof Number && y instanceof Number)) {
+        return x.toString() === y.toString();
+      }
+
+      // At last checking prototypes as good as we can
+      if (!(x instanceof Object && y instanceof Object)) {
+        return false;
+      }
+
+      if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
+        return false;
+      }
+
+      if (x.constructor !== y.constructor) {
+        return false;
+      }
+
+      if (x.prototype !== y.prototype) {
+        return false;
+      }
+
+      // Check for infinitive linking loops
+      if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
+        return false;
+      }
+
+      // Quick checking of one object being a subset of another.
+      // todo: cache the structure of arguments[0] for performance
+      for (p in y) {
+        if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
+          return false;
+        } else if (typeof y[p] !== typeof x[p]) {
+          return false;
+        }
+      }
+
+      for (p in x) {
+        if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
+          return false;
+        } else if (typeof y[p] !== typeof x[p]) {
+          return false;
+        }
+
+        switch (typeof (x[p])) {
+          case 'object':
+          case 'function':
+
+            leftChain.push(x);
+            rightChain.push(y);
+
+            if (!compare2Objects(x[p], y[p])) {
+              return false;
+            }
+
+            leftChain.pop();
+            rightChain.pop();
+            break;
+
+          default:
+            if (x[p] !== y[p]) {
+              return false;
+            }
+            break;
+        }
+      }
+
+      return true;
+    }
+
+    if (arguments.length < 1) {
+      return true; //Die silently? Don't know how to handle such case, please help...
+      // throw "Need two or more arguments to compare";
+    }
+
+    for (i = 1, l = arguments.length; i < l; i++) {
+
+      leftChain = []; //Todo: this can be cached
+      rightChain = [];
+
+      if (!compare2Objects(arguments[0], arguments[i])) {
+        return false;
+      }
+    }
+
+    return true;
+  },
+  splitPhone: function (phone) {
+    return String(phone).replace(/\s/g, '').replace(/(?=(\d{4})+$)/g, ' ')
+  },
+  // https://blog.csdn.net/jacoox/article/details/80719456
+  // https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/129、https://segmentfault.com/a/1190000012490380
+  // 防抖: 其中 func 为需要进行防抖操作的函数(如发送联想请求的函数),delay 为延迟时间
+  debounce: function (func, delay) {
+    var timer = null
+    return function () {
+      var _this = this
+      var _arg = arguments
+      clearTimeout(timer)
+      timer = setTimeout(function () {
+        func.apply(_this, _arg)
+      }, delay)
+    }
+  },
+  // 节流: 其中 func 为需要进行节流操作的函数,wait为等待时间
+  throttle: function (func, wait) {
+    var lastTime = 0
+    return function (e) {
+      var now = +new Date()
+      if (now - lastTime > wait) {
+        func.apply(this, arguments)
+        lastTime = now
+      }
+    }
+  },
+  isWeiXinBrower: function () {
+    var ua = navigator.userAgent.toLowerCase();
+    if (ua.match(/MicroMessenger/i) == 'micromessenger') {
+      return true;
+    } else {
+      return false;
+    }
+  },
+  // 解决 客户端2.9.5 replace失效问题
+  locationReplace: function (url) {
+    if (history.replaceState) {
+      history.replaceState(null, document.title, url);
+      history.go(0);
+    } else {
+      location.replace(url);
+    }
+  },
+  iosAppFns: function () {
+    window.JyObjMessage = new Object()
+    window.JyObj = {
+      //清除 JyObjMessage
+      clearMessage: function () {
+        JyObjMessage = new Object();
+      },
+      //隐藏显示底部菜单栏 0:隐藏;1:显示
+      hiddenBottom: function (val) {
+        JyObjMessage["hidden"] = val;
+        window.webkit.messageHandlers.hiddenBottom.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //微信登录
+      loginByWeixin: function () {
+        window.webkit.messageHandlers.loginByWeixin.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //分享功能
+      share: function (type, title, content, link) {
+        JyObjMessage["type"] = type
+        JyObjMessage["title"] = title
+        JyObjMessage["content"] = content
+        JyObjMessage["link"] = link
+        window.webkit.messageHandlers.share.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //保存用户token
+      saveUserToken: function (val) {
+        JyObjMessage["token"] = val;
+        window.webkit.messageHandlers.saveUserToken.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //获取用户token
+      getUserToken: function () {
+        return JyObj.IosCall("getUserToken")
+      },
+      //移除用户token
+      removeUserToken: function () {
+        window.webkit.messageHandlers.removeUserToken.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //查看开关状态 是否接受消息
+      checkNoticePermission: function () {
+        return JyObj.IosCall("checkNoticePermission")
+      },
+      //打开接受消息开关
+      openSystemNotification: function () {
+        window.webkit.messageHandlers.openSystemNotification.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //获取极光推送id
+      getPushRid: function () {
+        return JyObj.IosCall("getPushRid")
+      },
+      //跳转外部链接
+      openExternalLink: function (url, title) {
+        JyObjMessage["url"] = url
+        JyObjMessage["title"] = title
+        window.webkit.messageHandlers.openExternalLink.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //获取当前版本号
+      getVersion: function () {
+        return JyObj.IosCall("getVersion")
+      },
+      alert: function (content) {
+        JyObjMessage["content"] = content
+        window.webkit.messageHandlers.alert.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //是否安装了微信
+      isInstallWeixin: function () {
+        return JyObj.IosCall("isInstallWeixin")
+      },
+      //登录加密
+      getCipherText: function (val) {
+        JyObjMessage["phone"] = val
+        return JyObj.IosCall("getCipherText", JyObjMessage)
+      },
+      //刷新首页和订阅页面
+      checkLab: function () {
+        window.webkit.messageHandlers.checkLab.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //登录成功后向客户端传参
+      loginSuccess: function (status) {
+        JyObjMessage["status"] = status
+        window.webkit.messageHandlers.loginSuccess.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //客户端登录页面点击返回 跳转到搜索首页
+      backUrl: function (val) {
+        JyObjMessage["status"] = val;
+        window.webkit.messageHandlers.backUrl.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //清空通知栏消息
+      clearPushMessage: function () {
+        window.webkit.messageHandlers.clearPushMessage.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //隐藏小红点
+      hideRedSpotOnMenu: function (menu) {
+        JyObjMessage["menu"] = menu;
+        window.webkit.messageHandlers.hideRedSpotOnMenu.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //显示小红点
+      showRedSpotOnMenu: function (menu) {
+        JyObjMessage["menu"] = menu;
+        window.webkit.messageHandlers.showRedSpotOnMenu.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //微信支付
+      wxPay: function (order) {
+        JyObjMessage["order"] = order;
+        window.webkit.messageHandlers.wxPay.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //支付宝支付
+      aliPay: function (order) {
+        JyObjMessage["order"] = order;
+        window.webkit.messageHandlers.aliPay.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //获取原生的推送id
+      getOtherPushRid: function () {
+        return JyObj.IosCall("getOtherPushRid")
+      },
+      //获取手机型号
+      getPhoneBrand: function () {
+        return JyObj.IosCall("getPhoneBrand")
+      },
+      //获取定位
+      getLocation: function () {
+        return JyObj.IosCall("getLocation")
+      },
+      //切换菜单
+      chooseTab: function (indexTab) {
+        JyObjMessage["indexTab"] = indexTab;
+        window.webkit.messageHandlers.chooseTab.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //打开照相机
+      skipCamera: function () {
+        window.webkit.messageHandlers.skipCamera.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //打开相册
+      skipAlbum: function () {
+        window.webkit.messageHandlers.skipAlbum.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //点击返回调用
+      judgeIsHidden: function (referer) {
+        JyObjMessage["referer"] = referer;
+        window.webkit.messageHandlers.judgeIsHidden.postMessage(JyObjMessage);
+        JyObj.clearMessage();
+      },
+      //返回值 处理
+      IosCall: function (functionName, args) {
+        if (args != "" && args != undefined) {
+          JyObj.clearMessage();
+        }
+        var payload = { "jsName": functionName, "arguments": args };
+        var res = prompt(JSON.stringify(payload));
+        if (res != "") {
+          var resObj = JSON.parse(res)
+          var type = resObj.type
+          switch (type) {
+            case "int":
+              return parseInt(resObj.value)
+            case "string":
+              return resObj.value
+            case "bool":
+              if (resObj.value == "true") {
+                return true
+              }
+              return false
+            default:
+              return ""
+          }
+        }
+        return ""
+      }
+    }
+  }
+}
+
+vTools.$env = {
+  inWeiXin: vTools.isWeiXinBrower(),
+  platform: vTools.isWeiXinBrower() ? 'wx' : 'app',
+  operateSystem: vTools.androidOrIOS()
+}
+
+// iosApp全局注册内置方法
+if (!vTools.$env.inWeiXin && vTools.$env.operateSystem === 'ios') {
+  vTools.iosAppFns()
+}
+
+/*
+   * 时间格式化函数(将时间格式化为,2019年08月12日,2019-08-12,2019/08/12的形式)
+   *
+   *
+   *   pattern参数(想要什么格式的数据就传入什么格式的数据)
+   *     · 'yyyy-MM-dd'  ---> 输出如2019-09-20
+   *     · 'yyyy-MM-dd hh:mm'  --->  输出如2019-09-20 08:20
+   *     · 'yyyy-MM-dd hh:mm:ss'  --->  输出如2019-09-20 08:20:23
+   *     · 'yyyy/MM/dd'  --->  输出如2019/09/20
+   *     · 'yyyy年MM月dd日'  --->  输出如2019年09月20日
+   *     · 'yyyy年MM月dd日 hh时mm分'  --->  输出如2019年09月20日 08时20分
+   *     · 'yyyy年MM月dd日 hh时mm分ss秒'  --->  输出如2019年09月20日 08时20分23秒
+   *     · 'yyyy年MM月dd日 hh时mm分ss秒 EE'  --->  输出如2019年09月20日 08时20分23秒 周二
+   *     · 'yyyy年MM月dd日 hh时mm分ss秒 EEE'  --->  输出如2019年09月20日 08时20分23秒 星期二
+   *
+   *  参考: https://www.cnblogs.com/mr-wuxiansheng/p/6296646.html
+   */
+Date.prototype.pattern = function (fmt) {
+  var o = {
+    'y+': this.getFullYear(),
+    'M+': this.getMonth() + 1, //月份
+    'd+': this.getDate(), //日
+    'h+': this.getHours() % 12 == 0 ? 12 : this.getHours() % 12, //小时
+    'H+': this.getHours(), //小时
+    'm+': this.getMinutes(), //分
+    's+': this.getSeconds(), //秒
+    'q+': Math.floor((this.getMonth() + 3) / 3), //季度
+    'S': this.getMilliseconds(), //毫秒
+    'E+': this.getDay(), // 周
+  };
+  var week = {
+    '0': '日',
+    '1': '一',
+    '2': '二',
+    '3': '三',
+    '4': '四',
+    '5': '五',
+    '6': '六'
+  };
+  if (/(y+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
+  }
+  if (/(E+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '星期' : '周') : '') + week[
+      this.getDay() + '']);
+  }
+  for (var k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) {
+      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k])
+        .length)));
+    }
+  }
+  return fmt;
+}

+ 46 - 25
src/App.vue

@@ -1,32 +1,53 @@
 <template>
 <template>
-  <div id="app">
-    <div id="nav">
-      <router-link to="/">Home</router-link> |
-      <router-link to="/about">About</router-link>
-    </div>
-    <router-view/>
-  </div>
+  <layout id="app">
+    <template v-slot:main>
+      <transition :name="transitionName" appear>
+        <keep-alive :include="cashViews">
+          <router-view class="router j-container" :class="env.platform" />
+        </keep-alive>
+      </transition>
+    </template>
+  </layout>
 </template>
 </template>
-
-<style lang="scss">
-#app {
-  font-family: Avenir, Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
+<script>
+import layout from '@/components/layout.vue'
+export default {
+  name: 'App',
+  components: {
+    layout
+  },
+  computed: {
+    transitionName () {
+      return this.$store.state.direction
+    }
+  },
+  data () {
+    return {
+      // 需要被缓存的组件,组件的name属性数组
+      cashViews: ['home'],
+      env: this.$env
+    }
+  }
 }
 }
-
-#nav {
-  padding: 30px;
-
-  a {
-    font-weight: bold;
+</script>
+<style lang="scss">
+  @import "@/style/base.scss";
+  @import "@/style/common.scss";
+  #app {
+    font-family: "Avenir", Helvetica, Arial, sans-serif;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
     color: #2c3e50;
     color: #2c3e50;
+    width: 100%;
+    height: 100%;
+    position: relative;
+  }
 
 
-    &.router-link-exact-active {
-      color: #42b983;
-    }
+  .router {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    overflow-x: hidden;
+    overflow-y: auto;
   }
   }
-}
 </style>
 </style>

+ 19 - 0
src/api/demo.ts

@@ -0,0 +1,19 @@
+import qs from 'qs'
+import $request from '@/api/index'
+
+export function getDemo (data: any) {
+  return $request({
+    url: '/jypay/wx/getwxSdkSign',
+    method: 'get',
+    params: data
+  })
+}
+
+export function setDemo (data: any) {
+  data = qs.stringify(data)
+  return $request({
+    url: '/jypay/wx/getwxSdkSign',
+    method: 'post',
+    data
+  })
+}

+ 53 - 0
src/api/index.ts

@@ -0,0 +1,53 @@
+import axios from 'axios'
+import { Toast } from 'vant'
+
+// 配置api地址
+// axios设置
+const $ajax = axios.create({
+  baseURL: process.env.VUE_APP_BASE_API,
+  timeout: 6000,
+  withCredentials: true
+})
+
+/* 拦截器 */
+$ajax.interceptors.request.use(config => {
+  // 在请求发送之前做一些事
+  console.log('--发送之前--', config)
+  // 获取插入token
+  return config
+}, function (error) {
+  // 当出现请求错误是做一些事
+  console.log('--请求超时--', error)
+  Toast('请求超时,请重试')
+  return Promise.reject(error)
+})
+
+// 添加一个返回拦截器
+$ajax.interceptors.response.use((response) => {
+  // 是否需要toast弹窗
+  if (response.config.headers.noToast !== 1) {
+    // 判断是否需要重新登录
+    if (response.data.error_msg === '需要登录' || response.data.error_code === 1001) {
+      Toast({
+        message: '需要登录',
+        forbidClick: true,
+        duration: 1500
+      })
+    }
+    if (response.data.error_code !== 0) {
+      Toast({
+        message: response.data.error_msg || '请求失败,请重试',
+        forbidClick: true,
+        duration: 1500
+      })
+    }
+  }
+
+  return response
+}, function (error) {
+  console.log('--响应超时--', error)
+  Toast('响应超时,请重试')
+  return Promise.reject(error)
+})
+
+export default $ajax

BIN
src/assets/image/empty.png


BIN
src/assets/image/icon-ali-pay.png


BIN
src/assets/image/icon-check.png


BIN
src/assets/image/icon-checkbox-choose.png


BIN
src/assets/image/icon-duihao.png


BIN
src/assets/image/icon-wx-pay.png


BIN
src/assets/image/img-404.jpg


BIN
src/assets/logo.png


+ 0 - 61
src/components/HelloWorld.vue

@@ -1,61 +0,0 @@
-<template>
-  <div class="hello">
-    <h1>{{ msg }}</h1>
-    <p>
-      For a guide and recipes on how to configure / customize this project,<br>
-      check out the
-      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
-    </p>
-    <h3>Installed CLI Plugins</h3>
-    <ul>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
-    </ul>
-    <h3>Essential Links</h3>
-    <ul>
-      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
-      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
-      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
-      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
-      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
-    </ul>
-    <h3>Ecosystem</h3>
-    <ul>
-      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
-      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
-      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
-      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
-      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
-    </ul>
-  </div>
-</template>
-
-<script lang="ts">
-import { Component, Prop, Vue } from 'vue-property-decorator'
-
-@Component
-export default class HelloWorld extends Vue {
-  @Prop() private msg!: string;
-}
-</script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="scss">
-h3 {
-  margin: 40px 0 0;
-}
-ul {
-  list-style-type: none;
-  padding: 0;
-}
-li {
-  display: inline-block;
-  margin: 0 10px;
-}
-a {
-  color: #42b983;
-}
-</style>

+ 84 - 0
src/components/common/CountDownButton.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="countdown">
+    <van-button
+      class="countdown-button"
+      size="small"
+      :disabled="buttonDisabeld"
+      :loading="sLoading"
+      @click="buttonClick"
+    >{{ buttonText }}</van-button>
+  </div>
+</template>
+
+<script lang="ts">
+import { Component, Vue, Prop } from 'vue-property-decorator'
+import { Button } from 'vant'
+
+// @ is an alias to /src
+@Component({
+  name: 'countdown-button',
+  components: {
+    [Button.name]: Button
+  }
+})
+export default class CountdownButton extends Vue {
+  @Prop({ default: '发送验证码' }) defaultText: string | undefined;
+  @Prop({ default: false }) disabled: boolean | undefined;
+  @Prop({ default: 60 }) countdown: number | undefined;
+  @Prop({ default: false }) loading: boolean | undefined;
+
+  timerId = 0
+
+  count: any = 0
+
+  get buttonText () {
+    // const dText = `${this.timerId ? '重新' : ''}${this.defaultText}`
+    const dText = this.defaultText
+    return this.count <= 0 ? dText : `${this.count}s`
+  }
+
+  get sLoading () {
+    return this.loading
+  }
+
+  get buttonDisabeld () {
+    // 如果count不为0,则说明定时器一直在走
+    return !!this.count || this.disabled
+  }
+
+  buttonClick () {
+    this.$emit('click', this.startTimer)
+  }
+
+  startTimer (start = 0) {
+    this.count = start || this.countdown
+    this.timerId = setInterval(() => {
+      this.count--
+      if (this.count <= 0) {
+        clearInterval(this.timerId)
+      }
+    }, 1000)
+  }
+
+  reset () {
+    clearInterval(this.timerId)
+    this.timerId = 0
+    this.count = 0
+  }
+}
+</script>
+
+<style lang="scss">
+.countdown {
+  .countdown-button {
+    color: #2ABED1;
+    font-size: 14px;
+    border: none;
+    // height: 35Px;
+    &[disabled] {
+      color: #9B9CA3;
+      opacity: 1;
+    }
+  }
+}
+</style>

+ 84 - 0
src/components/common/Empty.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="empty-container">
+    <div class="empty-content-position mt50">
+      <div class="image" :class="state"></div>
+      <div class="empty-main">
+        <slot name="default"></slot>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { Component, Vue, Prop } from 'vue-property-decorator'
+
+  // @ is an alias to /src
+  @Component
+export default class Empty extends Vue {
+    @Prop({ default: 'empty-image' })
+    state!: string;
+}
+</script>
+
+<style lang="scss">
+  .empty-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    flex: 1;
+    height: 100%;
+    padding: 16px;
+    box-sizing: border-box;
+    background-color: rgba(245, 244, 249, 1);
+
+    .empty-content-position {
+      display: flex;
+      align-items: center;
+      flex-direction: column;
+      justify-content: center;
+    }
+
+    .mt50 {
+      margin-top: -50px;
+    }
+
+    .empty-main {
+      @extend .empty-content-position;
+      text-align: center;
+    }
+
+    .max-width70 {
+      width: 70%;
+    }
+
+    .max-width80 {
+      width: 80%;
+    }
+
+    .tip-text {
+      color: #5F5E64;
+      font-size: 14px;
+      line-height: 20px;
+      text-align: center;
+    }
+
+    .tip-sub-text {
+      color: #9B9CA3;
+      font-size: 13px;
+      line-height: 20px;
+      margin-top: 6px;
+      text-align: center;
+    }
+
+    .image {
+      width: 160px;
+      height: 160px;
+    }
+
+    .empty-image {
+      background: url(~@/assets/image/empty.png) no-repeat;
+      background-size: contain;
+    }
+  }
+</style>

+ 39 - 0
src/components/common/Image.vue

@@ -0,0 +1,39 @@
+<template>
+  <van-image class="j-image" :src="src" lazy-load>
+    <template v-slot:loading>
+      <van-loading type="spinner" size="20" />
+    </template>
+    <template v-slot:error>
+      <img src="@/assets/image/img-404.jpg" />
+    </template>
+  </van-image>
+</template>
+
+<script lang="ts">
+import { Component, Vue, Prop } from 'vue-property-decorator'
+import { Image as VanImage, Loading } from 'vant'
+
+// @ is an alias to /src
+@Component({
+  components: {
+    [VanImage.name]: VanImage,
+    [Loading.name]: Loading
+  }
+})
+export default class Empty extends Vue {
+  @Prop({ default: '' }) src: string | any;
+}
+</script>
+
+<style lang="scss">
+.j-image {
+  width: 100%;
+  height: 100%;
+  border-radius: 6px;
+  overflow: hidden;
+  img {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 209 - 0
src/components/layout.vue

@@ -0,0 +1,209 @@
+<template>
+  <div class="j-container" :class="{'transparent-header': headerInfo.transparentHeader}">
+    <div class="j-header jy-app-header" v-if="!weixinBrowser" :class="{hideBorder: headerInfo.hideBorder}">
+      <van-icon class="header-left" :style="headerInfo.actionLeftStyle" :class="{ hide: headerInfo.actionLeftHide }" name="arrow-left" @click="goBack" />
+      <span class="header-title" :style="headerInfo.titleStyle">{{ headerInfo.title }}</span>
+      <span class="header-right" :style="headerInfo.actionRightStyle" @click="actionRight">{{ headerInfo.actionRightText }}</span>
+    </div>
+    <div class="j-main">
+      <slot name="main"></slot>
+    </div>
+    <div class="j-footer">
+      <slot name="footer"></slot>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { Component, Vue, Watch } from 'vue-property-decorator'
+import { mapState, mapMutations } from 'vuex'
+import { Icon } from 'vant'
+
+// @ is an alias to /src
+@Component({
+  name: 'layout',
+  components: {
+    [Icon.name]: Icon
+  },
+  methods: {
+    ...mapState([
+      'layoutConf',
+      'defaultLayoutConf'
+    ]),
+    ...mapMutations({
+      updateLayoutConfig: 'updateLayoutConfig',
+      clearLayoutConfig: 'clearLayoutConfig'
+    })
+  }
+})
+
+export default class Layout extends Vue {
+  protected layoutConf!: any
+  protected defaultLayoutConf!: any
+  protected updateLayoutConfig!: any
+  protected clearLayoutConfig!: any
+
+  useConf = false
+
+  @Watch('$route.path')
+  onRouterChange () {
+    this.useConf = this.$route.meta.layoutConf
+    // console.log('headerConfig', this.conf)
+  }
+
+  get weixinBrowser () {
+    return navigator.userAgent.toLowerCase().indexOf('micromessenger') !== -1
+  }
+
+  get conf () {
+    if (this.useConf) {
+      return this.layoutConf()
+    } else {
+      return this.defaultLayoutConf()
+    }
+  }
+
+  get headerInfo () {
+    return {
+      title: this.conf.title || this.$route.meta.title,
+      transparentHeader: this.conf.transparentHeader,
+      actionLeftHide: this.conf.actionLeftHide,
+      actionRightText: this.conf.actionRightText,
+      titleStyle: this.conf.titleStyle,
+      actionLeftStyle: this.conf.actionLeftStyle,
+      actionRightStyle: this.conf.actionRightStyle,
+      hideBorder: this.$route.meta.hideBorder || false
+    }
+  }
+
+  mounted () {
+    // console.log('layout mounted')
+  }
+
+  goBack () {
+    window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
+  }
+
+  actionRight () {
+    this.conf.actionRightCallback && this.conf.actionRightCallback()
+  }
+}
+</script>
+
+<style lang="scss">
+.j-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  &.transparent-header {
+    .j-header {
+      position: absolute;
+      width: 100%;
+      background-color: transparent;
+      &:after {
+        height: 0;
+      }
+      .header-left {
+        color: #fff;
+      }
+    }
+  }
+}
+
+// 全局布局样式
+.j-header {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 17px;
+  min-height: 20px;
+  background-color: #fff;
+  z-index: 2;
+
+  // app头部样式
+  &.jy-app-header {
+    height: 80px;
+    padding: 40px 12px 0 12px;
+    box-sizing: border-box;
+    background: #fff;
+    font-family: "Avenir", Helvetica, Arial, sans-serif;
+  }
+  &.hideBorder {
+    &::after {
+      content: unset;
+    }
+  }
+  &:after {
+    content: '';
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    width: 100%;
+    height: 1Px;
+    background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.1) 65%, transparent 35%);
+  }
+  .header-left,
+  .header-title,
+  .header-right {
+    display: flex;
+    align-items: center;
+  }
+  .header-left.hide,
+  .header-right.hide {
+    visibility: hidden;
+  }
+
+  .header-left {
+    color: #5f5e64;
+    color: #444;
+    font-size: 20px;
+  }
+  .header-title {
+    position: absolute;
+    left: 50%;
+    color: #171826;
+    transform: translateX(-50%);
+  }
+  .header-right {
+    height: 100%;
+    min-width: 20px;
+    font-size: 15px;
+    color: #2ABED1;
+  }
+}
+
+.j-main {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+  overflow-y: scroll;
+  overflow-x: hidden;
+  font-size: 16px;
+  // 所有都j-main开启ios-touch
+  -webkit-overflow-scrolling: touch;
+  .calc-height-1px {
+    height: calc(100% + 1Px);
+  }
+
+  // 单独关闭ios惯性滑动
+  &.no-ios-touch {
+    -webkit-overflow-scrolling: auto;
+  }
+
+  // &.ios-touch {
+  //   // 开启ios惯性滑动,列表的 item 必须由一个根div元素包括
+  //   -webkit-overflow-scrolling: touch;
+  //   // 解决ios滑动白屏问题
+  //   &>.calc-height-1px,
+  //   &>div {
+  //     height: calc(100% + 1px);
+  //   }
+  // }
+}
+
+.j-footer {
+  box-shadow: 0px -2Px 8Px rgba(54, 147, 179, 0.051);
+}
+</style>

+ 8 - 0
src/main.ts

@@ -2,8 +2,16 @@ import Vue from 'vue'
 import App from './App.vue'
 import App from './App.vue'
 import router from './router'
 import router from './router'
 import store from './store'
 import store from './store'
+import '@/utils/'
+import { Component } from 'vue-property-decorator'
+import { Toast, Lazyload } from 'vant'
+import VueBus from './vue_bus'
+
+Component.registerHooks(['beforeRouteEnter', 'beforeRouteLeave', 'beforeRouteUpdate'])
+Toast.setDefaultOptions({ getContainer: '#app' })
 
 
 Vue.config.productionTip = false
 Vue.config.productionTip = false
+Vue.use(Toast).use(Lazyload).use(VueBus)
 
 
 new Vue({
 new Vue({
   router,
   router,

+ 90 - 15
src/router/index.ts

@@ -1,29 +1,104 @@
 import Vue from 'vue'
 import Vue from 'vue'
-import VueRouter, { RouteConfig } from 'vue-router'
-import Home from '../views/Home.vue'
+import VueRouter from 'vue-router'
+import store from '@/store'
 
 
-Vue.use(VueRouter)
+declare const _hmt: any
 
 
-const routes: Array<RouteConfig> = [
+if (process.env.NODE_DEV !== 'production') {
+  Vue.use(VueRouter)
+}
+
+let routes = [
   {
   {
     path: '/',
     path: '/',
-    name: 'Home',
-    component: Home
+    redirect: 'home'
+  },
+  {
+    path: '/home',
+    name: 'home',
+    component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue'),
+    meta: {
+      title: '伙伴计划',
+      isBack: false
+    }
   },
   },
   {
   {
-    path: '/about',
-    name: 'About',
-    // route level code-splitting
-    // this generates a separate chunk (about.[hash].js) for this route
-    // which is lazy-loaded when the route is visited.
-    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
+    path: '/404',
+    name: '404',
+    component: () => import(/* webpackChunkName: "404" */ '@/views/404.vue'),
+    meta: {
+      title: '404'
+    }
   }
   }
 ]
 ]
 
 
-const router = new VueRouter({
-  mode: 'history',
+const routerContext = require.context('./modules', true, /\.ts$/)
+routerContext.keys().forEach(route => {
+  // 如果是根目录的 index.js 、不处理
+  if (route.startsWith('./index')) {
+    return
+  }
+  const routerModule = routerContext(route)
+  /**
+   * 兼容 import export 和 require module.export 两种规范
+   */
+  routes = routes.concat(routerModule.default || routerModule)
+})
+
+const tempObj: any = {
+  path: '*',
+  redirect: {
+    name: '404'
+  }
+}
+routes = routes.concat(tempObj)
+
+const myRouter = new VueRouter({
+  mode: 'history', // require service support
   base: process.env.BASE_URL,
   base: process.env.BASE_URL,
+  scrollBehavior: () => ({ x: 0, y: 0 }),
   routes
   routes
 })
 })
 
 
-export default router
+const history = window.sessionStorage
+const tempHist: any = history.getItem('count')
+let historyCount: any = (tempHist * 1) || 0
+history.setItem('/', String(0))
+
+myRouter.beforeEach((to, from, next) => {
+  // 设置页面标题
+  document.title = to.meta.title
+  store.commit('updatePageTitle', to.meta.title)
+  try {
+    ;(_hmt as any).push(['_trackPageview', to.fullPath])
+  } catch (error) {
+    console.log(error)
+  }
+  // 页面切换动画
+  if (to.params.direction) {
+    store.commit('updateDirection', to.params.direction)
+  } else {
+    const toIndex = history.getItem(to.path)
+    const fromIndex = history.getItem(from.path)
+    // 判断并记录跳转页面是否访问过,以此判断跳转过渡方式
+    if (toIndex) {
+      if (!fromIndex || parseInt(toIndex, 10) > parseInt(fromIndex, 10) || (toIndex === '0' && fromIndex === '0')) {
+        store.commit('updateDirection', 'forward')
+      } else {
+        store.commit('updateDirection', 'back')
+      }
+    } else {
+      ++historyCount
+      history.setItem('count', String(historyCount))
+      to.path !== '/' && history.setItem(to.path, String(historyCount))
+      store.commit('updateDirection', 'forward')
+    }
+  }
+  next()
+})
+
+// myRouter.afterEach((to, from) => {
+//   // 全局路由守卫
+// })
+
+export default myRouter

+ 18 - 0
src/router/modules/auth.ts

@@ -0,0 +1,18 @@
+export default [
+  {
+    path: '/auth/enterprise',
+    name: 'auth-enterprise',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/auth/Enterprise.vue'),
+    meta: {
+      title: '认证伙伴'
+    }
+  },
+  {
+    path: '/auth/everyman',
+    name: 'auth-everyman',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/auth/Everyman.vue'),
+    meta: {
+      title: '认证伙伴'
+    }
+  }
+]

+ 10 - 0
src/router/modules/demo.ts

@@ -0,0 +1,10 @@
+export default [
+  {
+    path: '/demo',
+    name: 'demo',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/Demo.vue'),
+    meta: {
+      title: '测试页面'
+    }
+  }
+]

+ 34 - 0
src/router/modules/main.ts

@@ -0,0 +1,34 @@
+export default [
+  {
+    path: '/help',
+    name: 'help',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/Help.vue'),
+    meta: {
+      title: '在线帮助'
+    }
+  },
+  {
+    path: '/readme',
+    name: 'readme',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/Readme.vue'),
+    meta: {
+      title: '加入伙伴'
+    }
+  },
+  {
+    path: '/result/:type',
+    name: 'result',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/Result.vue'),
+    meta: {
+      title: '提现 or 申请'
+    }
+  },
+  {
+    path: '/logout',
+    name: 'logout',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/Logout.vue'),
+    meta: {
+      title: '注销伙伴计划'
+    }
+  }
+]

+ 18 - 0
src/router/modules/record.ts

@@ -0,0 +1,18 @@
+export default [
+  {
+    path: '/record/list',
+    name: 'record-list',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/record/List.vue'),
+    meta: {
+      title: '资金明细'
+    }
+  },
+  {
+    path: '/record/details',
+    name: 'record-details',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/record/Details.vue'),
+    meta: {
+      title: '明细详情'
+    }
+  }
+]

+ 26 - 0
src/router/modules/share.ts

@@ -0,0 +1,26 @@
+export default [
+  {
+    path: '/share/list',
+    name: 'share-list',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/share/List.vue'),
+    meta: {
+      title: '分享赚钱'
+    }
+  },
+  {
+    path: '/share/details',
+    name: 'share-details',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/share/Details.vue'),
+    meta: {
+      title: '分享赚钱'
+    }
+  },
+  {
+    path: '/share/create',
+    name: 'share-create',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/share/Share.vue'),
+    meta: {
+      title: '创建分享'
+    }
+  }
+]

+ 18 - 0
src/router/modules/withdrawal.ts

@@ -0,0 +1,18 @@
+export default [
+  {
+    path: '/withdrawal/enterprise',
+    name: 'withdrawal-enterprise',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/withdrawal/Enterprise.vue'),
+    meta: {
+      title: '提现'
+    }
+  },
+  {
+    path: '/withdrawal/everyman',
+    name: 'withdrawal-everyman',
+    component: () => import(/* webpackChunkName: "test" */ '@/views/withdrawal/Everyman.vue'),
+    meta: {
+      title: '提现'
+    }
+  }
+]

+ 66 - 5
src/store/index.ts

@@ -1,15 +1,76 @@
 import Vue from 'vue'
 import Vue from 'vue'
 import Vuex from 'vuex'
 import Vuex from 'vuex'
 
 
-Vue.use(Vuex)
+if (process.env.NODE_DEV !== 'production') {
+  Vue.use(Vuex)
+}
+
+const files = require.context('./modules', false, /\.ts$/)
+const modules: any = {}
+
+files.keys().forEach(key => {
+  const tempK = key.replace(/(\.\/|\.ts)/g, '')
+  modules[tempK] = files(key).default
+})
 
 
 export default new Vuex.Store({
 export default new Vuex.Store({
   state: {
   state: {
+    pageTitle: '剑鱼标讯',
+    direction: 'forward', // 页面切换方向
+    defaultLayoutConf: {
+      // layout标题,优先级高于$route.meta
+      title: '',
+      transparentHeader: false,
+      // 是否隐藏左侧返回按钮
+      actionLeftHide: '',
+      actionRightText: '',
+      titleStyle: {},
+      actionLeftStyle: {},
+      actionRightStyle: {},
+      actionRightCallback: ''
+    },
+    layoutConf: {
+      // layout标题,优先级高于$route.meta
+      title: '',
+      transparentHeader: false,
+      // 是否隐藏左侧返回按钮
+      actionLeftHide: '',
+      actionRightText: '',
+      titleStyle: {},
+      actionLeftStyle: {},
+      actionRightStyle: {},
+      actionRightCallback: () => {
+        // console.log('actionRight')
+      }
+    }
   },
   },
   mutations: {
   mutations: {
+    // 更新页面切换方向
+    updateDirection (state, direction) {
+      state.direction = direction
+    },
+    updatePageTitle (state, title) {
+      state.pageTitle = title
+    },
+    // 更新全局头部信息
+    updateLayoutConfig (state: any, conf: any) {
+      // 先clear
+      for (const key in state.defaultLayoutConf) {
+        state.layoutConf[key] = state.defaultLayoutConf[key]
+      }
+      // 在赋值
+      for (const key in conf) {
+        state.layoutConf[key] = conf[key]
+      }
+    },
+    // 将数据恢复默认状态
+    clearLayoutConfig (state: any) {
+      for (const key in state.defaultLayoutConf) {
+        state.layoutConf[key] = state.defaultLayoutConf[key]
+      }
+    }
   },
   },
-  actions: {
-  },
-  modules: {
-  }
+  actions: {},
+  getters: {},
+  modules
 })
 })

+ 40 - 0
src/store/modules/demo.ts

@@ -0,0 +1,40 @@
+import {
+  getDemo,
+  setDemo
+} from '@/api/demo'
+
+export default {
+  namespaced: true,
+  state: {
+    // 数据报告详情
+    reportInfo: JSON.parse(sessionStorage.getItem('demo') ||
+      JSON.stringify({
+        text: ''
+      }
+      ))
+  },
+  mutations: {
+    // 保存数据报告列表
+    saveDemo (state: any, data: any) {
+      for (const key in data) {
+        state.demo[key] = data[key]
+      }
+      sessionStorage.setItem('demo', JSON.stringify(data))
+    }
+  },
+  actions: {
+    async getDemo (state: any, data: any) {
+      try {
+        const res = await getDemo(data)
+        return res.data
+      } catch (error) {}
+    },
+    async setDemo (state: any, data: any) {
+      try {
+        const res = await setDemo(data)
+        return res.data
+      } catch (error) {}
+    }
+  },
+  getters: {}
+}

+ 208 - 0
src/style/_mixin.scss

@@ -0,0 +1,208 @@
+@mixin center($width: null, $height: null) {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+
+    @if not $width and not $height {
+        transform: translate(-50%, -50%);
+    }
+
+    @else if $width and $height {
+        width: $width;
+        height: $height;
+        margin: -($height / 2) #{0 0} -($width / 2);
+    }
+
+    @else if not $height {
+        width: $width;
+        margin-left: -($width / 2);
+        transform: translateY(-50%);
+    }
+
+    @else {
+        height: $height;
+        margin-top: -($height / 2);
+        transform: translateX(-50%);
+    }
+}
+
+
+
+@mixin opacity($opacity) {
+    opacity: $opacity;
+    $opacity-ie: $opacity * 100;
+    filter: alpha(opacity=$opacity-ie);
+}
+
+@mixin ell() {
+    //
+    overflow: hidden;
+    -ms-text-overflow: ellipsis;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+//多行超出省略号
+@mixin ell2() {
+    word-break: break-all;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-box-orient: vertical;
+    -webkit-line-clamp: 2;
+    overflow: hidden;
+}
+
+//.arrow{
+// @include arrow(bottom,10px,#F00);
+//
+@mixin arrow($direction, $size, $color) {
+    width: 0;
+    height: 0;
+    line-height: 0;
+    font-size: 0;
+    overflow: hidden;
+    border-width: $size;
+    cursor: pointer;
+
+    @if $direction==top {
+        border-style: dashed dashed solid dashed;
+        border-color: transparent transparent $color transparent;
+        border-top: none;
+    }
+
+    @else if $direction==bottom {
+        border-style: solid dashed dashed dashed;
+        border-color: $color transparent transparent transparent;
+        border-bottom: none;
+    }
+
+    @else if $direction==right {
+        border-style: dashed dashed dashed solid;
+        border-color: transparent transparent transparent $color;
+        border-right: none;
+    }
+
+    @else if $direction==left {
+        border-style: dashed solid dashed dashed;
+        border-color: transparent $color transparent transparent;
+        border-left: none;
+    }
+}
+
+// clearfix 
+@mixin clr {
+    &:after {
+        clear: both;
+        content: '.';
+        display: block;
+        height: 0;
+        line-height: 0;
+        overflow: hidden;
+    }
+
+    *height: 1%;
+}
+
+/*渐变(从上到下)*/
+@mixin linear-gradient($direction:bottom, $color1:transparent, $color2:#306eff, $color3:transparent) {
+    //background: -webkit-linear-gradient($direction,$colorTop, $colorCenter, $colorBottom); /* Safari 5.1 - 6.0 */
+    background: -o-linear-gradient($direction, $color1, $color2, $color3);
+    /* Opera 11.1 - 12.0 */
+    background: -moz-linear-gradient($direction, $color1, $color2, $color3);
+    /* Firefox 3.6 - 15 */
+    background: linear-gradient(to $direction, $color1, $color2, $color3);
+    /* 标准的语法 */
+
+}
+
+/* 行高 */
+@mixin line-height($height:30px, $line-height:30px) {
+    @if ($height !=null) {
+        height: $height;
+    }
+
+    @if ($line-height !=null) {
+        line-height: $line-height;
+    }
+}
+
+/* 定义滚动条样式 圆角和阴影不需要则传入null */
+@mixin scrollBar($width:10px, $height:10px, $outColor:$bgColor, $innerColor:$bgGrey, $radius:5px, $shadow:null) {
+
+    /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
+    &::-webkit-scrollbar {
+        width: $width;
+        height: $height;
+        background-color: $outColor;
+    }
+
+    /*定义滚动条轨道 内阴影+圆角*/
+    &::-webkit-scrollbar-track {
+        @if ($shadow !=null) {
+            -webkit-box-shadow: $shadow;
+        }
+
+        @if ($radius !=null) {
+            border-radius: $radius;
+        }
+
+        background-color: $outColor;
+    }
+
+    /*定义滑块 内阴影+圆角*/
+    &::-webkit-scrollbar-thumb {
+        @if ($shadow !=null) {
+            -webkit-box-shadow: $shadow;
+        }
+
+        @if ($radius !=null) {
+            border-radius: $radius;
+        }
+
+        background-color: $innerColor;
+        border: 1px solid $innerColor;
+    }
+}
+
+/* css3动画 默认3s宽度到200px */
+@mixin animation($from:(width:0px), $to:(width:200px), $name:mymove, $animate:mymove 2s 1 linear infinite) {
+    -webkit-animation: $animate;
+    -o-animation: $animate;
+    animation: $animate;
+
+    @keyframes #{$name} {
+        from {
+
+            @each $key,
+            $value in $from {
+                #{$key}: #{$value};
+            }
+        }
+
+        to {
+
+            @each $key,
+            $value in $to {
+                #{$key}: #{$value};
+            }
+        }
+    }
+
+    @-webkit-keyframes #{$name} {
+        from {
+
+            @each $key,
+            $value in $from {
+                $key: $value;
+            }
+        }
+
+        to {
+
+            @each $key,
+            $value in $to {
+                $key: $value;
+            }
+        }
+    }
+}

+ 7 - 0
src/style/_variables.scss

@@ -0,0 +1,7 @@
+$tabbarHeight: 50px;
+$topNavHeight: 44px;
+
+@function addPx($a, $b) {
+  @return $a + $b;
+}
+

+ 257 - 0
src/style/base.scss

@@ -0,0 +1,257 @@
+html,
+body {
+  width: 100%;
+  height: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  box-sizing: border-box;
+}
+
+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,
+sumary {
+  margin: 0;
+  padding: 0;
+}
+
+html,
+body {
+  /* max-width: 750px; */
+  -webkit-text-size-adjust: 100%;
+  margin: 0 auto;
+  font-size: 0.24rem;
+  background: rgba(245, 244, 249, 1);
+  color: #3d3d3d;
+  font-family: "Microsoft YaHei", "Helvetica Neue", "Roboto", "Segoe UI",
+    "PingFang SC", "Hiragino Sans GB", sans-serif;
+}
+
+// 取消滚动条
+html::-webkit-scrollbar,
+body::-webkit-scrollbar {
+  display: none;
+}
+
+ul,
+li,
+ol {
+  list-style: none;
+}
+
+/*清除输入框内阴影*/
+input,
+textarea,
+select,
+button {
+  outline: none;
+  border: 0;
+  -webkit-appearance: none;
+  appearance: none;
+}
+
+button,
+span,
+div {
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+  /* -webkit-user-modify:read-only; */
+}
+
+img {
+  border: 0;
+  vertical-align: middle;
+  max-width: 100%;
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+a {
+  text-decoration: none;
+  color: #3d3d3d;
+  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+  -webkit-user-select: none;
+  -moz-user-focus: none;
+  -moz-user-select: none;
+}
+
+/*禁用长按页面时的弹出菜单(iOS下有效) ,img和a标签都要加*/
+img,
+a {
+  -webkit-touch-callout: none;
+}
+
+em,
+i {
+  font-style: normal;
+}
+
+/*兼容ios调取h5页面的头部*/
+.ios-head {
+  display: none;
+  position: fixed;
+  top: 0;
+  padding-top: 15px;
+  background: #18974b;
+  width: 100%;
+  z-index: 100;
+}
+
+.ellipsis {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  text-align: justify
+}
+
+/* 超过2行省略号显示 */
+.ellipsis-2 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  text-align: justify
+}
+
+/* 超过3行省略号显示 */
+.ellipsis-3 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+}
+
+.clearfix:after {
+  display: block;
+  clear: both;
+  content: '';
+  visibility: hidden;
+  height: 0;
+}
+
+input,
+button,
+select,
+textarea {
+  outline: none;
+}
+
+/* page change */
+// FROM: https://juejin.im/post/5ba358a56fb9a05d2068401d
+$--transition-time: .3s;
+
+// ============
+// fold-left -> forward
+.forward-enter-active {
+  animation-name: fold-left-in;
+  animation-duration: $--transition-time;
+}
+.forward-leave-active {
+  animation-name: fold-left-out;
+  animation-duration: $--transition-time;
+}
+@keyframes fold-left-in {
+  0% {
+    -webkit-transform: translate3d(100%, 0, 0);
+    transform: translate3d(100%, 0, 0);
+    /* visibility: visible; */
+  }
+  /*50% {
+    transform: translate3d(50%, 0, 0);
+  }*/
+  100% {
+    -webkit-transform: translate3d(0, 0, 0);
+    transform: translate3d(0, 0, 0);
+  }
+}
+@keyframes fold-left-out {
+  0% {
+    -webkit-transform: translate3d(0, 0, 0);
+    transform: translate3d(0, 0, 0);
+  }
+  /*50% {
+    transform: translate3d(-50%, 0 , 0);
+  }*/
+  100% {
+    -webkit-transform: translate3d(-100%, 0, 0);
+    transform: translate3d(-100%, 0, 0);
+    /* visibility: hidden; */
+  }
+}
+
+// fold-right -> back
+.back-enter-active {
+  animation-name: fold-right-in;
+  animation-duration: $--transition-time;
+}
+.back-leave-active {
+  animation-name: fold-right-out;
+  animation-duration: $--transition-time;
+}
+@keyframes fold-right-in{
+  0% {
+    width: 100%;
+    -webkit-transform: translate3d(-100%, 0, 0);
+    transform: translate3d(-100%, 0, 0);
+    /* visibility: visible; */
+  }
+  /*50% {
+    transform: translate3d(50%, 0, 0);
+  }*/
+  100% {
+    width: 100%;
+    -webkit-transform: translate3d(0, 0, 0);
+    transform: translate3d(0, 0, 0);
+  }
+}
+@keyframes fold-right-out  {
+  0% {
+    width: 100%;
+    -webkit-transform: translate3d(0, 0, 0);
+    transform: translate3d(0, 0, 0);
+  }
+  /*50% {
+    transform: translate3d(-50%, 0 , 0);
+  }*/
+  100% {
+    width: 100%;
+    -webkit-transform: translate3d(100%, 0, 0);
+    transform: translate3d(100%, 0, 0);
+    /* visibility: hidden; */
+  }
+}

+ 140 - 0
src/style/common.scss

@@ -0,0 +1,140 @@
+@import './pic-icon.scss';
+@import './vant-reset.scss';
+
+$color_main: #2ABED1;
+
+// 文字颜色高亮
+.highlight-text {
+  color: $color_main;
+}
+
+input, textarea {
+  caret-color: $color_main;
+}
+
+// 8px
+.margin-top8 {
+  margin-top: 8px;
+}
+.margin-bottom8 {
+  margin-bottom: 8px;
+}
+
+// 取消滚动条
+.no-scrollbar::-webkit-scrollbar {
+  display: none;
+}
+
+// button相关组件 -------------
+.j-button {
+  color: #fff;
+  background-color: $color_main;
+}
+
+.j-button-select {
+  padding: 6px 12px;
+  color: #5F5E64;
+  font-size: 14px;
+  background: #F5F6F7;
+  border-radius: 4px;
+  &.active {
+    position: relative;
+    color: #2ABED1;
+    background: #E8FAFD;
+    &::after {
+      content: '';
+      position: absolute;
+      right: 0;
+      bottom: 0;
+      background: url(~@/assets/image/icon-check.png);
+      width: 14px;
+      height: 14px;
+      background-size: 100% 100%;
+    }
+  }
+}
+
+// 底部按钮组
+.j-button-group {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 16px 12px;
+  background-color: #fff;
+}
+
+.j-button-confirm,
+.j-button-cancel {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex: 1;
+  width: 100%;
+  height: 46px;
+  font-size: 18px;
+  line-height: inherit;
+  text-align: center;
+  border-radius: 8px;
+  &:before {
+    content: ' ';
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    width: 100%;
+    height: 100%;
+    background-color: #000;
+    border: inherit;
+    border-color: #000;
+    border-radius: inherit;
+    -webkit-transform: translate(-50%,-50%);
+    transform: translate(-50%,-50%);
+    opacity: 0;
+  }
+  &:active:before {
+    opacity: 0.1;
+  }
+}
+
+.j-button-cancel {
+  margin-right: 13px;
+  color: #5f5e64;
+  background-color: #edeff2;
+}
+
+.j-button-confirm {
+  @extend .j-button;
+}
+
+button[disabled] {
+  opacity: 0.5;
+}
+
+// 自定义tag
+.j-tag {
+  box-sizing: border-box;
+  display: flex;
+  align-items: center;
+  min-height: 22px;
+  padding: 0 8px;
+  white-space: nowrap;
+  border-radius: 11px;
+  font-size: 13px;
+  line-height: 18px;
+  border: 1px solid transparent;
+}
+
+.tag-warning {
+  color: #FF9F40;
+  border-color: #FF9F40;
+}
+
+.tag-danger {
+  color: #FB483D;
+  border-color: #FB483D;
+}
+
+.tag-success {
+  color: #2ABED1;
+  border-color: #2ABED1;
+}

+ 25 - 0
src/style/pic-icon.scss

@@ -0,0 +1,25 @@
+.j-icon {
+  display: inline-block;
+  width: 20px;
+  height: 20px;
+}
+
+.icon-duihao {
+  background: transparent url(~@/assets/image/icon-duihao.png) no-repeat center;
+  background-size: contain;
+}
+
+.icon-checkbox-choose {
+  background: transparent url(~@/assets/image/icon-checkbox-choose.png) no-repeat center;
+  background-size: contain;
+}
+
+.icon-wx-pay {
+  background: transparent url(~@/assets/image/icon-wx-pay.png) no-repeat center;
+  background-size: contain;
+}
+
+.icon-ali-pay {
+  background: transparent url(~@/assets/image/icon-ali-pay.png) no-repeat center;
+  background-size: contain;
+}

+ 82 - 0
src/style/vant-reset.scss

@@ -0,0 +1,82 @@
+.one-cell {
+  .van-cell {
+    font-size: 16px;
+    color: #171826;
+    padding: 15px 16px;
+    .van-cell__title {
+      width: auto;
+    }
+    .van-field__control {
+      font-size: 14px;
+      color: #9B9CA3;
+    }
+    .van-cell__value {
+      font-size: 13px;
+    }
+  }
+}
+
+// 选择器重置样式
+.j-date-time-picker {
+  .j-picker-header {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0 16px;
+    height: 64px;
+    font-size: 16px;
+    .header-title {
+      min-width: 80px;
+      color: #171826;
+      font-size: 20px;
+    }
+    .j-icon {
+      color: #C0C4CC;
+      text-align: right;
+    }
+  }
+
+  .picker-footer {}
+
+  .van-picker__frame {
+    background: rgba(42, 190, 209, 0.05);
+    &::after, &::before {
+      border: none;
+    }
+  }
+  .van-picker-column__item {
+    font-size: 18px;
+  }
+  .van-picker-column__item--selected {
+    color: #2ABED1;
+    font-size: 20px;
+  }
+}
+
+// // cell右侧group样式
+// .van-cell-group {
+//   .van-cell {
+//     .van-cell__value {
+//       font-size: 14px;
+//     }
+//     .van-icon.van-icon-arrow {
+//       color: #C0C4CC;
+//     }
+//   }
+// }
+
+// 重置icon样式
+
+// 重置checkbox disabled样式
+.van-checkbox__icon .van-icon {
+  // border: none;
+}
+
+.van-checkbox__icon--disabled.van-checkbox__icon--checked {
+  .van-icon {
+    color: #fff;
+    background-color: #e5e5e5;
+    border-color: transparent;
+  }
+}

+ 22 - 0
src/utils/globalDirectives.ts

@@ -0,0 +1,22 @@
+import Vue from 'vue'
+
+// Vue自定义指令文档:https://cn.vuejs.org/v2/guide/custom-directive.html
+Vue.directive('auto-focus', {
+  // 当被绑定的元素插入到 DOM 中时……
+  inserted: function (el) {
+    const autoFocusArr = ['input', 'textarea']
+    const tagName = el.nodeName.toLowerCase()
+    const isTag = autoFocusArr.find((item) => {
+      return item === tagName
+    })
+
+    setTimeout(() => {
+      if (isTag) {
+        el.focus()
+      } else {
+        const dom: any = el.querySelector('.van-field__control')
+        dom.focus()
+      }
+    }, 30)
+  }
+})

+ 40 - 0
src/utils/globalFilters.ts

@@ -0,0 +1,40 @@
+import Vue from 'vue'
+import {
+  dateFormatter,
+  dateFromNow,
+  addSpaceForTel,
+  upPrice,
+  formatPrice
+} from './globalFunctions'
+
+// 注册全局过滤器
+// 分转元
+Vue.filter('fen2Yuan', (v: number) => v / 100)
+
+// 时间格式化(同时间格式化函数)
+Vue.filter('dateFormatter', dateFormatter)
+// 时间戳转换 多少秒、多少分、多少小时前、多少天前  超出10天显示年月日
+Vue.filter('dateFromNow', dateFromNow)
+// 手机号加空格
+Vue.filter('addSpaceForTel', addSpaceForTel)
+
+// 金额大写
+Vue.filter('upPrice', upPrice)
+// 金额3位逗号分隔
+Vue.filter('formatPrice', formatPrice)
+
+// Vue.filter('addSpaceForBank', function (v) {
+//   // 纯数字银行卡号字符串加空格
+//   // return v.replace(/\s/g, '').replace(/(\d{4})(?=\d)/g, '$1 ')
+//   // 带有*的银行卡号字符串加空格
+//   return v.replace(/\s/g, '').replace(/(.{4})/g, '$1 ')
+// })
+
+// Vue.filter('addConfusionForBank', function (v) {
+//   // 带有*的银行卡号字符串加空格
+//   if (String(v).length < 12) {
+//     return v.replace(/\s/g, '').replace(/^(\d{2})\d+(\d{2})$/, '$1 **** **** $2')
+//   } else {
+//     return v.replace(/\s/g, '').replace(/^(\d{4})\d+(\d{4})$/, '$1 **** **** $2')
+//   }
+// })

+ 253 - 0
src/utils/globalFunctions.ts

@@ -0,0 +1,253 @@
+import moment from 'moment'
+
+// 是否是微信浏览器 ------------>
+export function isWeiXinBrowser () {
+  return navigator.userAgent.toLowerCase().indexOf('micromessenger') !== -1
+}
+
+// 在安卓或者ios中
+export function androidOrIOS () {
+  const u = navigator.userAgent.toLowerCase()
+  let agent = ''
+  if (/iphone|ipod|ipad|ios/.test(u)) {
+    agent = 'ios'
+  } else {
+    agent = 'android'
+  }
+  return agent
+}
+
+// 字符串处理相关函数
+// 手机号中间4位加* ------------>
+export function addConfusionForTel (tel: string) {
+  const reg = /^(\d{3})\d{4}(\d{4})$/
+  return tel.replace(reg, '$1****$2')
+}
+// 手机号加空格 ------------>
+export function addSpaceForTel (tel: string) {
+  const regMap = {
+    isConfuse: /^(\d{3})\*{4}(\d{4})$/,
+    addSpace: /^(\d{3})(\d{4})(\d{4})$/
+  }
+  const confusion = regMap.isConfuse.test(tel)
+  if (confusion) {
+    return tel.replace(regMap.isConfuse, '$1 **** $2')
+  } else {
+    return tel.replace(regMap.addSpace, '$1 $2 $3')
+  }
+}
+
+// 金额大写,链接:https://juejin.im/post/5a2a7a5051882535cd4abfce
+// upDigit(1682) result:"人民币壹仟陆佰捌拾贰元整"
+// upDigit(-1693) result:"欠壹仟陆佰玖拾叁元整"
+export function upPrice (n: number) {
+  const fraction = ['角', '分', '厘']
+  const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
+  const unit = [
+    ['元', '万', '亿'],
+    ['', '拾', '佰', '仟']
+  ]
+  // const head = n < 0 ? '欠人民币' : '人民币'
+  const head = ''
+  n = Math.abs(n)
+  let s = ''
+  for (let i = 0; i < fraction.length; i++) {
+    s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '')
+  }
+  s = s || '整'
+  n = Math.floor(n)
+  for (let i = 0; i < unit[0].length && n > 0; i++) {
+    let p = ''
+    for (let j = 0; j < unit[1].length && n > 0; j++) {
+      p = digit[n % 10] + unit[1][j] + p
+      n = Math.floor(n / 10)
+    }
+    s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s
+    // s = p + unit[0][i] + s;
+  }
+  return head + s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整')
+}
+
+// 金额3位逗号分隔  ------------>
+export function formatPrice (s: string | number, n = -1, comma = false) {
+  if (n !== -1) n = n > 0 && n <= 20 ? n : 2
+  const intS = parseInt(String(s))
+  let point = '.'
+  let left = []
+  let right = ''
+  s = parseFloat((s + '').replace(/[^\d.-]/g, ''))
+  // 没传n或者n为-1,默认(如果为整数,则不保留小数。如果为浮点数,则保留两位小数)
+  if (n === -1) {
+    if (s === intS) {
+      n = 0
+      right = ''
+      point = ''
+    } else {
+      n = 2
+      s = s.toFixed(n)
+      right = s.split('.')[1]
+    }
+    s = s + ''
+    left = s.split('.')[0].split('').reverse()
+  } else {
+    s = parseFloat((s + '').replace(/[^\d.-]/g, '')).toFixed(n) + ''
+    left = s.split('.')[0].split('').reverse()
+    right = s.split('.')[1]
+  }
+
+  if (comma) {
+    let t = ''
+    for (let i = 0; i < left.length; i++) {
+      t += left[i] + ((i + 1) % 3 === 0 && (i + 1) !== left.length ? ',' : '')
+    }
+    return t.split('').reverse().join('') + point + right
+  }
+
+  return left.reverse().join('') + point + right
+}
+
+// 时间格式化相关函数
+/*
+* 时间格式化函数(将时间格式化为,2019年08月12日,2019-08-12,2019/08/12的形式)
+*   pattern参数(想要什么格式的数据就传入什么格式的数据)
+*     · 'yyyy-MM-dd'  ---> 输出如2019-09-20
+*     · 'yyyy-MM-dd HH:mm'  --->  输出如2019-09-20 18:20
+*     · 'yyyy-MM-dd HH:mm:ss'  --->  输出如2019-09-20 06:20:23
+*     · 'yyyy/MM/dd'  --->  输出如2019/09/20
+*     · 'yyyy年MM月dd日'  --->  输出如2019年09月20日
+*     · 'yyyy年MM月dd日 HH时mm分'  --->  输出如2019年09月20日 18时20分
+*     · 'yyyy年MM月dd日 HH时mm分ss秒'  --->  输出如2019年09月20日 18时20分23秒
+*     · 'yyyy年MM月dd日 HH时mm分ss秒 EE'  --->  输出如2019年09月20日 18时20分23秒 周二
+*     · 'yyyy年MM月dd日 HH时mm分ss秒 EEE'  --->  输出如2019年09月20日 18时20分23秒 星期二
+*  参考: https://www.cnblogs.com/mr-wuxiansheng/p/6296646.html
+*/
+export function dateFormatter (date: any, fmt = 'yyyy-MM-dd HH:mm:ss') {
+  // 将传入的date转为时间对象
+  if (!date) return ''
+  date = new Date(moment(date).valueOf())
+  const o: any = {
+    'y+': date.getFullYear(),
+    'M+': date.getMonth() + 1, // 月份
+    'd+': date.getDate(), // 日
+    // 12小时制
+    'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, // 小时
+    // 24小时制
+    'H+': date.getHours(), // 小时
+    'm+': date.getMinutes(), // 分
+    's+': date.getSeconds(), // 秒
+    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+    S: date.getMilliseconds(), // 毫秒
+    'E+': date.getDay() // 周
+  }
+  const week = ['日', '一', '二', '三', '四', '五', '六']
+
+  if (/(y+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
+  }
+  if (/(E+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '星期' : '周') : '') + week[date.getDay()])
+  }
+  for (const k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) {
+      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
+    }
+  }
+  return fmt
+}
+
+// 时间戳转换 多少秒、多少分、多少小时前、多少天前  超出10天显示年月日
+// 传入一个时间戳
+export function dateFromNow (timestamp: number) {
+  const date1 = new Date(timestamp) // 开始时间
+  const date2 = new Date() // 结束时间
+  const date3 = date2.getTime() - date1.getTime() // 时间差的毫秒数
+  // 计算出相差天数
+  const days = Math.floor(date3 / (24 * 3600 * 1000))
+  // 计算出小时数
+  const leave1 = date3 % (24 * 3600 * 1000) // 计算天数后剩余的毫秒数
+  const hours = Math.floor(leave1 / (3600 * 1000))
+  // 计算相差分钟数
+  const leave2 = leave1 % (3600 * 1000) // 计算小时数后剩余的毫秒数
+  const minutes = Math.floor(leave2 / (60 * 1000))
+  // 计算相差秒数
+  let td = '30秒前'
+  if (days > 0) {
+    if (days > 10) {
+      const date1year = date1.getFullYear()
+      const date2year = date2.getFullYear()
+      let date1month: any = date1.getMonth() + 1
+      let date1day: any = date1.getDate()
+      if (date1month < 10) {
+        date1month = '0' + date1month
+      }
+      if (date1day < 10) {
+        date1day = '0' + date1day
+      }
+      if (date1year < date2year) {
+        td = date1.getFullYear() + '-' + date1month + '-' + date1day
+      } else {
+        td = date1month + '-' + date1day
+      }
+    } else {
+      td = days + '天前'
+    }
+  } else if (hours > 0) {
+    td = hours + '小时前'
+  } else if (minutes > 0) {
+    td = minutes + '分钟前'
+  }
+  return td
+}
+
+// DOM操作相关函数
+// 键盘抬起底部按钮隐藏
+// 传入输入框的DOM对象 和 footer的DOM对象
+export function inputFocusHideFooter (inputs: Array<any>, footer: any) {
+  const isShowBtn = function (f: boolean) {
+    if (f) {
+      footer.style.display = ''
+    } else {
+      footer.style.display = 'none'
+    }
+  }
+
+  const ua = window.navigator.userAgent.toLocaleLowerCase()
+  const isIOS = /iphone|ipad|ipod/.test(ua)
+  const isAndroid = /android/.test(ua)
+
+  // 监听输入框的软键盘弹起和收起事件
+  if (isIOS) {
+    inputs.forEach((item) => {
+      item.addEventListener('focus', function () {
+        console.log('IOS 键盘弹出')
+        // IOS 键盘弹起后操作
+        isShowBtn(false)
+      }, false)
+
+      // IOS 键盘收起:IOS 点击输入框以外区域或点击收起按钮,输入框都会失去焦点,键盘会收起,
+      item.addEventListener('blur', () => {
+        console.log('IOS 键盘收起')
+        // IOS 键盘收起后操作
+        isShowBtn(true)
+      })
+    })
+  }
+
+  // Andriod 键盘收起:Andriod 键盘弹起或收起页面高度会发生变化,以此为依据获知键盘收起
+  if (isAndroid) {
+    const clientHeight = document.documentElement.clientHeight || document.body.clientHeight
+
+    window.addEventListener('resize', function () {
+      const nowClientHeight = document.documentElement.clientHeight || document.body.clientHeight
+      if (clientHeight > nowClientHeight) {
+        // 键盘弹出的事件处理
+        console.log('Android 键盘弹出')
+        isShowBtn(false)
+      } else {
+        // 键盘收起的事件处理
+        isShowBtn(true)
+        console.log('Android 键盘收起')
+      }
+    }, false)
+  }
+}

+ 19 - 0
src/utils/globalFunctionsForApp.ts

@@ -0,0 +1,19 @@
+import {
+  isWeiXinBrowser
+} from './globalFunctions'
+declare const JyObj: any
+
+const inWeiXinBrowser = isWeiXinBrowser()
+
+// 是否是微信浏览器
+// 参数0隐藏底部导航,参数1,显示底部导航
+export function hiddenBottomBar (params = 0) {
+  // 如果是app端,隐藏底部导航
+  if (!inWeiXinBrowser) {
+    try {
+      JyObj.hiddenBottom(params + '')
+    } catch (error) {
+      console.log(error)
+    }
+  }
+}

+ 17 - 0
src/utils/index.ts

@@ -0,0 +1,17 @@
+import Vue from 'vue'
+import './globalDirectives'
+import './globalFilters'
+import { isWeiXinBrowser } from '@/utils/globalFunctions'
+
+declare module 'vue/types/vue' {
+  interface Vue {
+    $env: any;
+  }
+}
+
+const inWeiXinBrowser = isWeiXinBrowser()
+
+Vue.prototype.$env = {
+  isWeiXinBrowser: inWeiXinBrowser,
+  platform: inWeiXinBrowser ? 'wx' : 'app'
+}

+ 35 - 0
src/views/404.vue

@@ -0,0 +1,35 @@
+<template>
+  <div class="page-404">
+    <div class="j-main">
+      <empty>这里什么也没有</empty>
+    </div>
+    <div class="j-button-group j-footer">
+      <router-link tag="button" class="j-button-confirm" replace to="/" >返回首页</router-link>
+    </div>
+  </div>
+</template>
+<script>
+import { Component, Vue } from 'vue-property-decorator'
+import empty from '@/components/common/Empty.vue'
+
+@Component({
+  name: 'page404',
+  components: {
+    empty
+  }
+})
+
+export default class Page404 extends Vue {
+  created () {
+    const route = this.$route
+    const redirectedFrom = route.redirectedFrom
+    if (redirectedFrom) {
+      console.log('路由:%c %s %c未找到', 'color: red', redirectedFrom, 'color: #000')
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+  .page-404 {}
+</style>

+ 196 - 0
src/views/Demo.vue

@@ -0,0 +1,196 @@
+<template>
+  <div class="j-test">
+    <!-- 项目内部页面导航 -->
+    <van-cell-group v-for="(conf, index) in confPageArr" :key="index" :title="conf.title">
+      <van-cell
+        v-for="(item, iindex) in conf.urls"
+        :key="iindex"
+        :title="item.meta ? item.meta.title : item.path"
+        :to="item.path"
+        :label="item.name"
+        is-link
+       />
+    </van-cell-group>
+
+    <!-- 测试环境跳转开发环境导航 -->
+    <van-cell-group v-for="(conf, index) in confTestArr" :key="index" :title="conf.title + ' - ' + conf.subTitle">
+      <van-cell
+        v-for="(item, iindex) in conf.urls"
+        :key="iindex"
+        :title="conf.subTitle + ' -> ' + item.path"
+        :url="conf.baseUrl + item.path + querySESSIONID"
+        value=""
+        is-link
+       />
+    </van-cell-group>
+
+    <!-- SESSIONID -->
+    <van-cell-group :title="'SESSIONID  (' + SIDTITLE + ')'">
+      <van-cell :value="SESSIONID ? SESSIONID : '无SESSIONID'" />
+    </van-cell-group>
+
+    <div class="loader-container">
+      <div class="loader">
+        <div class="arc"></div>
+        <div class="arc"></div>
+        <div class="arc"></div>
+      </div>
+      <div class="text">loading...</div>
+    </div>
+
+  </div>
+</template>
+
+<script lang="ts">
+import { Component, Vue } from 'vue-property-decorator'
+import { Cell, CellGroup } from 'vant'
+import Cookies from 'js-cookie'
+
+@Component({
+  name: 'test',
+  components: {
+    [Cell.name]: Cell,
+    [CellGroup.name]: CellGroup
+  }
+})
+export default class Test extends Vue {
+  // 项目内部页面导航
+  confPageArr = [
+    {
+      title: '路由导航',
+      baseUrl: '',
+      subTitle: '',
+      urls: (this.$router as any).options.routes
+    }
+  ]
+
+  // 测试环境跳转开发环境导航
+  confTestArr = [
+    {
+      title: '开发环境',
+      baseUrl: 'http://192.168.20.216:8080/datareport/page',
+      subTitle: '20.216',
+      urls: [
+        {
+          path: '/home'
+        },
+        {
+          path: '/test'
+        }
+      ]
+    }
+  ]
+
+  SESSIONID: any = ''
+  SIDTITLE = ''
+
+  get querySESSIONID () {
+    return `?SESSIONID=${this.SESSIONID}`
+  }
+
+  mounted () {
+    this.getAndSetSessionId()
+  }
+
+  // 获取或者设置SESSIONID
+  getAndSetSessionId () {
+    // 从地址栏获取参数
+    const sId = this.$route.query.SESSIONID
+    if (sId) {
+      // 设置sessionId到浏览器
+      this.SESSIONID = sId
+      this.SIDTITLE = '设置SESSIONID'
+      Cookies.set('SESSIONID', sId, { expires: 7, path: '/' })
+    } else {
+      // 获取sessionId到浏览器
+      this.SIDTITLE = '获取SESSIONID'
+      this.SESSIONID = Cookies.get('SESSIONID')
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.j-test {
+  .loader-container {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    width: 100%;
+    height: 150px;
+  }
+
+  .loader {
+    position: relative;
+    transform-style: preserve-3d;
+    perspective: 800;
+
+    display: flex;
+    flex-wrap: wrap;
+    width: 36px;
+    height: 36px;
+
+    --primary-color: #4ec0e9;
+
+    .arc {
+      position: absolute;
+      content: "";
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      border-radius: 50%;
+      border-bottom: 3px solid var(--primary-color);
+
+      @for $i from 1 through 3 {
+        &:nth-child(#{$i}) {
+          animation: rotate#{$i} 1.15s linear infinite;
+        }
+      }
+
+      &:nth-child(1) {
+        animation-delay: -0.8s;
+      }
+
+      &:nth-child(2) {
+        animation-delay: -0.4s;
+      }
+
+      &:nth-child(3) {
+        animation-delay: 0s;
+      }
+    }
+  }
+
+  @keyframes rotate1 {
+    from {
+      transform: rotateX(35deg) rotateY(-45deg) rotateZ(0);
+    }
+
+    to {
+      transform: rotateX(35deg) rotateY(-45deg) rotateZ(1turn);
+    }
+  }
+
+  @keyframes rotate2 {
+    from {
+      transform: rotateX(50deg) rotateY(10deg) rotateZ(0);
+    }
+
+    to {
+      transform: rotateX(50deg) rotateY(10deg) rotateZ(1turn);
+    }
+  }
+
+  @keyframes rotate3 {
+    from {
+      transform: rotateX(35deg) rotateY(55deg) rotateZ(0);
+    }
+
+    to {
+      transform: rotateX(35deg) rotateY(55deg) rotateZ(1turn);
+    }
+  }
+}
+</style>

+ 1 - 1
src/views/About.vue → src/views/Help.vue

@@ -1,5 +1,5 @@
 <template>
 <template>
   <div class="about">
   <div class="about">
-    <h1>This is an about page</h1>
+    <h1>This is an home page</h1>
   </div>
   </div>
 </template>
 </template>

+ 2 - 15
src/views/Home.vue

@@ -1,18 +1,5 @@
 <template>
 <template>
-  <div class="home">
-    <img alt="Vue logo" src="../assets/logo.png">
-    <HelloWorld msg="Welcome to Your Vue.js App"/>
+  <div class="about">
+    <h1>This is an home page</h1>
   </div>
   </div>
 </template>
 </template>
-
-<script>
-// @ is an alias to /src
-import HelloWorld from '@/components/HelloWorld.vue'
-
-export default {
-  name: 'Home',
-  components: {
-    HelloWorld
-  }
-}
-</script>

+ 5 - 0
src/views/Logout.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/Readme.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/Result.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/auth/Enterprise.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/auth/Everyman.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/record/Details.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/record/List.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/share/Details.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/share/List.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/share/Share.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/withdrawal/Enterprise.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 5 - 0
src/views/withdrawal/Everyman.vue

@@ -0,0 +1,5 @@
+<template>
+  <div class="about">
+    <h1>This is an home page</h1>
+  </div>
+</template>

+ 19 - 0
src/vue_bus.ts

@@ -0,0 +1,19 @@
+import _Vue from 'vue'
+
+declare module 'vue/types/vue' {
+  interface Vue {
+    $bus: any;
+  }
+}
+class VueBus {
+  static install (Vue: any) {
+    const bus = new Vue()
+    Vue.bus = bus
+    Vue.prototype.$bus = bus
+  }
+}
+// eslint-disable-next-line
+if ('Vue' in window) {
+  _Vue.use(VueBus)
+}
+export default VueBus

+ 179 - 0
vue.config.js

@@ -0,0 +1,179 @@
+const merge = require('webpack-merge')
+const tsImportPluginFactory = require('ts-import-plugin')
+const autoprefixer = require('autoprefixer')
+// const pxtorem = require('postcss-pxtorem')
+const pxtoviewport = require('postcss-px-to-viewport')
+
+const externals = {
+  vue: 'Vue',
+  'vue-router': 'VueRouter',
+  vuex: 'Vuex',
+  axios: 'axios',
+  'js-cookie': 'Cookies',
+  vant: 'vant',
+  moment: 'moment'
+}
+
+// cdn地址获取访问(国外): https://www.jsdelivr.com/
+// cdn地址获取访问(国内): https://www.bootcdn.cn/
+
+const cdn = {
+  css: [
+    // '//unpkg.com/element-ui@2.10.1/lib/theme-chalk/index.css'
+  ],
+  js: [
+    '//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
+    '//cdn.jsdelivr.net/npm/vue-router@3.1.5/dist/vue-router.min.js',
+    '//cdn.jsdelivr.net/npm/vuex@3.4.0/dist/vuex.min.js',
+    '//cdn.jsdelivr.net/npm/vant@2.8.2/lib/vant.min.js',
+    '//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
+    '//cdn.jsdelivr.net/npm/js-cookie@2.2.1/src/js.cookie.min.js',
+    '//cdn.jsdelivr.net/npm/moment@2.26.0/moment.min.js',
+    '//res.wx.qq.com/open/js/jweixin-1.6.0.js'
+  ]
+}
+
+const s_version = process.env.npm_package_version.replace(/\./g, '')
+
+module.exports = {
+  publicPath: process.env.BASE_URL,
+  parallel: false,
+  productionSourceMap: false,
+  outputDir: 'datareport',
+  devServer: {
+    port: '8080',
+    open: false,
+    disableHostCheck: true,
+    proxy: {
+      '^/dev/api': {
+        target: 'http://web2-jytest.jianyu360.cn',
+        changeOrigin: true,
+        logLevel: 'debug',
+        pathRewrite: {
+          '^/datareport/api': '/datareport/api'
+        }
+      },
+      '^/jypay': {
+        target: 'http://web2-jytest.jianyu360.cn',
+        changeOrigin: true,
+        logLevel: 'debug',
+        pathRewrite: {
+          '^/jypay': '/jypay'
+        }
+      },
+      '^/subscribepay': {
+        target: 'http://web2-jytest.jianyu360.cn',
+        changeOrigin: true,
+        logLevel: 'debug',
+        pathRewrite: {
+          '^/subscribepay': '/subscribepay'
+        }
+      }
+    }
+  },
+  css: {
+    // extract: {
+    //   // filename: `css/[name].css?v=${s_version}`,
+    //   // chunkFilename: `css/[name].css?v=${s_version}`
+    //   filename: 'css/[name].[contenthash:10].css?v=[contenthash:8]',
+    //   chunkFilename: 'css/[name].[contenthash:10].css?v=[contenthash:8]'
+    // },
+    loaderOptions: {
+      sass: {
+        prependData: '@import "@/style/_mixin.scss";@import "@/style/_variables.scss";' // 全局引入
+      },
+      postcss: {
+        plugins: [
+          autoprefixer(),
+          pxtoviewport(({
+            unitToConvert: 'px',
+            viewportWidth: 375,
+            unitPrecision: 5,
+            propList: [
+              '*'
+            ],
+            viewportUnit: 'vw',
+            fontViewportUnit: 'vw',
+            selectorBlackList: [],
+            minPixelValue: 1,
+            mediaQuery: false,
+            replace: true,
+            exclude: /(\/|\\)(node_modules)(\/|\\)/
+          }))
+          // pxtorem({
+          //   rootValue: 37.5,
+          //   propList: ['*']
+          // })
+        ]
+      }
+    }
+  },
+  chainWebpack: config => {
+    // 防止多页面打包卡顿
+    // eslint-disable-next-line no-unused-expressions
+    // config.plugins.delete('named-chunks')
+
+    if (process.env.NODE_ENV === 'production') {
+      config.output
+        .chunkFilename('js/[name].[contenthash:10].js?v=[contenthash:8]')
+        .filename('js/[name].[contenthash:10].js?v=[contenthash:8]')
+
+      // 打包时需要执行,开发环境运行不需要执行
+      config.externals(externals)
+
+      // 如果使用多页面打包,使用vue inspect --plugins查看html是否在结果数组中
+      config.plugin('html').tap(args => {
+        // https://github.com/DanielRuf/html-minifier-terser#options-quick-reference
+        // 禁止html压缩去空行
+        // args[0].minify.collapseWhitespace = false
+
+        // html中添加cdn
+        args[0].cdn = cdn
+        args[0].version = s_version
+        return args
+      })
+    } else {
+      config.plugin('html').tap(args => {
+        // html中添加wxjsssk的cdn
+        args[0].cdn = {
+          js: [
+            cdn.js[cdn.js.length - 1]
+          ]
+        }
+        return args
+      })
+    }
+
+    // 分析静态资源
+    if (process.env.use_analyzer) {
+      config
+        .plugin('webpack-bundle-analyzer')
+        .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
+    }
+
+    // 修复HMR
+    config.resolve.symlinks(true)
+
+    config.module
+      .rule('ts')
+      .use('ts-loader')
+      .tap(options => {
+        options = merge(options, {
+          transpileOnly: true,
+          getCustomTransformers: () => ({
+            before: [
+              tsImportPluginFactory({
+                libraryName: 'vant',
+                libraryDirectory: 'es',
+                style: true
+              })
+            ]
+          }),
+          compilerOptions: {
+            module: 'es2015'
+          }
+        })
+        return options
+      })
+  }
+}

+ 81 - 5
yarn.lock

@@ -780,7 +780,7 @@
     "@babel/types" "^7.4.4"
     "@babel/types" "^7.4.4"
     esutils "^2.0.2"
     esutils "^2.0.2"
 
 
-"@babel/runtime@^7.8.4", "@babel/runtime@^7.9.6":
+"@babel/runtime@7.x", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.6":
   version "7.10.5"
   version "7.10.5"
   resolved "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.10.5.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c"
   resolved "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.10.5.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c"
   integrity sha1-MD2L1EDs1aSR6uYRf9M2dphnTFw=
   integrity sha1-MD2L1EDs1aSR6uYRf9M2dphnTFw=
@@ -906,6 +906,11 @@
     "@types/minimatch" "*"
     "@types/minimatch" "*"
     "@types/node" "*"
     "@types/node" "*"
 
 
+"@types/js-cookie@^2.2.6":
+  version "2.2.6"
+  resolved "https://registry.npm.taobao.org/@types/js-cookie/download/@types/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f"
+  integrity sha1-8aHLNa/0e8XPsFywxEHKkekUwm8=
+
 "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4":
 "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4":
   version "7.0.5"
   version "7.0.5"
   resolved "https://registry.npm.taobao.org/@types/json-schema/download/@types/json-schema-7.0.5.tgz?cache=0&sync_timestamp=1591720749429&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fjson-schema%2Fdownload%2F%40types%2Fjson-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
   resolved "https://registry.npm.taobao.org/@types/json-schema/download/@types/json-schema-7.0.5.tgz?cache=0&sync_timestamp=1591720749429&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fjson-schema%2Fdownload%2F%40types%2Fjson-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
@@ -936,6 +941,11 @@
   resolved "https://registry.npm.taobao.org/@types/q/download/@types/q-1.5.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fq%2Fdownload%2F%40types%2Fq-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
   resolved "https://registry.npm.taobao.org/@types/q/download/@types/q-1.5.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fq%2Fdownload%2F%40types%2Fq-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
   integrity sha1-FZJUFOCtLNdlv+9YhC9+JqesyyQ=
   integrity sha1-FZJUFOCtLNdlv+9YhC9+JqesyyQ=
 
 
+"@types/qs@^6.9.4":
+  version "6.9.4"
+  resolved "https://registry.npm.taobao.org/@types/qs/download/@types/qs-6.9.4.tgz?cache=0&sync_timestamp=1595669457213&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fqs%2Fdownload%2F%40types%2Fqs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a"
+  integrity sha1-pZ6FHBuhbAUT6hI4MN1jmgoVy2o=
+
 "@types/webpack-env@^1.15.2":
 "@types/webpack-env@^1.15.2":
   version "1.15.2"
   version "1.15.2"
   resolved "https://registry.npm.taobao.org/@types/webpack-env/download/@types/webpack-env-1.15.2.tgz#927997342bb9f4a5185a86e6579a0a18afc33b0a"
   resolved "https://registry.npm.taobao.org/@types/webpack-env/download/@types/webpack-env-1.15.2.tgz#927997342bb9f4a5185a86e6579a0a18afc33b0a"
@@ -984,6 +994,11 @@
     semver "^7.3.2"
     semver "^7.3.2"
     tsutils "^3.17.1"
     tsutils "^3.17.1"
 
 
+"@vant/icons@1.2.3":
+  version "1.2.3"
+  resolved "https://registry.npm.taobao.org/@vant/icons/download/@vant/icons-1.2.3.tgz?cache=0&sync_timestamp=1595992774370&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40vant%2Ficons%2Fdownload%2F%40vant%2Ficons-1.2.3.tgz#c46b7ddd32363f790944c3af974b73bda80df7a1"
+  integrity sha1-xGt93TI2P3kJRMOvl0tzvagN96E=
+
 "@vue/babel-helper-vue-jsx-merge-props@^1.0.0":
 "@vue/babel-helper-vue-jsx-merge-props@^1.0.0":
   version "1.0.0"
   version "1.0.0"
   resolved "https://registry.npm.taobao.org/@vue/babel-helper-vue-jsx-merge-props/download/@vue/babel-helper-vue-jsx-merge-props-1.0.0.tgz#048fe579958da408fb7a8b2a3ec050b50a661040"
   resolved "https://registry.npm.taobao.org/@vue/babel-helper-vue-jsx-merge-props/download/@vue/babel-helper-vue-jsx-merge-props-1.0.0.tgz#048fe579958da408fb7a8b2a3ec050b50a661040"
@@ -1691,7 +1706,7 @@ atob@^2.1.2:
   resolved "https://registry.npm.taobao.org/atob/download/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
   resolved "https://registry.npm.taobao.org/atob/download/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
   integrity sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=
   integrity sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=
 
 
-autoprefixer@^9.8.0:
+autoprefixer@^9.8.0, autoprefixer@^9.8.5:
   version "9.8.5"
   version "9.8.5"
   resolved "https://registry.npm.taobao.org/autoprefixer/download/autoprefixer-9.8.5.tgz?cache=0&sync_timestamp=1594444669685&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fautoprefixer%2Fdownload%2Fautoprefixer-9.8.5.tgz#2c225de229ddafe1d1424c02791d0c3e10ccccaa"
   resolved "https://registry.npm.taobao.org/autoprefixer/download/autoprefixer-9.8.5.tgz?cache=0&sync_timestamp=1594444669685&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fautoprefixer%2Fdownload%2Fautoprefixer-9.8.5.tgz#2c225de229ddafe1d1424c02791d0c3e10ccccaa"
   integrity sha1-LCJd4indr+HRQkwCeR0MPhDMzKo=
   integrity sha1-LCJd4indr+HRQkwCeR0MPhDMzKo=
@@ -1714,6 +1729,13 @@ aws4@^1.8.0:
   resolved "https://registry.npm.taobao.org/aws4/download/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
   resolved "https://registry.npm.taobao.org/aws4/download/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
   integrity sha1-oXs6jqgRBg501H0wYSJACtRJeuI=
   integrity sha1-oXs6jqgRBg501H0wYSJACtRJeuI=
 
 
+axios@^0.19.2:
+  version "0.19.2"
+  resolved "https://registry.npm.taobao.org/axios/download/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
+  integrity sha1-PqNsXYgY0NX4qKl6bTa4bNwAyyc=
+  dependencies:
+    follow-redirects "1.5.10"
+
 babel-code-frame@^6.22.0:
 babel-code-frame@^6.22.0:
   version "6.26.0"
   version "6.26.0"
   resolved "https://registry.npm.taobao.org/babel-code-frame/download/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
   resolved "https://registry.npm.taobao.org/babel-code-frame/download/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@@ -2872,6 +2894,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
   dependencies:
   dependencies:
     ms "2.0.0"
     ms "2.0.0"
 
 
+debug@=3.1.0:
+  version "3.1.0"
+  resolved "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+  integrity sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=
+  dependencies:
+    ms "2.0.0"
+
 debug@^3.1.1, debug@^3.2.5:
 debug@^3.1.1, debug@^3.2.5:
   version "3.2.6"
   version "3.2.6"
   resolved "https://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
   resolved "https://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -3912,6 +3941,13 @@ flush-write-stream@^1.0.0:
     inherits "^2.0.3"
     inherits "^2.0.3"
     readable-stream "^2.3.6"
     readable-stream "^2.3.6"
 
 
+follow-redirects@1.5.10:
+  version "1.5.10"
+  resolved "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.5.10.tgz?cache=0&sync_timestamp=1592518405030&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
+  integrity sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=
+  dependencies:
+    debug "=3.1.0"
+
 follow-redirects@^1.0.0:
 follow-redirects@^1.0.0:
   version "1.12.1"
   version "1.12.1"
   resolved "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.12.1.tgz?cache=0&sync_timestamp=1592518405030&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.12.1.tgz#de54a6205311b93d60398ebc01cf7015682312b6"
   resolved "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.12.1.tgz?cache=0&sync_timestamp=1592518405030&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.12.1.tgz#de54a6205311b93d60398ebc01cf7015682312b6"
@@ -4951,6 +4987,11 @@ jest-worker@^25.4.0:
     merge-stream "^2.0.0"
     merge-stream "^2.0.0"
     supports-color "^7.0.0"
     supports-color "^7.0.0"
 
 
+js-cookie@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.npm.taobao.org/js-cookie/download/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
+  integrity sha1-aeEG3F1YBolFYpAqpbrsN0Tpsrg=
+
 js-message@1.0.5:
 js-message@1.0.5:
   version "1.0.5"
   version "1.0.5"
   resolved "https://registry.npm.taobao.org/js-message/download/js-message-1.0.5.tgz#2300d24b1af08e89dd095bc1a4c9c9cfcb892d15"
   resolved "https://registry.npm.taobao.org/js-message/download/js-message-1.0.5.tgz#2300d24b1af08e89dd095bc1a4c9c9cfcb892d15"
@@ -5532,6 +5573,11 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
   dependencies:
   dependencies:
     minimist "^1.2.5"
     minimist "^1.2.5"
 
 
+moment@^2.24.0:
+  version "2.27.0"
+  resolved "https://registry.npm.taobao.org/moment/download/moment-2.27.0.tgz?cache=0&sync_timestamp=1592516115109&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmoment%2Fdownload%2Fmoment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
+  integrity sha1-i/9OPiaiNiIN/j423nVrbrqgEF0=
+
 move-concurrently@^1.0.1:
 move-concurrently@^1.0.1:
   version "1.0.1"
   version "1.0.1"
   resolved "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
   resolved "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@@ -5761,7 +5807,7 @@ oauth-sign@~0.9.0:
   resolved "https://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
   resolved "https://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
   integrity sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=
   integrity sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=
 
 
-object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+object-assign@>=4.0.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
   version "4.1.1"
   version "4.1.1"
   resolved "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
   resolved "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
   integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
   integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@@ -6526,6 +6572,14 @@ postcss-ordered-values@^4.1.2:
     postcss "^7.0.0"
     postcss "^7.0.0"
     postcss-value-parser "^3.0.0"
     postcss-value-parser "^3.0.0"
 
 
+postcss-px-to-viewport@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npm.taobao.org/postcss-px-to-viewport/download/postcss-px-to-viewport-1.1.1.tgz#a25ca410b553c9892cc8b525cc710da47bf1aa55"
+  integrity sha1-olykELVTyYksyLUlzHENpHvxqlU=
+  dependencies:
+    object-assign ">=4.0.1"
+    postcss ">=5.0.2"
+
 postcss-reduce-initial@^4.0.3:
 postcss-reduce-initial@^4.0.3:
   version "4.0.3"
   version "4.0.3"
   resolved "https://registry.npm.taobao.org/postcss-reduce-initial/download/postcss-reduce-initial-4.0.3.tgz?cache=0&sync_timestamp=1595813583667&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-reduce-initial%2Fdownload%2Fpostcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df"
   resolved "https://registry.npm.taobao.org/postcss-reduce-initial/download/postcss-reduce-initial-4.0.3.tgz?cache=0&sync_timestamp=1595813583667&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-reduce-initial%2Fdownload%2Fpostcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df"
@@ -6593,7 +6647,7 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0:
   resolved "https://registry.npm.taobao.org/postcss-value-parser/download/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
   resolved "https://registry.npm.taobao.org/postcss-value-parser/download/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
   integrity sha1-RD9qIM7WSBor2k+oUypuVdeJoss=
   integrity sha1-RD9qIM7WSBor2k+oUypuVdeJoss=
 
 
-postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
+postcss@>=5.0.2, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6:
   version "7.0.32"
   version "7.0.32"
   resolved "https://registry.npm.taobao.org/postcss/download/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d"
   resolved "https://registry.npm.taobao.org/postcss/download/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d"
   integrity sha1-QxDW7jRwU9o0M9sr5JKIPWLOxZ0=
   integrity sha1-QxDW7jRwU9o0M9sr5JKIPWLOxZ0=
@@ -7964,6 +8018,13 @@ tryer@^1.0.1:
   resolved "https://registry.npm.taobao.org/tryer/download/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
   resolved "https://registry.npm.taobao.org/tryer/download/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
   integrity sha1-8shUBoALmw90yfdGW4HqrSQSUvg=
   integrity sha1-8shUBoALmw90yfdGW4HqrSQSUvg=
 
 
+ts-import-plugin@^1.6.6:
+  version "1.6.6"
+  resolved "https://registry.npm.taobao.org/ts-import-plugin/download/ts-import-plugin-1.6.6.tgz#d348f0e16bca7ffef74cc582cee10ad0e73204bf"
+  integrity sha1-00jw4WvKf/73TMWCzuEK0OcyBL8=
+  dependencies:
+    tslib "^1.11.1"
+
 ts-loader@^6.2.2:
 ts-loader@^6.2.2:
   version "6.2.2"
   version "6.2.2"
   resolved "https://registry.npm.taobao.org/ts-loader/download/ts-loader-6.2.2.tgz#dffa3879b01a1a1e0a4b85e2b8421dc0dfff1c58"
   resolved "https://registry.npm.taobao.org/ts-loader/download/ts-loader-6.2.2.tgz#dffa3879b01a1a1e0a4b85e2b8421dc0dfff1c58"
@@ -7990,7 +8051,7 @@ tsconfig-paths@^3.9.0:
     minimist "^1.2.0"
     minimist "^1.2.0"
     strip-bom "^3.0.0"
     strip-bom "^3.0.0"
 
 
-tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
+tslib@^1.11.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
   version "1.13.0"
   version "1.13.0"
   resolved "https://registry.npm.taobao.org/tslib/download/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
   resolved "https://registry.npm.taobao.org/tslib/download/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
   integrity sha1-yIHhPMcBWJTtkUhi0nZDb6mkcEM=
   integrity sha1-yIHhPMcBWJTtkUhi0nZDb6mkcEM=
@@ -8290,6 +8351,16 @@ validate-npm-package-license@^3.0.1:
     spdx-correct "^3.0.0"
     spdx-correct "^3.0.0"
     spdx-expression-parse "^3.0.0"
     spdx-expression-parse "^3.0.0"
 
 
+vant@^2.8.2:
+  version "2.9.4"
+  resolved "https://registry.npm.taobao.org/vant/download/vant-2.9.4.tgz#fd29e9fa767dfd4fe539a014949f2e5161221b89"
+  integrity sha1-/Snp+nZ9/U/lOaAUlJ8uUWEiG4k=
+  dependencies:
+    "@babel/runtime" "7.x"
+    "@vant/icons" "1.2.3"
+    "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0"
+    vue-lazyload "1.2.3"
+
 vary@~1.1.2:
 vary@~1.1.2:
   version "1.1.2"
   version "1.1.2"
   resolved "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
   resolved "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
@@ -8336,6 +8407,11 @@ vue-hot-reload-api@^2.3.0:
   resolved "https://registry.npm.taobao.org/vue-hot-reload-api/download/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
   resolved "https://registry.npm.taobao.org/vue-hot-reload-api/download/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
   integrity sha1-UylVzB6yCKPZkLOp+acFdGV+CPI=
   integrity sha1-UylVzB6yCKPZkLOp+acFdGV+CPI=
 
 
+vue-lazyload@1.2.3:
+  version "1.2.3"
+  resolved "https://registry.npm.taobao.org/vue-lazyload/download/vue-lazyload-1.2.3.tgz#901f9ec15c7e6ca78781a2bae4a343686bdedb2c"
+  integrity sha1-kB+ewVx+bKeHgaK65KNDaGve2yw=
+
 vue-loader@^15.9.2:
 vue-loader@^15.9.2:
   version "15.9.3"
   version "15.9.3"
   resolved "https://registry.npm.taobao.org/vue-loader/download/vue-loader-15.9.3.tgz#0de35d9e555d3ed53969516cac5ce25531299dda"
   resolved "https://registry.npm.taobao.org/vue-loader/download/vue-loader-15.9.3.tgz#0de35d9e555d3ed53969516cac5ce25531299dda"