瀏覽代碼

feat: 协议政策集合模板

黎海 2 年之前
當前提交
efc9cde286
共有 48 個文件被更改,包括 23146 次插入0 次删除
  1. 14 0
      .gitignore
  2. 48 0
      README.md
  3. 13 0
      babel.config.js
  4. 36 0
      commitlint.config.js
  5. 52 0
      default.gitlab-ci.yml
  6. 20011 0
      package-lock.json
  7. 120 0
      package.json
  8. 27 0
      public/index.html
  9. 17 0
      src/App.vue
  10. 二進制
      src/assets/nodata.png
  11. 32 0
      src/common/auth.js
  12. 179 0
      src/common/err_collect.js
  13. 38 0
      src/common/errorHandler.js
  14. 190 0
      src/common/http.js
  15. 10 0
      src/common/index.js
  16. 36 0
      src/common/login.js
  17. 44 0
      src/common/matchMenu.js
  18. 195 0
      src/common/open.js
  19. 7 0
      src/common/reg.js
  20. 757 0
      src/common/share.js
  21. 205 0
      src/common/tool.js
  22. 10 0
      src/common/utils.js
  23. 114 0
      src/common/validHelper.js
  24. 14 0
      src/config/env.js
  25. 58 0
      src/directives/index.js
  26. 34 0
      src/filters/filter.js
  27. 27 0
      src/main.js
  28. 23 0
      src/pages/home/index.vue
  29. 238 0
      src/pages/home/xiaoshuo.vue
  30. 42 0
      src/router/index.js
  31. 12 0
      src/server/home.js
  32. 19 0
      src/server/login.js
  33. 4 0
      src/server/urls.js
  34. 11 0
      src/store/index.js
  35. 22 0
      src/store/modules/comVal.js
  36. 47 0
      src/style/common.less
  37. 47 0
      src/style/reset.less
  38. 50 0
      src/style/root.less
  39. 19 0
      tests/e2e/custom-assertions/elementCount.js
  40. 26 0
      tests/e2e/reports/CHROME_71.0.3578.80_Windows NT_test.xml
  41. 26 0
      tests/e2e/reports/CHROME_74.0.3729.108_Windows NT_test.xml
  42. 14 0
      tests/e2e/specs/test.js
  43. 5 0
      tests/unit/.eslintrc.js
  44. 12 0
      tests/unit/components/hello.spec.js
  45. 11 0
      tests/unit/pages/loan.spec.js
  46. 66 0
      vue.config.js
  47. 1 0
      安装插件.bat
  48. 163 0
      规范说明

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
1
+  .DS_Store
2
+/node_modules
3
+/dist/
4
+npm-debug.log*
5
+yarn-debug.log*
6
+yarn-error.log*
7
+ 
8
+# Editor directories and files
9
+.idea
10
+.vscode
11
+*.suo
12
+*.ntvs*
13
+*.njsproj
14
+*.sln

+ 48 - 0
README.md

@@ -0,0 +1,48 @@
1
+# web
2
+
3
+## Project setup
4
+
5
+```
6
+npm install
7
+```
8
+
9
+### Compiles and hot-reloads for development
10
+
11
+```
12
+npm run dev
13
+```
14
+
15
+### Compiles and minifies for production
16
+
17
+```
18
+npm run build
19
+```
20
+
21
+### Run your tests
22
+
23
+```
24
+e2e test (端到端测试)
25
+npm run test
26
+
27
+unit test (单元测试)
28
+npm run unit
29
+```
30
+
31
+### Lints and fixes files
32
+
33
+```
34
+npm run lint
35
+```
36
+
37
+eslint 详细规则:https://eslint.org/docs/rules/
38
+
39
+### Customize configuration
40
+
41
+See [Configuration Reference](https://cli.vuejs.org/config/).
42
+
43
+### 规划
44
+
45
+线上配置:
46
+vue.config.js中配置线上域名和地址
47
+接口配置:
48
+common->http中接口封装配置,config->env.js中配置接口域名

+ 13 - 0
babel.config.js

@@ -0,0 +1,13 @@
1
+module.exports = {
2
+  presets: ['@vue/app'],
3
+  sourceType: 'unambiguous',
4
+  plugins: [
5
+    [
6
+      'component',
7
+      {
8
+        libraryName: 'yd-ui',
9
+        style: false,
10
+      },
11
+    ],
12
+  ],
13
+}

+ 36 - 0
commitlint.config.js

@@ -0,0 +1,36 @@
1
+const types = [
2
+  'build', // 修改项目的的构建系统(xcodebuild、webpack、glup等)的提交
3
+  'ci', // 修改项目的持续集成流程(Kenkins、Travis等)的提交
4
+  'chore', // 构建过程或辅助工具的变化,翻译为日常琐事
5
+  'docs', // 文档提交(documents)
6
+  'feat', // 新功能(feature)
7
+  'fix', // bug已经修复。 适合于一次提交直接修复问题
8
+  'to', // bug还未修复。适合于多次提交。最终修复问题提交时使用fix
9
+  'pref', // 优化相关,比如提升性能、体验(performance)
10
+  'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)
11
+  'revert', // 回滚到上一个版本
12
+  'style', // 不影响程序逻辑的代码修改、主要是样式方面的优化、修改
13
+  'test', // 测试相关的开发
14
+  'sync', // 同步主线或分支的Bug
15
+]
16
+
17
+typeEnum = {
18
+  rules: {
19
+    'type-enum': [2, 'always', types],
20
+  },
21
+  value: () => types,
22
+}
23
+
24
+module.exports = {
25
+  extends: ['@commitlint/config-conventional'],
26
+  rules: {
27
+    'type-case': [0],
28
+    'type-empty': [2, 'never'],
29
+    'scope-empty': [0],
30
+    'scope-case': [0],
31
+    'subject-full-stop': [0, 'never'],
32
+    'subject-case': [0, 'never'],
33
+    'header-max-length': [0, 'always', 72],
34
+    'type-enum': typeEnum.rules['type-enum'],
35
+  },
36
+}

+ 52 - 0
default.gitlab-ci.yml

@@ -0,0 +1,52 @@
1
+# 定义 stages
2
+stages:
3
+  - install
4
+  - build
5
+  - deploy
6
+
7
+# 每个job之前运行的命令
8
+before_script:
9
+  - whoami
10
+  - pwd
11
+
12
+cache:
13
+  key: ${CI_BUILD_REF_NAME}
14
+  paths:
15
+    - node_modules/ # 为node_modules增加缓存
16
+
17
+install:
18
+  stage: install
19
+  script:
20
+    - echo "======= 开始 安装依赖 ======="
21
+    - npm install
22
+    # 可以切换为淘宝镜像
23
+    # - npm --registry https://registry.npm.taobao.org install express
24
+    - echo "======= 完成 安装依赖 ======="
25
+  only:
26
+    - master
27
+  when: manual
28
+
29
+build:
30
+  stage: build
31
+  script:
32
+    - echo "======= 开始 构建 ======="
33
+    - npm run build:p
34
+    - echo "======= 结束 构建 ======="
35
+  artifacts:
36
+    expire_in: 1 week # 生成文件保存周期
37
+    name: '${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}'
38
+    paths:
39
+      - dist # 编译后生成的文件夹名
40
+  only:
41
+    - master
42
+
43
+deploy:
44
+  stage: deploy
45
+  script:
46
+    - echo "======= 开始 部署 ======="
47
+    - npm run qiniu
48
+    - npm run deploy
49
+    - echo "======= 完成 部署 ======="
50
+  when: manual
51
+  only:
52
+    - master

File diff suppressed because it is too large
+ 20011 - 0
package-lock.json


+ 120 - 0
package.json

@@ -0,0 +1,120 @@
1
+{
2
+  "name": "web",
3
+  "version": "0.1.0",
4
+  "private": true,
5
+  "scripts": {
6
+    "dev": "cross-env env_config=dev vue-cli-service serve",
7
+    "build": "cross-env env_config=test vue-cli-service build",
8
+    "build:t": "cross-env env_config=test vue-cli-service build",
9
+    "build:p": "cross-env env_config=prod vue-cli-service build",
10
+    "lint": "vue-cli-service lint",
11
+    "qiniu": "bash ./scripts/cdn/qiniu.sh dist",
12
+    "deploy": "bash ./scripts/deploy/deploy.sh dist",
13
+    "release": "npm run build:p && npm run qiniu && npm run deploy",
14
+    "server": "npm run build:t && bash ./scripts/deploy/transport_test_file.sh dist",
15
+    "fix": "prettier --write .",
16
+    "lint-staged": "lint-staged"
17
+  },
18
+  "dependencies": {
19
+    "axios": "^0.18.0",
20
+    "core-js": "^2.6.12",
21
+    "crypto-js": "^3.1.9-1",
22
+    "swiper": "^4.5.0",
23
+    "vue": "^2.6.6",
24
+    "vue-router": "^3.0.1",
25
+    "vuex": "^3.0.1",
26
+    "weixin-js-sdk": "^1.4.0-test"
27
+  },
28
+  "devDependencies": {
29
+    "@commitlint/cli": "^11.0.0",
30
+    "@commitlint/config-conventional": "^11.0.0",
31
+    "@vue/cli-plugin-babel": "^3.5.0",
32
+    "@vue/cli-plugin-eslint": "^3.5.0",
33
+    "@vue/cli-plugin-unit-jest": "^3.5.0",
34
+    "@vue/cli-service": "^3.5.0",
35
+    "@vue/test-utils": "1.0.0-beta.29",
36
+    "@babel/eslint-parser": "^7.17.0",
37
+    "babel-core": "7.0.0-bridge.0",
38
+    "babel-jest": "^23.6.0",
39
+    "babel-plugin-component": "^1.1.1",
40
+    "cross-env": "^5.2.1",
41
+    "eslint": "^5.16.0",
42
+    "eslint-plugin-vue": "^5.0.0",
43
+    "husky": "^4.3.0",
44
+    "less": "^3.0.4",
45
+    "less-loader": "^4.1.0",
46
+    "vue-loader": "^14.2.2",
47
+    "vue-template-compiler": "^2.5.21"
48
+  },
49
+  "eslintConfig": {
50
+    "root": true,
51
+    "env": {
52
+      "node": true
53
+    },
54
+    "extends": [
55
+      "plugin:vue/essential",
56
+      "eslint:recommended"
57
+    ],
58
+    "rules": {
59
+      "no-undef": 0,
60
+      "no-console": 0,
61
+      "no-unused-vars": 0,
62
+      "indent": "off",
63
+      "vue/script-indent": [
64
+        "error",
65
+        2
66
+      ]
67
+    },
68
+    "parserOptions": {
69
+      "parser": "@babel/eslint-parser"
70
+    }
71
+  },
72
+  "postcss": {
73
+    "plugins": {
74
+      "autoprefixer": {}
75
+    }
76
+  },
77
+  "browserslist": [
78
+    "> 1%",
79
+    "last 2 versions",
80
+    "not ie <= 8"
81
+  ],
82
+  "jest": {
83
+    "moduleFileExtensions": [
84
+      "js",
85
+      "jsx",
86
+      "json",
87
+      "vue"
88
+    ],
89
+    "transform": {
90
+      "^.+\\.vue$": "vue-jest",
91
+      ".+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
92
+      "^.+\\.jsx?$": "babel-jest"
93
+    },
94
+    "transformIgnorePatterns": [
95
+      "/node_modules/"
96
+    ],
97
+    "moduleNameMapper": {
98
+      "^@/(.*)$": "<rootDir>/src/$1"
99
+    },
100
+    "snapshotSerializers": [
101
+      "jest-serializer-vue"
102
+    ],
103
+    "testMatch": [
104
+      "**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
105
+    ],
106
+    "testURL": "http://localhost/"
107
+  },
108
+  "husky": {
109
+    "hooks": {
110
+      "pre-commit": "lint-staged",
111
+      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
112
+    }
113
+  },
114
+  "lint-staged": {
115
+    "*.{js,jsx}": [
116
+      "prettier --write .",
117
+      "git add"
118
+    ]
119
+  }
120
+}

+ 27 - 0
public/index.html

@@ -0,0 +1,27 @@
1
+<!DOCTYPE html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="utf-8">
5
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+    <meta name="format-detection" content="telephone=no">
7
+    <meta name="format-detection" content="email=no">
8
+    <meta name="format-detection" content="address=no">
9
+    <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=0">
10
+    <meta itemprop="share_url" content="url" />
11
+    <meta itemprop="image" content="url" />
12
+    <meta itemprop="share_pic_url" content="" />
13
+    <title>项目名</title>
14
+  </head>
15
+  <body class="adf">
16
+    <noscript>
17
+      <strong>您的浏览器版本太低,请升级之后再来访问哦~</strong>
18
+    </noscript>
19
+    <div id="app"></div>
20
+    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
21
+    <!-- built files will be auto injected -->
22
+    <% for (var i in htmlWebpackPlugin.options.cdn.js) { %>
23
+      <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
24
+    <% } %>
25
+  </body>
26
+  <!-- <script type="text/javascript" src="https://ydcommon.51yund.com/test_web_hd/vendor/vconsole.min.js"></script> -->
27
+</html>

+ 17 - 0
src/App.vue

@@ -0,0 +1,17 @@
1
+<template>
2
+  <div id="app">
3
+    <keep-alive>
4
+      <router-view v-if="$route.meta.keepAlive"></router-view>
5
+    </keep-alive>
6
+    <router-view v-if="!$route.meta.keepAlive"></router-view>
7
+    <div class="fix-bottom"></div>
8
+  </div>
9
+</template>
10
+
11
+<style scoped>
12
+.fix-bottom {
13
+  position: fixed;
14
+  left: 0;
15
+  bottom: 0;
16
+}
17
+</style>

二進制
src/assets/nodata.png


+ 32 - 0
src/common/auth.js

@@ -0,0 +1,32 @@
1
+import { logFlag, logPath } from '@/config/env'
2
+let isFirst = false;
3
+export default (router, hasLogin) => {
4
+  router.beforeEach((to, from, next) => {
5
+    if (to.meta.title) {
6
+      document.title = to.meta.title;
7
+    }
8
+    isFirst = globalVue ? false : true;
9
+    if (to.meta.auth && !hasLogin) { //需要登录而未登录
10
+      let backUrl = _getTargetUrl(to, from);
11
+      // tool.toLogin(backUrl);//如果不加backUrl则登录回调会跳转到前一个页面
12
+    } else {
13
+      next()
14
+    }
15
+  })
16
+}
17
+
18
+function _getTargetUrl (to, from) {
19
+  let backUrl = location.href.split("?")[0];
20
+  if (!isFirst) { //只有非首次进入才做处理
21
+    if (from.path === '/' && to.path != '/') { //首页到其它页
22
+      backUrl += to.path.slice(1);
23
+    } else { //其它页互相跳(包括跳到首页)
24
+      backUrl = backUrl.replace(from.path, to.path);
25
+    }
26
+  }
27
+
28
+  if (location.search) {
29
+    backUrl += location.search;
30
+  }
31
+  return backUrl;
32
+}

+ 179 - 0
src/common/err_collect.js

@@ -0,0 +1,179 @@
1
+//本地持久化存储方案
2
+//初始化时清除过期存储,vue初始化后将需要记录的信息通过请求发送
3
+window.viewWillAppear = function () {}
4
+window.viewDidAppear = function () {}
5
+window.viewWillDisappear = function () {}
6
+window.viewDidDisappear = function () {}
7
+function YdStorage() {
8
+  this.source = window.localStorage
9
+  this.initClear() //去除过期缓存
10
+}
11
+YdStorage.prototype = {
12
+  constructor: function () {
13
+    this.name = 'storage'
14
+  },
15
+  initClear: function () {
16
+    var data = this.source
17
+    for (var item in data) {
18
+      if (!data.hasOwnProperty(item)) return //过滤掉原型上的属性
19
+      if (data[item].indexOf('_expires_') !== -1) {
20
+        tarItem = JSON.parse(data[item])
21
+        if (Date.now() >= tarItem._expires_) {
22
+          this.remove(item)
23
+        }
24
+      }
25
+    }
26
+  },
27
+  setItem: function (name, value, expires, needPost) {
28
+    //expires是秒, needPost标记是否需要发送到服务器
29
+    if (expires) {
30
+      var obj = {
31
+        value: value,
32
+        _expires_: Date.now() + expires * 1000,
33
+      }
34
+      if (needPost) {
35
+        obj._needPost_ = true //这个用来标记是否需要发送
36
+      }
37
+      localStorage.setItem(name, JSON.stringify(obj))
38
+    } else {
39
+      var type = Object.prototype.toString.call(value).slice(8, -1)
40
+      if (type == 'Object' || type == 'Array') {
41
+        value = JSON.stringify(value)
42
+      }
43
+      localStorage.setItem(name, value)
44
+    }
45
+  },
46
+  getItem: function (name) {
47
+    var item = localStorage.getItem(name)
48
+    if (!item) return ''
49
+    try {
50
+      //先将拿到的试着进行json转为对象的形式
51
+      item = JSON.parse(item)
52
+    } catch (error) {
53
+      //如果不行就不是json的字符串,就直接返回
54
+      item = item
55
+    }
56
+    if (!item._expires_) return item
57
+    if (Date.now() > item._expires_) {
58
+      this.remove(name)
59
+      return ''
60
+    } else {
61
+      return item.value
62
+    }
63
+  },
64
+  remove: function (name) {
65
+    localStorage.removeItem(name)
66
+  },
67
+  postItem: function () {
68
+    var data = window.localStorage
69
+    for (var item in data) {
70
+      if (!data.hasOwnProperty(item)) return //过滤掉原型上的属性
71
+      if (data[item].indexOf('_needPost_') !== -1) {
72
+        //需要发送
73
+        var tarItem = JSON.parse(data[item])
74
+        if (window.tool && tool.$throwJS) {
75
+          tool.$throwJS(tarItem.value)
76
+        }
77
+        this.remove(item)
78
+      }
79
+    }
80
+  },
81
+}
82
+window.ydStorage = new YdStorage()
83
+
84
+window.onerror = function (msg, url, line, col, error) {
85
+  //捕捉js错误
86
+  if (!msg) return true
87
+  if (typeof msg === 'string') {
88
+    msg = msg.split(':')[1]
89
+    if (msg) msg = msg.replace(/^\s+|\s+$/g, '') //去掉左右空格
90
+  }
91
+  msg && ydDealErr(msg, true)
92
+  return true //这句的作用是防止浏览器报错而阻塞进程
93
+}
94
+window.addEventListener(
95
+  'error',
96
+  function (error) {
97
+    //捕捉资源加载错误,资源加载出错不会冒泡到window.onerror
98
+    var path = error.target.src
99
+    if (!path || path === location.href) return //条件一说明捕获的是js异常,条件二捕获的是img src为空
100
+    ydDealErr(path)
101
+  },
102
+  true,
103
+)
104
+
105
+function ydDealErr(msg, isJs) {
106
+  var hosts = location.host
107
+  if (hosts.indexOf('localhost') > -1 || hosts.indexOf('test') > -1) {
108
+    console.error(msg)
109
+  }
110
+  var obj = { err_msg: msg }
111
+  if (window.tool && window.tool.$throwJS) {
112
+    window.tool.$throwJS(obj)
113
+  } else {
114
+    obj.err_path = location.href
115
+    obj.err_time = _ydErrDealTime()
116
+    if (isJs) {
117
+      postJsErr(obj)
118
+    } else {
119
+      ydStorage.setItem(msg, obj, 3600 * 24 * 7, true) //信息存储7天,过期删除
120
+    }
121
+  }
122
+}
123
+function _ydErrDealTime() {
124
+  var timeSc = new Date()
125
+  var year, month, day, hour, minute, second, timeStr //时间默认值
126
+  year = timeSc.getFullYear() // 获取完整的年份(4位,1970)
127
+  month = checkTime(timeSc.getMonth() + 1) // 获取月份(0-11,0代表1月,用的时候记得加上1)
128
+  day = checkTime(timeSc.getDate()) // 获取日(1-31)
129
+  hour = checkTime(timeSc.getHours()) // 获取小时数(0-23)
130
+  minute = checkTime(timeSc.getMinutes()) // 获取分钟数(0-59)
131
+  second = checkTime(timeSc.getSeconds()) // 获取分钟数(0-59)
132
+  function checkTime(m) {
133
+    return m < 10 ? '0' + m : m
134
+  }
135
+  timeStr =
136
+    year + '/' + month + '/' + day + ' ' + hour + ':' + minute + ':' + second
137
+  return timeStr
138
+}
139
+function postJsErr(data) {
140
+  var errInstance = axios.create({
141
+    timeout: 1000 * 10,
142
+    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
143
+  })
144
+  var errFd = new FormData()
145
+  var uId = getCookieValue('user_id') || localStorage.getItem('user_id') || 0
146
+  errFd.append('user_id', uId)
147
+  errFd.append('cmd', 'vue_jserr')
148
+  errFd.append('device_id', 'yuedongweb')
149
+  errFd.append('data', JSON.stringify(data))
150
+  var uri = 'https://api.51yund.com'
151
+  errInstance
152
+    .post(uri + '/sport/report', errFd)
153
+    .then(function () {})
154
+    .catch(function () {
155
+      ydStorage.setItem(data.err_msg, data, 3600 * 24 * 7, true) //信息存储7天,过期删除
156
+    })
157
+}
158
+function getCookieValue(name) {
159
+  var nameEQ = name + '='
160
+  var ca = document.cookie.split(';')
161
+  for (var i = 0; i < ca.length; i++) {
162
+    var c = ca[i]
163
+    while (c.charAt(0) == ' ') c = c.substring(1, c.length)
164
+    if (c.indexOf(nameEQ) == 0) {
165
+      return c.substring(nameEQ.length, c.length)
166
+    }
167
+  }
168
+  return ''
169
+}
170
+// 这两句是为了捕捉网页崩溃,模拟网页崩溃:chrome://crash
171
+// window.addEventListener('load', function () { //进入页面
172
+//     localStorage.setItem('good_exit', 'pending');
173
+// });
174
+// window.addEventListener('beforeunload', function () {  //正常关闭网页或退出
175
+//     localStorage.setItem('good_exit', 'true');
176
+// });
177
+// if(localStorage.getItem('good_exit') != 'true') {
178
+//     ydDealErr('网页崩溃')
179
+// }

+ 38 - 0
src/common/errorHandler.js

@@ -0,0 +1,38 @@
1
+import Vue from 'vue'
2
+import {logFlag} from '@/config/env'
3
+export default () =>{
4
+    const errorHandler = (err, vm, info) => {
5
+        // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
6
+        let compName = "";
7
+        if (vm) {
8
+            compName = _formatComponentName(vm);
9
+            compName = compName.replace(/\\/g, "/");
10
+        }
11
+        let obj = {
12
+            component: compName, 
13
+            hook: info, 
14
+            err_msg: err + ''
15
+        }
16
+        let hosts = location.host;
17
+        if((hosts.indexOf("localhost") > -1 || hosts.indexOf("test") > -1)){
18
+            console.table(obj);
19
+        }
20
+        tool.$throwJS(obj);
21
+    }
22
+    Vue.config.errorHandler = errorHandler;
23
+}
24
+
25
+//获取当前组件的路径
26
+function _formatComponentName(vm) {
27
+    if (vm.$root === vm) return 'root';
28
+    let name = vm._isVue
29
+        ? (vm.$options && vm.$options.name) ||
30
+        (vm.$options && vm.$options._componentTag)
31
+        : vm.name;
32
+    return (
33
+        (name ? 'component <' + name + '>' : 'anonymous component') +
34
+        (vm._isVue && vm.$options && vm.$options.__file
35
+            ? ' at ' + (vm.$options && vm.$options.__file)
36
+            : '')
37
+    );
38
+}

+ 190 - 0
src/common/http.js

@@ -0,0 +1,190 @@
1
+// 封装axios请求
2
+import axios from 'axios'
3
+import CryptoJS from 'crypto-js';
4
+import {basePath} from '@/config/env'
5
+let onGetSession = false; //是否正在请求sessionkey
6
+const post = async (url, parms={}, isfromGetsskey) => {
7
+	//这里是防止sessionkey接口很慢的情况下,其它正在排队的请求继续发送,导致短时间内多个重复请求(100ms一次)
8
+	if(onGetSession && !isfromGetsskey){ 
9
+		await _sleep(200); 
10
+		return post(url, parms)
11
+	}
12
+	let instance = axios.create({
13
+		timeout: 1000 * 10,
14
+		headers: {'Content-Type': 'application/x-www-form-urlencoded'}
15
+	});
16
+	let sskey = tool.getYdUserKey("session_key");
17
+	let userId = tool.getYdUserKey('user_id') || 0;
18
+	let xyy = tool.getYdUserKey('xyy') || 'zachhe';
19
+	if(!sskey && !isfromGetsskey){
20
+		sskey = await tool.getSessionKey(userId, xyy);
21
+	}
22
+	parms = Object.assign({
23
+		user_id: userId,
24
+		xyy: xyy,
25
+		platform: 'web',
26
+		session_key: sskey,
27
+		session_from: 1,
28
+		timestamp: Date.parse(new Date())/1000 + tool.timeSpace + ''
29
+	}, parms)
30
+
31
+	parms.sign = _hamcSha(url,parms,sskey);
32
+	let qs = require('qs');
33
+	let reqUrl = url.indexOf('http') > -1 ? url : basePath + url;
34
+	return instance.post(reqUrl, qs.stringify(parms)).then(async function(res){
35
+		if(!res.data || res.data.code === undefined || !res.data.msg){
36
+			tool.$throw('结构体异常', parms, url, res.data);
37
+		}
38
+		let data = Object.assign({code:-1001, msg:'网络出问题了~'}, res.data);
39
+		if(data.code === 7007){ //sessionkey过期
40
+			tool.$throw('sskey过期', parms, url);
41
+			if(isfromGetsskey){
42
+				_dealSessionExpire()
43
+			} else {
44
+				delete parms.session_key;
45
+				if(!onGetSession){ //前面没有正在请求的sessionkey
46
+					onGetSession = true;
47
+					let returnSskey = await tool.getSessionKey(parms.user_id,parms.xyy);
48
+					onGetSession = false;
49
+					if(!returnSskey) return {}; //没有获取到sesskey时,停止后面的请求,常见的场景是xyy过期
50
+					return post(url, parms, true)
51
+				} else { //前面有在请求的sessionkey
52
+					await _sleep(100); //排队等100毫秒再去请求
53
+					return post(url, parms)
54
+				}
55
+			}
56
+		}
57
+		if(data.code !== 0 && data.code !== 4004 && data.code !== 7007){
58
+			if(data.code === 1 && data.msg === '参数不合法'){ //将参数不合法的请求上报
59
+				tool.$throw('参数不合法', parms, url);
60
+				let timeSpace = 0;
61
+				if(res.headers.server_time){
62
+					timeSpace = parseInt(res.headers.server_time) - parms.timestamp;
63
+				}
64
+				if(Math.abs(timeSpace) > 300){ //本地和服务器时间超过五分钟导致的参数不合法
65
+					tool.timeSpace = timeSpace;
66
+					delete parms.timestamp;
67
+					return post(url, parms)
68
+				} 
69
+				if(globalVue) globalVue.$message('服务异常,请稍后重试')
70
+			} else if(globalVue){
71
+				globalVue.$message(data.msg)
72
+			}
73
+		}
74
+		if(data.code === 4004){ //user_id和xyy不匹配,最常见的是用户被设置成了广告用户,xyy发生变化
75
+			tool.$throw('登录过期', parms, url); 
76
+			tool.toLogin();
77
+		}
78
+		return data;
79
+	}).catch(res=>{
80
+		tool.$throw(res, parms, reqUrl); 
81
+		if(isfromGetsskey){ //获取sessionkey超时
82
+			tool.fetchSKtime++;
83
+			if(tool.fetchSKtime < 3){ //最多请求三次,不行就出提示弹窗,常见场景有:1获取sessionkey超时,2按钮跳转时的事件上报(弱网下事件上报没完成页面就直接跳转走了,导致请求abort)
84
+				return post(url, parms, true)
85
+			} else {
86
+				_dealSessionExpire()
87
+			}
88
+		} else if(globalVue){ //弱网下按钮跳转的事件上报被cancel时,不用出$message弹窗
89
+			globalVue.$message('当前网络不给力,请稍后重试');
90
+		}
91
+		return {code: -1002, msg:'网络出问题了~'};
92
+	});
93
+}
94
+
95
+//不带默认参数的请求,主要用于错误上报等
96
+const postOnly = async (url, parms={}, headers={}) => {
97
+	let instance = axios.create({
98
+		timeout: 1000 * 10,
99
+		headers: {'Content-Type': 'application/x-www-form-urlencoded'}
100
+	});
101
+	let qs = require('qs');
102
+	return instance.post(url, qs.stringify(parms)).then(function(res){
103
+		return res.data;
104
+	}).catch(res=>{
105
+		let errObj = JSON.parse(parms.data);
106
+		if(errObj.err_msg && window.ydStorage){
107
+			ydStorage.setItem(errObj.err_msg, errObj, 3600*24*7, true);
108
+		}
109
+	})
110
+}
111
+
112
+const get = (url, parms={},headers={}) => {
113
+	url += '?';
114
+	for(let key in parms){
115
+		url += key + '=' + parms[key] + '&';
116
+	}
117
+	let option = Object.assign({},headers);
118
+	let instance = axios.create({
119
+		timeout: 1000 * 10,
120
+		headers: option
121
+	});
122
+
123
+	return instance.get(url).then(function(res){
124
+		return res.data;
125
+	}).catch(res=>{
126
+		console.log('error',res);
127
+	});
128
+}
129
+
130
+export default () => {
131
+	if (typeof window.$http == 'undefined') {
132
+		window.$http = {
133
+			post: post,
134
+			postOnly: postOnly,
135
+			get: get
136
+		}
137
+	}
138
+}
139
+
140
+function _sortArgs(data){
141
+    var args;
142
+    var argsArr = [];
143
+    for(args in data){
144
+        if(data[args]==null || data[args]==undefined){
145
+            data[args] = ""
146
+        }
147
+        if (args != "xyy" && args != "sign" && args != "content" && args != "feeling" && args != "nick" && args != "alipay_name"){
148
+            argsArr.push(args);
149
+        }
150
+    }
151
+    argsArr.sort();
152
+    var argStr = "";
153
+    for (var i = 0; i < argsArr.length;i++){
154
+        var item = argsArr[i];
155
+        argStr = argStr + item + "=" + data[item] + '&';
156
+	}
157
+    argStr=argStr.substring(0, argStr.length-1);
158
+	argStr = encodeURIComponent(argStr);
159
+	argStr = argStr.replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/!/g, '%21').replace(/~/g, '%7E').replace(/\*/g, '%2A').replace(/'/g, '%27');
160
+    return argStr
161
+}
162
+function _hamcSha(uri,data,sectionKoken){
163
+    var url = encodeURIComponent(uri);
164
+    var paramsStr = _sortArgs(data);
165
+    var args = "POST&" + url + "&" + paramsStr;
166
+    var hash = CryptoJS.HmacSHA1(args, sectionKoken);
167
+    var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
168
+    return hashInBase64
169
+}
170
+
171
+function _dealSessionExpire() {
172
+	if(window.globalVue){
173
+		globalVue.$confirm('很抱歉,访问出现错误', '提示', {
174
+            confirmButtonText: '刷新页面',
175
+            cancelButtonText: '重新登录',
176
+            type: 'warning'
177
+        }).then(() => {
178
+            window.location.reload();
179
+        }).catch(() => {
180
+            tool.toLogin();        
181
+        });
182
+	} else {
183
+		tool.toLogin();
184
+	}
185
+}
186
+function _sleep(interval) {
187
+	return new Promise(resolve => {
188
+		setTimeout(resolve, interval);
189
+	})
190
+}

+ 10 - 0
src/common/index.js

@@ -0,0 +1,10 @@
1
+// 全局注入 
2
+import { injectTool } from './tool'
3
+import injectHttp from './http'
4
+import errorLog from './errorHandler.js'
5
+
6
+export const injectGlobal = () => {
7
+  injectTool();
8
+  injectHttp();
9
+  // errorLog();
10
+}

+ 36 - 0
src/common/login.js

@@ -0,0 +1,36 @@
1
+export default (cb) => {
2
+    let userId, xyy;
3
+    userId = localStorage.getItem('user_id'); 
4
+    xyy = localStorage.getItem('xyy');
5
+    let sid = tool.getUrlParam('xyy');
6
+    let isLogin = tool.getUrlParam('is_login');
7
+    if(sid && isLogin){ //如果是授权登录的回调,用url上的信息覆盖本地存储的信息
8
+        xyy = sid; 
9
+        userId = tool.getUrlParam('user_id');
10
+        // 这段代码是为了清除url上的xyy,防止敏感信息暴露
11
+        let path = location.href;
12
+        if(path.indexOf('xyy') > -1){
13
+            path = path.replace(/&xyy=[^&]*/g, '')
14
+            history.replaceState({}, "", path);
15
+        }
16
+    }
17
+    
18
+    if(!tool.getIntValue(userId) || !tool.getIntValue(xyy)){
19
+        userId = 0;
20
+        xyy = "abc452"
21
+    }
22
+
23
+    rc-challenge-help
24
+
25
+    let sessionkey = tool.getYdUserKey('session_key');
26
+    if(!sessionkey){
27
+        tool.getSessionKey(userId, xyy, cb);
28
+    } else {
29
+        if(userId != 0){ //更新user_id和xyy
30
+            localStorage.setItem('user_id', userId);
31
+            localStorage.setItem('xyy', xyy);
32
+        }
33
+        let hasLogin = userId == 0 ? false : true;
34
+        cb(hasLogin);
35
+    }
36
+}

+ 44 - 0
src/common/matchMenu.js

@@ -0,0 +1,44 @@
1
+export default class menuMethod {	
2
+	static matchMenu(menu,path) {
3
+		if(path=='/'){
4
+			jumpUrl(menu);
5
+		} else{
6
+			let permission=find(menu,path);
7
+			if(!permission){
8
+				globalVue.$router.push({path:'/'});
9
+				globalVue.$alert('抱歉,找不到该页面', '标题名称', {
10
+					confirmButtonText: '确定',
11
+					callback: () => {
12
+						jumpUrl(menu);
13
+					}
14
+				});
15
+			}
16
+		}
17
+	}
18
+}
19
+
20
+// 匹配查找,看是否有访问当前url的权限
21
+function find(menu,path) {
22
+	for(let i=0;i<menu.length;i++){
23
+		if(menu[i].children){
24
+		let result=find(menu[i].children,path);
25
+			if(result) return true;
26
+		} else if(menu[i].path==path){
27
+			return true;
28
+		}
29
+	}
30
+}
31
+// 路由跳转
32
+function jumpUrl(menu){
33
+	if(menu[0].path){
34
+		globalVue.$router.push({path:menu[0].path});
35
+	} else {
36
+		if(menu[0].children[0].children){
37
+			globalVue.$router.push({path:menu[0].children[0].children[0].path});
38
+		} else {
39
+			globalVue.$router.push({path:menu[0].children[0].path});
40
+		}
41
+	}
42
+}
43
+
44
+ 

+ 195 - 0
src/common/open.js

@@ -0,0 +1,195 @@
1
+
2
+function ajax(options){
3
+    var xhr = null;
4
+    var params = formsParams(options.data);
5
+    //创建对象
6
+    if(window.XMLHttpRequest){
7
+        xhr = new XMLHttpRequest()
8
+    } else {
9
+        xhr = new ActiveXObject("Microsoft.XMLHTTP");
10
+    }
11
+    // 连接
12
+    if(options.type == "GET"){
13
+        xhr.open(options.type,options.url + "?"+ params,options.async);
14
+        xhr.send(null)
15
+    } else if(options.type == "POST"){
16
+        xhr.open(options.type,options.url,options.async);
17
+        xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
18
+        xhr.send(params);
19
+    }
20
+    xhr.onreadystatechange = function(){
21
+        if(xhr.readyState == 4 && xhr.status == 200){
22
+            options.success(xhr.responseText);
23
+        }
24
+    }
25
+    function formsParams(data){
26
+        var arr = [];
27
+        for(var prop in data){
28
+            arr.push(prop + "=" + data[prop]);
29
+        }
30
+        return arr.join("&");
31
+    }
32
+
33
+}
34
+
35
+ajax({
36
+    url : "a.php",  // url---->地址
37
+    type : "POST",   // type ---> 请求方式
38
+    async : true,   // async----> 同步:false,异步:true
39
+    data : {        //传入信息
40
+        name : "张三",
41
+        age : 18
42
+    },
43
+    success : function(data){   //返回接受信息
44
+        console.log(data);
45
+    }
46
+})
47
+
48
+function reg_one(){
49
+
50
+    var a = document.querySelector("#form-step-1 > ti-form-element:nth-child(2) > ti-password-validator").shadowRoot.querySelector("ti-password").shadowRoot.querySelector("div > input[type=password]")
51
+
52
+    var b  = document.querySelector("#form-step-1 > ti-form-element:nth-child(3) > ti-password > input[type=password]");
53
+
54
+    var aa = document.querySelector("#form-step-1 > ti-form-element:nth-child(2) > ti-password-validator").shadowRoot.querySelector("ti-password");
55
+    var bb = document.querySelector("#form-step-1 > ti-form-element:nth-child(3) > ti-password")
56
+
57
+    var em = document.querySelector("#form-step-1 > ti-form-element:nth-child(1) > input[type=email]");
58
+    var co = document.querySelector("#form-step-1 > ti-form-element:nth-child(5) > input[type=text]");
59
+
60
+
61
+    window.inputValue = function (dom, st) {
62
+        var evt = new InputEvent('input', {
63
+            inputType: 'insertText',
64
+            data: st,
65
+            dataTransfer: null,
66
+            isComposing: false
67
+        });
68
+        dom.value =st;
69
+        dom.dispatchEvent(evt);
70
+    }
71
+
72
+    inputValue(a,'sfYDnsdiY11');
73
+
74
+    inputValue(b,'sfYDnsdiY11');
75
+
76
+    inputValue(bb,'sfYDnsdiY11');
77
+
78
+    inputValue(aa,'sfYDnsdiY11');
79
+
80
+    inputValue(em,'hruztr@hotmail.com');
81
+
82
+    inputValue(co,'518000');
83
+
84
+
85
+    setTimeout(function(){
86
+        var btn = document.querySelector("#continueBtn").shadowRoot.querySelector("button");
87
+        btn.click();
88
+
89
+    },1000);
90
+
91
+}
92
+
93
+
94
+
95
+
96
+reg_one();
97
+
98
+
99
+setTimeout(function(){
100
+
101
+    reg_two();
102
+
103
+},3000);
104
+
105
+
106
+
107
+function reg_two(){
108
+
109
+
110
+    //第二步填写
111
+
112
+    var r_name = document.querySelector("#form-step-2 > ti-form-element:nth-child(1) > input[type=text]");
113
+
114
+    inputValue(r_name,"xiaochun");
115
+
116
+    var xe = document.querySelector("#form-step-2 > ti-form-element:nth-child(2) > input[type=text]");
117
+
118
+    inputValue(xe,"he");
119
+
120
+    var cu  =  document.querySelector("#form-step-2 > ti-form-element:nth-child(3) > input[type=text]");
121
+
122
+    inputValue(cu,"广东大学");
123
+
124
+
125
+    var sf = document.querySelector("#form-step-2 > ti-form-element:nth-child(4) > select");
126
+    sf.selectedIndex = 1;
127
+
128
+
129
+    var ly = document.querySelector("#appAreaFormElement > select");
130
+
131
+
132
+    ly.selectedIndex  = 1;
133
+
134
+    var e_name = document.querySelector("#cnjpInputs > ti-form-element:nth-child(3) > input[type=text]");
135
+    inputValue(e_name,'xiaochun');
136
+
137
+
138
+    var e_he = document.querySelector("#cnjpInputs > ti-form-element:nth-child(4) > input[type=text]");
139
+
140
+    inputValue(e_he,"he");
141
+
142
+
143
+    var e_cou =  document.querySelector("#cnjpInputs > ti-form-element:nth-child(5) > input[type=text]");
144
+
145
+    inputValue(e_cou,'GDDX');
146
+
147
+    var box = document.querySelector("#form-step-2 > div:nth-child(8) > ti-checkbox");
148
+    box.click();
149
+
150
+    var ph  = document.querySelector("#cnjpInputs > ti-form-element:nth-child(1) > ti-phone-input").shadowRoot.querySelector("div > input");
151
+    inputValue(ph,'13147083749');
152
+    var yph = document.querySelector("#cnjpInputs > ti-form-element:nth-child(1) > ti-phone-input");
153
+    yph.setAttribute("area-code","131");
154
+    yph.setAttribute("phone-number","47083749");
155
+    yph.setAttribute("is-valid","")
156
+
157
+    var s_yph = document.querySelector("#cnjpInputs > ti-form-element:nth-child(1)");
158
+    s_yph.setAttribute('class',"ti-form-element ti-form-element-is-required hydrated");
159
+    s_yph.setAttribute('required','');
160
+
161
+
162
+    setTimeout(function(){
163
+
164
+        //按钮强行变化
165
+        var r_btn = document.querySelector("#registerBtn");
166
+        r_btn.removeAttribute("disabled");
167
+        r_btn.click();
168
+
169
+    },1000);
170
+
171
+}
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+
182
+
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+
192
+
193
+
194
+
195
+

+ 7 - 0
src/common/reg.js

@@ -0,0 +1,7 @@
1
+export default { 
2
+    idCard: /^\d{6}((?:19|20)((?:\d{2}(?:0[13578]|1[02])(?:0[1-9]|[12]\d|3[01]))|(?:\d{2}(?:0[13456789]|1[012])(?:0[1-9]|[12]\d|30))|(?:\d{2}02(?:0[1-9]|1\d|2[0-8]))|(?:(?:0[48]|[2468][048]|[13579][26])0229)))\d{2}(\d)[xX\d]$/,
3
+	name:  /^[\u4e00-\u9fa5]{2,4}$/,
4
+	email: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
5
+	phone: /^1[3456789]\d{9}$/,
6
+	vfCode: /^\d{6}$/,
7
+}

File diff suppressed because it is too large
+ 757 - 0
src/common/share.js


+ 205 - 0
src/common/tool.js

@@ -0,0 +1,205 @@
1
+import * as config from '@/config/env'
2
+import store from '@/store/index'
3
+import urls from '@/server/urls'
4
+const tool = {
5
+  timeSpace: 0, //本地和服务器的时间间隔
6
+  fetchSKtime: 0, //获取sessionkey超时的重试次数
7
+  getUrlParam: function (name) {
8
+    const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
9
+    if (location.href.indexOf("?") < 0) return "";
10
+    let r = location.href.split("?")[1].match(reg);
11
+    if (r != null) {
12
+      let d = decodeURIComponent(r[2]);
13
+      return tool.getIntValue(d);
14
+    }
15
+    return '';
16
+  },
17
+  getCookieValue: function (name) {
18
+    let nameEQ = name + "=";
19
+    let ca = document.cookie.split(';');
20
+    for (let i = 0; i < ca.length; i++) {
21
+      let c = ca[i];
22
+      while (c.charAt(0) == ' ') c = c.substring(1, c.length);
23
+      if (c.indexOf(nameEQ) == 0) {
24
+        return c.substring(nameEQ.length, c.length);
25
+      }
26
+    }
27
+    return "";
28
+  },
29
+  getIntValue (value) {
30
+    if (value === 'null' || value === 'undefined' || value === null || value === undefined) {
31
+      return ''
32
+    } else if (value === '0') {
33
+      return 0
34
+    }
35
+    return value
36
+  },
37
+  createCookie (name, value, days) {
38
+    let expires = "";
39
+    if (days) {
40
+      var date = new Date();
41
+      date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
42
+      expires = "; expires=" + date.toGMTString();
43
+    }
44
+    let cookieDomain = "";
45
+    if (name == "user_id" || name == "xyy") {
46
+      cookieDomain = "; domain=51yund.com";
47
+    }
48
+    document.cookie = name + "=" + encodeURIComponent(value) + expires + cookieDomain + "; path=/";
49
+  },
50
+  getYdUserKey: function (key) {
51
+    // 取值的时候先取store,再取storage
52
+    let storeVal = store.state.comVal;
53
+    let val = "";
54
+    if (storeVal[key]) {
55
+      val = storeVal[key];
56
+    } else {
57
+      val = _getLocalStorage(key) || sessionStorage.getItem(key);
58
+      val = tool.getIntValue(val);
59
+      if (!val) {
60
+        val = tool.getUrlParam(key);
61
+      }
62
+      if (val) {
63
+        store.dispatch('saveCommonValue', { key: key, value: val })
64
+      }
65
+    }
66
+    return val;
67
+  },
68
+  // 本地持久化存储
69
+  setStorage (name, value, expires) {
70
+    if (window.ydStorage) {
71
+      ydStorage.setItem(name, value, expires)
72
+    } else {
73
+      localStorage.setItem(name, value);
74
+    }
75
+  },
76
+  getSessionKey: function (userId, xyy, cb) {
77
+    var param = { "user_id": userId, "xyy": xyy };
78
+    return $http.post(urls.getsskey, param, true).then(function (res) {
79
+      if (res.code === 0) {
80
+        let sessionkey = res.session_key;
81
+        if (sessionkey) {
82
+          tool.fetchSKtime = 0; //获取成功之后把fetchSKtime还原
83
+          //存值的时候先存store,再存localStorage,存store为了取值更快,存storage为了看方便
84
+          store.dispatch('saveCommonValue', { key: 'session_key', value: sessionkey });
85
+          tool.setStorage("session_key", sessionkey, res.session_ttl);
86
+          store.dispatch('saveCommonValue', { 'key': 'user_id', 'value': userId });
87
+          localStorage.setItem("user_id", userId);
88
+          store.dispatch('saveCommonValue', { 'key': 'xyy', 'value': xyy });
89
+          localStorage.setItem("xyy", xyy);
90
+        }
91
+        let hasLogin = userId == 0 ? false : true;
92
+        if (cb) cb(hasLogin);
93
+        return sessionkey;
94
+      } else if (res.code !== 7007) { //userId和xyy不匹配或其它异常情况,最常见的场景是首次进来时,用户之前本地存储的xyy过期,7007的情况在http层统一处理
95
+        tool.toLogin();
96
+      }
97
+    }).catch(res => {
98
+      console.log('页面出错');
99
+    });
100
+  },
101
+  //简化版js节流,默认2s内只能点击一次
102
+  throttle: function (callback, duration = 2000) {
103
+    let lastTime = tool.lastTime || 0;
104
+    let now = new Date().getTime();
105
+    if (now - lastTime > duration) {
106
+      callback();
107
+      tool.lastTime = now;
108
+    }
109
+  },
110
+  //记录上报(访问来源上报、错误上报)
111
+  reportCmd: function (data) {
112
+    $http.postOnly(config.logPath + '/sport/report', data)
113
+  },
114
+  // 上报错误信息
115
+  postErrLog (data, cmdName) {
116
+    let hosts = location.host;
117
+    if ((hosts.indexOf("localhost") > -1 || hosts.indexOf("test") > -1) && !config.logFlag.dev) {
118
+      return;
119
+    }
120
+    let param = {
121
+      user_id: tool.getYdUserKey('user_id') || 0,
122
+      cmd: cmdName,
123
+      device_id: 'yuedongweb',
124
+      data: JSON.stringify(data)
125
+    }
126
+    tool.reportCmd(param);
127
+  },
128
+  $throwJS (data) { //抛出js异常
129
+    let obj = {  //公共部分
130
+      platform: "web",
131
+      local_path: location.pathname,
132
+      local_url: location.href,
133
+      package_name: config.logFlag.packageName
134
+    }
135
+    Object.assign(obj, data);
136
+    let filterJsErr = [];
137
+    if (config.filterErr && config.filterErr.length > 0) {
138
+      filterJsErr.push(...config.filterErr);
139
+    }
140
+    if (filterJsErr.indexOf(obj.err_msg) > -1) return;
141
+    let cmd_name = 'vue_jserr';
142
+    if (obj.err_msg && obj.err_msg.indexOf('http') > -1) {
143
+      cmd_name = 'vue_reserr';  //reserr表示资源加载异常(resource error)
144
+    }
145
+    tool.postErrLog(obj, cmd_name);
146
+  },
147
+  // 抛出请求异常
148
+  $throw (err, info, uri, response) {
149
+    let obj = {
150
+      local_url: location.href,
151
+      local_path: location.pathname,
152
+      err_msg: err + '',
153
+      req_params: info,
154
+      req_uri: uri
155
+    }
156
+    if (response) { //返回值结构体异常
157
+      obj.response = response
158
+    }
159
+    let filterErr = config.filterErr;
160
+    if (filterErr && filterErr.indexOf(obj.err_msg) > -1) return;
161
+    tool.postErrLog(obj, 'vue_reqerr');
162
+  },
163
+  //去登录
164
+  toLogin: function (backUrl) {
165
+    localStorage.removeItem('session_key');
166
+    localStorage.removeItem('xyy');
167
+    let cbUrl = backUrl ? backUrl : location.href;
168
+    cbUrl = encodeURIComponent(cbUrl);
169
+    // cbUrl = _clearUlrData(cbUrl);
170
+    console.log('配置登录页路由');
171
+  },
172
+}
173
+
174
+export const injectTool = () => {
175
+  if (typeof window.tool == 'undefined') {
176
+    window.tool = tool;
177
+  }
178
+}
179
+//跳转登录时去掉url上的user_id、xyy和is_login
180
+function _clearUlrData (cbUrl) {
181
+  if (cbUrl.indexOf("?") == -1) return encodeURIComponent(cbUrl);
182
+  let [url, query] = cbUrl.split("?");
183
+  let arr = ['user_id', 'xyy', 'is_login'];
184
+  for (let i = 0; i < arr.length; i++) {
185
+    if (query.indexOf(arr[i]) > -1) {
186
+      let reg = new RegExp('(^|&)' + arr[i] + '=[^&]*', 'g');
187
+      query = query.replace(reg, '')
188
+    }
189
+  }
190
+  if (query) {
191
+    if (query.indexOf('&') === 0) {  //最开始一位是&时去掉
192
+      query = query.slice(1);
193
+    }
194
+    url += '?' + query
195
+  }
196
+  return encodeURIComponent(url)
197
+}
198
+
199
+function _getLocalStorage (name) {
200
+  if (window.ydStorage) {
201
+    return ydStorage.getItem(name)
202
+  } else {
203
+    return localStorage.getItem(name);
204
+  }
205
+}

+ 10 - 0
src/common/utils.js

@@ -0,0 +1,10 @@
1
+// 自定义全局方法封装
2
+const utils = {
3
+  test: function () {},
4
+}
5
+
6
+export default () => {
7
+  if (typeof window.utils == 'undefined') {
8
+    window.utils = utils
9
+  }
10
+}

+ 114 - 0
src/common/validHelper.js

@@ -0,0 +1,114 @@
1
+/**
2
+ * @描述     表单校验
3
+ * @开发     Zach he
4
+ * @时间     2019-05-05
5
+ * @param  {object}   
6
+ *         options: {
7
+ *         		container:'className', 父容器class name 
8
+ *         }
9
+ * 
10
+ * @result {pass:false, msg:validObj.msg, dom:tempDom};
11
+ *         
12
+ * type枚举值与reg.js一致
13
+ */
14
+import reg from '@/common/reg.js'   //引入正则表达式
15
+
16
+const ValidHelper = (options) => {
17
+	let doms = document.querySelectorAll('.' + options.container);
18
+	if (doms == null || doms.length == 0) return {pass:true,msg:'未能查找到容器'};
19
+
20
+	for (var i = 0; i < doms.length; i++) {
21
+		let dom = doms[i];
22
+		let validDomArray = dom.querySelectorAll('.valid');
23
+
24
+		for (var j = 0; j < validDomArray.length; j++) {
25
+			let d = validDomArray[j];
26
+			let validStr = d.dataset['valid'];
27
+			if(d.className.indexOf('el-input')>-1){
28
+				validStr = d.querySelector('.el-input__inner').dataset['valid'];
29
+			}
30
+			if (validStr) {
31
+				let validStrArray = validStr.split(',');
32
+				let obj = {};
33
+
34
+				validStrArray.forEach((line)=>{
35
+					let attrArray = line.split(':');
36
+					obj[attrArray[0]] = attrArray[1];
37
+				}); 
38
+                 
39
+				let result = check(d, obj); 
40
+				if (!result.pass) { 
41
+					return result;
42
+				}
43
+			} else{       //只是纯input输入框校验空值
44
+				if (d.attributes.type == undefined) {
45
+					d = d.querySelector("input");
46
+				}
47
+                let val = d.value;
48
+				if(!val){
49
+					return {pass: false, msg: '请' + d.getAttribute('placeholder'), dom: d};
50
+				}
51
+			}
52
+		}
53
+	}
54
+	return {pass:true};
55
+}
56
+
57
+
58
+function check(tempDom,validObj) {
59
+	let tipWord="";
60
+	let val = "";
61
+	if (validObj.type.indexOf('select') != -1) {  //下拉框
62
+		tempDom=tempDom.querySelectorAll('.el-input__inner')[0];
63
+		tipWord=validObj.msg;
64
+		val = tempDom.value;
65
+	} else {
66
+		tipWord=tempDom.tagName == 'DIV' ? tempDom.querySelector('input').getAttribute('placeholder') : tempDom.getAttribute('placeholder');
67
+		tipWord = '请' + tipWord;
68
+		val = tempDom.className.indexOf('el-input') != -1 ?  tempDom.querySelector('input').value : tempDom.value;
69
+	} 
70
+	if(!val){
71
+        return {pass:false,msg:tipWord,dom:tempDom};
72
+	}
73
+	if (validObj.type.indexOf('phone') != -1) {   //校验手机
74
+		if (reg.phone.test(val)) return {pass:true}
75
+		else return {pass:false, msg:validObj.msg, dom:tempDom};
76
+	}
77
+	if (validObj.type.indexOf('vfCode') != -1) {  //校验验证码
78
+		if (reg.vfCode.test(val)) return {pass:true}
79
+		else return {pass:false, msg:validObj.msg, dom:tempDom};
80
+	} 
81
+	return {pass:true}
82
+}
83
+
84
+const handle = (validResult,isHandle) => {	 
85
+	if(isHandle){                     //校验不通过时添加class
86
+		let d = validResult.dom.tagName == "DIV" ? validResult.dom.querySelector('input') : validResult.dom;
87
+		let classVal=d.getAttribute('class');
88
+		if(classVal.indexOf('valid-red')=='-1'){
89
+			classVal=classVal.concat(' valid-red');
90
+			d.setAttribute('class',classVal);
91
+		}
92
+		globalVue.$message({type:'info',message:validResult.msg,duration: 1500});	
93
+	} else{                          //输入框获取焦点时移除class
94
+		let classVal=validResult.getAttribute('class');
95
+		classVal=classVal.replace('valid-red',"");
96
+		validResult.setAttribute('class',classVal);
97
+	}
98
+
99
+	// 添加事件 
100
+	if (validResult.dom) {
101
+		let d = validResult.dom.tagName == "DIV" ? validResult.dom.querySelector('input') : validResult.dom;
102
+		d.addEventListener('focus',(e)=>{ 
103
+			let target = e.srcElement || e.target;
104
+			handle(target);
105
+		}); 
106
+	}
107
+}
108
+
109
+if (typeof window.$ValidHelper == "undefined"){
110
+	window.$ValidHelper = {};
111
+	window.$ValidHelper.check = ValidHelper;
112
+	window.$ValidHelper.handle = handle;
113
+} 
114
+	

+ 14 - 0
src/config/env.js

@@ -0,0 +1,14 @@
1
+// 环境配置
2
+// todo: 注意要修改下面packageName为项目名称
3
+let appId = 102 //sso登录服务的appid,传0表示走老的授权登录
4
+let appVersion = 1 //sso登录服务的版本,默认为第一版
5
+let logFlag = {
6
+  dev: false, // 开发和测试环境是否上报log
7
+  from: false, // 是否上传页面来源
8
+  packageName: 'test',
9
+}
10
+let basePath = 'https://' // api请求地址
11
+
12
+export {
13
+  basePath
14
+}

+ 58 - 0
src/directives/index.js

@@ -0,0 +1,58 @@
1
+import Vue from 'vue'
2
+Vue.directive('copy', {
3
+  //自定义指令                                      JS
4
+  bind: function (el, binding) {
5
+    el.onclick = function (e) {
6
+      const range = document.createRange()
7
+      range.selectNode(el)
8
+      const selection = window.getSelection()
9
+      if (selection.rangeCount > 0) selection.removeAllRanges()
10
+      selection.addRange(range)
11
+      document.execCommand('copy')
12
+      globalVue.toast('复制成功')
13
+    }
14
+  },
15
+})
16
+
17
+Vue.directive('deal', {
18
+  //处理输入框被遮挡问题
19
+  bind: function (el, binding) {
20
+    if (el.tagName !== 'INPUT') {
21
+      el = el.querySelector('input')
22
+    }
23
+    ;(el.onfocus = function (e) {
24
+      //处理思路:将输入框位置放在可视区域(除开软键盘)的中间位置
25
+      let offsetTop = e.target.getBoundingClientRect().top
26
+      let scrollDom = document.querySelector('.container')
27
+      let scrolltop = scrollDom.scrollTop
28
+      let emptyDom = document.createElement('div')
29
+      emptyDom.className = 'empty-input-deal'
30
+      scrollDom.appendChild(emptyDom) //底部空间不足时滚动拉不上来,所以用一个空div撑开底部空间
31
+      if (tool.isIosWeb()) {
32
+        //ios下软键盘弹起时页面会整体往上拉,所以要区别处理
33
+        let bottomDom = document.querySelector('.fix-bottom')
34
+        let screenHeightFirst = bottomDom.getBoundingClientRect().top
35
+        let timer = setTimeout(() => {
36
+          let screenHeight = bottomDom.getBoundingClientRect().top //注意这里不能用window.innerHeight,ios下不管软键盘是否弹出innerHeight都不变,
37
+          let keyBoardHeight = screenHeightFirst - screenHeight || 400 //获取软键盘高度,只在ios下有用,如果没有获取到值,就默认400
38
+          emptyDom.style.height = keyBoardHeight + 'px' //让空div的高度刚好等于软键盘的高度
39
+          if (offsetTop > (3 / 5) * screenHeightFirst) {
40
+            //如果输入框在整个页面的底部2/5的区域,点击时就让页面向上多滚动100px
41
+            scrollDom.scrollTop = scrolltop + 100
42
+          }
43
+          timer = null
44
+        }, 100)
45
+      } else {
46
+        //安卓下保证输入框滚动到可视区中间往上150px的地方
47
+        emptyDom.style.height = '400px' //安卓内嵌webview页面无法获取软键盘高度,uc浏览器可以用window.innerHeight的差值获取
48
+        let tarH = offsetTop - window.innerHeight / 2 //输入框距离可视区中间点的差值
49
+        scrollDom.scrollTop = scrolltop + tarH + 150
50
+      }
51
+    }),
52
+      (el.onblur = function (e) {
53
+        let scrollDom = document.querySelector('.container')
54
+        let emptyDom = document.querySelector('.empty-input-deal')
55
+        scrollDom.removeChild(emptyDom)
56
+      })
57
+  },
58
+})

+ 34 - 0
src/filters/filter.js

@@ -0,0 +1,34 @@
1
+import Vue from 'vue'
2
+/**
3
+ * 日期格式重置   页面用法:{{'26565353' | formatDate(yyyy/MM/dd hh:mm)}}  返回值:'2017/08/22 18:30'
4
+ */
5
+Vue.filter('formatDate', function (value, fmt) {
6
+  if (!value) return value
7
+  let timeSc = new Date(value * 1000)
8
+  if (/(y+)/.test(fmt)) {
9
+    fmt = fmt.replace(
10
+      RegExp.$1,
11
+      (timeSc.getFullYear() + '').substr(4 - RegExp.$1.length),
12
+    )
13
+  }
14
+  let o = {
15
+    'M+': timeSc.getMonth() + 1,
16
+    'd+': timeSc.getDate(),
17
+    'h+': timeSc.getHours(),
18
+    'm+': timeSc.getMinutes(),
19
+    's+': timeSc.getSeconds(),
20
+  }
21
+  for (let k in o) {
22
+    if (new RegExp(`(${k})`).test(fmt)) {
23
+      let str = o[k] + ''
24
+      fmt = fmt.replace(
25
+        RegExp.$1,
26
+        RegExp.$1.length === 1 ? str : padLeftZero(str),
27
+      )
28
+    }
29
+  }
30
+  function padLeftZero(str) {
31
+    return ('00' + str).substr(str.length)
32
+  }
33
+  return fmt
34
+})

+ 27 - 0
src/main.js

@@ -0,0 +1,27 @@
1
+import Vue from 'vue'
2
+import App from './App.vue'
3
+import router from './router/index'
4
+import store from './store/index'
5
+import { injectGlobal } from './common/'
6
+// import login from './common/login'
7
+import auth from './common/auth'
8
+import './style/reset.less'
9
+import './style/common.less'
10
+import './filters/filter'
11
+import './directives/index'
12
+//全局注入
13
+injectGlobal()
14
+
15
+//这段代码是为了先拿到sessionkey再初始化项目,避免直接访问页面不存在user_id和xyy的情况
16
+window.globalVue = ''
17
+// login((hasLogin) => {
18
+auth(router, false)
19
+Vue.config.productionTip = false
20
+const globalVue = new Vue({
21
+  router,
22
+  store,
23
+  render: (h) => h(App),
24
+}).$mount('#app')
25
+window.globalVue = globalVue
26
+window.ydStorage && ydStorage.postItem()
27
+// })

+ 23 - 0
src/pages/home/index.vue

@@ -0,0 +1,23 @@
1
+<template>
2
+  <div class="container">welcome come here</div>
3
+</template>
4
+
5
+<script>
6
+export default {
7
+  name: 'home',
8
+  data() {
9
+    return {}
10
+  },
11
+  mounted: function () {},
12
+  created: function () {},
13
+  methods: {},
14
+}
15
+</script>
16
+<style lang="less" scoped>
17
+@import url(../../style/root.less);
18
+.container {
19
+  font-size: 0.14rem;
20
+  padding-top: 0.2rem;
21
+  text-align: center;
22
+}
23
+</style>

File diff suppressed because it is too large
+ 238 - 0
src/pages/home/xiaoshuo.vue


+ 42 - 0
src/router/index.js

@@ -0,0 +1,42 @@
1
+import Vue from 'vue'
2
+import Router from 'vue-router'
3
+
4
+//下面四句代码是为了处理router.beforeEach的异常报错(https://blog.csdn.net/haidong55/article/details/100939076)
5
+const originalPush = Router.prototype.push
6
+Router.prototype.push = function push (location, onResolve, onReject) {
7
+  if (onResolve || onReject)
8
+    return originalPush.call(this, location, onResolve, onReject)
9
+  return originalPush.call(this, location).catch((err) => err)
10
+}
11
+
12
+Vue.use(Router)
13
+
14
+export default new Router({
15
+  mode: 'hash',
16
+  routes: [
17
+    {
18
+      path: '/',
19
+      component: () => import('@/pages/home/xiaoshuo'),
20
+      meta: {
21
+        title: '悦动圈',
22
+        auth: false,
23
+      },
24
+    },
25
+    {
26
+      path: '/index',
27
+      component: () => import('@/pages/home/index'),
28
+      meta: {
29
+        title: '悦动圈',
30
+        auth: false,
31
+      },
32
+    },
33
+    {
34
+      path: '/xiaoshuo',
35
+      component: () => import('@/pages/home/xiaoshuo'),
36
+      meta: {
37
+        title: '隐私政策',
38
+        auth: false,
39
+      },
40
+    },
41
+  ],
42
+})

+ 12 - 0
src/server/home.js

@@ -0,0 +1,12 @@
1
+// created by zach He on 2019-04-29
2
+// 会员 登录、注册、修改密码等等
3
+import url from './urls'
4
+
5
+export default class Home {
6
+  /**
7
+   * @描述  1.1	获取首页banner图
8
+   */
9
+  static getBanner(parms) {
10
+    return $http.post(url.getBanner, parms)
11
+  }
12
+}

+ 19 - 0
src/server/login.js

@@ -0,0 +1,19 @@
1
+/**
2
+ * Created by zach He on 2019-04-29
3
+ */
4
+import url from './urls'
5
+
6
+export default class LoginMember {
7
+  static wxLogin(params) {
8
+    return $http.post(url.wxLogin, params)
9
+  }
10
+
11
+  static qqLogin(params) {
12
+    return $http.post(url.qqLogin, params)
13
+  }
14
+
15
+  // 登出管理
16
+  static loginOut(params) {
17
+    return $http.post(url.loginOut, params)
18
+  }
19
+}

+ 4 - 0
src/server/urls.js

@@ -0,0 +1,4 @@
1
+export default {
2
+  //登录获取签名
3
+  getsskey: '/sport/get_session_key',
4
+}

+ 11 - 0
src/store/index.js

@@ -0,0 +1,11 @@
1
+import Vue from 'vue'
2
+import Vuex from 'vuex'
3
+import comVal from './modules/comVal'
4
+
5
+Vue.use(Vuex)
6
+
7
+export default new Vuex.Store({
8
+  modules: {
9
+    comVal,
10
+  },
11
+})

+ 22 - 0
src/store/modules/comVal.js

@@ -0,0 +1,22 @@
1
+const state = {
2
+  name: 666,
3
+}
4
+
5
+const actions = {
6
+  saveCommonValue({ commit }, value) {
7
+    commit('SAVE_COMMON_VALUE', value)
8
+  },
9
+}
10
+
11
+const mutations = {
12
+  SAVE_COMMON_VALUE(state, obj) {
13
+    state[obj.key] = obj.value
14
+    // state.data = data;
15
+  },
16
+}
17
+
18
+export default {
19
+  state,
20
+  actions,
21
+  mutations,
22
+}

+ 47 - 0
src/style/common.less

@@ -0,0 +1,47 @@
1
+.yd-message {
2
+  display: table;
3
+  position: fixed;
4
+  z-index: 2001;
5
+  width: 80%;
6
+  top: 0;
7
+  right: 0;
8
+  left: 0;
9
+  bottom: 0;
10
+  margin: auto;
11
+  text-align: center;
12
+  span {
13
+    background-color: rgba(0, 0, 0, 0.6);
14
+    color: #fff;
15
+    border-radius: 0.04rem;
16
+    padding: 0.1rem 0.12rem;
17
+    font-size: 0.14rem;
18
+    display: inline-block;
19
+    line-height: 0.2rem;
20
+    min-width: 40vw;
21
+  }
22
+}
23
+.fr {
24
+  float: right;
25
+}
26
+.arrow-r {
27
+  position: relative;
28
+  padding-right: 0.12rem;
29
+  &:before {
30
+    content: '';
31
+    height: 6px;
32
+    width: 6px;
33
+    border-width: 2px 2px 0 0;
34
+    border-color: #c8c8cd;
35
+    border-style: solid;
36
+    transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);
37
+    position: absolute;
38
+    top: 50%;
39
+    margin-top: -4px;
40
+    right: 0;
41
+  }
42
+}
43
+.container {
44
+  height: 100vh;
45
+  overflow-y: auto;
46
+  -webkit-overflow-scrolling: touch;
47
+}

+ 47 - 0
src/style/reset.less

@@ -0,0 +1,47 @@
1
+/*样式重置*/
2
+body,
3
+html {
4
+  margin: 0;
5
+  font: normal normal normal 14px/20px sans-serif;
6
+  line-height: 1;
7
+}
8
+html * {
9
+  margin: 0;
10
+  padding: 0;
11
+  box-sizing: border-box;
12
+}
13
+html body {
14
+  max-width: 640px !important;
15
+  margin: 0 auto;
16
+}
17
+.clearfix:after {
18
+  content: '\0020';
19
+  display: block;
20
+  height: 0;
21
+  clear: both;
22
+}
23
+.clearfix {
24
+  _zoom: 1;
25
+}
26
+ul {
27
+  list-style: none;
28
+}
29
+li,
30
+i,
31
+em {
32
+  font-style: normal;
33
+}
34
+input {
35
+  outline: medium;
36
+  border: none;
37
+}
38
+a:focus,
39
+select {
40
+  outline: none;
41
+}
42
+a {
43
+  text-decoration: none;
44
+}
45
+a:hover {
46
+  text-decoration: none;
47
+}

+ 50 - 0
src/style/root.less

@@ -0,0 +1,50 @@
1
+@mainColor: #11d59c; //全局主色调
2
+@width: 300px;
3
+.widh(@w: 100px, @h: 200px) {
4
+  width: @w;
5
+  height: @h;
6
+}
7
+.lett(@l: 100px, @t: 100px) {
8
+  left: @l;
9
+  top: @t;
10
+}
11
+.bg(@color: #696) {
12
+  background-color: @color;
13
+  cursor: pointer;
14
+}
15
+.hei(@height: 30px) {
16
+  height: @height;
17
+  line-height: @height;
18
+}
19
+@box: {
20
+  width: @width - 100;
21
+  height: 200px;
22
+  border: solid 1px red;
23
+  margin: 0 auto;
24
+  color: @mainColor;
25
+};
26
+@absolute: {
27
+  position: absolute;
28
+  .widh(200px);
29
+  .lett(100px, 100px);
30
+  z-index: 10;
31
+};
32
+.btnBg(@bgColor: #696) {
33
+  transition: all 0.2s;
34
+  &:hover {
35
+    background-color: @bgColor;
36
+  }
37
+}
38
+.btn(@color: #696) {
39
+  transition: all 0.2s;
40
+  &:hover {
41
+    color: @color;
42
+  }
43
+}
44
+.abso(@l: 0, @t: 0, @w: 100%) {
45
+  position: absolute;
46
+  z-index: 10;
47
+  left: @l;
48
+  top: @t;
49
+  width: @w;
50
+}

+ 19 - 0
tests/e2e/custom-assertions/elementCount.js

@@ -0,0 +1,19 @@
1
+// A custom Nightwatch assertion.
2
+// The assertion name is the filename.
3
+// Example usage:
4
+//
5
+//   browser.assert.elementCount(selector, count)
6
+//
7
+// For more information on custom assertions see:
8
+// http://nightwatchjs.org/guide#writing-custom-assertions
9
+
10
+exports.assertion = function elementCount(selector, count) {
11
+  this.message = `Testing if element <${selector}> has count: ${count}`
12
+  this.expected = count
13
+  this.pass = (val) => val === count
14
+  this.value = (res) => res.value
15
+  function evaluator(_selector) {
16
+    return document.querySelectorAll(_selector).length
17
+  }
18
+  this.command = (cb) => this.api.execute(evaluator, [selector], cb)
19
+}

+ 26 - 0
tests/e2e/reports/CHROME_71.0.3578.80_Windows NT_test.xml

@@ -0,0 +1,26 @@
1
+<?xml version="1.0" encoding="UTF-8" ?>
2
+<testsuites errors="0"
3
+            failures="0"
4
+            tests="1">
5
+
6
+  <testsuite name="test"
7
+    errors="0" failures="0" hostname="" id="" package="test" skipped="0"
8
+    tests="1" time="3.642" timestamp="Fri, 15 Mar 2019 05:44:11 GMT">
9
+  
10
+    <testcase name="default e2e tests" classname="test" time="3.642" assertions="4">
11
+
12
+    
13
+
14
+    
15
+
16
+    
17
+
18
+    
19
+    </testcase>
20
+  
21
+
22
+  
23
+
24
+  
25
+  </testsuite>
26
+</testsuites>

+ 26 - 0
tests/e2e/reports/CHROME_74.0.3729.108_Windows NT_test.xml

@@ -0,0 +1,26 @@
1
+<?xml version="1.0" encoding="UTF-8" ?>
2
+<testsuites errors="0"
3
+            failures="0"
4
+            tests="1">
5
+
6
+  <testsuite name="test"
7
+    errors="0" failures="0" hostname="" id="" package="test" skipped="0"
8
+    tests="1" time="15.46" timestamp="Tue, 30 Apr 2019 10:48:09 GMT">
9
+  
10
+    <testcase name="default e2e tests" classname="test" time="15.46" assertions="4">
11
+
12
+    
13
+
14
+    
15
+
16
+    
17
+
18
+    
19
+    </testcase>
20
+  
21
+
22
+  
23
+
24
+  
25
+  </testsuite>
26
+</testsuites>

+ 14 - 0
tests/e2e/specs/test.js

@@ -0,0 +1,14 @@
1
+// For authoring Nightwatch tests, see
2
+// http://nightwatchjs.org/guide#usage
3
+
4
+module.exports = {
5
+  'default e2e tests': (browser) => {
6
+    browser
7
+      .url(process.env.VUE_DEV_SERVER_URL)
8
+      .waitForElementVisible('#app', 5000)
9
+      .assert.elementPresent('.hello')
10
+      .assert.containsText('h1', 'Welcome to Your Vue.js ,样式由less书写')
11
+      .assert.elementCount('img', 1)
12
+      .end()
13
+  },
14
+}

+ 5 - 0
tests/unit/.eslintrc.js

@@ -0,0 +1,5 @@
1
+module.exports = {
2
+  env: {
3
+    jest: true,
4
+  },
5
+}

+ 12 - 0
tests/unit/components/hello.spec.js

@@ -0,0 +1,12 @@
1
+import { shallowMount } from '@vue/test-utils'
2
+import HelloWorld from '@/components/modules/helloWorld.vue'
3
+
4
+describe('helloWorld.vue', () => {
5
+  it('renders props.msg when passed', () => {
6
+    const msg = 'new message'
7
+    const wrapper = shallowMount(HelloWorld, {
8
+      propsData: { msg },
9
+    })
10
+    expect(wrapper.text()).toMatch(msg)
11
+  })
12
+})

+ 11 - 0
tests/unit/pages/loan.spec.js

@@ -0,0 +1,11 @@
1
+import { mount } from '@vue/test-utils'
2
+import home from '@/pages/home/index.vue'
3
+import HelloWorld from '@/components/modules/helloWorld.vue'
4
+
5
+describe('HelloWorld.vue', () => {
6
+  it('render img', () => {
7
+    const msg = 'new message'
8
+    const wrapper = mount(home)
9
+    expect(wrapper.contains(HelloWorld)).toBe(true)
10
+  })
11
+})

+ 66 - 0
vue.config.js

@@ -0,0 +1,66 @@
1
+// vue.config.js 配置说明
2
+//官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
3
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
4
+const cdn = {
5
+  js: [
6
+    'https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js',
7
+    'https://cdn.jsdelivr.net/npm/crypto-js@4.0.0/crypto-js.js',
8
+    'https://unpkg.com/vue-router@3.0.0/dist/vue-router.js',
9
+  ],
10
+}
11
+module.exports = {
12
+  //配置线上域名&地址
13
+  publicPath: process.env.env_config === 'prod' ? 'https://h5-cms-vue.ijolijoli.com/agreement/dist' :
14
+    process.env.env_config === 'test' ? '/agreement/dist' : '/dist/',
15
+  // 设置跨域
16
+  crossorigin: 'anonymous',
17
+
18
+  /**
19
+   * 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
20
+   *  map文件的作用在于:项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错。
21
+   *  有了map就可以像未加密的代码一样,准确的输出是哪一行哪一列有错。
22
+   * */
23
+  productionSourceMap: false,
24
+  transpileDependencies: ['yd-ui', 'swiper', 'dom7'],
25
+  // 它支持webPack-dev-server的所有选项
26
+  devServer: {
27
+    host: '0.0.0.0',
28
+    port: 8082, // 端口号
29
+    https: false, // https:{type:Boolean}
30
+    open: false, //配置自动启动浏览器
31
+    proxy: '', // 配置跨域处理
32
+  },
33
+
34
+  //这是一个不进行任何 schema 验证的对象,因此它可以用来传递任何第三方插件选项
35
+  pluginOptions: {
36
+    // 设置线上部署路径,发布时请修改 eg: /data2/wwwroot/web/vue/vapps/vip
37
+    onlineRootPath: '__ONLINE_ROOT_PATH__',
38
+    testRootPath: '__TEST_ROOT_PATH__',
39
+  },
40
+  configureWebpack: {
41
+    externals: {
42
+      vue: 'Vue',
43
+      'vue-router': 'VueRouter',
44
+      'crypto-js': 'CryptoJS',
45
+      axios: 'axios',
46
+    },
47
+    // 这是七牛找不到资源时的紧急处理方法,通过修改hash位数修改编译后的资源文件名
48
+    // output: {
49
+    //     filename: `js/[name].[hash:6].js`,
50
+    //     chunkFilename: `js/[name].[hash:6].js`
51
+    // },
52
+    // plugins: [
53
+    //     new MiniCssExtractPlugin({
54
+    //         filename: `css/[name].[hash:6].css`,
55
+    //         chunkFilename: `css/[name].[hash:6].css`
56
+    //     })
57
+    // ]
58
+  },
59
+
60
+  chainWebpack: (config) => {
61
+    config.plugin('html').tap((options) => {
62
+      options[0].cdn = cdn
63
+      return options
64
+    })
65
+  },
66
+}

+ 1 - 0
安装插件.bat

@@ -0,0 +1 @@
1
+npm install --registry=https://registry.npm.taobao.org --verbose 

+ 163 - 0
规范说明

@@ -0,0 +1,163 @@
1
+项目结构与规范说明
2
+
3
+一、目录结构说明
4
+src
5
+	assets			静态资源(图片、字体等)
6
+	common 			项目全局公共函数封装
7
+	components		全部组件
8
+		common 		通用组件,不带有任何业务数据,只带有通用样式或特定样式以及事件;以文件夹的形式隔开,如果存在资源则建立子目录存放
9
+		modules		业务组件,带有通用业务数据;
10
+	directives		全局指令 按文件名划分
11
+	filters			全局筛选指令
12
+	pages			页面容器
13
+		login		路由为/home的页面,该页面如果涉及子页面,则创建children目录进行存放,多级递归。注意这里多级时路由配置也需要多级配置
14
+			children
15
+				child.vue
16
+			index.vue
17
+	plugins 		js第三方插件
18
+	router			路由配置
19
+		map			各个页面对应的路由配置,注意:这里是按页面来配置
20
+	server			前端领域业务封装,文件名按领域来区分;测试用例针对领域来编写流程
21
+		testData	按领域来存放测试数据
22
+	store			vuex 模块
23
+		modules		细分各个模块
24
+		index		vuex入口
25
+	style			全局样式
26
+		common.less 全局公共样式
27
+		root.less	全局less变量		
28
+tests
29
+	e2e				端到端测试
30
+	unit			单元测试
31
+vue.config.js		工程基本配置文件-端口、编译输出目录...
32
+
33
+
34
+三、js规范
35
+变量
36
+1.当前作用域顶端定义
37
+2.var禁用 在块级作用域下使用let
38
+3.在全局作用域下,函数及变量使用const定义
39
+4.字符串统一使用单引号,标签或者属性的值采用双引号
40
+
41
+函数
42
+1.that=this; 函数内部存在嵌套函数时,必须在最外层定义this指向
43
+2.内部私有方法写在return之后
44
+3.判断是否相等用全等号
45
+
46
+类
47
+1.定义顺序:变量、公开方法、私有方法
48
+
49
+模块
50
+1.export default 写到最底部
51
+2.export 变量写在一起靠底部位置
52
+
53
+命名
54
+1.变量、函数、vue组件名称 小写开头 驼峰规则
55
+2.私有加前缀(下划线)
56
+3.类命名 大写开始 驼峰规则
57
+4.资源文件全部用小写命名(包括文件夹),如backbtn.png、 province_city.json
58
+
59
+四、css规范
60
+每个style标签必去加上scoped
61
+样式规则:
62
+.classname{
63
+	//定位规则
64
+
65
+	//盒子属性
66
+
67
+	//其它
68
+}
69
+
70
+classname:命名规则 小写;多个单词-(中划线)分格
71
+
72
+层级样式减少标签直接使用方式;
73
+层级定义样式规则:
74
+.父级
75
+	.父级-子级
76
+		.父级-子级-子级		
77
+class相隔必须用-(中划线)
78
+id相隔必须用_(下划线)
79
+
80
+样式封装块,重复样式定义继承处理
81
+.commonclassname
82
+
83
+.classname {
84
+	//定位规则
85
+
86
+	//盒子属性
87
+
88
+	//其它
89
+
90
+	&:伪类 {
91
+
92
+	}
93
+
94
+	// 嵌套层级
95
+	.classname {
96
+		
97
+	}
98
+
99
+	.classname {
100
+
101
+	}
102
+}
103
+
104
+五、路由规范
105
+	根据页面来制定路由区域
106
+
107
+六、Vue文件规范
108
+<template>
109
+	// v-on => @
110
+	// v-bind => :
111
+</template>
112
+<script>
113
+	// 模块引入
114
+	// 对象接口
115
+	export default {
116
+		props:{
117
+			// 父级传入属性 
118
+	  	},
119
+		data(){
120
+			return {
121
+				组件相关初始化数据定义
122
+			}
123
+		},	
124
+
125
+		created(){
126
+			// 异步装载初始化数据
127
+			this.methodC();
128
+		},
129
+
130
+		components:{
131
+			// 引用的组件
132
+
133
+		},
134
+		
135
+		methods:{
136
+			// 一些数据交互的问题
137
+			methodA() {
138
+
139
+			},
140
+			methodB() {
141
+
142
+			},
143
+			async methodC() {
144
+
145
+			}
146
+		},
147
+		computed:{
148
+			// 需要属性计算的操作
149
+		},
150
+		watch:{
151
+			// 检控属性变化
152
+		}
153
+	}
154
+</script>
155
+<style lang="less" scoped>
156
+	
157
+</style>
158
+
159
+
160
+
161
+
162
+
163
+