init

FFIB 5 年之前
當前提交
58e01fb688
共有 88 個文件被更改,包括 8357 次插入0 次删除
  1. 二進制
      .DS_Store
  2. 4 0
      .eslintignore
  3. 31 0
      .eslintrc.js
  4. 2 0
      .gitattributes
  5. 2 0
      .gitignore
  6. 13 0
      .gitignorefile
  7. 7 0
      .prettierrc
  8. 1 0
      .stylelintignore
  9. 16 0
      .stylelintrc.json
  10. 0 0
      README.md
  11. 56 0
      gulpfile.js
  12. 60 0
      package.json
  13. 二進制
      src/.DS_Store
  14. 83 0
      src/app.js
  15. 23 0
      src/app.json
  16. 97 0
      src/app.wxss
  17. 79 0
      src/brand/brand.wxss
  18. 37 0
      src/brand/config.js
  19. 二進制
      src/component/.DS_Store
  20. 62 0
      src/component/btnFooter-protocol/btnFooter-protocol.js
  21. 4 0
      src/component/btnFooter-protocol/btnFooter-protocol.json
  22. 42 0
      src/component/btnFooter-protocol/btnFooter-protocol.less
  23. 11 0
      src/component/btnFooter-protocol/btnFooter-protocol.wxml
  24. 36 0
      src/component/btnFooter-protocol/btnFooter-protocol.wxss
  25. 二進制
      src/component/dialog-input/.DS_Store
  26. 127 0
      src/component/dialog-input/dialog-input.js
  27. 4 0
      src/component/dialog-input/dialog-input.json
  28. 115 0
      src/component/dialog-input/dialog-input.less
  29. 24 0
      src/component/dialog-input/dialog-input.wxml
  30. 122 0
      src/component/dialog-input/dialog-input.wxss
  31. 1 0
      src/component/stepper/add.svg
  32. 1 0
      src/component/stepper/minus.svg
  33. 27 0
      src/component/stepper/stepper.js
  34. 4 0
      src/component/stepper/stepper.json
  35. 17 0
      src/component/stepper/stepper.less
  36. 5 0
      src/component/stepper/stepper.wxml
  37. 13 0
      src/component/stepper/stepper.wxss
  38. 二進制
      src/pages/.DS_Store
  39. 205 0
      src/pages/index/index.js
  40. 6 0
      src/pages/index/index.json
  41. 310 0
      src/pages/index/index.less
  42. 93 0
      src/pages/index/index.wxml
  43. 255 0
      src/pages/index/index.wxss
  44. 85 0
      src/pages/order/order.js
  45. 5 0
      src/pages/order/order.json
  46. 204 0
      src/pages/order/order.less
  47. 64 0
      src/pages/order/order.wxml
  48. 201 0
      src/pages/order/order.wxss
  49. 126 0
      src/project.config.json
  50. 二進制
      src/resources/.DS_Store
  51. 二進制
      src/resources/common/.DS_Store
  52. 二進制
      src/resources/common/location.png
  53. 9 0
      src/sitemap.json
  54. 96 0
      src/style/button.less
  55. 75 0
      src/style/button.wxss
  56. 85 0
      src/style/less/layout.less
  57. 70 0
      src/style/less/shape.less
  58. 48 0
      src/style/less/variable.less
  59. 19 0
      src/template/btn/btn.less
  60. 17 0
      src/template/btn/btn.wxml
  61. 14 0
      src/template/btn/btn.wxss
  62. 81 0
      src/template/footer/footer.less
  63. 37 0
      src/template/footer/footer.wxml
  64. 74 0
      src/template/footer/footer.wxss
  65. 65 0
      src/template/resultView/resultView.js
  66. 5 0
      src/template/resultView/resultView.json
  67. 99 0
      src/template/resultView/resultView.less
  68. 33 0
      src/template/resultView/resultView.wxml
  69. 91 0
      src/template/resultView/resultView.wxss
  70. 1 0
      src/template/resultView/success.svg
  71. 18 0
      src/template/webView/webView.js
  72. 3 0
      src/template/webView/webView.json
  73. 1 0
      src/template/webView/webView.wxml
  74. 1 0
      src/template/webView/webView.wxss
  75. 二進制
      src/utils/.DS_Store
  76. 155 0
      src/utils/network.js
  77. 34 0
      src/utils/router.js
  78. 27 0
      src/utils/routerInterceptor.js
  79. 10 0
      src/utils/urls.js
  80. 二進制
      src/utils/wxParse/.DS_Store
  81. 302 0
      src/utils/wxParse/html2json.js
  82. 183 0
      src/utils/wxParse/htmlparser.js
  83. 2412 0
      src/utils/wxParse/showdown.js
  84. 206 0
      src/utils/wxParse/wxDiscode.js
  85. 159 0
      src/utils/wxParse/wxParse.js
  86. 967 0
      src/utils/wxParse/wxParse.wxml
  87. 270 0
      src/utils/wxParse/wxParse.wxss
  88. 10 0
      src/wxs/stringFilter.wxs

二進制
.DS_Store


+ 4 - 0
.eslintignore

@@ -0,0 +1,4 @@
1
+*/utils/wxParse/*.js
2
+*/utils/barcode.js
3
+gulpfile.js
4
+*.wxs

+ 31 - 0
.eslintrc.js

@@ -0,0 +1,31 @@
1
+module.exports = {
2
+  parser: "babel-eslint",
3
+  env: {
4
+    browser: true,
5
+    es6: true,
6
+  },
7
+  extends: [
8
+    'airbnb-base', 'prettier', 'plugin:prettier/recommended',
9
+  ],
10
+  plugins: ["prettier"],
11
+  globals: {
12
+    Atomics: 'readonly',
13
+    SharedArrayBuffer: 'readonly',
14
+    "wx": "readonly",
15
+    "App": "readonly",
16
+    "Page": "readonly",
17
+    "Component": "readonly",
18
+    "getApp": "readonly",
19
+    "getCurrentPages": "readonly",
20
+    "requirePlugin": "readonly"
21
+  },
22
+  parserOptions: {
23
+    ecmaVersion: 2018,
24
+    sourceType: 'module',
25
+  },
26
+  rules: {
27
+    "prettier/prettier": "error",
28
+    "camelcase": ["off", {"ignoreDestructuring": true}],
29
+    "no-underscore-dangle": ["off", {"allowAfterThis": true}]
30
+  },
31
+};

+ 2 - 0
.gitattributes

@@ -0,0 +1,2 @@
1
+app.json merge=ours
2
+

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
1
+node_modules
2
+package-lock.json

+ 13 - 0
.gitignorefile

@@ -0,0 +1,13 @@
1
+*swp
2
+.idea
3
+.DS_Store
4
+package-lock.json
5
+
6
+logs
7
+*.log
8
+npm-debug.log*
9
+yarn-debug.log*
10
+yarn-error.log*
11
+node_modules
12
+miniprogram/mock
13
+miniprogram/node_modules

+ 7 - 0
.prettierrc

@@ -0,0 +1,7 @@
1
+{
2
+    "eslintIntegration": true,
3
+    "stylelintIntegration": true,
4
+    "tabWidth": 2,
5
+    "singleQuote": true,
6
+    "semi": false
7
+}

+ 1 - 0
.stylelintignore

@@ -0,0 +1 @@
1
+*.wxss

+ 16 - 0
.stylelintrc.json

@@ -0,0 +1,16 @@
1
+{
2
+  "plugins": ["stylelint-prettier"],
3
+  "extends": ["stylelint-config-standard", "stylelint-config-recess-order"],
4
+  "rules": {
5
+    "prettier/prettier": true,
6
+    "at-rule-no-unknown": [true, {"ignoreAtRules" :[
7
+      "mixin", "extend", "content"
8
+    ]}],
9
+    "unit-no-unknown": [true, {"ignoreUnits": [
10
+      "rpx"
11
+    ]}],
12
+    "selector-type-no-unknown": [true, {"ignoreTypes": [
13
+      "swiper", "swiper-item", "page"
14
+    ]}]
15
+  }
16
+}

+ 0 - 0
README.md


+ 56 - 0
gulpfile.js

@@ -0,0 +1,56 @@
1
+// const gulp = require('gulp');
2
+const { watch, src, dest, series, parallel } = require('gulp');
3
+const rename = require('gulp-rename');
4
+const less = require('gulp-less');
5
+const replace = require('gulp-replace');
6
+const through2 = require('through2');
7
+const jeditor = require('gulp-json-editor');
8
+const gulpInstall = require('gulp-install');
9
+
10
+const srcDir = './src';
11
+// const appJson = require('./miniprogram/app.json');
12
+
13
+function wxss() {
14
+  return (
15
+    src([`${srcDir}/**/*.less`, `!${srcDir}/**/_*.less`, `!${srcDir}/style/less/*.less`,`!${srcDir}/**/*.wxss`])
16
+      .pipe(replace(/\@(import\s[^@;]*)+(;import|\bimport|;|b)?/g, ($1) => {
17
+        let isMixin = $1.indexOf(".less")
18
+        if (isMixin == -1) {
19
+          return `\/*T${$1}T*\/`
20
+        } else {
21
+          return $1
22
+        }
23
+      }))
24
+      .pipe(
25
+        less().on('error', function(err) {
26
+          console.error(err);
27
+          this.emit('end'); // 防止中断
28
+        })
29
+      )
30
+      .pipe(
31
+        rename(path => {
32
+          path.extname = '.wxss';
33
+        })
34
+      )
35
+      .pipe(replace(/\/\*T(@import\s[^@;]*;)?(T\*\/)?/g, '$1'))
36
+      .pipe(
37
+        dest(file => {
38
+          return file.base;
39
+        })
40
+      )
41
+  );
42
+}
43
+
44
+
45
+function watchCSS() {
46
+  watch(`${srcDir}/**/*.less`, series(wxss));
47
+}
48
+
49
+function clean() {
50
+  del(['./dist/**'])
51
+}
52
+
53
+exports.watchCSS = watchCSS;
54
+exports.wxss = wxss;
55
+exports.dev =  series(wxss, watchCSS);
56
+exports.default = wxss;

+ 60 - 0
package.json

@@ -0,0 +1,60 @@
1
+{
2
+  "name": "kodo",
3
+  "version": "1.0.0",
4
+  "description": "统览系统",
5
+  "main": "index.js",
6
+  "miniprogram": "dist",
7
+  "scripts": {
8
+    "dev": "gulp dev",
9
+    "init": "tnpm install && cd src && tnpm install",
10
+    "eslint": "./node_modules/.bin/eslint src",
11
+    "eslint:fix": "./node_modules/.bin/eslint --fix src",
12
+    "stylelint": "./node_modules/.bin/stylelint src/**/*.less src/**/**/*.less src/**/**/**/*.less src/**/**/**/**/*.less src/**/**/**/**/**/*.less src/**/**/**/**/**/**/*.less",
13
+    "stylelint:fix": "./node_modules/.bin/stylelint src/**/*.less src/**/**/*.less src/**/**/**/*.less src/**/**/**/**/*.less src/**/**/**/**/**/*.less src/**/**/**/**/**/**/*.less --fix",
14
+    "format": "prettier --write",
15
+    "test": "echo \"Error: no test specified\" && exit 1"
16
+  },
17
+  "config": {
18
+    "commitizen": {
19
+      "path": "node_modules/cz-customizable"
20
+    },
21
+    "cz-customizable": {
22
+      "config": "node_modules/commitlint-config-imt/cz-config.js"
23
+    }
24
+  },
25
+  "repository": {
26
+    "type": "git",
27
+    "url": "http://git.xfoto.com.cn/SCRM/miniProgram.git"
28
+  },
29
+  "author": "FFIB",
30
+  "devDependencies": {
31
+    "babel-eslint": "^10.0.3",
32
+    "eslint": "^6.7.2",
33
+    "eslint-config-airbnb": "^18.0.1",
34
+    "eslint-config-airbnb-base": "^14.0.0",
35
+    "eslint-config-prettier": "^6.7.0",
36
+    "eslint-plugin-import": "^2.19.1",
37
+    "eslint-plugin-jsx-a11y": "^6.2.3",
38
+    "eslint-plugin-prettier": "^3.1.2",
39
+    "eslint-plugin-react": "^7.14.3",
40
+    "eslint-plugin-react-hooks": "^1.7.0",
41
+    "gulp": "^4.0.0",
42
+    "gulp-install": "^1.1.0",
43
+    "gulp-json-editor": "^2.5.0",
44
+    "gulp-less": "^4.0.1",
45
+    "gulp-rename": "^1.4.0",
46
+    "gulp-replace": "^1.0.0",
47
+    "htmllint": "^0.8.0",
48
+    "husky": "^1.2.0",
49
+    "lint-staged": "^8.1.0",
50
+    "prettier": "^1.19.1",
51
+    "stylelint": "^12.0.0",
52
+    "stylelint-config-recess-order": "^2.0.3",
53
+    "stylelint-config-standard": "^19.0.0",
54
+    "stylelint-prettier": "^1.1.2",
55
+    "through2": "^3.0.0"
56
+  },
57
+  "dependencies": {
58
+    "wxml-to-canvas": "^1.0.3"
59
+  }
60
+}

二進制
src/.DS_Store


+ 83 - 0
src/app.js

@@ -0,0 +1,83 @@
1
+const urls = require('./utils/urls.js')
2
+const network = require('./utils/network.js')
3
+const brandConfig = require('./brand/config.js')
4
+const router = require('./utils/router.js')
5
+const routerInterceptor = require('./utils/routerInterceptor')
6
+
7
+App({
8
+  copywriting: brandConfig.copywriting,
9
+  brandConfig,
10
+  urls,
11
+  network,
12
+  router,
13
+  routerInterceptor,
14
+  globalData: {
15
+    userInfo: {},
16
+    isLogin: false,
17
+    isWarranty: false,
18
+    hasSale: false,
19
+    isWxwork: false,
20
+    levels: [
21
+      '影友',
22
+      'LRC会员',
23
+      '银卡会员',
24
+      '金卡会员',
25
+      '白金卡会员',
26
+      '黑金卡会员'
27
+    ]
28
+  },
29
+
30
+  sceneHandler: null,
31
+
32
+  onLaunch() {},
33
+
34
+  onShow(options) {
35
+    this._warrantyCardCallBack(options.referrerInfo)
36
+  },
37
+
38
+  // 保修卡回调操作
39
+  _warrantyCardCallBack(referrerInfo) {
40
+    if (
41
+      referrerInfo === undefined ||
42
+      referrerInfo.appId !== 'wxeb490c6f9b154ef9'
43
+    ) {
44
+      return
45
+    }
46
+
47
+    this.globalData.isWarranty = true
48
+    wx.switchTab({
49
+      url: '/pages/member/mine/mine'
50
+    })
51
+  },
52
+
53
+  // 静默登录
54
+  login(cb) {
55
+    const that = this
56
+    network.login(res => {
57
+      that.globalData.userInfo = res
58
+      that.globalData.isLogin = true
59
+      cb(res)
60
+    })
61
+  },
62
+
63
+  // 获取用户unionid
64
+  getUserInfo(param, cb) {
65
+    const that = this
66
+    network.post({
67
+      url: urls.MINI_USERINFO,
68
+      data: {
69
+        encryptedData: param.encryptedData,
70
+        iv: param.iv
71
+      },
72
+      success(res) {
73
+        that.globalData.userInfo = res.data
74
+        that.globalData.isLogin = true
75
+        wx.setStorageSync('userInfo', res.data)
76
+        cb()
77
+      },
78
+      complete() {
79
+        wx.hideLoading()
80
+      }
81
+    })
82
+  }
83
+})

+ 23 - 0
src/app.json

@@ -0,0 +1,23 @@
1
+{
2
+  "pages": [
3
+    "pages/index/index",
4
+    "pages/order/order",
5
+    "template/webView/webView",
6
+    "template/resultView/resultView"
7
+  ],
8
+  "window": {
9
+    "backgroundTextStyle": "dark",
10
+    "navigationBarBackgroundColor": "#000000",
11
+    "navigationBarTitleText": "尖货接龙",
12
+    "navigationBarTextStyle": "white"
13
+  },
14
+  "permission": {
15
+    "scope.userLocation": {
16
+      "desc": "你的位置信息将用于申请保修卡"
17
+    }
18
+  },
19
+  "navigateToMiniProgramAppIdList": [
20
+    "wxeb490c6f9b154ef9"
21
+  ],
22
+  "sitemapLocation": "sitemap.json"
23
+}

+ 97 - 0
src/app.wxss

@@ -0,0 +1,97 @@
1
+/**app.wxss**/
2
+@import 'brand/brand.wxss';
3
+@import 'style/button.wxss';
4
+page {
5
+  height: 100%;
6
+}
7
+
8
+.button-hover {
9
+  opacity: 0.8;
10
+  background-color: black;
11
+}
12
+
13
+/*去除所有button边框*/
14
+button::after {
15
+  border: none;
16
+}
17
+
18
+button {
19
+  box-sizing: content-box;
20
+  border-radius: 0;
21
+}
22
+
23
+/* 背景图片 */
24
+.background {
25
+  position: absolute;
26
+  z-index: -1;
27
+
28
+  width: 100%;
29
+  height: 100%;
30
+}
31
+
32
+/** button **/
33
+.bottom-button {
34
+  display: flex;
35
+  flex-direction: column;
36
+  align-items: center;
37
+  justify-content: flex-end;
38
+  flex-grow: 2;
39
+}
40
+
41
+.bottom-view {
42
+  display: flex;
43
+  flex-direction: column;
44
+  justify-content: flex-end;
45
+  flex-grow: 2;
46
+  width: 100%;
47
+}
48
+
49
+.bottom-view .sale-button {
50
+  display: flex;
51
+  justify-content: center;
52
+  align-items: center;
53
+  margin-bottom: 0;
54
+  width: 100%;
55
+  height: calc(50px + env(safe-area-inset-bottom));
56
+  border-radius: 0;
57
+  padding: 0 0 0 0;
58
+  background-color: #1869ad;
59
+  color: #fff;
60
+  font-size: 40rpx;
61
+}
62
+
63
+.bottom-button .sale-button {
64
+  display: flex;
65
+  align-items: center;
66
+  justify-content: center;
67
+  margin-bottom: 50rpx;
68
+  min-width: 200rpx;
69
+  border-radius: 50rpx;
70
+  padding: 5rpx 70rpx 5rpx 70rpx;
71
+  background-color: #fff;
72
+  color: #1869ad;
73
+  font-size: 36rpx;
74
+}
75
+
76
+.bottom-button .consumer-button {
77
+  display: flex;
78
+  align-items: center;
79
+  justify-content: center;
80
+  margin-bottom: 50rpx;
81
+  min-width: 300rpx;
82
+  border-radius: 50rpx;
83
+  padding: 5rpx 70rpx 5rpx 70rpx;
84
+  background-color: #fff;
85
+  color: #1869ad;
86
+  font-size: 36rpx;
87
+}
88
+
89
+.bottom-view .sale-button[pressed] {
90
+  background-color: rgba(24, 105, 173, 0.8);
91
+  color: rgba(255, 255, 255, 0.8);
92
+}
93
+
94
+.bottom-view .sale-button[disabled] {
95
+  background-color: rgba(24, 105, 173, 0.6);
96
+  color: rgba(255, 255, 255, 0.6);
97
+}

+ 79 - 0
src/brand/brand.wxss

@@ -0,0 +1,79 @@
1
+text {
2
+  color: #fff;
3
+}
4
+
5
+.user-name {
6
+  color: #fff;
7
+}
8
+
9
+.button {
10
+  display: flex;
11
+  justify-content: center;
12
+  align-items: center;
13
+  width: 35%;
14
+  color: #1869ad;
15
+  border-radius: 40%/120%;
16
+
17
+  font-size: 80%;
18
+  letter-spacing: 2px;
19
+  font-weight: 500;
20
+  background-color: #fff;
21
+}
22
+
23
+/* index */
24
+.sale-view .integral-view {
25
+  border: 2px solid #fff;
26
+}
27
+
28
+.sale-view .integral-view .content {
29
+  color: #fff;
30
+}
31
+
32
+/* commodity */
33
+.commodity-desc text {
34
+  font-size: 90%;
35
+}
36
+
37
+.commodity-desc .title {
38
+  font-size: 100%;
39
+}
40
+
41
+/* info */
42
+form .cell {
43
+  background-color: #fff;
44
+}
45
+
46
+/* form .cell input {
47
+  color: #fff;
48
+} */
49
+
50
+form text,
51
+form button {
52
+  color: #1869ad;
53
+}
54
+
55
+.empty-hint {
56
+  color: #fff;
57
+}
58
+
59
+.hint-main text {
60
+  color: #1869ad;
61
+}
62
+
63
+.hint-main .confirm {
64
+  background-color: #1869ad;
65
+  color: #fff;
66
+}
67
+/* sale_integral */
68
+.integral-title {
69
+  background-color: #95b9d9;
70
+  color: #fff;
71
+}
72
+
73
+.integral-production {
74
+  background-color: #f8f8f8;
75
+}
76
+
77
+.integral-production text {
78
+  color: #1869ad;
79
+}

+ 37 - 0
src/brand/config.js

@@ -0,0 +1,37 @@
1
+module.exports = {
2
+  baseURL: 'https://kodo.xfoto.com.cn',
3
+  brandID: 'ywkRTh5vtswRvhh8uLvTTQ',
4
+  demo: true,
5
+  copywriting: {
6
+    info: {
7
+      headerTitle:
8
+        'THANK YOU!\n感谢您购买腾龙镜头\n请完善您的个人信息领取保修卡',
9
+      coupon: '并领取意外维修劵',
10
+      hintTitle: '提 交 成 功',
11
+      inputDesc: '请完善您的信息获取5+1年保修服务',
12
+      hintDesc: '恭喜您获得\n腾龙官方5+1年保修服务',
13
+      hintCoupon: '和意外维修劵',
14
+      hintBtn: '领取'
15
+    },
16
+    saleInfo: {
17
+      headerTitle: 'THANK YOU!\n感谢您销售腾龙正品行货镜头\n二维码验证有效',
18
+      hintTitle: '提 交 成 功',
19
+      hintDesc:
20
+        '本次销售您消耗{{integral}}卡路里\n共计消耗{{totalIntegral}}卡路里',
21
+      hintBtn: '确定'
22
+    },
23
+    verification: {
24
+      commodityTitle: '感谢您购买腾龙镜头',
25
+      userInfoBtn: '申请电子保修卡'
26
+    },
27
+    sale_verification: {
28
+      commodityTitle: '请确认您所销售商品型号和编码',
29
+      userInfoBtn: '确认出库'
30
+    },
31
+    equipment: {
32
+      emptyHint: '您还没有绑定腾龙镜头',
33
+      couponTitle: '腾龙镜头意外维修券',
34
+      couponDesc: '此券用于镜头保修范围外的意外或人为损伤的维修费'
35
+    }
36
+  }
37
+}

二進制
src/component/.DS_Store


+ 62 - 0
src/component/btnFooter-protocol/btnFooter-protocol.js

@@ -0,0 +1,62 @@
1
+// component/btnFooter-protocol/btnFooter-protocol.js
2
+Component({
3
+  /**
4
+   * Component properties
5
+   */
6
+  properties: {
7
+    btnClass: {
8
+      type: String,
9
+      value: 'button1'
10
+    },
11
+
12
+    btnTxt: {
13
+      type: String,
14
+      value: '确认'
15
+    },
16
+
17
+    btnDisalbed: {
18
+      type: Boolean,
19
+      value: false,
20
+      observer(newVal, _) {
21
+        this.setData({
22
+          disabled: newVal && this.data.protocolImg === 2
23
+        })
24
+      }
25
+    },
26
+
27
+    openType: {
28
+      type: String,
29
+      value: ''
30
+    }
31
+  },
32
+
33
+  /**
34
+   * Component initial data
35
+   */
36
+  data: {
37
+    protocolImg: 2,
38
+    disabled: false
39
+  },
40
+
41
+  /**
42
+   * Component methods
43
+   */
44
+  methods: {
45
+    switchAuthorization() {
46
+      this.setData({
47
+        disabled: this.data.protocolImg !== 1 || this.data.btnDisalbed,
48
+        protocolImg: 3 - this.data.protocolImg
49
+      })
50
+    },
51
+
52
+    openUrl(e) {
53
+      wx.navigateTo({
54
+        url: `/template/webView/webView?url=${e.currentTarget.dataset.url}`
55
+      })
56
+    },
57
+
58
+    confirm(e) {
59
+      this.triggerEvent('action', e)
60
+    }
61
+  }
62
+})

+ 4 - 0
src/component/btnFooter-protocol/btnFooter-protocol.json

@@ -0,0 +1,4 @@
1
+{
2
+  "component": true,
3
+  "usingComponents": {}
4
+}

+ 42 - 0
src/component/btnFooter-protocol/btnFooter-protocol.less

@@ -0,0 +1,42 @@
1
+@import './src/style/less/variable.less';
2
+@import './src/style/less/layout.less';
3
+@import '/style/button.wxss';
4
+@import '/template/btn/btn.wxss';
5
+
6
+.btn-footer {
7
+  .flex-center(column);
8
+
9
+  position: fixed;
10
+  bottom: 0;
11
+  z-index: 999;
12
+  justify-content: center;
13
+  width: 100%;
14
+  height: @btnFooterWithProtocol-height;
15
+  background-color: @color-white;
16
+  box-shadow: 0 -5px 9px 2px fade(@color-black, 10%);
17
+
18
+  .protocol {
19
+    .flex-center(row);
20
+
21
+    image {
22
+      .square(@icon-small-size);
23
+
24
+      margin-right: @spacing-inline;
25
+    }
26
+
27
+    text {
28
+      font-size: @font-label;
29
+      color: @color-light-gray;
30
+    }
31
+
32
+    .clickable {
33
+      color: @color-brand;
34
+    }
35
+  }
36
+
37
+  button {
38
+    width: @visual-width;
39
+    height: 100rpx !important;
40
+    margin-top: @spacing-inline;
41
+  }
42
+}

+ 11 - 0
src/component/btnFooter-protocol/btnFooter-protocol.wxml

@@ -0,0 +1,11 @@
1
+<import src="/template/btn/btn.wxml"></import>
2
+<view class="btn-footer">
3
+  <view class="protocol">
4
+    <image src="/resources/protocol{{protocolImg}}.png" bindtap="switchAuthorization"></image>
5
+    <text>点击确认即表示已阅读并同意</text>
6
+    <text class="clickable" data-url="https://kodo.tamron.cn/mp/privacy.html" bindtap="openUrl">《隐私条款》</text>
7
+    <text>和</text>
8
+    <text class="clickable" data-url="https://kodo.tamron.cn/mp/regulations.html" bindtap="openUrl">《会员章程》</text>
9
+  </view>
10
+  <template is="iconBtn" data="{{btnClass: btnClass, btnTxt: btnTxt, disabled: disabled, action: 'confirm', openType: openType}}"></template>
11
+</view>

+ 36 - 0
src/component/btnFooter-protocol/btnFooter-protocol.wxss

@@ -0,0 +1,36 @@
1
+@import '/style/button.wxss';
2
+@import '/template/btn/btn.wxss';
3
+.btn-footer {
4
+  display: flex;
5
+  flex-direction: column;
6
+  align-items: center;
7
+  position: fixed;
8
+  bottom: 0;
9
+  z-index: 999;
10
+  justify-content: center;
11
+  width: 100%;
12
+  height: 200rpx;
13
+  background-color: #fff;
14
+  box-shadow: 0 -5px 9px 2px rgba(0, 0, 0, 0.1);
15
+}
16
+.btn-footer .protocol {
17
+  display: flex;
18
+  align-items: center;
19
+}
20
+.btn-footer .protocol image {
21
+  width: 32rpx;
22
+  height: 32rpx;
23
+  margin-right: 12rpx;
24
+}
25
+.btn-footer .protocol text {
26
+  font-size: 8pt;
27
+  color: #959595;
28
+}
29
+.btn-footer .protocol .clickable {
30
+  color: #0967b2;
31
+}
32
+.btn-footer button {
33
+  width: 702rpx;
34
+  height: 100rpx !important;
35
+  margin-top: 12rpx;
36
+}

二進制
src/component/dialog-input/.DS_Store


+ 127 - 0
src/component/dialog-input/dialog-input.js

@@ -0,0 +1,127 @@
1
+// component/dialog-input/dialog-input.js
2
+Component({
3
+  /**
4
+   * Component properties
5
+   */
6
+  properties: {
7
+    title: {
8
+      type: String,
9
+      value: ''
10
+    },
11
+
12
+    btn: {
13
+      type: Object,
14
+      value: {}
15
+    },
16
+
17
+    msg: {
18
+      type: Array,
19
+      value: [],
20
+      observer(newVal, _) {
21
+        let btnDisabled = false
22
+        newVal.forEach(v => {
23
+          if (!v.hasValue && v.txt === '') {
24
+            btnDisabled = true
25
+          }
26
+        })
27
+
28
+        this.setData({
29
+          btnDisabled
30
+        })
31
+      }
32
+    },
33
+
34
+    show: {
35
+      type: Boolean,
36
+      value: false,
37
+      observer(newVal, oldVal) {
38
+        if (newVal === oldVal) {
39
+          return
40
+        }
41
+        if (newVal) {
42
+          this.setData({
43
+            isShow: true
44
+          })
45
+          const animation1 = wx.createAnimation({
46
+            duration: 400,
47
+            timingFunction: 'linear'
48
+          })
49
+          animation1.opacity(1).step()
50
+
51
+          const animation2 = wx.createAnimation({
52
+            duration: 500,
53
+            timingFunction: 'linear'
54
+          })
55
+          animation2.opacity(1).step()
56
+          this.setData({
57
+            bgAnimationData: animation1.export(),
58
+            animationData: animation2.export()
59
+          })
60
+        } else {
61
+          const animation1 = wx.createAnimation({
62
+            duration: 400,
63
+            timingFunction: 'linear'
64
+          })
65
+          animation1.opacity(0).step()
66
+
67
+          const animation2 = wx.createAnimation({
68
+            duration: 500,
69
+            timingFunction: 'linear'
70
+          })
71
+
72
+          animation2.opacity(0).step()
73
+          this.setData({
74
+            bgAnimationData: animation1.export(),
75
+            animationData: animation2.export()
76
+          })
77
+
78
+          const that = this
79
+          setTimeout(() => {
80
+            that.setData({
81
+              isShow: false
82
+            })
83
+          }, 500)
84
+        }
85
+      }
86
+    }
87
+  },
88
+
89
+  /**
90
+   * Component initial data
91
+   */
92
+  data: {
93
+    btnDisabled: false,
94
+    name: '',
95
+    animationData: {},
96
+    bgAnimationData: {},
97
+    isShow: false
98
+  },
99
+
100
+  /**
101
+   * Component methods
102
+   */
103
+  methods: {
104
+    cancel() {
105
+      this.triggerEvent('cancel', !this.data.show)
106
+    },
107
+
108
+    nameInput(e) {
109
+      this.setData({
110
+        name: e.detail.value
111
+      })
112
+    },
113
+
114
+    getPhoneNumber(e) {
115
+      this.triggerEvent('getPhoneNumber', e.detail)
116
+    },
117
+
118
+    submit() {
119
+      this.triggerEvent('submit', { name: this.data.name })
120
+    }
121
+  },
122
+
123
+  pageLifetimes: {
124
+    // 组件所在页面的生命周期函数
125
+    show() {}
126
+  }
127
+})

+ 4 - 0
src/component/dialog-input/dialog-input.json

@@ -0,0 +1,4 @@
1
+{
2
+  "component": true,
3
+  "usingComponents": {}
4
+}

+ 115 - 0
src/component/dialog-input/dialog-input.less

@@ -0,0 +1,115 @@
1
+@import './src/style/less/variable.less';
2
+@import './src/style/less/layout.less';
3
+@import './src/style/less/shape.less';
4
+
5
+.dialog-input {
6
+  .flex-center(row);
7
+
8
+  position: absolute;
9
+  top: 0;
10
+  justify-content: center;
11
+  width: 100%;
12
+  height: 100%;
13
+  background-color: rgba(0, 0, 0, 0.6);
14
+  opacity: 0;
15
+}
16
+
17
+.main {
18
+  position: relative;
19
+  .flex-center(column);
20
+
21
+  justify-content: center;
22
+  width: 600rpx;
23
+  background-color: @color-white;
24
+  border-radius: @radius-big;
25
+  opacity: 0;
26
+}
27
+
28
+.cancel {
29
+  position: absolute;
30
+  top: 30rpx;
31
+  right: 20rpx;
32
+  .cross(30rpx, @color-light-gray);
33
+}
34
+
35
+.main .title {
36
+  .flex-center(row);
37
+
38
+  justify-content: center;
39
+  width: 100%;
40
+  height: 60rpx;
41
+  margin-top: 60rpx;
42
+  font-size: @font-primary;
43
+
44
+  text {
45
+    color: @color-brand;
46
+  }
47
+}
48
+
49
+form {
50
+  width: 500rpx;
51
+  margin: @spacing-view 0;
52
+
53
+  .item {
54
+    .flex-center(row);
55
+
56
+    box-sizing: border-box;
57
+    justify-content: flex-start;
58
+    width: 100%;
59
+    height: 80rpx;
60
+    margin-top: @spacing-item;
61
+    border: 1px @color-bg solid;
62
+    border-radius: @radius;
63
+
64
+    input {
65
+      box-sizing: border-box;
66
+      width: calc(100% - 2px);
67
+      height: calc(100% - 2px);
68
+      padding: @spacing-inline;
69
+      font-size: @font-secondary;
70
+    }
71
+
72
+    .placeholder {
73
+      font-size: @font-tertiary;
74
+      color: @color-light-gray;
75
+    }
76
+
77
+    button {
78
+      .flex-center(row);
79
+
80
+      box-sizing: border-box;
81
+      justify-content: space-between;
82
+      width: calc(100% - 2px);
83
+      height: calc(100% - 2px);
84
+      padding: @spacing-inline;
85
+      background-color: @color-white;
86
+
87
+      .placeholder {
88
+        font-size: @font-tertiary;
89
+        color: #767676;
90
+      }
91
+
92
+      .txt {
93
+        font-size: @font-secondary;
94
+        color: @color-black;
95
+      }
96
+
97
+      .arrow {
98
+        .arrow(right; @color-light-gray; 14rpx);
99
+      }
100
+    }
101
+  }
102
+
103
+  .confirm {
104
+    .flex-center(row);
105
+
106
+    box-sizing: border-box;
107
+    justify-content: center;
108
+    width: 500rpx;
109
+    height: 80rpx;
110
+    margin-top: @spacing-view;
111
+    color: @color-white;
112
+    background-color: @color-brand;
113
+    border-radius: @radius;
114
+  }
115
+}

+ 24 - 0
src/component/dialog-input/dialog-input.wxml

@@ -0,0 +1,24 @@
1
+<view class="dialog-input" wx:if="{{isShow}}" animation="{{bgAnimationData}}">
2
+  <view class="main" animation="{{animationData}}">
3
+    <view class="cancel" bindtap="cancel"></view>
4
+    <view class="title">
5
+      <text>{{title}}</text>
6
+    </view>
7
+    <form>
8
+      <view class="container">
9
+        <block wx:for="{{msg}}">
10
+        <view class="item" wx:if="{{item.type == 'input'}}">
11
+          <input placeholder="{{item.txt || item.placeholder}}" placeholder-class="placeholder" bindinput="nameInput"></input>
12
+        </view>
13
+        <view class="item" wx:if="{{item.type == 'phone'}}">
14
+          <button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">
15
+            <text class="{{item.txt == '' ? 'placeholder': 'txt'}}">{{item.txt || item.placeholder}}</text>
16
+            <view class="arrow"></view>
17
+          </button>
18
+        </view>
19
+        </block>
20
+      </view>
21
+      <button class="confirm" disabled="{{btnDisabled}}" bindtap="submit">{{btn.txt}}</button>
22
+    </form>
23
+  </view>
24
+</view>

+ 122 - 0
src/component/dialog-input/dialog-input.wxss

@@ -0,0 +1,122 @@
1
+.dialog-input {
2
+  display: flex;
3
+  align-items: center;
4
+  position: absolute;
5
+  top: 0;
6
+  justify-content: center;
7
+  width: 100%;
8
+  height: 100%;
9
+  background-color: rgba(0, 0, 0, 0.6);
10
+  opacity: 0;
11
+}
12
+.main {
13
+  position: relative;
14
+  display: flex;
15
+  flex-direction: column;
16
+  align-items: center;
17
+  justify-content: center;
18
+  width: 600rpx;
19
+  background-color: #fff;
20
+  border-radius: 10px;
21
+  opacity: 0;
22
+}
23
+.cancel {
24
+  position: absolute;
25
+  top: 30rpx;
26
+  right: 20rpx;
27
+  display: inline-block;
28
+  width: 30rpx;
29
+  height: 7.5rpx;
30
+  overflow: visible;
31
+  font-size: 0;
32
+  line-height: 0;
33
+  vertical-align: middle;
34
+  background: #959595;
35
+  border-radius: 7.5rpx;
36
+  transform: rotate(45deg);
37
+}
38
+.cancel::after {
39
+  display: block;
40
+  width: 30rpx;
41
+  height: 7.5rpx;
42
+  content: '/';
43
+  background: #959595;
44
+  border-radius: 7.5rpx;
45
+  transform: rotate(-90deg);
46
+}
47
+.main .title {
48
+  display: flex;
49
+  align-items: center;
50
+  justify-content: center;
51
+  width: 100%;
52
+  height: 60rpx;
53
+  margin-top: 60rpx;
54
+  font-size: 14pt;
55
+}
56
+.main .title text {
57
+  color: #0967b2;
58
+}
59
+form {
60
+  width: 500rpx;
61
+  margin: 36rpx 0;
62
+}
63
+form .item {
64
+  display: flex;
65
+  align-items: center;
66
+  box-sizing: border-box;
67
+  justify-content: flex-start;
68
+  width: 100%;
69
+  height: 80rpx;
70
+  margin-top: 24rpx;
71
+  border: 1px #efefef solid;
72
+  border-radius: 5px;
73
+}
74
+form .item input {
75
+  box-sizing: border-box;
76
+  width: calc(100% - 2px);
77
+  height: calc(100% - 2px);
78
+  padding: 12rpx;
79
+  font-size: 13pt;
80
+}
81
+form .item .placeholder {
82
+  font-size: 11pt;
83
+  color: #959595;
84
+}
85
+form .item button {
86
+  display: flex;
87
+  align-items: center;
88
+  box-sizing: border-box;
89
+  justify-content: space-between;
90
+  width: calc(100% - 2px);
91
+  height: calc(100% - 2px);
92
+  padding: 12rpx;
93
+  background-color: #fff;
94
+}
95
+form .item button .placeholder {
96
+  font-size: 11pt;
97
+  color: #767676;
98
+}
99
+form .item button .txt {
100
+  font-size: 13pt;
101
+  color: #000;
102
+}
103
+form .item button .arrow {
104
+  width: 14rpx;
105
+  height: 14rpx;
106
+  box-sizing: border-box;
107
+  border-top: 2px solid #959595;
108
+  border-right: 2px solid #959595;
109
+  transform: rotate(45deg);
110
+}
111
+form .confirm {
112
+  display: flex;
113
+  align-items: center;
114
+  box-sizing: border-box;
115
+  justify-content: center;
116
+  width: 500rpx;
117
+  height: 80rpx;
118
+  margin-top: 36rpx;
119
+  color: #fff;
120
+  background-color: #0967b2;
121
+  border-radius: 5px;
122
+}

+ 1 - 0
src/component/stepper/add.svg

@@ -0,0 +1 @@
1
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1587110727868" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1164" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 85.333333c235.637333 0 426.666667 191.029333 426.666667 426.666667S747.637333 938.666667 512 938.666667 85.333333 747.637333 85.333333 512 276.362667 85.333333 512 85.333333z m0 234.666667a32 32 0 0 0-32 32v128H352a32 32 0 0 0 0 64h128v128a32 32 0 0 0 64 0V544h128a32 32 0 0 0 0-64H544V352a32 32 0 0 0-32-32z" p-id="1165" fill="#09bb07"></path></svg>

+ 1 - 0
src/component/stepper/minus.svg

@@ -0,0 +1 @@
1
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1587110843176" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1962" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M510.293333 126.862222c51.863704 0 102.115556 10.145185 149.428148 30.151111 45.700741 19.342222 86.755556 47.028148 122.026667 82.29926 35.271111 35.271111 62.957037 76.325926 82.299259 122.026666 20.005926 47.312593 30.151111 97.564444 30.151112 149.428148s-10.145185 102.115556-30.151112 149.428149c-19.342222 45.700741-47.028148 86.755556-82.299259 122.026666-35.271111 35.271111-76.325926 62.957037-122.026667 82.299259-47.217778 20.100741-97.564444 30.340741-149.428148 30.340741S408.177778 884.622222 360.865185 864.616296c-45.700741-19.342222-86.755556-47.028148-122.026666-82.299259-35.271111-35.271111-62.957037-76.325926-82.29926-122.026667-20.005926-47.312593-30.151111-97.564444-30.151111-149.428148S136.533333 408.651852 156.539259 361.339259c19.342222-45.700741 47.028148-86.755556 82.29926-122.026666 35.271111-35.271111 76.325926-62.957037 122.026666-82.29926 47.312593-20.005926 97.659259-30.151111 149.428148-30.151111m0-42.666666c-235.614815 0-426.666667 191.051852-426.666666 426.666666s191.051852 426.666667 426.666666 426.666667 426.666667-191.051852 426.666667-426.666667-190.957037-426.666667-426.666667-426.666666z" p-id="1963" fill="#09bb07"></path><path d="M709.499259 532.195556h-398.222222c-11.757037 0-21.333333-9.576296-21.333333-21.333334s9.576296-21.333333 21.333333-21.333333h398.222222c11.757037 0 21.333333 9.576296 21.333334 21.333333s-9.576296 21.333333-21.333334 21.333334z" p-id="1964" fill="#09bb07"></path></svg>

+ 27 - 0
src/component/stepper/stepper.js

@@ -0,0 +1,27 @@
1
+// component/stepper/stepper.js
2
+Component({
3
+  data: {
4
+    num: 0
5
+  },
6
+
7
+  /**
8
+   * 组件的方法列表
9
+   */
10
+  methods: {
11
+    bindadd() {
12
+      this.setData({
13
+        num: this.data.num + 1
14
+      })
15
+
16
+      this.triggerEvent('stepperChanged', this.data.num)
17
+    },
18
+
19
+    bindminus() {
20
+      this.setData({
21
+        num: this.data.num - 1
22
+      })
23
+
24
+      this.triggerEvent('stepperChanged', this.data.num)
25
+    }
26
+  }
27
+})

+ 4 - 0
src/component/stepper/stepper.json

@@ -0,0 +1,4 @@
1
+{
2
+  "component": true,
3
+  "usingComponents": {}
4
+}

+ 17 - 0
src/component/stepper/stepper.less

@@ -0,0 +1,17 @@
1
+@import './src/style/less/variable.less';
2
+
3
+.stepper {
4
+  display: flex;
5
+  align-items: center;
6
+
7
+  .icon {
8
+    width: 72rpx;
9
+    height: 72rpx;
10
+  }
11
+
12
+  .num {
13
+    padding: 0 @spacing-short-text;
14
+    color: #000;
15
+    text-align: center;
16
+  }
17
+}

+ 5 - 0
src/component/stepper/stepper.wxml

@@ -0,0 +1,5 @@
1
+<view class="stepper">
2
+  <image class="icon" src="minus.svg" bindtap="bindminus" wx:if="{{ num !== 0 }}"></image>
3
+  <text class="num" wx:if="{{ num !== 0 }}">{{ num }}</text>
4
+  <image class="icon" src="add.svg" bindtap="bindadd"></image>
5
+</view>

+ 13 - 0
src/component/stepper/stepper.wxss

@@ -0,0 +1,13 @@
1
+.stepper {
2
+  display: flex;
3
+  align-items: center;
4
+}
5
+.stepper .icon {
6
+  width: 72rpx;
7
+  height: 72rpx;
8
+}
9
+.stepper .num {
10
+  padding: 0 4rpx;
11
+  color: #000;
12
+  text-align: center;
13
+}

二進制
src/pages/.DS_Store


+ 205 - 0
src/pages/index/index.js

@@ -0,0 +1,205 @@
1
+const WxParse = require('../../utils/wxParse/wxParse.js')
2
+
3
+const app = getApp()
4
+
5
+app.routerInterceptor.checkLoginStatus({
6
+  /**
7
+   * 页面的初始数据
8
+   */
9
+  data: {
10
+    kol: {},
11
+    commodities: [],
12
+
13
+    consumers: [],
14
+
15
+    pack_id: 'FMHBanG9d9uCm9CAEAdMVa',
16
+    pack: {},
17
+
18
+    nums: [],
19
+    total: 0,
20
+    isLodding: false
21
+  },
22
+
23
+  /**
24
+   * 生命周期函数--监听页面加载
25
+   */
26
+  onLoad() {
27
+    this.getData()
28
+    wx.hideHomeButton()
29
+  },
30
+
31
+  getData() {
32
+    const that = this
33
+    app.network.post({
34
+      url: app.urls.PACK,
35
+      data: {
36
+        pack_id: 'FMHBanG9d9uCm9CAEAdMVa'
37
+      },
38
+      success(res) {
39
+        const { sales, pack, goods } = res.data
40
+        const conusmers = sales.map(sale => {
41
+          const s = sale
42
+          s.content = sale.saleinfo.reduce(
43
+            (accumulator, cur) => `${accumulator + cur.title} x ${cur.num} \n`,
44
+            ''
45
+          )
46
+          s.created_at = that.timeago(s.created_at)
47
+          return s
48
+        })
49
+        WxParse.wxParse('article', 'html', pack.pack_detail, that, 5)
50
+        pack.pack_detail = ''
51
+
52
+        const commodities = goods.map(commodity => {
53
+          const c = commodity
54
+          c.price /= 100
55
+          return c
56
+        })
57
+        that.setData({
58
+          kol: res.data.kol,
59
+          commodities,
60
+          nums: Array(res.data.goods).fill(0),
61
+          consumers: conusmers,
62
+          pack,
63
+          isLodding: true
64
+        })
65
+      }
66
+    })
67
+  },
68
+
69
+  bindstepperChanged(e) {
70
+    const { nums, commodities } = this.data
71
+    nums[e.currentTarget.dataset.index] = e.detail
72
+
73
+    const total = commodities.reduce(
74
+      (accumulator, cur, index) =>
75
+        accumulator + (cur.price * 100 * nums[index]) / 100,
76
+      0
77
+    )
78
+
79
+    this.setData({
80
+      nums,
81
+      total
82
+    })
83
+  },
84
+
85
+  navigateToOrder() {
86
+    const { nums } = this.data
87
+    let { commodities } = this.data
88
+    commodities = commodities
89
+      .map((curr, index) => {
90
+        return Object.assign(curr, { num: nums[index] })
91
+      })
92
+      .filter(curr => {
93
+        return curr.num > 0
94
+      })
95
+
96
+    const that = this
97
+    wx.navigateTo({
98
+      url: '../order/order',
99
+      success(res) {
100
+        res.eventChannel.emit('acceptDataFromOpenerPage', {
101
+          commodities,
102
+          total: that.data.total,
103
+          kol: that.data.kol,
104
+          pack_id: that.data.pack_id
105
+        })
106
+      }
107
+    })
108
+  },
109
+
110
+  getUserInfo(e) {
111
+    if (e.detail.userInfo === undefined) {
112
+      wx.showModal({
113
+        title: '提示',
114
+        content: '需要您的授权,才能继续报名',
115
+        showCancel: false
116
+      })
117
+    } else {
118
+      wx.showLoading({
119
+        title: '登陆中'
120
+      })
121
+
122
+      const that = this
123
+      app.getUserInfo(
124
+        {
125
+          iv: e.detail.iv,
126
+          encryptedData: e.detail.encryptedData
127
+        },
128
+        () => {
129
+          that.navigateToOrder()
130
+        }
131
+      )
132
+    }
133
+  },
134
+
135
+  onShareAppMessage() {
136
+    return {
137
+      title: this.data.pack.title,
138
+      path: '/pages/index/index'
139
+    }
140
+  },
141
+
142
+  timeago(date) {
143
+    // dateTimeStamp是一个时间毫秒,注意时间戳是秒的形式,在这个毫秒的基础上除以1000,就是十位数的时间戳。13位数的都是时间毫秒。
144
+    const dateTimeStamp = new Date(
145
+      Date.parse(date.replace(/-/g, '/'))
146
+    ).getTime()
147
+
148
+    const minute = 1000 * 60 // 把分,时,天,周,半个月,一个月用毫秒表示
149
+    const hour = minute * 60
150
+    const day = hour * 24
151
+    const week = day * 7
152
+    const halfamonth = day * 15
153
+    const month = day * 30
154
+    const now = new Date().getTime() // 获取当前时间毫秒
155
+    const diffValue = now - dateTimeStamp // 时间差
156
+
157
+    if (diffValue < 0) {
158
+      return ''
159
+    }
160
+    const minC = diffValue / minute // 计算时间差的分,时,天,周,月
161
+    const hourC = diffValue / hour
162
+    const dayC = diffValue / day
163
+    const weekC = diffValue / week
164
+    const monthC = diffValue / month
165
+    let result = ''
166
+
167
+    if (monthC >= 1 && monthC <= 3) {
168
+      result = ` ${parseInt(monthC)}月前`
169
+    } else if (weekC >= 1 && weekC <= 3) {
170
+      result = ` ${parseInt(weekC)}周前`
171
+    } else if (dayC >= 1 && dayC <= 6) {
172
+      result = ` ${parseInt(dayC)}天前`
173
+    } else if (hourC >= 1 && hourC <= 23) {
174
+      result = ` ${parseInt(hourC)}小时前`
175
+    } else if (minC >= 1 && minC <= 59) {
176
+      result = ` ${parseInt(minC)}分钟前`
177
+    } else if (diffValue >= 0 && diffValue <= minute) {
178
+      result = '刚刚'
179
+    } else {
180
+      const datetime = new Date()
181
+      datetime.setTime(dateTimeStamp)
182
+      const Nyear = datetime.getFullYear()
183
+      const Nmonth =
184
+        datetime.getMonth() + 1 < 10
185
+          ? `0${datetime.getMonth() + 1}`
186
+          : datetime.getMonth() + 1
187
+      const Ndate =
188
+        datetime.getDate() < 10 ? `0${datetime.getDate()}` : datetime.getDate()
189
+      const Nhour =
190
+        datetime.getHours() < 10
191
+          ? `0${datetime.getHours()}`
192
+          : datetime.getHours()
193
+      const Nminute =
194
+        datetime.getMinutes() < 10
195
+          ? `0${datetime.getMinutes()}`
196
+          : datetime.getMinutes()
197
+      const Nsecond =
198
+        datetime.getSeconds() < 10
199
+          ? `0${datetime.getSeconds()}`
200
+          : datetime.getSeconds()
201
+      result = `${Nyear}-${Nmonth}-${Ndate}`
202
+    }
203
+    return result
204
+  }
205
+})

+ 6 - 0
src/pages/index/index.json

@@ -0,0 +1,6 @@
1
+{
2
+  "usingComponents": {
3
+    "stepper": "/component/stepper/stepper"
4
+  },
5
+  "disableScroll": true
6
+}

+ 310 - 0
src/pages/index/index.less

@@ -0,0 +1,310 @@
1
+@import './src/style/less/variable.less';
2
+@import './src/style/less/layout.less';
3
+@import './src/style/less/shape.less';
4
+@import '/template/footer/footer.wxss';
5
+@import '/template/btn/btn.wxss';
6
+@import '/utils/wxParse/wxParse.wxss';
7
+
8
+page {
9
+  background-color: @color-bg;
10
+}
11
+
12
+.wrap {
13
+  box-sizing: border-box;
14
+  display: flex;
15
+  flex-direction: column;
16
+  align-items: center;
17
+  height: 100%;
18
+  background-color: @color-bg;
19
+}
20
+
21
+.header {
22
+  position: relative;
23
+  width: 100%;
24
+
25
+  .banner-img {
26
+    position: absolute;
27
+    width: 750rpx;
28
+    height: 400rpx;
29
+  }
30
+
31
+  // .banner-container {
32
+  //   position: absolute;
33
+  //   top: 0;
34
+  //   z-index: 1;
35
+  //   width: 100%;
36
+  //   height: 400rpx;
37
+  // }
38
+
39
+  .user-info {
40
+    position: relative;
41
+    box-sizing: border-box;
42
+    display: flex;
43
+    align-items: flex-end;
44
+    width: 750rpx;
45
+    height: 200rpx;
46
+    padding: 0 @spacing-item @spacing-item;
47
+    margin-top: 200rpx;
48
+    background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.8));
49
+
50
+    .avatar {
51
+      width: 100rpx;
52
+      height: 100rpx;
53
+      border-radius: 6px;
54
+    }
55
+
56
+    .nickname {
57
+      margin: 0 0 @spacing-inline @spacing-inline;
58
+      font-size: @font-primary;
59
+      color: #fff;
60
+    }
61
+
62
+    .subscribe {
63
+      display: flex;
64
+      flex-grow: 2;
65
+      align-items: center;
66
+      justify-content: flex-end;
67
+      margin: 0 0 @spacing-inline @spacing-inline;
68
+
69
+      .num {
70
+        font-size: @font-secondary;
71
+        font-weight: 600;
72
+        color: @color-white;
73
+      }
74
+
75
+      .label {
76
+        margin-left: @spacing-short-text;
77
+        font-size: @font-quaternary;
78
+        color: #cbcbcb;
79
+      }
80
+    }
81
+  }
82
+
83
+  .desc {
84
+    margin-top: @spacing-view;
85
+    margin-bottom: 0rpx;
86
+    margin-left: @spacing-item;
87
+
88
+    text {
89
+      color: @color-light-gray;
90
+    }
91
+  }
92
+}
93
+
94
+.main {
95
+  box-sizing: border-box;
96
+  width: @visual-width;
97
+  margin-top: @spacing-view;
98
+}
99
+
100
+.commodity-container {
101
+  .flex-center(column);
102
+
103
+  box-sizing: border-box;
104
+  width: 100%;
105
+  padding: @spacing-view @spacing-item;
106
+  background-color: @color-white;
107
+
108
+  .commodity-title-container {
109
+    display: flex;
110
+    flex-direction: column;
111
+    width: 100%;
112
+
113
+    .title {
114
+      font-size: @font-primary;
115
+      font-weight: 600;
116
+      color: @color-black;
117
+    }
118
+
119
+    .expired {
120
+      margin-top: @spacing-short-text;
121
+      font-size: @font-quaternary;
122
+      color: @color-light-gray;
123
+    }
124
+  }
125
+
126
+  .commodity-details {
127
+    width: 100%;
128
+    margin-top: @spacing-item;
129
+  }
130
+
131
+  .commodity-list {
132
+    .flex-center(column);
133
+
134
+    width: 100%;
135
+    margin-top: @spacing-view;
136
+
137
+    .commodity-item {
138
+      display: flex;
139
+      align-items: center;
140
+      justify-content: space-between;
141
+      width: 100%;
142
+      height: 200rpx;
143
+      padding: @spacing-item 0;
144
+      border-bottom: 1px @color-bg solid;
145
+
146
+      .commodity-left {
147
+        .flex-center(row);
148
+
149
+        height: 200rpx;
150
+
151
+        .commodity-img {
152
+          width: 180rpx;
153
+          height: 180rpx;
154
+        }
155
+
156
+        .commodity-content {
157
+          display: flex;
158
+          flex-direction: column;
159
+          height: 100%;
160
+          margin-left: @spacing-item;
161
+
162
+          .commodity-title {
163
+            font-size: @font-secondary;
164
+            color: @color-gray;
165
+          }
166
+
167
+          .commodity-desc {
168
+            margin-top: @spacing-short-text;
169
+            font-size: @font-quaternary;
170
+            color: @color-light-gray;
171
+          }
172
+        }
173
+      }
174
+
175
+      .commodity-price {
176
+        display: flex;
177
+        flex-grow: 2;
178
+        align-items: flex-end;
179
+
180
+        text {
181
+          font-size: @font-primary;
182
+          color: #e64340;
183
+        }
184
+      }
185
+    }
186
+
187
+    .commodity-right {
188
+      display: flex;
189
+      flex-direction: column;
190
+      align-items: flex-end;
191
+      height: 100%;
192
+
193
+      .bought {
194
+        font-size: @font-quaternary;
195
+        color: #e64340;
196
+        text-align: right;
197
+      }
198
+
199
+      .stepper-container {
200
+        margin-top: @spacing-item;
201
+      }
202
+    }
203
+  }
204
+}
205
+
206
+.consumer-container {
207
+  padding: @spacing-item;
208
+  background-color: @color-white;
209
+
210
+  .consumer-title {
211
+    height: 80rpx;
212
+
213
+    text {
214
+      font-size: @font-primary;
215
+      font-weight: 600;
216
+      color: @color-gray;
217
+    }
218
+  }
219
+
220
+  .swiper-container {
221
+    width: 100%;
222
+    height: 200rpx;
223
+  }
224
+
225
+  .consumer-item {
226
+    width: @visual-width - @spacing-item * 2;
227
+
228
+    .consumer-info {
229
+      display: flex;
230
+      align-items: center;
231
+
232
+      .avatar {
233
+        width: 56rpx;
234
+        height: 56rpx;
235
+        border-radius: 28rpx;
236
+      }
237
+
238
+      .name {
239
+        margin-left: @spacing-inline;
240
+        font-size: @font-tertiary;
241
+        font-weight: 600;
242
+        color: @color-black;
243
+      }
244
+
245
+      .time {
246
+        margin-top: 4rpx;
247
+        margin-left: @spacing-item;
248
+        font-size: @font-label;
249
+        color: lighten(@color-light-gray, 20%);
250
+      }
251
+    }
252
+
253
+    .consumer-commodity {
254
+      display: flex;
255
+      justify-content: flex-end;
256
+      width: 100%;
257
+      margin-top: @spacing-inline;
258
+
259
+      text {
260
+        width: 100%;
261
+        font-size: @font-tertiary;
262
+        color: @color-gray;
263
+        text-align: right;
264
+      }
265
+    }
266
+  }
267
+}
268
+
269
+.btn-footer {
270
+  .flex-center(row);
271
+
272
+  position: fixed;
273
+  bottom: 0;
274
+  box-sizing: border-box;
275
+  justify-content: center;
276
+  width: 100%;
277
+  height: @btnFooter-height;
278
+  padding: @spacing-item;
279
+  background-color: @color-white;
280
+  box-shadow: 0 -5px 9px 2px fade(@color-black, 10%);
281
+
282
+  .btn-footer-container {
283
+    display: flex;
284
+    width: 100%;
285
+    height: 100rpx;
286
+    overflow: hidden;
287
+    border: 1px #09bb07 solid;
288
+    border-radius: @radius;
289
+
290
+    .left {
291
+      .flex-center(row);
292
+
293
+      justify-content: center;
294
+      width: 40%;
295
+      height: 100rpx;
296
+      background-color: @color-white;
297
+
298
+      text {
299
+        font-size: @font-primary;
300
+        font-weight: 700;
301
+        color: #e64340;
302
+      }
303
+    }
304
+
305
+    .right {
306
+      width: 100%;
307
+      height: 100%;
308
+    }
309
+  }
310
+}

+ 93 - 0
src/pages/index/index.wxml

@@ -0,0 +1,93 @@
1
+<scroll-view
2
+  class="wrap"
3
+  scroll-y="{{ true }}"
4
+  enable-flex="{{ true }}"
5
+  style="padding-bottom:{{ total === 0 ? '36rpx' : '196rpx' }}">
6
+  <view style="z-index:999;background-color:#fff;width:100%;height:100%;" wx:if="{{isLoading}}"></view>
7
+  <view class="header">
8
+    <image class="banner-img" src="{{ kol.banner_url }}"></image>
9
+
10
+    <view class="user-info">
11
+      <image class="avatar" src="{{ kol.avatar_url }}"></image>
12
+      <text class="nickname">{{ kol.nickname }}</text>
13
+      <view class="subscribe">
14
+        <text class="num">{{ kol.fans }}</text>
15
+        <text class="label">粉丝</text>
16
+      </view>
17
+    </view>
18
+    <view class="desc">
19
+      <text>{{ kol.intro }}</text>
20
+    </view>
21
+  </view>
22
+  <view class="main">
23
+    <view class="commodity-container">
24
+      <view class="commodity-title-container">
25
+        <text class="title">{{ pack.title }}</text>
26
+        <text class="expired">将于 {{ pack.expired_at }} 结束</text>
27
+      </view>
28
+      <view class="commodity-details">
29
+        <import src="/utils/wxParse/wxParse.wxml"></import>
30
+        <template is="wxParse" data="{{wxParseData:article.nodes}}"></template>
31
+      </view>
32
+      <view class="commodity-list">
33
+        <block wx:for="{{ commodities }}" wx:key="good_id">
34
+          <view class="commodity-item">
35
+            <view class="commodity-left">
36
+              <image class="commodity-img" src="{{ item.image_url }}"></image>
37
+              <view class="commodity-content">
38
+                <text class="commodity-title">{{ item.title }}</text>
39
+                <text class="commodity-desc">{{ item.desc }}</text>
40
+                <view class="commodity-price">
41
+                  <text>¥{{ item.price }}</text>
42
+                </view>
43
+              </view>
44
+            </view>
45
+            <view class="commodity-right">
46
+              <text class="bought"wx:if="{{item.has_sale_num}}">已购 {{ item.has_sale_num }}</text>
47
+              <view class="stepper-container">
48
+                <stepper data-index="{{ index }}" bindstepperChanged="bindstepperChanged"></stepper>
49
+              </view>
50
+            </view>
51
+          </view>
52
+        </block>
53
+      </view>
54
+    </view>
55
+    <view class="consumer-container" wx:if="{{ consumers.length > 0 }}">
56
+      <swiper
57
+        class="swiper-container"
58
+        autoplay="{{ true }}"
59
+        interval="2000"
60
+        vertical="{{ true }}"
61
+        circular="{{ true }}"
62
+      >
63
+        <block wx:for="{{ consumers }}">
64
+          <swiper-item class="consumer-item">
65
+            <view class="consumer-info">
66
+              <image class="avatar" src="{{ item.userinfo.avatar }}"></image>
67
+              <text class="name">{{ item.userinfo.nickname }}</text>
68
+              <text class="time">{{ item.created_at }}</text>
69
+            </view>
70
+            <view class="consumer-commodity">
71
+              <text>{{ item.content }}</text>
72
+            </view>
73
+          </swiper-item>
74
+        </block>
75
+      </swiper>
76
+    </view>
77
+  </view>
78
+</scroll-view>
79
+
80
+<import src="/template/btn/btn.wxml"></import>
81
+<view class="btn-footer" wx:if="{{ total !== 0 }}">
82
+  <view class="btn-footer-container">
83
+    <view class="left">
84
+      <text class="money">¥{{ total }}</text>
85
+    </view>
86
+    <view class="right">
87
+      <template
88
+        is="iconBtn"
89
+        data="{{btnClass: 'button4', btnTxt: '购买', openType: 'getUserInfo', action: 'getUserInfo'}}"
90
+      ></template>
91
+    </view>
92
+  </view>
93
+</view>

+ 255 - 0
src/pages/index/index.wxss

@@ -0,0 +1,255 @@
1
+@import '/template/footer/footer.wxss';
2
+@import '/template/btn/btn.wxss';
3
+@import '/utils/wxParse/wxParse.wxss';
4
+page {
5
+  background-color: #efefef;
6
+}
7
+.wrap {
8
+  box-sizing: border-box;
9
+  display: flex;
10
+  flex-direction: column;
11
+  align-items: center;
12
+  height: 100%;
13
+  background-color: #efefef;
14
+}
15
+.header {
16
+  position: relative;
17
+  width: 100%;
18
+}
19
+.header .banner-img {
20
+  position: absolute;
21
+  width: 750rpx;
22
+  height: 400rpx;
23
+}
24
+.header .user-info {
25
+  position: relative;
26
+  box-sizing: border-box;
27
+  display: flex;
28
+  align-items: flex-end;
29
+  width: 750rpx;
30
+  height: 200rpx;
31
+  padding: 0 24rpx 24rpx;
32
+  margin-top: 200rpx;
33
+  background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.8));
34
+}
35
+.header .user-info .avatar {
36
+  width: 100rpx;
37
+  height: 100rpx;
38
+  border-radius: 6px;
39
+}
40
+.header .user-info .nickname {
41
+  margin: 0 0 12rpx 12rpx;
42
+  font-size: 14pt;
43
+  color: #fff;
44
+}
45
+.header .user-info .subscribe {
46
+  display: flex;
47
+  flex-grow: 2;
48
+  align-items: center;
49
+  justify-content: flex-end;
50
+  margin: 0 0 12rpx 12rpx;
51
+}
52
+.header .user-info .subscribe .num {
53
+  font-size: 13pt;
54
+  font-weight: 600;
55
+  color: #fff;
56
+}
57
+.header .user-info .subscribe .label {
58
+  margin-left: 4rpx;
59
+  font-size: 10pt;
60
+  color: #cbcbcb;
61
+}
62
+.header .desc {
63
+  margin-top: 36rpx;
64
+  margin-bottom: 0rpx;
65
+  margin-left: 24rpx;
66
+}
67
+.header .desc text {
68
+  color: #959595;
69
+}
70
+.main {
71
+  box-sizing: border-box;
72
+  width: 702rpx;
73
+  margin-top: 36rpx;
74
+}
75
+.commodity-container {
76
+  display: flex;
77
+  flex-direction: column;
78
+  align-items: center;
79
+  box-sizing: border-box;
80
+  width: 100%;
81
+  padding: 36rpx 24rpx;
82
+  background-color: #fff;
83
+}
84
+.commodity-container .commodity-title-container {
85
+  display: flex;
86
+  flex-direction: column;
87
+  width: 100%;
88
+}
89
+.commodity-container .commodity-title-container .title {
90
+  font-size: 14pt;
91
+  font-weight: 600;
92
+  color: #000;
93
+}
94
+.commodity-container .commodity-title-container .expired {
95
+  margin-top: 4rpx;
96
+  font-size: 10pt;
97
+  color: #959595;
98
+}
99
+.commodity-container .commodity-details {
100
+  width: 100%;
101
+  margin-top: 24rpx;
102
+}
103
+.commodity-container .commodity-list {
104
+  display: flex;
105
+  flex-direction: column;
106
+  align-items: center;
107
+  width: 100%;
108
+  margin-top: 36rpx;
109
+}
110
+.commodity-container .commodity-list .commodity-item {
111
+  display: flex;
112
+  align-items: center;
113
+  justify-content: space-between;
114
+  width: 100%;
115
+  height: 200rpx;
116
+  padding: 24rpx 0;
117
+  border-bottom: 1px #efefef solid;
118
+}
119
+.commodity-container .commodity-list .commodity-item .commodity-left {
120
+  display: flex;
121
+  align-items: center;
122
+  height: 200rpx;
123
+}
124
+.commodity-container .commodity-list .commodity-item .commodity-left .commodity-img {
125
+  width: 180rpx;
126
+  height: 180rpx;
127
+}
128
+.commodity-container .commodity-list .commodity-item .commodity-left .commodity-content {
129
+  display: flex;
130
+  flex-direction: column;
131
+  height: 100%;
132
+  margin-left: 24rpx;
133
+}
134
+.commodity-container .commodity-list .commodity-item .commodity-left .commodity-content .commodity-title {
135
+  font-size: 13pt;
136
+  color: #3e3e3e;
137
+}
138
+.commodity-container .commodity-list .commodity-item .commodity-left .commodity-content .commodity-desc {
139
+  margin-top: 4rpx;
140
+  font-size: 10pt;
141
+  color: #959595;
142
+}
143
+.commodity-container .commodity-list .commodity-item .commodity-price {
144
+  display: flex;
145
+  flex-grow: 2;
146
+  align-items: flex-end;
147
+}
148
+.commodity-container .commodity-list .commodity-item .commodity-price text {
149
+  font-size: 14pt;
150
+  color: #e64340;
151
+}
152
+.commodity-container .commodity-list .commodity-right {
153
+  display: flex;
154
+  flex-direction: column;
155
+  align-items: flex-end;
156
+  height: 100%;
157
+}
158
+.commodity-container .commodity-list .commodity-right .bought {
159
+  font-size: 10pt;
160
+  color: #e64340;
161
+  text-align: right;
162
+}
163
+.commodity-container .commodity-list .commodity-right .stepper-container {
164
+  margin-top: 24rpx;
165
+}
166
+.consumer-container {
167
+  padding: 24rpx;
168
+  background-color: #fff;
169
+}
170
+.consumer-container .consumer-title {
171
+  height: 80rpx;
172
+}
173
+.consumer-container .consumer-title text {
174
+  font-size: 14pt;
175
+  font-weight: 600;
176
+  color: #3e3e3e;
177
+}
178
+.consumer-container .swiper-container {
179
+  width: 100%;
180
+  height: 200rpx;
181
+}
182
+.consumer-container .consumer-item {
183
+  width: 654rpx;
184
+}
185
+.consumer-container .consumer-item .consumer-info {
186
+  display: flex;
187
+  align-items: center;
188
+}
189
+.consumer-container .consumer-item .consumer-info .avatar {
190
+  width: 56rpx;
191
+  height: 56rpx;
192
+  border-radius: 28rpx;
193
+}
194
+.consumer-container .consumer-item .consumer-info .name {
195
+  margin-left: 12rpx;
196
+  font-size: 11pt;
197
+  font-weight: 600;
198
+  color: #000;
199
+}
200
+.consumer-container .consumer-item .consumer-info .time {
201
+  margin-top: 4rpx;
202
+  margin-left: 24rpx;
203
+  font-size: 8pt;
204
+  color: #c8c8c8;
205
+}
206
+.consumer-container .consumer-item .consumer-commodity {
207
+  display: flex;
208
+  justify-content: flex-end;
209
+  width: 100%;
210
+  margin-top: 12rpx;
211
+}
212
+.consumer-container .consumer-item .consumer-commodity text {
213
+  width: 100%;
214
+  font-size: 11pt;
215
+  color: #3e3e3e;
216
+  text-align: right;
217
+}
218
+.btn-footer {
219
+  display: flex;
220
+  align-items: center;
221
+  position: fixed;
222
+  bottom: 0;
223
+  box-sizing: border-box;
224
+  justify-content: center;
225
+  width: 100%;
226
+  height: 160rpx;
227
+  padding: 24rpx;
228
+  background-color: #fff;
229
+  box-shadow: 0 -5px 9px 2px rgba(0, 0, 0, 0.1);
230
+}
231
+.btn-footer .btn-footer-container {
232
+  display: flex;
233
+  width: 100%;
234
+  height: 100rpx;
235
+  overflow: hidden;
236
+  border: 1px #09bb07 solid;
237
+  border-radius: 5px;
238
+}
239
+.btn-footer .btn-footer-container .left {
240
+  display: flex;
241
+  align-items: center;
242
+  justify-content: center;
243
+  width: 40%;
244
+  height: 100rpx;
245
+  background-color: #fff;
246
+}
247
+.btn-footer .btn-footer-container .left text {
248
+  font-size: 14pt;
249
+  font-weight: 700;
250
+  color: #e64340;
251
+}
252
+.btn-footer .btn-footer-container .right {
253
+  width: 100%;
254
+  height: 100%;
255
+}

+ 85 - 0
src/pages/order/order.js

@@ -0,0 +1,85 @@
1
+const app = getApp()
2
+
3
+Page({
4
+  /**
5
+   * Page initial data
6
+   */
7
+  data: {
8
+    hasAddress: false,
9
+    commodities: [],
10
+    address: {},
11
+    kol: {},
12
+    pack_id: '',
13
+    total: 0
14
+  },
15
+
16
+  onLoad() {
17
+    const eventChannel = this.getOpenerEventChannel()
18
+    const that = this
19
+    eventChannel.on('acceptDataFromOpenerPage', data => {
20
+      that.setData({
21
+        commodities: data.commodities,
22
+        total: data.total,
23
+        kol: data.kol,
24
+        pack_id: data.pack_id
25
+      })
26
+    })
27
+  },
28
+
29
+  navigateToAddress() {
30
+    const that = this
31
+    wx.chooseAddress({
32
+      success: res => {
33
+        res.detailInfo =
34
+          res.provinceName + res.cityName + res.countyName + res.detailInfo
35
+        that.setData({
36
+          address: res,
37
+          hasAddress: true
38
+        })
39
+      }
40
+    })
41
+  },
42
+
43
+  confirm() {
44
+    const { commodities } = this.data
45
+    // const body = commodities.reduce(
46
+    //   (accumulator, c) => `${accumulator}${c.title}x${c.num};`,
47
+    //   ''
48
+    // )
49
+    console.log(body)
50
+    app.network.post({
51
+      url: app.urls.ORDER_CREATE,
52
+      data: {
53
+        pack_id: this.data.pack_id,
54
+        kol_id: this.data.kol.kol_id,
55
+        total_fee: this.data.total * 100,
56
+        goods_info: JSON.stringify(this.data.commodities),
57
+        name: this.data.address.userName || '',
58
+        phone: this.data.address.telNumber || '',
59
+        address: this.data.address.detailInfo || '',
60
+        body: '尖货接龙'
61
+      },
62
+      success(res1) {
63
+        const { wxpay_params } = res1.data
64
+        wx.requestPayment({
65
+          timeStamp: wxpay_params.timeStamp,
66
+          nonceStr: wxpay_params.nonceStr,
67
+          package: wxpay_params.package,
68
+          signType: wxpay_params.signType,
69
+          paySign: wxpay_params.paySign,
70
+          success: res2 => {
71
+            wx.reLaunch({
72
+              url: '/pages/index/index',
73
+              success() {
74
+                wx.showToast({
75
+                  title: '购买成功',
76
+                  icon: 'success'
77
+                })
78
+              }
79
+            })
80
+          }
81
+        })
82
+      }
83
+    })
84
+  }
85
+})

+ 5 - 0
src/pages/order/order.json

@@ -0,0 +1,5 @@
1
+{
2
+  "usingComponents": {},
3
+  "disableScroll": true,
4
+  "navigationBarTitleText": "订单确认"
5
+}

+ 204 - 0
src/pages/order/order.less

@@ -0,0 +1,204 @@
1
+@import './src/style/less/variable.less';
2
+@import './src/style/less/layout.less';
3
+@import './src/style/less/shape.less';
4
+@import '/template/footer/footer.wxss';
5
+@import '/template/btn/btn.wxss';
6
+
7
+.order-view {
8
+  .flex-center(column);
9
+  .with-btnFooter();
10
+
11
+  width: 100%;
12
+  background-color: @color-bg;
13
+
14
+  text {
15
+    color: @color-black;
16
+  }
17
+
18
+  view {
19
+    color: @color-black;
20
+  }
21
+}
22
+
23
+.address {
24
+  .flex-center(row);
25
+
26
+  box-sizing: border-box;
27
+  width: @visual-width;
28
+  min-height: 80rpx;
29
+  padding: 0 @spacing-item;
30
+  margin: @spacing-item;
31
+  background-color: @color-white;
32
+  border-radius: @radius;
33
+
34
+  .item() {
35
+    .square(40rpx);
36
+
37
+    .icon {
38
+      .full-image();
39
+    }
40
+  }
41
+
42
+  .left {
43
+    .item();
44
+  }
45
+
46
+  .content {
47
+    .flex-center(column);
48
+
49
+    box-sizing: border-box;
50
+    width: @visual-width - 80rpx - @spacing-item * 2;
51
+    min-height: 80rpx;
52
+    padding: @spacing-inline @spacing-item;
53
+
54
+    view {
55
+      .flex-center(row);
56
+
57
+      width: 100%;
58
+      margin-top: @spacing-inline;
59
+    }
60
+
61
+    .top {
62
+      justify-content: flex-start;
63
+
64
+      .name {
65
+        font-size: @font-primary;
66
+      }
67
+
68
+      .phone {
69
+        margin-left: @spacing-item;
70
+        font-size: @font-quaternary;
71
+        color: @color-gray;
72
+      }
73
+    }
74
+
75
+    .bottom {
76
+      font-size: @font-tertiary;
77
+    }
78
+  }
79
+
80
+  .right {
81
+    .item();
82
+
83
+    .arrow {
84
+      .arrow(right; @color-dark-bg; @icon-small-size);
85
+    }
86
+  }
87
+
88
+  &-hover {
89
+    background-color: darken(@color-white, 15%);
90
+  }
91
+}
92
+
93
+.authorization {
94
+  justify-content: center;
95
+
96
+  text {
97
+    margin-left: @spacing-short-text;
98
+    font-size: @font-secondary;
99
+    color: @color-brand;
100
+  }
101
+
102
+  .plus {
103
+    .plus(4rpx; @color-brand);
104
+    .square(@icon-little-small-size);
105
+  }
106
+}
107
+
108
+.goods {
109
+  .flex-center(row);
110
+
111
+  width: @visual-width;
112
+  height: 200rpx;
113
+  margin-top: @spacing-inline;
114
+  background-color: @color-white;
115
+  border-radius: @radius;
116
+
117
+  .left {
118
+    .flex-center(row);
119
+
120
+    justify-content: center;
121
+    width: 200rpx;
122
+    height: 100%;
123
+
124
+    image {
125
+      .square(180rpx);
126
+    }
127
+  }
128
+
129
+  .right {
130
+    box-sizing: border-box;
131
+    display: flex;
132
+    flex-direction: column;
133
+    justify-content: space-between;
134
+    width: @visual-width - 200rpx;
135
+    height: 100%;
136
+    padding: @spacing-item;
137
+
138
+    .top {
139
+      width: 100%;
140
+
141
+      .title {
142
+        font-size: @font-primary;
143
+      }
144
+    }
145
+
146
+    .bottom {
147
+      .flex-center(row);
148
+
149
+      justify-content: space-between;
150
+      width: 100%;
151
+
152
+      .num {
153
+        font-size: @font-primary;
154
+      }
155
+    }
156
+  }
157
+}
158
+
159
+.btn-footer {
160
+  .flex-center(row);
161
+
162
+  position: fixed;
163
+  bottom: 0;
164
+  box-sizing: border-box;
165
+  justify-content: center;
166
+  width: 100%;
167
+  height: @btnFooter-height;
168
+  padding: @spacing-item;
169
+  background-color: @color-white;
170
+  box-shadow: 0 -5px 9px 2px fade(@color-black, 10%);
171
+
172
+  .btn-footer-container {
173
+    display: flex;
174
+    width: 100%;
175
+    height: 100rpx;
176
+    overflow: hidden;
177
+    border: 1px #09bb07 solid;
178
+    border-radius: @radius;
179
+
180
+    &-disabled {
181
+      border: 1px @color-light-gray solid !important;
182
+    }
183
+
184
+    .left {
185
+      .flex-center(row);
186
+
187
+      justify-content: center;
188
+      width: 40%;
189
+      height: 100rpx;
190
+      background-color: @color-white;
191
+
192
+      text {
193
+        font-size: @font-primary;
194
+        font-weight: 700;
195
+        color: #e64340;
196
+      }
197
+    }
198
+
199
+    .right {
200
+      width: 100%;
201
+      height: 100%;
202
+    }
203
+  }
204
+}

+ 64 - 0
src/pages/order/order.wxml

@@ -0,0 +1,64 @@
1
+<view class="order-view">
2
+  <view
3
+    class="address"
4
+    hover-class="address-hover"
5
+    bindtap="navigateToAddress"
6
+    wx:if="{{ hasAddress }}"
7
+  >
8
+    <view class="left">
9
+      <image class="icon" src="/resources/common/location.png"></image>
10
+    </view>
11
+    <view class="content">
12
+      <view class="top">
13
+        <text class="name">{{ address.userName }}</text>
14
+        <text class="phone">{{ address.telNumber }}</text>
15
+      </view>
16
+      <view class="bottom">{{ address.detailInfo }}</view>
17
+    </view>
18
+    <view class="right">
19
+      <view class="icon arrow"></view>
20
+    </view>
21
+  </view>
22
+  <view
23
+    class="address authorization"
24
+    hover-class="address-hover"
25
+    bindtap="navigateToAddress"
26
+    wx:elif="{{ !hasAddress }}"
27
+  >
28
+    <view class="plus"></view>
29
+    <text>新建地址</text>
30
+  </view>
31
+  <view class="goods-container">
32
+    <block wx:for="{{ commodities }}">
33
+      <view class="goods">
34
+        <view class="left">
35
+          <image class="" src="{{ item.image_url }}" mode="aspectFit"></image>
36
+        </view>
37
+        <view class="right">
38
+          <view class="top">
39
+            <text class="title">{{ item.title }}</text>
40
+          </view>
41
+          <view class="bottom">
42
+            <text class="integral" wx:if="{{ item.price > 0 }}">¥{{ item.price }}</text>
43
+            <text class="num">x{{ item.num }}</text>
44
+          </view>
45
+        </view>
46
+      </view>
47
+    </block>
48
+  </view>
49
+</view>
50
+
51
+<import src="/template/btn/btn.wxml"></import>
52
+<view class="btn-footer">
53
+  <view class="btn-footer-container {{hasAddress ? '' : 'btn-footer-container-disabled'}}">
54
+    <view class="left" wx:if="{{ total > 0 }}">
55
+      <text class="money">¥{{ total }}</text>
56
+    </view>
57
+    <view class="right">
58
+      <template
59
+        is="iconBtn"
60
+        data="{{btnClass: 'button4', btnTxt: '立即支付', disabled: !hasAddress, action: 'confirm'}}"
61
+      ></template>
62
+    </view>
63
+  </view>
64
+</view>

+ 201 - 0
src/pages/order/order.wxss

@@ -0,0 +1,201 @@
1
+@import '/template/footer/footer.wxss';
2
+@import '/template/btn/btn.wxss';
3
+.order-view {
4
+  display: flex;
5
+  flex-direction: column;
6
+  align-items: center;
7
+  height: calc(100% - 160rpx);
8
+  width: 100%;
9
+  background-color: #efefef;
10
+}
11
+.order-view text {
12
+  color: #000;
13
+}
14
+.order-view view {
15
+  color: #000;
16
+}
17
+.address {
18
+  display: flex;
19
+  align-items: center;
20
+  box-sizing: border-box;
21
+  width: 702rpx;
22
+  min-height: 80rpx;
23
+  padding: 0 24rpx;
24
+  margin: 24rpx;
25
+  background-color: #fff;
26
+  border-radius: 5px;
27
+}
28
+.address .left {
29
+  width: 40rpx;
30
+  height: 40rpx;
31
+}
32
+.address .left .icon {
33
+  width: 100%;
34
+  height: 100%;
35
+}
36
+.address .content {
37
+  display: flex;
38
+  flex-direction: column;
39
+  align-items: center;
40
+  box-sizing: border-box;
41
+  width: 574rpx;
42
+  min-height: 80rpx;
43
+  padding: 12rpx 24rpx;
44
+}
45
+.address .content view {
46
+  display: flex;
47
+  align-items: center;
48
+  width: 100%;
49
+  margin-top: 12rpx;
50
+}
51
+.address .content .top {
52
+  justify-content: flex-start;
53
+}
54
+.address .content .top .name {
55
+  font-size: 14pt;
56
+}
57
+.address .content .top .phone {
58
+  margin-left: 24rpx;
59
+  font-size: 10pt;
60
+  color: #3e3e3e;
61
+}
62
+.address .content .bottom {
63
+  font-size: 11pt;
64
+}
65
+.address .right {
66
+  width: 40rpx;
67
+  height: 40rpx;
68
+}
69
+.address .right .icon {
70
+  width: 100%;
71
+  height: 100%;
72
+}
73
+.address .right .arrow {
74
+  width: 32rpx;
75
+  height: 32rpx;
76
+  box-sizing: border-box;
77
+  border-top: 2px solid #3e3e3e;
78
+  border-right: 2px solid #3e3e3e;
79
+  transform: rotate(45deg);
80
+}
81
+.address-hover {
82
+  background-color: #d9d9d9;
83
+}
84
+.authorization {
85
+  justify-content: center;
86
+}
87
+.authorization text {
88
+  margin-left: 4rpx;
89
+  font-size: 13pt;
90
+  color: #0967b2;
91
+}
92
+.authorization .plus {
93
+  position: relative;
94
+  width: 24rpx;
95
+  height: 24rpx;
96
+}
97
+.authorization .plus::before {
98
+  position: absolute;
99
+  top: calc(50% - 4rpx / 2);
100
+  left: 0;
101
+  width: 100%;
102
+  height: 4rpx;
103
+  content: '';
104
+  background-color: #0967b2;
105
+  border-radius: 4rpx;
106
+}
107
+.authorization .plus::after {
108
+  position: absolute;
109
+  top: 0;
110
+  left: calc(50% - 4rpx / 2);
111
+  width: 4rpx;
112
+  height: 100%;
113
+  content: '';
114
+  background-color: #0967b2;
115
+  border-radius: 4rpx;
116
+}
117
+.goods {
118
+  display: flex;
119
+  align-items: center;
120
+  width: 702rpx;
121
+  height: 200rpx;
122
+  margin-top: 12rpx;
123
+  background-color: #fff;
124
+  border-radius: 5px;
125
+}
126
+.goods .left {
127
+  display: flex;
128
+  align-items: center;
129
+  justify-content: center;
130
+  width: 200rpx;
131
+  height: 100%;
132
+}
133
+.goods .left image {
134
+  width: 180rpx;
135
+  height: 180rpx;
136
+}
137
+.goods .right {
138
+  box-sizing: border-box;
139
+  display: flex;
140
+  flex-direction: column;
141
+  justify-content: space-between;
142
+  width: 502rpx;
143
+  height: 100%;
144
+  padding: 24rpx;
145
+}
146
+.goods .right .top {
147
+  width: 100%;
148
+}
149
+.goods .right .top .title {
150
+  font-size: 14pt;
151
+}
152
+.goods .right .bottom {
153
+  display: flex;
154
+  align-items: center;
155
+  justify-content: space-between;
156
+  width: 100%;
157
+}
158
+.goods .right .bottom .num {
159
+  font-size: 14pt;
160
+}
161
+.btn-footer {
162
+  display: flex;
163
+  align-items: center;
164
+  position: fixed;
165
+  bottom: 0;
166
+  box-sizing: border-box;
167
+  justify-content: center;
168
+  width: 100%;
169
+  height: 160rpx;
170
+  padding: 24rpx;
171
+  background-color: #fff;
172
+  box-shadow: 0 -5px 9px 2px rgba(0, 0, 0, 0.1);
173
+}
174
+.btn-footer .btn-footer-container {
175
+  display: flex;
176
+  width: 100%;
177
+  height: 100rpx;
178
+  overflow: hidden;
179
+  border: 1px #09bb07 solid;
180
+  border-radius: 5px;
181
+}
182
+.btn-footer .btn-footer-container-disabled {
183
+  border: 1px #959595 solid !important;
184
+}
185
+.btn-footer .btn-footer-container .left {
186
+  display: flex;
187
+  align-items: center;
188
+  justify-content: center;
189
+  width: 40%;
190
+  height: 100rpx;
191
+  background-color: #fff;
192
+}
193
+.btn-footer .btn-footer-container .left text {
194
+  font-size: 14pt;
195
+  font-weight: 700;
196
+  color: #e64340;
197
+}
198
+.btn-footer .btn-footer-container .right {
199
+  width: 100%;
200
+  height: 100%;
201
+}

+ 126 - 0
src/project.config.json

@@ -0,0 +1,126 @@
1
+{
2
+	"description": "项目配置文件。",
3
+	"setting": {
4
+		"urlCheck": true,
5
+		"es6": true,
6
+		"enhance": true,
7
+		"postcss": true,
8
+		"minified": false,
9
+		"newFeature": true,
10
+		"coverView": true,
11
+		"nodeModules": true,
12
+		"autoAudits": false,
13
+		"showShadowRootInWxmlPanel": true,
14
+		"scopeDataCheck": false,
15
+		"checkInvalidKey": true,
16
+		"checkSiteMap": false,
17
+		"uploadWithSourceMap": true,
18
+		"babelSetting": {
19
+			"ignore": [],
20
+			"disablePlugins": [],
21
+			"outputPath": ""
22
+		}
23
+	},
24
+	"compileType": "miniprogram",
25
+	"libVersion": "2.10.1",
26
+	"appid": "wx82344118083f47c8",
27
+	"projectname": "%E7%BB%9F%E8%A7%88",
28
+	"scripts": {
29
+		"beforeCompile": "",
30
+		"beforePreview": "",
31
+		"beforeUpload": ""
32
+	},
33
+	"simulatorType": "wechat",
34
+	"simulatorPluginLibVersion": {},
35
+	"condition": {
36
+		"search": {
37
+			"current": -1,
38
+			"list": []
39
+		},
40
+		"conversation": {
41
+			"current": -1,
42
+			"list": []
43
+		},
44
+		"plugin": {
45
+			"current": -1,
46
+			"list": []
47
+		},
48
+		"game": {
49
+			"current": -1,
50
+			"list": []
51
+		},
52
+		"gamePlugin": {
53
+			"current": -1,
54
+			"list": []
55
+		},
56
+		"miniprogram": {
57
+			"current": -1,
58
+			"list": [
59
+				{
60
+					"id": 0,
61
+					"name": "pages/cameraCompatible/cameraCompatible",
62
+					"pathName": "pages/cameraCompatible/cameraCompatible",
63
+					"query": "activity_id=nBJ56hN9ik94js4duSh34V&isSignIn=1",
64
+					"scene": 1047
65
+				},
66
+				{
67
+					"id": 1,
68
+					"name": "pages/index/index",
69
+					"pathName": "pages/index/index",
70
+					"query": "q=http://kodo.tamron.cn/v/0+21+KUjsUKURRRRRRUK6KxxK",
71
+					"scene": 1011
72
+				},
73
+				{
74
+					"id": 5,
75
+					"name": "公众号菜单栏",
76
+					"pathName": "pages/index/index",
77
+					"query": "page=memberCard",
78
+					"scene": 1035,
79
+					"referrerInfo": {}
80
+				},
81
+				{
82
+					"id": 3,
83
+					"name": "一物一码",
84
+					"pathName": "pages/index/index",
85
+					"query": "code_ticket=P.URL.CN/0MX5D3AIAJZ2:PW3RPK",
86
+					"scene": 1124
87
+				},
88
+				{
89
+					"id": 4,
90
+					"name": "开卡",
91
+					"pathName": "pages/index/index",
92
+					"query": "openCard=1",
93
+					"scene": 1074
94
+				},
95
+				{
96
+					"id": 5,
97
+					"name": "会员活动详情页",
98
+					"pathName": "pages/member/activity/activity",
99
+					"query": "activity_id=KNFYGrDrBGe4MzVw9wNpxL",
100
+					"scene": null
101
+				},
102
+				{
103
+					"id": 6,
104
+					"name": "测试",
105
+					"pathName": "pages/index/index",
106
+					"query": "q= http://kodo.tamron.cn/v/0+20+KUscUKUwwKzzxUxRRKRs33RCV5ODKVQZ7CHBK4F75Q7PHSJWLFEO1RJ9DQHZYEIQX1Q2DH30",
107
+					"scene": 1011
108
+				},
109
+				{
110
+					"id": -1,
111
+					"name": "template/resultView/resultView",
112
+					"pathName": "template/resultView/resultView",
113
+					"query": "q= http%3A%2F%2Fkodo.tamron.cn%2Fv%2F0%2B21%2BKUKy6UKURw6jjyUK6KxKyTKTA9LLUFW13BDY2HD3B1ODC74EIPI14UTE0LHFDDV3G7QY3QTX",
114
+					"scene": null
115
+				},
116
+				{
117
+					"id": -1,
118
+					"name": "pages/commodity/commodity",
119
+					"pathName": "pages/commodity/commodity",
120
+					"query": "",
121
+					"scene": null
122
+				}
123
+			]
124
+		}
125
+	}
126
+}

二進制
src/resources/.DS_Store


二進制
src/resources/common/.DS_Store


二進制
src/resources/common/location.png


+ 9 - 0
src/sitemap.json

@@ -0,0 +1,9 @@
1
+{
2
+  "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
3
+  "rules": [
4
+    {
5
+      "action": "allow",
6
+      "page": "*"
7
+    }
8
+  ]
9
+}

+ 96 - 0
src/style/button.less

@@ -0,0 +1,96 @@
1
+@import './less/variable.less';
2
+
3
+.button1 {
4
+  padding: 0;
5
+  background-color: @color-brand;
6
+  border-radius: @radius;
7
+
8
+  &-hover {
9
+    color: shade(@color-white, 10%) !important;
10
+    background-color: darken(@color-brand, 5%) !important;
11
+
12
+    text {
13
+      color: shade(@color-white, 10%) !important;
14
+    }
15
+  }
16
+
17
+  &[disabled] {
18
+    color: fade(@color-white, 60%) !important;
19
+    background-color: fade(@color-brand, 60%) !important;
20
+
21
+    text {
22
+      color: fade(@color-white, 60%) !important;
23
+    }
24
+  }
25
+}
26
+
27
+.button3 {
28
+  padding: 0;
29
+  background-color: @color-other1;
30
+
31
+  &-hover {
32
+    color: shade(@color-white, 10%) !important;
33
+    background-color: darken(@color-other1, 8%) !important;
34
+
35
+    text {
36
+      color: shade(@color-white, 10%) !important;
37
+    }
38
+  }
39
+
40
+  &[disabled] {
41
+    color: fade(@color-white, 60%) !important;
42
+    background-color: fade(@color-other1, 60%) !important;
43
+
44
+    text {
45
+      color: fade(@color-white, 60%) !important;
46
+    }
47
+  }
48
+}
49
+
50
+.button2 {
51
+  padding: 0;
52
+  background-color: @color-brand;
53
+  border-radius: @radius;
54
+
55
+  text {
56
+    color: @color-white;
57
+  }
58
+
59
+  &-hover {
60
+    color: shade(@color-white, 10%) !important;
61
+    background-color: darken(@color-brand, 5%) !important;
62
+
63
+    text {
64
+      color: shade(@color-white, 10%) !important;
65
+    }
66
+  }
67
+
68
+  &[disabled] {
69
+    color: darken(@color-white, 10%) !important;
70
+    background-color: @color-light-gray !important;
71
+  }
72
+}
73
+
74
+.button4 {
75
+  height: 100%;
76
+  padding: 0;
77
+  background-color: #09bb07;
78
+
79
+  text {
80
+    color: @color-white;
81
+  }
82
+
83
+  &-hover {
84
+    color: shade(@color-white, 10%) !important;
85
+    background-color: darken(#09bb07, 5%) !important;
86
+
87
+    text {
88
+      color: shade(@color-white, 10%) !important;
89
+    }
90
+  }
91
+
92
+  &[disabled] {
93
+    color: darken(@color-white, 10%) !important;
94
+    background-color: @color-light-gray !important;
95
+  }
96
+}

+ 75 - 0
src/style/button.wxss

@@ -0,0 +1,75 @@
1
+.button1 {
2
+  padding: 0;
3
+  background-color: #0967b2;
4
+  border-radius: 5px;
5
+}
6
+.button1-hover {
7
+  color: #e6e6e6 !important;
8
+  background-color: #08599a !important;
9
+}
10
+.button1-hover text {
11
+  color: #e6e6e6 !important;
12
+}
13
+.button1[disabled] {
14
+  color: rgba(255, 255, 255, 0.6) !important;
15
+  background-color: rgba(9, 103, 178, 0.6) !important;
16
+}
17
+.button1[disabled] text {
18
+  color: rgba(255, 255, 255, 0.6) !important;
19
+}
20
+.button3 {
21
+  padding: 0;
22
+  background-color: #d2b167;
23
+}
24
+.button3-hover {
25
+  color: #e6e6e6 !important;
26
+  background-color: #c9a148 !important;
27
+}
28
+.button3-hover text {
29
+  color: #e6e6e6 !important;
30
+}
31
+.button3[disabled] {
32
+  color: rgba(255, 255, 255, 0.6) !important;
33
+  background-color: rgba(210, 177, 103, 0.6) !important;
34
+}
35
+.button3[disabled] text {
36
+  color: rgba(255, 255, 255, 0.6) !important;
37
+}
38
+.button2 {
39
+  padding: 0;
40
+  background-color: #0967b2;
41
+  border-radius: 5px;
42
+}
43
+.button2 text {
44
+  color: #fff;
45
+}
46
+.button2-hover {
47
+  color: #e6e6e6 !important;
48
+  background-color: #08599a !important;
49
+}
50
+.button2-hover text {
51
+  color: #e6e6e6 !important;
52
+}
53
+.button2[disabled] {
54
+  color: #e6e6e6 !important;
55
+  background-color: #959595 !important;
56
+}
57
+.button4 {
58
+  height: 100%;
59
+  padding: 0;
60
+  background-color: #09bb07;
61
+}
62
+.button4 text {
63
+  color: #fff;
64
+}
65
+.button4-hover {
66
+  color: #e6e6e6 !important;
67
+  background-color: #08a206 !important;
68
+}
69
+.button4-hover text {
70
+  color: #e6e6e6 !important;
71
+}
72
+.button4[disabled] {
73
+  color: #e6e6e6 !important;
74
+  background-color: #959595 !important;
75
+}

+ 85 - 0
src/style/less/layout.less

@@ -0,0 +1,85 @@
1
+@import './variable.less';
2
+
3
+// flex
4
+.flex-center(column) {
5
+  display: flex;
6
+  flex-direction: column;
7
+  align-items: center;
8
+}
9
+
10
+.flex-center(row) {
11
+  display: flex;
12
+  align-items: center;
13
+}
14
+
15
+.banner(@height: 400rpx) {
16
+  width: 100%;
17
+  height: @height;
18
+
19
+  image {
20
+    .full-image();
21
+  }
22
+}
23
+
24
+// list
25
+.table-view(@width: 100%; @spacing: 12rpx) {
26
+  .flex-center(column);
27
+
28
+  width: 100%;
29
+  padding-bottom: @spacing * 2;
30
+
31
+  &-item {
32
+    width: @width;
33
+    margin-top: @spacing;
34
+  }
35
+}
36
+
37
+.collection-view(@width: 650rpx; @row: 4; @spacing-top: 24rpx; @spacing-left: 24rpx) {
38
+  .flex-center(row);
39
+
40
+  flex-wrap: wrap;
41
+  width: @width;
42
+
43
+  &-item {
44
+    width: (@width - @spacing-left * (@row - 1)) / @row;
45
+    margin-top: @spacing-top;
46
+    margin-right: @spacing-left;
47
+
48
+    &:nth-child(@{row}n) {
49
+      margin-right: 0;
50
+    }
51
+  }
52
+}
53
+
54
+.footer() {
55
+  position: fixed;
56
+  bottom: 0;
57
+  display: flex;
58
+  width: 100%;
59
+  height: calc(80rpx + env(safe-area-inset-bottom));
60
+}
61
+
62
+.full-image() {
63
+  width: 100%;
64
+  height: 100%;
65
+}
66
+
67
+.square(@len) {
68
+  width: @len;
69
+  height: @len;
70
+}
71
+
72
+.circle(@radius) {
73
+  width: @radius;
74
+  height: @radius;
75
+  overflow: hidden;
76
+  border-radius: @radius / 2;
77
+}
78
+
79
+.with-btnFooter() {
80
+  height: calc(100% - @btnFooter-height);
81
+}
82
+
83
+.with-footer() {
84
+  height: calc(100% - @descFooter-height);
85
+}

+ 70 - 0
src/style/less/shape.less

@@ -0,0 +1,70 @@
1
+@import './variable.less';
2
+
3
+.arrow(right; @color; @size) {
4
+  .square(@size);
5
+
6
+  box-sizing: border-box;
7
+  border-top: 2px solid @color;
8
+  border-right: 2px solid @color;
9
+  transform: rotate(45deg);
10
+}
11
+
12
+.triangle-center(bottom; @color; @len: 25rpx) {
13
+  border-right: @len solid transparent;
14
+  border-bottom: @len solid @color;
15
+  border-left: @len solid transparent;
16
+}
17
+
18
+.triangle-corner(bottomRight; @color; @len) {
19
+  border-bottom: @len solid @color;
20
+  border-left: @len solid transparent;
21
+}
22
+
23
+.cross(@len; @color) {
24
+  display: inline-block;
25
+  width: @len;
26
+  height: @len / 4;
27
+  overflow: visible;
28
+  font-size: 0;
29
+  line-height: 0;
30
+  vertical-align: middle;
31
+  background: @color;
32
+  border-radius: @len / 4;
33
+  transform: rotate(45deg);
34
+
35
+  &::after {
36
+    display: block;
37
+    width: @len;
38
+    height: @len / 4;
39
+    content: '/';
40
+    background: @color;
41
+    border-radius: @len / 4;
42
+    transform: rotate(-90deg);
43
+  }
44
+}
45
+
46
+.plus(@borderWidth; @color) {
47
+  position: relative;
48
+
49
+  &::before {
50
+    position: absolute;
51
+    top: calc(50% - @borderWidth / 2);
52
+    left: 0;
53
+    width: 100%;
54
+    height: @borderWidth;
55
+    content: '';
56
+    background-color: @color-brand;
57
+    border-radius: @borderWidth;
58
+  }
59
+
60
+  &::after {
61
+    position: absolute;
62
+    top: 0;
63
+    left: calc(50% - @borderWidth / 2);
64
+    width: @borderWidth;
65
+    height: 100%;
66
+    content: '';
67
+    background-color: @color-brand;
68
+    border-radius: @borderWidth;
69
+  }
70
+}

+ 48 - 0
src/style/less/variable.less

@@ -0,0 +1,48 @@
1
+//color
2
+@color-bg: #efefef;
3
+@color-dark-bg: #3e3e3e;
4
+@color-black: #000;
5
+@color-white: #fff;
6
+@color-brand: #0967b2;
7
+@color-other1: #d2b167;
8
+@color-gray: #3e3e3e;
9
+@color-light-gray: #959595;
10
+
11
+//font
12
+@font-h1: 40pt;
13
+@font-h2: 20pt;
14
+@font-h3: 18pt;
15
+@font-title: 17pt;
16
+@font-primary: 14pt;
17
+@font-secondary: 13pt;
18
+@font-tertiary: 11pt;
19
+@font-quaternary: 10pt;
20
+@font-label: 8pt;
21
+
22
+// spacing
23
+@spacing-view: 36rpx;
24
+@spacing-item: 24rpx;
25
+@spacing-inline: 12rpx;
26
+
27
+@spacing-text: 12rpx;
28
+@spacing-short-text: 4rpx;
29
+
30
+@padding-quaternary: 12rpx;
31
+
32
+//icon size
33
+@icon-min-size: 16rpx;
34
+@icon-little-small-size: 24rpx;
35
+@icon-small-size: 32rpx;
36
+@icon-middle-size: 48rpx;
37
+@icon-big-size: 60rpx;
38
+@icon-large-size: 72rpx;
39
+
40
+//height
41
+@btnFooter-height: 160rpx;
42
+@descFooter-height: 100rpx;
43
+@btnFooterWithProtocol-height: 200rpx;
44
+
45
+@radius: 5px;
46
+@radius-big: 10px;
47
+
48
+@visual-width: 750rpx - @spacing-item * 2;

+ 19 - 0
src/template/btn/btn.less

@@ -0,0 +1,19 @@
1
+@import './src/style/less/variable.less';
2
+@import './src/style/less/layout.less';
3
+
4
+.icon-btn {
5
+  .flex-center(row);
6
+
7
+  justify-content: center;
8
+  padding: 0;
9
+
10
+  text {
11
+    font-size: @font-primary;
12
+  }
13
+
14
+  image {
15
+    width: @icon-middle-size;
16
+    height: @icon-middle-size;
17
+    margin-left: @spacing-inline;
18
+  }
19
+}

+ 17 - 0
src/template/btn/btn.wxml

@@ -0,0 +1,17 @@
1
+<template name="iconBtn">
2
+  <button
3
+    class="icon-btn {{ btnClass }}"
4
+    hover-class="{{ btnClass }}-hover"
5
+    disabled="{{ disabled || false }}"
6
+    open-type="{{ openType }}"
7
+    bindtap="{{ (openType == '' || openType == undefined) ? action : '' }}"
8
+    bindgetuserinfo="{{ action }}"
9
+    lang="zh_CN"
10
+  >
11
+    <text>{{ btnTxt }}</text>
12
+    <image
13
+      src="{{ btnIcon || '/resources/common/left_arrow1.png' }}"
14
+      hidden="{{ disabled }}"
15
+    ></image>
16
+  </button>
17
+</template>

+ 14 - 0
src/template/btn/btn.wxss

@@ -0,0 +1,14 @@
1
+.icon-btn {
2
+  display: flex;
3
+  align-items: center;
4
+  justify-content: center;
5
+  padding: 0;
6
+}
7
+.icon-btn text {
8
+  font-size: 14pt;
9
+}
10
+.icon-btn image {
11
+  width: 48rpx;
12
+  height: 48rpx;
13
+  margin-left: 12rpx;
14
+}

+ 81 - 0
src/template/footer/footer.less

@@ -0,0 +1,81 @@
1
+@import './src/style/less/variable.less';
2
+@import './src/style/less/layout.less';
3
+@import '/template/btn/btn.wxss';
4
+
5
+.member-footer {
6
+  position: fixed;
7
+  bottom: 0;
8
+  z-index: 999;
9
+  display: flex;
10
+  width: 100%;
11
+  height: @descFooter-height;
12
+  background-color: #c8c8c8;
13
+
14
+  .left {
15
+    .flex-center(row);
16
+
17
+    justify-content: center;
18
+    width: 40%;
19
+    height: 100%;
20
+
21
+    image {
22
+      width: 80%;
23
+    }
24
+  }
25
+
26
+  .right {
27
+    .flex-center(row);
28
+
29
+    width: 70%;
30
+    height: 100%;
31
+
32
+    text {
33
+      margin-left: @spacing-item;
34
+      font-size: @font-label;
35
+      color: @color-gray;
36
+    }
37
+  }
38
+}
39
+
40
+.btn-footer() {
41
+  .flex-center(row);
42
+
43
+  position: fixed;
44
+  bottom: 0;
45
+  justify-content: center;
46
+  width: 100%;
47
+  height: @btnFooter-height;
48
+  background-color: @color-white;
49
+  box-shadow: 0 -5px 9px 2px fade(@color-black, 10%);
50
+
51
+  button {
52
+    height: 100rpx;
53
+  }
54
+}
55
+
56
+.btn-footer1 {
57
+  .btn-footer();
58
+
59
+  button {
60
+    width: @visual-width;
61
+  }
62
+}
63
+
64
+.btn-footer2 {
65
+  .btn-footer();
66
+
67
+  button {
68
+    width: 100%;
69
+    border-radius: 0 !important;
70
+  }
71
+
72
+  .left {
73
+    width: 250rpx;
74
+    border-radius: @radius 0 0 @radius;
75
+  }
76
+
77
+  .right {
78
+    width: 452rpx;
79
+    border-radius: 0 @radius @radius 0;
80
+  }
81
+}

+ 37 - 0
src/template/footer/footer.wxml

@@ -0,0 +1,37 @@
1
+<template name="memberFooter">
2
+  <view class="member-footer">
3
+    <view class="left">
4
+      <image mode="widthFix" src="/resources/member/lrclogo1.png"></image>
5
+    </view>
6
+
7
+    <view class="right">
8
+      <wxs src="./../../wxs/stringFilter.wxs" module="tools"></wxs>
9
+      <text>{{ tools.filter(text) }}</text>
10
+    </view>
11
+  </view>
12
+</template>
13
+
14
+<import src="/template/btn/btn.wxml"></import>
15
+<template name="btnFooter">
16
+  <view class="btn-footer1">
17
+    <template is="iconBtn" data="{{btnClass, btnTxt, disabled, action, openType}}"></template>
18
+  </view>
19
+</template>
20
+
21
+<template name="multiBtnFooter">
22
+  <view class="btn-footer2">
23
+    <view class="left">
24
+      <template
25
+        is="iconBtn"
26
+        data="{{btnClass: btnClass1, btnTxt: btnTxt1, disabled: disabled1, action: action1, btnIcon: btnIcon1, openType: openType1}}"
27
+      ></template>
28
+    </view>
29
+    <view class="right">
30
+      <template
31
+        is="iconBtn"
32
+        data="{{btnClass: btnClass2, btnTxt: btnTxt2, disabled: disabled2, action: action2, btnIcon: btnIcon2, openType: openType2}}"
33
+      ></template>
34
+    </view>
35
+  </view>
36
+</template>
37
+

+ 74 - 0
src/template/footer/footer.wxss

@@ -0,0 +1,74 @@
1
+@import '/template/btn/btn.wxss';
2
+.member-footer {
3
+  position: fixed;
4
+  bottom: 0;
5
+  z-index: 999;
6
+  display: flex;
7
+  width: 100%;
8
+  height: 100rpx;
9
+  background-color: #c8c8c8;
10
+}
11
+.member-footer .left {
12
+  display: flex;
13
+  align-items: center;
14
+  justify-content: center;
15
+  width: 40%;
16
+  height: 100%;
17
+}
18
+.member-footer .left image {
19
+  width: 80%;
20
+}
21
+.member-footer .right {
22
+  display: flex;
23
+  align-items: center;
24
+  width: 70%;
25
+  height: 100%;
26
+}
27
+.member-footer .right text {
28
+  margin-left: 24rpx;
29
+  font-size: 8pt;
30
+  color: #3e3e3e;
31
+}
32
+.btn-footer1 {
33
+  display: flex;
34
+  align-items: center;
35
+  position: fixed;
36
+  bottom: 0;
37
+  justify-content: center;
38
+  width: 100%;
39
+  height: 160rpx;
40
+  background-color: #fff;
41
+  box-shadow: 0 -5px 9px 2px rgba(0, 0, 0, 0.1);
42
+}
43
+.btn-footer1 button {
44
+  height: 100rpx;
45
+}
46
+.btn-footer1 button {
47
+  width: 702rpx;
48
+}
49
+.btn-footer2 {
50
+  display: flex;
51
+  align-items: center;
52
+  position: fixed;
53
+  bottom: 0;
54
+  justify-content: center;
55
+  width: 100%;
56
+  height: 160rpx;
57
+  background-color: #fff;
58
+  box-shadow: 0 -5px 9px 2px rgba(0, 0, 0, 0.1);
59
+}
60
+.btn-footer2 button {
61
+  height: 100rpx;
62
+}
63
+.btn-footer2 button {
64
+  width: 100%;
65
+  border-radius: 0 !important;
66
+}
67
+.btn-footer2 .left {
68
+  width: 250rpx;
69
+  border-radius: 5px 0 0 5px;
70
+}
71
+.btn-footer2 .right {
72
+  width: 452rpx;
73
+  border-radius: 0 5px 5px 0;
74
+}

+ 65 - 0
src/template/resultView/resultView.js

@@ -0,0 +1,65 @@
1
+Page({
2
+  data: {
3
+    title: '兑换成功',
4
+    desc: '',
5
+    isVirtual: false,
6
+    desc1: '在个人中心->专享劵,查看已兑换的劵。',
7
+    desc2:
8
+      '请留意微信服务通知内 小程序TAMRON的订阅消息,查收实物商品的邮寄情况。',
9
+    desc3:
10
+      '专属礼品已兑换,请留意微信服务通知内 小程序 TARMON 的订阅消息,查收专属礼品的邮寄情况。',
11
+    action: 'navigateBackToMall',
12
+    btnTitle: '返回',
13
+    isHightlight: false
14
+  },
15
+
16
+  onLoad(options) {
17
+    if (options.goods) {
18
+      const goods = JSON.parse(options.goods)
19
+      let desc = this.data.desc1
20
+      if (goods.only_for_member) {
21
+        desc = this.data.desc3
22
+      } else if (goods.good_type === 0) {
23
+        desc = this.data.desc2
24
+      }
25
+      this.setData({
26
+        isVirtual: goods.good_type === 1,
27
+        desc
28
+      })
29
+    } else {
30
+      const eventChannel = this.getOpenerEventChannel()
31
+      const that = this
32
+      eventChannel.on('acceptDataFromOpenerPage', data => {
33
+        that.setData({
34
+          title: data.title,
35
+          desc: data.desc,
36
+          action: 'navigateToMine',
37
+          btnTitle: data.btnTitle,
38
+          isHightlight: data.isHightlight
39
+        })
40
+      })
41
+    }
42
+  },
43
+
44
+  navigateToMine() {
45
+    wx.switchTab({
46
+      url: '/pages/member/mine/mine'
47
+    })
48
+  },
49
+
50
+  navigateBackToMall() {
51
+    wx.navigateBack({
52
+      delta: 2
53
+    })
54
+  },
55
+
56
+  navigateToVoucher() {
57
+    wx.redirectTo({
58
+      url: '/pages/member/mine/voucher/voucher'
59
+    })
60
+  },
61
+
62
+  officialAccountError(e) {
63
+    console.log(e)
64
+  }
65
+})

+ 5 - 0
src/template/resultView/resultView.json

@@ -0,0 +1,5 @@
1
+{
2
+  "component": true,
3
+  "usingComponents": {},
4
+  "disableScroll": true
5
+}

+ 99 - 0
src/template/resultView/resultView.less

@@ -0,0 +1,99 @@
1
+@import './src/style/less/variable.less';
2
+@import './src/style/less/layout.less';
3
+
4
+.result-view {
5
+  .flex-center(column);
6
+
7
+  box-sizing: border-box;
8
+  width: 100%;
9
+  height: 100%;
10
+  padding: @spacing-item;
11
+  background-color: @color-bg;
12
+}
13
+
14
+.logo {
15
+  width: 200rpx;
16
+  height: 200rpx;
17
+  margin-top: 50rpx;
18
+
19
+  image {
20
+    .full-image();
21
+  }
22
+}
23
+
24
+.content {
25
+  .flex-center(column);
26
+
27
+  width: 100%;
28
+  margin-top: @spacing-item;
29
+
30
+  .title {
31
+    margin-top: @spacing-item;
32
+    font-size: @font-title;
33
+    font-weight: 600;
34
+    color: @color-black;
35
+  }
36
+
37
+  .desc {
38
+    margin-top: @spacing-item;
39
+    font-size: @font-tertiary;
40
+    color: @color-light-gray;
41
+    text-align: center;
42
+  }
43
+
44
+  .hint {
45
+    margin-top: @spacing-item;
46
+    .flex-center(row);
47
+
48
+    image {
49
+      .square(@icon-large-size);
50
+    }
51
+
52
+    text {
53
+      margin-left: @spacing-inline;
54
+      font-size: @font-primary;
55
+      color: @color-brand;
56
+    }
57
+  }
58
+}
59
+
60
+.action {
61
+  width: 100%;
62
+  margin-top: @spacing-item;
63
+
64
+  view {
65
+    .flex-center(column);
66
+
67
+    justify-content: center;
68
+    width: 100%;
69
+    height: 80rpx;
70
+    margin-top: @spacing-inline;
71
+    color: @color-white;
72
+    background-color: @color-brand;
73
+    border-radius: @radius;
74
+  }
75
+}
76
+
77
+.action-bottom {
78
+  .flex-center(column);
79
+
80
+  position: fixed;
81
+  bottom: @spacing-view;
82
+  width: 100%;
83
+
84
+  view {
85
+    .flex-center(column);
86
+
87
+    justify-content: center;
88
+    width: auto;
89
+    padding: @spacing-inline @spacing-view;
90
+    color: @color-brand;
91
+    border: 1px @color-light-gray solid;
92
+    border-radius: @radius;
93
+  }
94
+}
95
+
96
+.official-account {
97
+  width: 702rpx;
98
+  margin-top: @spacing-view;
99
+}

+ 33 - 0
src/template/resultView/resultView.wxml

@@ -0,0 +1,33 @@
1
+
2
+<view class="result-view" wx:if="{{isHightlight}}">
3
+  <view class="logo">
4
+    <image src="success.svg"></image>
5
+  </view>
6
+  <view class="content">
7
+    <text class="title">{{ title }}</text>
8
+    <view class="hint">
9
+      <image src="/resources/trumpet.svg"></image>
10
+      <text>{{ desc }}</text>
11
+    </view>
12
+    <view class="official-account">
13
+      <official-account binderror="officialAccountError"></official-account>
14
+    </view>
15
+  </view>
16
+  <view class="action-bottom">
17
+    <view class="goOn" bindtap="{{ action }}">{{ btnTitle }}</view>
18
+  </view>
19
+</view>
20
+
21
+<view class="result-view" wx:else>
22
+  <view class="logo">
23
+    <image src="success.svg"></image>
24
+  </view>
25
+  <view class="content">
26
+    <text class="title">{{ title }}</text>
27
+    <text class="desc">{{ desc }}</text>
28
+  </view>
29
+  <view class="action">
30
+    <view class="goOn" bindtap="{{ action }}">{{ btnTitle }}</view>
31
+    <view class="detail" bindtap="navigateToVoucher" wx:if="{{ isVirtual }}">去查看</view>
32
+  </view>
33
+</view>

+ 91 - 0
src/template/resultView/resultView.wxss

@@ -0,0 +1,91 @@
1
+.result-view {
2
+  display: flex;
3
+  flex-direction: column;
4
+  align-items: center;
5
+  box-sizing: border-box;
6
+  width: 100%;
7
+  height: 100%;
8
+  padding: 24rpx;
9
+  background-color: #efefef;
10
+}
11
+.logo {
12
+  width: 200rpx;
13
+  height: 200rpx;
14
+  margin-top: 50rpx;
15
+}
16
+.logo image {
17
+  width: 100%;
18
+  height: 100%;
19
+}
20
+.content {
21
+  display: flex;
22
+  flex-direction: column;
23
+  align-items: center;
24
+  width: 100%;
25
+  margin-top: 24rpx;
26
+}
27
+.content .title {
28
+  margin-top: 24rpx;
29
+  font-size: 17pt;
30
+  font-weight: 600;
31
+  color: #000;
32
+}
33
+.content .desc {
34
+  margin-top: 24rpx;
35
+  font-size: 11pt;
36
+  color: #959595;
37
+  text-align: center;
38
+}
39
+.content .hint {
40
+  margin-top: 24rpx;
41
+  display: flex;
42
+  align-items: center;
43
+}
44
+.content .hint image {
45
+  width: 72rpx;
46
+  height: 72rpx;
47
+}
48
+.content .hint text {
49
+  margin-left: 12rpx;
50
+  font-size: 14pt;
51
+  color: #0967b2;
52
+}
53
+.action {
54
+  width: 100%;
55
+  margin-top: 24rpx;
56
+}
57
+.action view {
58
+  display: flex;
59
+  flex-direction: column;
60
+  align-items: center;
61
+  justify-content: center;
62
+  width: 100%;
63
+  height: 80rpx;
64
+  margin-top: 12rpx;
65
+  color: #fff;
66
+  background-color: #0967b2;
67
+  border-radius: 5px;
68
+}
69
+.action-bottom {
70
+  display: flex;
71
+  flex-direction: column;
72
+  align-items: center;
73
+  position: fixed;
74
+  bottom: 36rpx;
75
+  width: 100%;
76
+}
77
+.action-bottom view {
78
+  display: flex;
79
+  flex-direction: column;
80
+  align-items: center;
81
+  justify-content: center;
82
+  width: auto;
83
+  padding: 12rpx 36rpx;
84
+  color: #0967b2;
85
+  border: 1px #959595 solid;
86
+  border-radius: 5px;
87
+}
88
+.official-account {
89
+  width: 702rpx;
90
+  margin-top: 36rpx;
91
+}

+ 1 - 0
src/template/resultView/success.svg

@@ -0,0 +1 @@
1
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1575547268867" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3403" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 981.333333C252.8 981.333333 42.666667 771.2 42.666667 512S252.8 42.666667 512 42.666667s469.333333 210.133333 469.333333 469.333333-210.133333 469.333333-469.333333 469.333333z m-50.432-326.101333L310.613333 504.32a32 32 0 0 0-45.226666 45.226667l174.72 174.762666a32.341333 32.341333 0 0 0 0.341333 0.341334l0.256 0.213333a32 32 0 0 0 50.048-6.144l337.450667-379.605333a32 32 0 1 0-47.872-42.496l-318.762667 358.613333z" fill="#0967b2" p-id="3404"></path></svg>

+ 18 - 0
src/template/webView/webView.js

@@ -0,0 +1,18 @@
1
+// template/webView/webView.js
2
+Page({
3
+  /**
4
+   * Page initial data
5
+   */
6
+  data: {
7
+    url: ''
8
+  },
9
+
10
+  /**
11
+   * Lifecycle function--Called when page load
12
+   */
13
+  onLoad(options) {
14
+    this.setData({
15
+      url: options.url
16
+    })
17
+  }
18
+})

+ 3 - 0
src/template/webView/webView.json

@@ -0,0 +1,3 @@
1
+{
2
+  "usingComponents": {}
3
+}

+ 1 - 0
src/template/webView/webView.wxml

@@ -0,0 +1 @@
1
+<web-view src="{{url}}"></web-view>

+ 1 - 0
src/template/webView/webView.wxss

@@ -0,0 +1 @@
1
+/* template/webView/webView.wxss */

二進制
src/utils/.DS_Store


+ 155 - 0
src/utils/network.js

@@ -0,0 +1,155 @@
1
+const urls = require('./urls.js')
2
+const brandConfig = require('../brand/config.js')
3
+
4
+// 服务器登录
5
+function _login(code, cb) {
6
+  wx.request({
7
+    url: urls.MINI_LOGIN_API_URL,
8
+    data: {
9
+      code
10
+    },
11
+    method: 'POST',
12
+    header: {
13
+      'content-type': 'application/x-www-form-urlencoded'
14
+    },
15
+    success(res) {
16
+      wx.setStorage({
17
+        key: 'userInfo',
18
+        data: res.data.data,
19
+        success() {
20
+          cb(res.data.data)
21
+        }
22
+      })
23
+    },
24
+    complete: () => {
25
+      wx.hideLoading()
26
+    }
27
+  })
28
+}
29
+
30
+function _slientLogin(cb) {
31
+  wx.showLoading({
32
+    title: '加载中...',
33
+    mask: true
34
+  })
35
+  const { environment } = wx.getSystemInfoSync()
36
+
37
+  if (environment === 'wxwork') {
38
+    wx.login({
39
+      success(res) {
40
+        console.log(res)
41
+        if (res.code) {
42
+          _login(res.code, cb)
43
+        } else {
44
+          wx.showToast({
45
+            title: '微信登录异常',
46
+            icon: 'none'
47
+          })
48
+          wx.hideLoading()
49
+        }
50
+      },
51
+      fail() {
52
+        wx.showToast({
53
+          title: '微信登录异常',
54
+          icon: 'none'
55
+        })
56
+        wx.hideLoading()
57
+      }
58
+    })
59
+
60
+    return
61
+  }
62
+  wx.login({
63
+    success(res) {
64
+      if (res.code) {
65
+        _login(res.code, cb)
66
+      } else {
67
+        wx.showToast({
68
+          title: '微信登录异常',
69
+          icon: 'none'
70
+        })
71
+        wx.hideLoading()
72
+      }
73
+    },
74
+    fail() {
75
+      wx.showToast({
76
+        title: '微信登录异常',
77
+        icon: 'none'
78
+      })
79
+      wx.hideLoading()
80
+    }
81
+  })
82
+}
83
+
84
+// 静默登录
85
+function login(cb) {
86
+  _slientLogin(cb)
87
+}
88
+
89
+function checkLoginStatus(cb) {
90
+  wx.getStorage({
91
+    key: 'userInfo',
92
+    success: res => {
93
+      cb(res.data)
94
+    },
95
+    fail: () => {
96
+      login(cb)
97
+    }
98
+  })
99
+}
100
+
101
+function post(params, isTip = true) {
102
+  const finalUrl = `${params.url}?ts=${Date.now()}`
103
+  checkLoginStatus(userInfo => {
104
+    const { user_id } = userInfo
105
+    const data = params.data || {}
106
+    data.user_id = user_id
107
+    wx.request({
108
+      url: finalUrl,
109
+      data,
110
+      method: 'POST',
111
+      header: {
112
+        'content-type': 'application/x-www-form-urlencoded'
113
+      },
114
+      success: res => {
115
+        // success
116
+        wx.hideLoading()
117
+        if (res.data.status !== 200 && isTip) {
118
+          wx.showToast({
119
+            title: res.data.description,
120
+            image: '/resources/alert_fail.svg'
121
+          })
122
+
123
+          if (params.fail) {
124
+            params.fail(res)
125
+          }
126
+        } else if (params.success) {
127
+          params.success(res.data)
128
+        }
129
+      },
130
+      fail: res => {
131
+        // fail
132
+        if (params.fail) {
133
+          params.fail(res)
134
+        }
135
+        wx.hideLoading()
136
+        wx.showToast({
137
+          title: '当前网络开小差, 请稍后再试',
138
+          image: '/resources/alert_fail.svg'
139
+        })
140
+      },
141
+      complete: res => {
142
+        // complete
143
+
144
+        if (params.complete) {
145
+          params.complete(res)
146
+        }
147
+      }
148
+    })
149
+  })
150
+}
151
+
152
+module.exports = {
153
+  post,
154
+  login
155
+}

+ 34 - 0
src/utils/router.js

@@ -0,0 +1,34 @@
1
+module.exports = {
2
+  index: '/pages/index/index',
3
+  webView: '/template/resultView/resultView',
4
+  member: {
5
+    activity: {
6
+      index: '/pages/member/activity/activity',
7
+      details: '/pages/member/activity/details/details'
8
+    },
9
+    mine: {
10
+      index: '/pages/member/mine/mine',
11
+      voucher: '/pages/member/mine/voucher/voucher',
12
+      voucherDetails: 'pages/member/mine/voucher/details/details',
13
+      benefits: {
14
+        index: '/pages/member/mine/benefits/benefits',
15
+        details: '/pages/member/mine/benefits/details/details'
16
+      },
17
+      integral: {
18
+        mall: '/pages/member/mine/integral/mall/mall',
19
+        mallDetails: '/pages/member/mine/integral/mall/details/details',
20
+        rule: '/pages/member/mine/integral/rule/rule'
21
+      }
22
+    },
23
+
24
+    saleManager: {
25
+      index: '/pages/sale_manager_home/sale_manager_home'
26
+    },
27
+
28
+    register: {
29
+      saleManager:
30
+        '/pages/register/sale_manager_register/sale_manager_register',
31
+      consumer: '/pages/register/consumer/consumer'
32
+    }
33
+  }
34
+}

+ 27 - 0
src/utils/routerInterceptor.js

@@ -0,0 +1,27 @@
1
+/**
2
+ * routerFillter --全局路由拦截器
3
+ * @function
4
+ * @param{Object} pageObj 当前页面的page对象
5
+ * @param{Boolean} flag 是否开启权限判断
6
+ */
7
+
8
+function checkLoginStatus(pageObj) {
9
+  const _onLoad = pageObj.onLoad
10
+  pageObj.onLoad = function onLoad(options) {
11
+    const that = this
12
+    // 这一步是自己定义获取登录状态的,只是个判断权限的
13
+    const app = getApp()
14
+    if (!app.globalData.isLogin) {
15
+      app.network.login(res => {
16
+        app.globalData.userInfo = res
17
+        app.globalData.isLogin = true
18
+        _onLoad.call(that, options)
19
+      })
20
+    } else {
21
+      _onLoad.call(that, options)
22
+    }
23
+  }
24
+  return Page(pageObj)
25
+}
26
+
27
+exports.checkLoginStatus = checkLoginStatus

+ 10 - 0
src/utils/urls.js

@@ -0,0 +1,10 @@
1
+const config = require('../brand/config.js')
2
+
3
+module.exports = {
4
+  // 小程序登录接口
5
+  MINI_LOGIN_API_URL: `${config.baseURL}/api/mini/login`,
6
+  MINI_USERINFO: `${config.baseURL}/api/mini/userinfo`,
7
+
8
+  PACK: `${config.baseURL}/api/pack`,
9
+  ORDER_CREATE: `${config.baseURL}/api/pay/wx/order_create`
10
+}

二進制
src/utils/wxParse/.DS_Store


+ 302 - 0
src/utils/wxParse/html2json.js

@@ -0,0 +1,302 @@
1
+/**
2
+ * html2Json 改造来自: https://github.com/Jxck/html2json
3
+ *
4
+ *
5
+ * author: Di (微信小程序开发工程师)
6
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
7
+ *               垂直微信小程序开发交流社区
8
+ *
9
+ * github地址: https://github.com/icindy/wxParse
10
+ *
11
+ * for: 微信小程序富文本解析
12
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
13
+ */
14
+
15
+const __placeImgeUrlHttps = 'https';
16
+let __emojisReg = '';
17
+let __emojisBaseSrc = '';
18
+let __emojis = {};
19
+const wxDiscode = require('./wxDiscode.js');
20
+const HTMLParser = require('./htmlparser.js');
21
+// Empty Elements - HTML 5
22
+const empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr');
23
+// Block Elements - HTML 5
24
+const block = makeMap('br,a,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video');
25
+
26
+// Inline Elements - HTML 5
27
+const inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var');
28
+
29
+// Elements that you can, intentionally, leave open
30
+// (and which close themselves)
31
+const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
32
+
33
+// Attributes that have their values filled in disabled="disabled"
34
+const fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected');
35
+
36
+// Special Elements (can contain anything)
37
+const special = makeMap('wxxxcode-style,script,style,view,scroll-view,block');
38
+function makeMap(str) {
39
+  const obj = {}; const
40
+    items = str.split(',');
41
+  for (let i = 0; i < items.length; i++) obj[items[i]] = true;
42
+  return obj;
43
+}
44
+
45
+function q(v) {
46
+  return `"${v}"`;
47
+}
48
+
49
+function removeDOCTYPE(html) {
50
+  return html
51
+    .replace(/<\?xml.*\?>\n/, '')
52
+    .replace(/<.*!doctype.*\>\n/, '')
53
+    .replace(/<.*!DOCTYPE.*\>\n/, '');
54
+}
55
+
56
+function trimHtml(html) {
57
+  return html
58
+    .replace(/\r?\n+/g, '')
59
+    .replace(/<!--.*?-->/ig, '')
60
+    .replace(/\/\*.*?\*\//ig, '')
61
+    .replace(/[ ]+</ig, '<');
62
+}
63
+
64
+
65
+function html2json(html, bindName) {
66
+  // 处理字符串
67
+  html = removeDOCTYPE(html);
68
+  html = trimHtml(html);
69
+  html = wxDiscode.strDiscode(html);
70
+  // 生成node节点
71
+  const bufArray = [];
72
+  const results = {
73
+    node: bindName,
74
+    nodes: [],
75
+    images: [],
76
+    imageUrls: [],
77
+  };
78
+  let index = 0;
79
+  HTMLParser(html, {
80
+    start(tag, attrs, unary) {
81
+      // debug(tag, attrs, unary);
82
+      // node for this element
83
+      const node = {
84
+        node: 'element',
85
+        tag,
86
+      };
87
+
88
+      if (bufArray.length === 0) {
89
+        node.index = index.toString();
90
+        index += 1;
91
+      } else {
92
+        var parent = bufArray[0];
93
+        if (parent.nodes === undefined) {
94
+          parent.nodes = [];
95
+        }
96
+        node.index = `${parent.index}.${parent.nodes.length}`;
97
+      }
98
+
99
+      if (block[tag]) {
100
+        node.tagType = 'block';
101
+      } else if (inline[tag]) {
102
+        node.tagType = 'inline';
103
+      } else if (closeSelf[tag]) {
104
+        node.tagType = 'closeSelf';
105
+      }
106
+
107
+      if (attrs.length !== 0) {
108
+        node.attr = attrs.reduce((pre, attr) => {
109
+          const { name } = attr;
110
+          let { value } = attr;
111
+          if (name == 'class') {
112
+            console.dir(value);
113
+            //  value = value.join("")
114
+            node.classStr = value;
115
+          }
116
+          // has multi attibutes
117
+          // make it array of attribute
118
+          if (name == 'style') {
119
+            console.dir(value);
120
+            //  value = value.join("")
121
+            node.styleStr = value;
122
+          }
123
+          if (value.match(/ /)) {
124
+            value = value.split(' ');
125
+          }
126
+
127
+
128
+          // if attr already exists
129
+          // merge it
130
+          if (pre[name]) {
131
+            if (Array.isArray(pre[name])) {
132
+              // already array, push to last
133
+              pre[name].push(value);
134
+            } else {
135
+              // single value, make it array
136
+              pre[name] = [pre[name], value];
137
+            }
138
+          } else {
139
+            // not exist, put it
140
+            pre[name] = value;
141
+          }
142
+
143
+          return pre;
144
+        }, {});
145
+      }
146
+
147
+      // 对img添加额外数据
148
+      if (node.tag === 'img') {
149
+        node.imgIndex = results.images.length;
150
+        let imgUrl = node.attr.src;
151
+        if (imgUrl[0] == '') {
152
+          imgUrl.splice(0, 1);
153
+        }
154
+        imgUrl = wxDiscode.urlToHttpUrl(imgUrl, __placeImgeUrlHttps);
155
+        node.attr.src = imgUrl;
156
+        node.from = bindName;
157
+        results.images.push(node);
158
+        results.imageUrls.push(imgUrl);
159
+      }
160
+
161
+      // 处理font标签样式属性
162
+      if (node.tag === 'font') {
163
+        const fontSize = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', '-webkit-xxx-large'];
164
+        const styleAttrs = {
165
+          color: 'color',
166
+          face: 'font-family',
167
+          size: 'font-size',
168
+        };
169
+        if (!node.attr.style) node.attr.style = [];
170
+        if (!node.styleStr) node.styleStr = '';
171
+        for (const key in styleAttrs) {
172
+          if (node.attr[key]) {
173
+            const value = key === 'size' ? fontSize[node.attr[key] - 1] : node.attr[key];
174
+            node.attr.style.push(styleAttrs[key]);
175
+            node.attr.style.push(value);
176
+            node.styleStr += `${styleAttrs[key]}: ${value};`;
177
+          }
178
+        }
179
+      }
180
+
181
+      // 临时记录source资源
182
+      if (node.tag === 'source') {
183
+        results.source = node.attr.src;
184
+      }
185
+
186
+      if (unary) {
187
+        // if this tag doesn't have end tag
188
+        // like <img src="hoge.png"/>
189
+        // add to parents
190
+        var parent = bufArray[0] || results;
191
+        if (parent.nodes === undefined) {
192
+          parent.nodes = [];
193
+        }
194
+        parent.nodes.push(node);
195
+      } else {
196
+        bufArray.unshift(node);
197
+      }
198
+    },
199
+    end(tag) {
200
+      // debug(tag);
201
+      // merge into parent tag
202
+      const node = bufArray.shift();
203
+      if (node.tag !== tag) console.error('invalid state: mismatch end tag');
204
+
205
+      // 当有缓存source资源时于于video补上src资源
206
+      if (node.tag === 'video' && results.source) {
207
+        node.attr.src = results.source;
208
+        delete results.source;
209
+      }
210
+
211
+      if (bufArray.length === 0) {
212
+        results.nodes.push(node);
213
+      } else {
214
+        const parent = bufArray[0];
215
+        if (parent.nodes === undefined) {
216
+          parent.nodes = [];
217
+        }
218
+        parent.nodes.push(node);
219
+      }
220
+    },
221
+    chars(text) {
222
+      // debug(text);
223
+      const node = {
224
+        node: 'text',
225
+        text,
226
+        textArray: transEmojiStr(text),
227
+      };
228
+
229
+      if (bufArray.length === 0) {
230
+        node.index = index.toString();
231
+        index += 1;
232
+        results.nodes.push(node);
233
+      } else {
234
+        const parent = bufArray[0];
235
+        if (parent.nodes === undefined) {
236
+          parent.nodes = [];
237
+        }
238
+        node.index = `${parent.index}.${parent.nodes.length}`;
239
+        parent.nodes.push(node);
240
+      }
241
+    },
242
+    comment(text) {
243
+      // debug(text);
244
+      // var node = {
245
+      //     node: 'comment',
246
+      //     text: text,
247
+      // };
248
+      // var parent = bufArray[0];
249
+      // if (parent.nodes === undefined) {
250
+      //     parent.nodes = [];
251
+      // }
252
+      // parent.nodes.push(node);
253
+    },
254
+  });
255
+  return results;
256
+}
257
+
258
+function transEmojiStr(str) {
259
+  // var eReg = new RegExp("["+__reg+' '+"]");
260
+//   str = str.replace(/\[([^\[\]]+)\]/g,':$1:')
261
+
262
+  const emojiObjs = [];
263
+  // 如果正则表达式为空
264
+  if (__emojisReg.length == 0 || !__emojis) {
265
+    var emojiObj = {};
266
+    emojiObj.node = 'text';
267
+    emojiObj.text = str;
268
+    array = [emojiObj];
269
+    return array;
270
+  }
271
+  // 这个地方需要调整
272
+  str = str.replace(/\[([^\[\]]+)\]/g, ':$1:');
273
+  const eReg = new RegExp('[:]');
274
+  var array = str.split(eReg);
275
+  for (let i = 0; i < array.length; i++) {
276
+    const ele = array[i];
277
+    var emojiObj = {};
278
+    if (__emojis[ele]) {
279
+      emojiObj.node = 'element';
280
+      emojiObj.tag = 'emoji';
281
+      emojiObj.text = __emojis[ele];
282
+      emojiObj.baseSrc = __emojisBaseSrc;
283
+    } else {
284
+      emojiObj.node = 'text';
285
+      emojiObj.text = ele;
286
+    }
287
+    emojiObjs.push(emojiObj);
288
+  }
289
+
290
+  return emojiObjs;
291
+}
292
+
293
+function emojisInit(reg = '', baseSrc = '/wxParse/emojis/', emojis) {
294
+  __emojisReg = reg;
295
+  __emojisBaseSrc = baseSrc;
296
+  __emojis = emojis;
297
+}
298
+
299
+module.exports = {
300
+  html2json,
301
+  emojisInit,
302
+};

+ 183 - 0
src/utils/wxParse/htmlparser.js

@@ -0,0 +1,183 @@
1
+/**
2
+ *
3
+ * htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
4
+ *
5
+ * author: Di (微信小程序开发工程师)
6
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
7
+ *               垂直微信小程序开发交流社区
8
+ *
9
+ * github地址: https://github.com/icindy/wxParse
10
+ *
11
+ * for: 微信小程序富文本解析
12
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
13
+ */
14
+// Regular Expressions for parsing tags and attributes
15
+const startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
16
+const endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/;
17
+const attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
18
+
19
+// Empty Elements - HTML 5
20
+const empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr');
21
+
22
+// Block Elements - HTML 5
23
+const block = makeMap('a,address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video');
24
+
25
+// Inline Elements - HTML 5
26
+const inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var');
27
+
28
+// Elements that you can, intentionally, leave open
29
+// (and which close themselves)
30
+const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
31
+
32
+// Attributes that have their values filled in disabled="disabled"
33
+const fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected');
34
+
35
+// Special Elements (can contain anything)
36
+const special = makeMap('wxxxcode-style,script,style,view,scroll-view,block');
37
+
38
+function HTMLParser(html, handler) {
39
+  let index; let chars; let match; const stack = []; let
40
+    last = html;
41
+  stack.last = function () {
42
+    return this[this.length - 1];
43
+  };
44
+
45
+  while (html) {
46
+    chars = true;
47
+
48
+    // Make sure we're not in a script or style element
49
+    if (!stack.last() || !special[stack.last()]) {
50
+      // Comment
51
+      if (html.indexOf('<!--') == 0) {
52
+        index = html.indexOf('-->');
53
+
54
+        if (index >= 0) {
55
+          if (handler.comment) { handler.comment(html.substring(4, index)); }
56
+          html = html.substring(index + 3);
57
+          chars = false;
58
+        }
59
+
60
+        // end tag
61
+      } else if (html.indexOf('</') == 0) {
62
+        match = html.match(endTag);
63
+
64
+        if (match) {
65
+          html = html.substring(match[0].length);
66
+          match[0].replace(endTag, parseEndTag);
67
+          chars = false;
68
+        }
69
+
70
+        // start tag
71
+      } else if (html.indexOf('<') == 0) {
72
+        match = html.match(startTag);
73
+
74
+        if (match) {
75
+          html = html.substring(match[0].length);
76
+          match[0].replace(startTag, parseStartTag);
77
+          chars = false;
78
+        }
79
+      }
80
+
81
+      if (chars) {
82
+        index = html.indexOf('<');
83
+        let text = '';
84
+        while (index === 0) {
85
+          text += '<';
86
+          html = html.substring(1);
87
+          index = html.indexOf('<');
88
+        }
89
+        text += index < 0 ? html : html.substring(0, index);
90
+        html = index < 0 ? '' : html.substring(index);
91
+
92
+        if (handler.chars) { handler.chars(text); }
93
+      }
94
+    } else {
95
+      html = html.replace(new RegExp(`([\\s\\S]*?)<\/${stack.last()}[^>]*>`), (all, text) => {
96
+        text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, '$1$2');
97
+        if (handler.chars) { handler.chars(text); }
98
+
99
+        return '';
100
+      });
101
+
102
+
103
+      parseEndTag('', stack.last());
104
+    }
105
+
106
+    if (html == last) { throw `Parse Error: ${html}`; }
107
+    last = html;
108
+  }
109
+
110
+  // Clean up any remaining tags
111
+  parseEndTag();
112
+
113
+  function parseStartTag(tag, tagName, rest, unary) {
114
+    tagName = tagName.toLowerCase();
115
+
116
+    if (block[tagName]) {
117
+      while (stack.last() && inline[stack.last()]) {
118
+        parseEndTag('', stack.last());
119
+      }
120
+    }
121
+
122
+    if (closeSelf[tagName] && stack.last() == tagName) {
123
+      parseEndTag('', tagName);
124
+    }
125
+
126
+    unary = empty[tagName] || !!unary;
127
+
128
+    if (!unary) { stack.push(tagName); }
129
+
130
+    if (handler.start) {
131
+      const attrs = [];
132
+
133
+      rest.replace(attr, function (match, name) {
134
+        const value = arguments[2] ? arguments[2]
135
+          : arguments[3] ? arguments[3]
136
+            : arguments[4] ? arguments[4]
137
+              : fillAttrs[name] ? name : '';
138
+
139
+        attrs.push({
140
+          name,
141
+          value,
142
+          escaped: value.replace(/(^|[^\\])"/g, '$1\\\"'), // "
143
+        });
144
+      });
145
+
146
+      if (handler.start) {
147
+        handler.start(tagName, attrs, unary);
148
+      }
149
+    }
150
+  }
151
+
152
+  function parseEndTag(tag, tagName) {
153
+    // If no tag name is provided, clean shop
154
+    if (!tagName) { var pos = 0; }
155
+
156
+    // Find the closest opened tag of the same type
157
+    else {
158
+      tagName = tagName.toLowerCase();
159
+      for (var pos = stack.length - 1; pos >= 0; pos--) {
160
+        if (stack[pos] == tagName) { break; }
161
+      }
162
+    }
163
+    if (pos >= 0) {
164
+      // Close all the open elements, up the stack
165
+      for (let i = stack.length - 1; i >= pos; i--) {
166
+        if (handler.end) { handler.end(stack[i]); }
167
+      }
168
+
169
+      // Remove the open elements from the stack
170
+      stack.length = pos;
171
+    }
172
+  }
173
+}
174
+
175
+
176
+function makeMap(str) {
177
+  const obj = {}; const
178
+    items = str.split(',');
179
+  for (let i = 0; i < items.length; i++) { obj[items[i]] = true; }
180
+  return obj;
181
+}
182
+
183
+module.exports = HTMLParser;

+ 2412 - 0
src/utils/wxParse/showdown.js

@@ -0,0 +1,2412 @@
1
+/**
2
+ *
3
+ * showdown: https://github.com/showdownjs/showdown
4
+ *
5
+ * author: Di (微信小程序开发工程师)
6
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
7
+ *               垂直微信小程序开发交流社区
8
+ *
9
+ * github地址: https://github.com/icindy/wxParse
10
+ *
11
+ * for: 微信小程序富文本解析
12
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
13
+ */
14
+
15
+function getDefaultOpts(simple) {
16
+  const defaultOptions = {
17
+    omitExtraWLInCodeBlocks: {
18
+      defaultValue: false,
19
+      describe: 'Omit the default extra whiteline added to code blocks',
20
+      type: 'boolean',
21
+    },
22
+    noHeaderId: {
23
+      defaultValue: false,
24
+      describe: 'Turn on/off generated header id',
25
+      type: 'boolean',
26
+    },
27
+    prefixHeaderId: {
28
+      defaultValue: false,
29
+      describe: 'Specify a prefix to generated header ids',
30
+      type: 'string',
31
+    },
32
+    headerLevelStart: {
33
+      defaultValue: false,
34
+      describe: 'The header blocks level start',
35
+      type: 'integer',
36
+    },
37
+    parseImgDimensions: {
38
+      defaultValue: false,
39
+      describe: 'Turn on/off image dimension parsing',
40
+      type: 'boolean',
41
+    },
42
+    simplifiedAutoLink: {
43
+      defaultValue: false,
44
+      describe: 'Turn on/off GFM autolink style',
45
+      type: 'boolean',
46
+    },
47
+    literalMidWordUnderscores: {
48
+      defaultValue: false,
49
+      describe: 'Parse midword underscores as literal underscores',
50
+      type: 'boolean',
51
+    },
52
+    strikethrough: {
53
+      defaultValue: false,
54
+      describe: 'Turn on/off strikethrough support',
55
+      type: 'boolean',
56
+    },
57
+    tables: {
58
+      defaultValue: false,
59
+      describe: 'Turn on/off tables support',
60
+      type: 'boolean',
61
+    },
62
+    tablesHeaderId: {
63
+      defaultValue: false,
64
+      describe: 'Add an id to table headers',
65
+      type: 'boolean',
66
+    },
67
+    ghCodeBlocks: {
68
+      defaultValue: true,
69
+      describe: 'Turn on/off GFM fenced code blocks support',
70
+      type: 'boolean',
71
+    },
72
+    tasklists: {
73
+      defaultValue: false,
74
+      describe: 'Turn on/off GFM tasklist support',
75
+      type: 'boolean',
76
+    },
77
+    smoothLivePreview: {
78
+      defaultValue: false,
79
+      describe: 'Prevents weird effects in live previews due to incomplete input',
80
+      type: 'boolean',
81
+    },
82
+    smartIndentationFix: {
83
+      defaultValue: false,
84
+      description: 'Tries to smartly fix identation in es6 strings',
85
+      type: 'boolean',
86
+    },
87
+  };
88
+  if (simple === false) {
89
+    return JSON.parse(JSON.stringify(defaultOptions));
90
+  }
91
+  const ret = {};
92
+  for (const opt in defaultOptions) {
93
+    if (defaultOptions.hasOwnProperty(opt)) {
94
+      ret[opt] = defaultOptions[opt].defaultValue;
95
+    }
96
+  }
97
+  return ret;
98
+}
99
+
100
+/**
101
+ * Created by Tivie on 06-01-2015.
102
+ */
103
+
104
+// Private properties
105
+const showdown = {};
106
+const parsers = {};
107
+let extensions = {};
108
+let globalOptions = getDefaultOpts(true);
109
+const flavor = {
110
+  github: {
111
+    omitExtraWLInCodeBlocks: true,
112
+    prefixHeaderId: 'user-content-',
113
+    simplifiedAutoLink: true,
114
+    literalMidWordUnderscores: true,
115
+    strikethrough: true,
116
+    tables: true,
117
+    tablesHeaderId: true,
118
+    ghCodeBlocks: true,
119
+    tasklists: true,
120
+  },
121
+  vanilla: getDefaultOpts(true),
122
+};
123
+
124
+/**
125
+ * helper namespace
126
+ * @type {{}}
127
+ */
128
+showdown.helper = {};
129
+
130
+/**
131
+ * TODO LEGACY SUPPORT CODE
132
+ * @type {{}}
133
+ */
134
+showdown.extensions = {};
135
+
136
+/**
137
+ * Set a global option
138
+ * @static
139
+ * @param {string} key
140
+ * @param {*} value
141
+ * @returns {showdown}
142
+ */
143
+showdown.setOption = function (key, value) {
144
+  globalOptions[key] = value;
145
+  return this;
146
+};
147
+
148
+/**
149
+ * Get a global option
150
+ * @static
151
+ * @param {string} key
152
+ * @returns {*}
153
+ */
154
+showdown.getOption = function (key) {
155
+  return globalOptions[key];
156
+};
157
+
158
+/**
159
+ * Get the global options
160
+ * @static
161
+ * @returns {{}}
162
+ */
163
+showdown.getOptions = function () {
164
+  return globalOptions;
165
+};
166
+
167
+/**
168
+ * Reset global options to the default values
169
+ * @static
170
+ */
171
+showdown.resetOptions = function () {
172
+  globalOptions = getDefaultOpts(true);
173
+};
174
+
175
+/**
176
+ * Set the flavor showdown should use as default
177
+ * @param {string} name
178
+ */
179
+showdown.setFlavor = function (name) {
180
+  if (flavor.hasOwnProperty(name)) {
181
+    const preset = flavor[name];
182
+    for (const option in preset) {
183
+      if (preset.hasOwnProperty(option)) {
184
+        globalOptions[option] = preset[option];
185
+      }
186
+    }
187
+  }
188
+};
189
+
190
+/**
191
+ * Get the default options
192
+ * @static
193
+ * @param {boolean} [simple=true]
194
+ * @returns {{}}
195
+ */
196
+showdown.getDefaultOptions = function (simple) {
197
+  return getDefaultOpts(simple);
198
+};
199
+
200
+/**
201
+ * Get or set a subParser
202
+ *
203
+ * subParser(name)       - Get a registered subParser
204
+ * subParser(name, func) - Register a subParser
205
+ * @static
206
+ * @param {string} name
207
+ * @param {function} [func]
208
+ * @returns {*}
209
+ */
210
+showdown.subParser = function (name, func) {
211
+  if (showdown.helper.isString(name)) {
212
+    if (typeof func !== 'undefined') {
213
+      parsers[name] = func;
214
+    } else {
215
+      if (parsers.hasOwnProperty(name)) {
216
+        return parsers[name];
217
+      }
218
+      throw Error(`SubParser named ${name} not registered!`);
219
+    }
220
+  }
221
+};
222
+
223
+/**
224
+ * Gets or registers an extension
225
+ * @static
226
+ * @param {string} name
227
+ * @param {object|function=} ext
228
+ * @returns {*}
229
+ */
230
+showdown.extension = function (name, ext) {
231
+  if (!showdown.helper.isString(name)) {
232
+    throw Error('Extension \'name\' must be a string');
233
+  }
234
+
235
+  name = showdown.helper.stdExtName(name);
236
+
237
+  // Getter
238
+  if (showdown.helper.isUndefined(ext)) {
239
+    if (!extensions.hasOwnProperty(name)) {
240
+      throw Error(`Extension named ${name} is not registered!`);
241
+    }
242
+    return extensions[name];
243
+
244
+    // Setter
245
+  }
246
+  // Expand extension if it's wrapped in a function
247
+  if (typeof ext === 'function') {
248
+    ext = ext();
249
+  }
250
+
251
+  // Ensure extension is an array
252
+  if (!showdown.helper.isArray(ext)) {
253
+    ext = [ext];
254
+  }
255
+
256
+  const validExtension = validate(ext, name);
257
+
258
+  if (validExtension.valid) {
259
+    extensions[name] = ext;
260
+  } else {
261
+    throw Error(validExtension.error);
262
+  }
263
+};
264
+
265
+/**
266
+ * Gets all extensions registered
267
+ * @returns {{}}
268
+ */
269
+showdown.getAllExtensions = function () {
270
+  return extensions;
271
+};
272
+
273
+/**
274
+ * Remove an extension
275
+ * @param {string} name
276
+ */
277
+showdown.removeExtension = function (name) {
278
+  delete extensions[name];
279
+};
280
+
281
+/**
282
+ * Removes all extensions
283
+ */
284
+showdown.resetExtensions = function () {
285
+  extensions = {};
286
+};
287
+
288
+/**
289
+ * Validate extension
290
+ * @param {array} extension
291
+ * @param {string} name
292
+ * @returns {{valid: boolean, error: string}}
293
+ */
294
+function validate(extension, name) {
295
+  const errMsg = (name) ? `Error in ${name} extension->` : 'Error in unnamed extension';
296
+  const ret = {
297
+    valid: true,
298
+    error: '',
299
+  };
300
+
301
+  if (!showdown.helper.isArray(extension)) {
302
+    extension = [extension];
303
+  }
304
+
305
+  for (let i = 0; i < extension.length; ++i) {
306
+    const baseMsg = `${errMsg} sub-extension ${i}: `;
307
+    const ext = extension[i];
308
+    if (typeof ext !== 'object') {
309
+      ret.valid = false;
310
+      ret.error = `${baseMsg}must be an object, but ${typeof ext} given`;
311
+      return ret;
312
+    }
313
+
314
+    if (!showdown.helper.isString(ext.type)) {
315
+      ret.valid = false;
316
+      ret.error = `${baseMsg}property "type" must be a string, but ${typeof ext.type} given`;
317
+      return ret;
318
+    }
319
+
320
+    let type = ext.type = ext.type.toLowerCase();
321
+
322
+    // normalize extension type
323
+    if (type === 'language') {
324
+      type = ext.type = 'lang';
325
+    }
326
+
327
+    if (type === 'html') {
328
+      type = ext.type = 'output';
329
+    }
330
+
331
+    if (type !== 'lang' && type !== 'output' && type !== 'listener') {
332
+      ret.valid = false;
333
+      ret.error = `${baseMsg}type ${type} is not recognized. Valid values: "lang/language", "output/html" or "listener"`;
334
+      return ret;
335
+    }
336
+
337
+    if (type === 'listener') {
338
+      if (showdown.helper.isUndefined(ext.listeners)) {
339
+        ret.valid = false;
340
+        ret.error = `${baseMsg}. Extensions of type "listener" must have a property called "listeners"`;
341
+        return ret;
342
+      }
343
+    } else if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
344
+      ret.valid = false;
345
+      ret.error = `${baseMsg + type} extensions must define either a "regex" property or a "filter" method`;
346
+      return ret;
347
+    }
348
+
349
+    if (ext.listeners) {
350
+      if (typeof ext.listeners !== 'object') {
351
+        ret.valid = false;
352
+        ret.error = `${baseMsg}"listeners" property must be an object but ${typeof ext.listeners} given`;
353
+        return ret;
354
+      }
355
+      for (const ln in ext.listeners) {
356
+        if (ext.listeners.hasOwnProperty(ln)) {
357
+          if (typeof ext.listeners[ln] !== 'function') {
358
+            ret.valid = false;
359
+            ret.error = `${baseMsg}"listeners" property must be an hash of [event name]: [callback]. listeners.${ln
360
+            } must be a function but ${typeof ext.listeners[ln]} given`;
361
+            return ret;
362
+          }
363
+        }
364
+      }
365
+    }
366
+
367
+    if (ext.filter) {
368
+      if (typeof ext.filter !== 'function') {
369
+        ret.valid = false;
370
+        ret.error = `${baseMsg}"filter" must be a function, but ${typeof ext.filter} given`;
371
+        return ret;
372
+      }
373
+    } else if (ext.regex) {
374
+      if (showdown.helper.isString(ext.regex)) {
375
+        ext.regex = new RegExp(ext.regex, 'g');
376
+      }
377
+      if (!ext.regex instanceof RegExp) {
378
+        ret.valid = false;
379
+        ret.error = `${baseMsg}"regex" property must either be a string or a RegExp object, but ${typeof ext.regex} given`;
380
+        return ret;
381
+      }
382
+      if (showdown.helper.isUndefined(ext.replace)) {
383
+        ret.valid = false;
384
+        ret.error = `${baseMsg}"regex" extensions must implement a replace string or function`;
385
+        return ret;
386
+      }
387
+    }
388
+  }
389
+  return ret;
390
+}
391
+
392
+/**
393
+ * Validate extension
394
+ * @param {object} ext
395
+ * @returns {boolean}
396
+ */
397
+showdown.validateExtension = function (ext) {
398
+  const validateExtension = validate(ext, null);
399
+  if (!validateExtension.valid) {
400
+    console.warn(validateExtension.error);
401
+    return false;
402
+  }
403
+  return true;
404
+};
405
+
406
+/**
407
+ * showdownjs helper functions
408
+ */
409
+
410
+if (!showdown.hasOwnProperty('helper')) {
411
+  showdown.helper = {};
412
+}
413
+
414
+/**
415
+ * Check if var is string
416
+ * @static
417
+ * @param {string} a
418
+ * @returns {boolean}
419
+ */
420
+showdown.helper.isString = function isString(a) {
421
+  return (typeof a === 'string' || a instanceof String);
422
+};
423
+
424
+/**
425
+ * Check if var is a function
426
+ * @static
427
+ * @param {string} a
428
+ * @returns {boolean}
429
+ */
430
+showdown.helper.isFunction = function isFunction(a) {
431
+  const getType = {};
432
+  return a && getType.toString.call(a) === '[object Function]';
433
+};
434
+
435
+/**
436
+ * ForEach helper function
437
+ * @static
438
+ * @param {*} obj
439
+ * @param {function} callback
440
+ */
441
+showdown.helper.forEach = function forEach(obj, callback) {
442
+  if (typeof obj.forEach === 'function') {
443
+    obj.forEach(callback);
444
+  } else {
445
+    for (let i = 0; i < obj.length; i++) {
446
+      callback(obj[i], i, obj);
447
+    }
448
+  }
449
+};
450
+
451
+/**
452
+ * isArray helper function
453
+ * @static
454
+ * @param {*} a
455
+ * @returns {boolean}
456
+ */
457
+showdown.helper.isArray = function isArray(a) {
458
+  return a.constructor === Array;
459
+};
460
+
461
+/**
462
+ * Check if value is undefined
463
+ * @static
464
+ * @param {*} value The value to check.
465
+ * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
466
+ */
467
+showdown.helper.isUndefined = function isUndefined(value) {
468
+  return typeof value === 'undefined';
469
+};
470
+
471
+/**
472
+ * Standardidize extension name
473
+ * @static
474
+ * @param {string} s extension name
475
+ * @returns {string}
476
+ */
477
+showdown.helper.stdExtName = function (s) {
478
+  return s.replace(/[_-]||\s/g, '').toLowerCase();
479
+};
480
+
481
+function escapeCharactersCallback(wholeMatch, m1) {
482
+  const charCodeToEscape = m1.charCodeAt(0);
483
+  return `~E${charCodeToEscape}E`;
484
+}
485
+
486
+/**
487
+ * Callback used to escape characters when passing through String.replace
488
+ * @static
489
+ * @param {string} wholeMatch
490
+ * @param {string} m1
491
+ * @returns {string}
492
+ */
493
+showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
494
+
495
+/**
496
+ * Escape characters in a string
497
+ * @static
498
+ * @param {string} text
499
+ * @param {string} charsToEscape
500
+ * @param {boolean} afterBackslash
501
+ * @returns {XML|string|void|*}
502
+ */
503
+showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) {
504
+  // First we have to escape the escape characters so that
505
+  // we can build a character class out of them
506
+  let regexString = `([${charsToEscape.replace(/([\[\]\\])/g, '\\$1')}])`;
507
+
508
+  if (afterBackslash) {
509
+    regexString = `\\\\${regexString}`;
510
+  }
511
+
512
+  const regex = new RegExp(regexString, 'g');
513
+  text = text.replace(regex, escapeCharactersCallback);
514
+
515
+  return text;
516
+};
517
+
518
+const rgxFindMatchPos = function (str, left, right, flags) {
519
+  const f = flags || '';
520
+  const g = f.indexOf('g') > -1;
521
+  const x = new RegExp(`${left}|${right}`, `g${f.replace(/g/g, '')}`);
522
+  const l = new RegExp(left, f.replace(/g/g, ''));
523
+  const pos = [];
524
+  let t; let s; let m; let start; let
525
+    end;
526
+
527
+  do {
528
+    t = 0;
529
+    while ((m = x.exec(str))) {
530
+      if (l.test(m[0])) {
531
+        if (!(t++)) {
532
+          s = x.lastIndex;
533
+          start = s - m[0].length;
534
+        }
535
+      } else if (t) {
536
+        if (!--t) {
537
+          end = m.index + m[0].length;
538
+          const obj = {
539
+            left: { start, end: s },
540
+            match: { start: s, end: m.index },
541
+            right: { start: m.index, end },
542
+            wholeMatch: { start, end },
543
+          };
544
+          pos.push(obj);
545
+          if (!g) {
546
+            return pos;
547
+          }
548
+        }
549
+      }
550
+    }
551
+  } while (t && (x.lastIndex = s));
552
+
553
+  return pos;
554
+};
555
+
556
+/**
557
+ * matchRecursiveRegExp
558
+ *
559
+ * (c) 2007 Steven Levithan <stevenlevithan.com>
560
+ * MIT License
561
+ *
562
+ * Accepts a string to search, a left and right format delimiter
563
+ * as regex patterns, and optional regex flags. Returns an array
564
+ * of matches, allowing nested instances of left/right delimiters.
565
+ * Use the "g" flag to return all matches, otherwise only the
566
+ * first is returned. Be careful to ensure that the left and
567
+ * right format delimiters produce mutually exclusive matches.
568
+ * Backreferences are not supported within the right delimiter
569
+ * due to how it is internally combined with the left delimiter.
570
+ * When matching strings whose format delimiters are unbalanced
571
+ * to the left or right, the output is intentionally as a
572
+ * conventional regex library with recursion support would
573
+ * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
574
+ * "<" and ">" as the delimiters (both strings contain a single,
575
+ * balanced instance of "<x>").
576
+ *
577
+ * examples:
578
+ * matchRecursiveRegExp("test", "\\(", "\\)")
579
+ * returns: []
580
+ * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
581
+ * returns: ["t<<e>><s>", ""]
582
+ * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
583
+ * returns: ["test"]
584
+ */
585
+showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
586
+  const matchPos = rgxFindMatchPos(str, left, right, flags);
587
+  const results = [];
588
+
589
+  for (let i = 0; i < matchPos.length; ++i) {
590
+    results.push([
591
+      str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
592
+      str.slice(matchPos[i].match.start, matchPos[i].match.end),
593
+      str.slice(matchPos[i].left.start, matchPos[i].left.end),
594
+      str.slice(matchPos[i].right.start, matchPos[i].right.end),
595
+    ]);
596
+  }
597
+  return results;
598
+};
599
+
600
+/**
601
+ *
602
+ * @param {string} str
603
+ * @param {string|function} replacement
604
+ * @param {string} left
605
+ * @param {string} right
606
+ * @param {string} flags
607
+ * @returns {string}
608
+ */
609
+showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) {
610
+  if (!showdown.helper.isFunction(replacement)) {
611
+    const repStr = replacement;
612
+    replacement = function () {
613
+      return repStr;
614
+    };
615
+  }
616
+
617
+  const matchPos = rgxFindMatchPos(str, left, right, flags);
618
+  let finalStr = str;
619
+  const lng = matchPos.length;
620
+
621
+  if (lng > 0) {
622
+    const bits = [];
623
+    if (matchPos[0].wholeMatch.start !== 0) {
624
+      bits.push(str.slice(0, matchPos[0].wholeMatch.start));
625
+    }
626
+    for (let i = 0; i < lng; ++i) {
627
+      bits.push(
628
+        replacement(
629
+          str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
630
+          str.slice(matchPos[i].match.start, matchPos[i].match.end),
631
+          str.slice(matchPos[i].left.start, matchPos[i].left.end),
632
+          str.slice(matchPos[i].right.start, matchPos[i].right.end),
633
+        ),
634
+      );
635
+      if (i < lng - 1) {
636
+        bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start));
637
+      }
638
+    }
639
+    if (matchPos[lng - 1].wholeMatch.end < str.length) {
640
+      bits.push(str.slice(matchPos[lng - 1].wholeMatch.end));
641
+    }
642
+    finalStr = bits.join('');
643
+  }
644
+  return finalStr;
645
+};
646
+
647
+/**
648
+ * POLYFILLS
649
+ */
650
+if (showdown.helper.isUndefined(console)) {
651
+  console = {
652
+    warn(msg) {
653
+      alert(msg);
654
+    },
655
+    log(msg) {
656
+      alert(msg);
657
+    },
658
+    error(msg) {
659
+      throw msg;
660
+    },
661
+  };
662
+}
663
+
664
+/**
665
+ * Created by Estevao on 31-05-2015.
666
+ */
667
+
668
+/**
669
+ * Showdown Converter class
670
+ * @class
671
+ * @param {object} [converterOptions]
672
+ * @returns {Converter}
673
+ */
674
+showdown.Converter = function (converterOptions) {
675
+  const
676
+    /**
677
+       * Options used by this converter
678
+       * @private
679
+       * @type {{}}
680
+       */
681
+    options = {};
682
+
683
+  /**
684
+       * Language extensions used by this converter
685
+       * @private
686
+       * @type {Array}
687
+       */
688
+  const langExtensions = [];
689
+
690
+  /**
691
+       * Output modifiers extensions used by this converter
692
+       * @private
693
+       * @type {Array}
694
+       */
695
+  const outputModifiers = [];
696
+
697
+  /**
698
+       * Event listeners
699
+       * @private
700
+       * @type {{}}
701
+       */
702
+  const listeners = {};
703
+
704
+  _constructor();
705
+
706
+  /**
707
+   * Converter constructor
708
+   * @private
709
+   */
710
+  function _constructor() {
711
+    converterOptions = converterOptions || {};
712
+
713
+    for (const gOpt in globalOptions) {
714
+      if (globalOptions.hasOwnProperty(gOpt)) {
715
+        options[gOpt] = globalOptions[gOpt];
716
+      }
717
+    }
718
+
719
+    // Merge options
720
+    if (typeof converterOptions === 'object') {
721
+      for (const opt in converterOptions) {
722
+        if (converterOptions.hasOwnProperty(opt)) {
723
+          options[opt] = converterOptions[opt];
724
+        }
725
+      }
726
+    } else {
727
+      throw Error(`Converter expects the passed parameter to be an object, but ${typeof converterOptions
728
+      } was passed instead.`);
729
+    }
730
+
731
+    if (options.extensions) {
732
+      showdown.helper.forEach(options.extensions, _parseExtension);
733
+    }
734
+  }
735
+
736
+  /**
737
+   * Parse extension
738
+   * @param {*} ext
739
+   * @param {string} [name='']
740
+   * @private
741
+   */
742
+  function _parseExtension(ext, name) {
743
+    name = name || null;
744
+    // If it's a string, the extension was previously loaded
745
+    if (showdown.helper.isString(ext)) {
746
+      ext = showdown.helper.stdExtName(ext);
747
+      name = ext;
748
+
749
+      // LEGACY_SUPPORT CODE
750
+      if (showdown.extensions[ext]) {
751
+        console.warn(`DEPRECATION WARNING: ${ext} is an old extension that uses a deprecated loading method.`
752
+          + 'Please inform the developer that the extension should be updated!');
753
+        legacyExtensionLoading(showdown.extensions[ext], ext);
754
+        return;
755
+        // END LEGACY SUPPORT CODE
756
+      } if (!showdown.helper.isUndefined(extensions[ext])) {
757
+        ext = extensions[ext];
758
+      } else {
759
+        throw Error(`Extension "${ext}" could not be loaded. It was either not found or is not a valid extension.`);
760
+      }
761
+    }
762
+
763
+    if (typeof ext === 'function') {
764
+      ext = ext();
765
+    }
766
+
767
+    if (!showdown.helper.isArray(ext)) {
768
+      ext = [ext];
769
+    }
770
+
771
+    const validExt = validate(ext, name);
772
+    if (!validExt.valid) {
773
+      throw Error(validExt.error);
774
+    }
775
+
776
+    for (let i = 0; i < ext.length; ++i) {
777
+      switch (ext[i].type) {
778
+        case 'lang':
779
+          langExtensions.push(ext[i]);
780
+          break;
781
+
782
+        case 'output':
783
+          outputModifiers.push(ext[i]);
784
+          break;
785
+      }
786
+      if (ext[i].hasOwnProperty(listeners)) {
787
+        for (const ln in ext[i].listeners) {
788
+          if (ext[i].listeners.hasOwnProperty(ln)) {
789
+            listen(ln, ext[i].listeners[ln]);
790
+          }
791
+        }
792
+      }
793
+    }
794
+  }
795
+
796
+  /**
797
+   * LEGACY_SUPPORT
798
+   * @param {*} ext
799
+   * @param {string} name
800
+   */
801
+  function legacyExtensionLoading(ext, name) {
802
+    if (typeof ext === 'function') {
803
+      ext = ext(new showdown.Converter());
804
+    }
805
+    if (!showdown.helper.isArray(ext)) {
806
+      ext = [ext];
807
+    }
808
+    const valid = validate(ext, name);
809
+
810
+    if (!valid.valid) {
811
+      throw Error(valid.error);
812
+    }
813
+
814
+    for (let i = 0; i < ext.length; ++i) {
815
+      switch (ext[i].type) {
816
+        case 'lang':
817
+          langExtensions.push(ext[i]);
818
+          break;
819
+        case 'output':
820
+          outputModifiers.push(ext[i]);
821
+          break;
822
+        default:// should never reach here
823
+          throw Error('Extension loader error: Type unrecognized!!!');
824
+      }
825
+    }
826
+  }
827
+
828
+  /**
829
+   * Listen to an event
830
+   * @param {string} name
831
+   * @param {function} callback
832
+   */
833
+  function listen(name, callback) {
834
+    if (!showdown.helper.isString(name)) {
835
+      throw Error(`Invalid argument in converter.listen() method: name must be a string, but ${typeof name} given`);
836
+    }
837
+
838
+    if (typeof callback !== 'function') {
839
+      throw Error(`Invalid argument in converter.listen() method: callback must be a function, but ${typeof callback} given`);
840
+    }
841
+
842
+    if (!listeners.hasOwnProperty(name)) {
843
+      listeners[name] = [];
844
+    }
845
+    listeners[name].push(callback);
846
+  }
847
+
848
+  function rTrimInputText(text) {
849
+    const rsp = text.match(/^\s*/)[0].length;
850
+    const rgx = new RegExp(`^\\s{0,${rsp}}`, 'gm');
851
+    return text.replace(rgx, '');
852
+  }
853
+
854
+  /**
855
+   * Dispatch an event
856
+   * @private
857
+   * @param {string} evtName Event name
858
+   * @param {string} text Text
859
+   * @param {{}} options Converter Options
860
+   * @param {{}} globals
861
+   * @returns {string}
862
+   */
863
+  this._dispatch = function dispatch(evtName, text, options, globals) {
864
+    if (listeners.hasOwnProperty(evtName)) {
865
+      for (let ei = 0; ei < listeners[evtName].length; ++ei) {
866
+        const nText = listeners[evtName][ei](evtName, text, this, options, globals);
867
+        if (nText && typeof nText !== 'undefined') {
868
+          text = nText;
869
+        }
870
+      }
871
+    }
872
+    return text;
873
+  };
874
+
875
+  /**
876
+   * Listen to an event
877
+   * @param {string} name
878
+   * @param {function} callback
879
+   * @returns {showdown.Converter}
880
+   */
881
+  this.listen = function (name, callback) {
882
+    listen(name, callback);
883
+    return this;
884
+  };
885
+
886
+  /**
887
+   * Converts a markdown string into HTML
888
+   * @param {string} text
889
+   * @returns {*}
890
+   */
891
+  this.makeHtml = function (text) {
892
+    // check if text is not falsy
893
+    if (!text) {
894
+      return text;
895
+    }
896
+
897
+    const globals = {
898
+      gHtmlBlocks: [],
899
+      gHtmlMdBlocks: [],
900
+      gHtmlSpans: [],
901
+      gUrls: {},
902
+      gTitles: {},
903
+      gDimensions: {},
904
+      gListLevel: 0,
905
+      hashLinkCounts: {},
906
+      langExtensions,
907
+      outputModifiers,
908
+      converter: this,
909
+      ghCodeBlocks: [],
910
+    };
911
+
912
+    // attacklab: Replace ~ with ~T
913
+    // This lets us use tilde as an escape char to avoid md5 hashes
914
+    // The choice of character is arbitrary; anything that isn't
915
+    // magic in Markdown will work.
916
+    text = text.replace(/~/g, '~T');
917
+
918
+    // attacklab: Replace $ with ~D
919
+    // RegExp interprets $ as a special character
920
+    // when it's in a replacement string
921
+    text = text.replace(/\$/g, '~D');
922
+
923
+    // Standardize line endings
924
+    text = text.replace(/\r\n/g, '\n'); // DOS to Unix
925
+    text = text.replace(/\r/g, '\n'); // Mac to Unix
926
+
927
+    if (options.smartIndentationFix) {
928
+      text = rTrimInputText(text);
929
+    }
930
+
931
+    // Make sure text begins and ends with a couple of newlines:
932
+    // text = '\n\n' + text + '\n\n';
933
+    text = text;
934
+    // detab
935
+    text = showdown.subParser('detab')(text, options, globals);
936
+
937
+    // stripBlankLines
938
+    text = showdown.subParser('stripBlankLines')(text, options, globals);
939
+
940
+    // run languageExtensions
941
+    showdown.helper.forEach(langExtensions, (ext) => {
942
+      text = showdown.subParser('runExtension')(ext, text, options, globals);
943
+    });
944
+
945
+    // run the sub parsers
946
+    text = showdown.subParser('hashPreCodeTags')(text, options, globals);
947
+    text = showdown.subParser('githubCodeBlocks')(text, options, globals);
948
+    text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
949
+    text = showdown.subParser('hashHTMLSpans')(text, options, globals);
950
+    text = showdown.subParser('stripLinkDefinitions')(text, options, globals);
951
+    text = showdown.subParser('blockGamut')(text, options, globals);
952
+    text = showdown.subParser('unhashHTMLSpans')(text, options, globals);
953
+    text = showdown.subParser('unescapeSpecialChars')(text, options, globals);
954
+
955
+    // attacklab: Restore dollar signs
956
+    text = text.replace(/~D/g, '$$');
957
+
958
+    // attacklab: Restore tildes
959
+    text = text.replace(/~T/g, '~');
960
+
961
+    // Run output modifiers
962
+    showdown.helper.forEach(outputModifiers, (ext) => {
963
+      text = showdown.subParser('runExtension')(ext, text, options, globals);
964
+    });
965
+    return text;
966
+  };
967
+
968
+  /**
969
+   * Set an option of this Converter instance
970
+   * @param {string} key
971
+   * @param {*} value
972
+   */
973
+  this.setOption = function (key, value) {
974
+    options[key] = value;
975
+  };
976
+
977
+  /**
978
+   * Get the option of this Converter instance
979
+   * @param {string} key
980
+   * @returns {*}
981
+   */
982
+  this.getOption = function (key) {
983
+    return options[key];
984
+  };
985
+
986
+  /**
987
+   * Get the options of this Converter instance
988
+   * @returns {{}}
989
+   */
990
+  this.getOptions = function () {
991
+    return options;
992
+  };
993
+
994
+  /**
995
+   * Add extension to THIS converter
996
+   * @param {{}} extension
997
+   * @param {string} [name=null]
998
+   */
999
+  this.addExtension = function (extension, name) {
1000
+    name = name || null;
1001
+    _parseExtension(extension, name);
1002
+  };
1003
+
1004
+  /**
1005
+   * Use a global registered extension with THIS converter
1006
+   * @param {string} extensionName Name of the previously registered extension
1007
+   */
1008
+  this.useExtension = function (extensionName) {
1009
+    _parseExtension(extensionName);
1010
+  };
1011
+
1012
+  /**
1013
+   * Set the flavor THIS converter should use
1014
+   * @param {string} name
1015
+   */
1016
+  this.setFlavor = function (name) {
1017
+    if (flavor.hasOwnProperty(name)) {
1018
+      const preset = flavor[name];
1019
+      for (const option in preset) {
1020
+        if (preset.hasOwnProperty(option)) {
1021
+          options[option] = preset[option];
1022
+        }
1023
+      }
1024
+    }
1025
+  };
1026
+
1027
+  /**
1028
+   * Remove an extension from THIS converter.
1029
+   * Note: This is a costly operation. It's better to initialize a new converter
1030
+   * and specify the extensions you wish to use
1031
+   * @param {Array} extension
1032
+   */
1033
+  this.removeExtension = function (extension) {
1034
+    if (!showdown.helper.isArray(extension)) {
1035
+      extension = [extension];
1036
+    }
1037
+    for (let a = 0; a < extension.length; ++a) {
1038
+      const ext = extension[a];
1039
+      for (var i = 0; i < langExtensions.length; ++i) {
1040
+        if (langExtensions[i] === ext) {
1041
+          langExtensions[i].splice(i, 1);
1042
+        }
1043
+      }
1044
+      for (let ii = 0; ii < outputModifiers.length; ++i) {
1045
+        if (outputModifiers[ii] === ext) {
1046
+          outputModifiers[ii].splice(i, 1);
1047
+        }
1048
+      }
1049
+    }
1050
+  };
1051
+
1052
+  /**
1053
+   * Get all extension of THIS converter
1054
+   * @returns {{language: Array, output: Array}}
1055
+   */
1056
+  this.getAllExtensions = function () {
1057
+    return {
1058
+      language: langExtensions,
1059
+      output: outputModifiers,
1060
+    };
1061
+  };
1062
+};
1063
+
1064
+/**
1065
+ * Turn Markdown link shortcuts into XHTML <a> tags.
1066
+ */
1067
+showdown.subParser('anchors', (text, options, globals) => {
1068
+  text = globals.converter._dispatch('anchors.before', text, options, globals);
1069
+
1070
+  const writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
1071
+    if (showdown.helper.isUndefined(m7)) {
1072
+      m7 = '';
1073
+    }
1074
+    wholeMatch = m1;
1075
+    const linkText = m2;
1076
+    let linkId = m3.toLowerCase();
1077
+    let url = m4;
1078
+    let title = m7;
1079
+
1080
+    if (!url) {
1081
+      if (!linkId) {
1082
+        // lower-case and turn embedded newlines into spaces
1083
+        linkId = linkText.toLowerCase().replace(/ ?\n/g, ' ');
1084
+      }
1085
+      url = `#${linkId}`;
1086
+
1087
+      if (!showdown.helper.isUndefined(globals.gUrls[linkId])) {
1088
+        url = globals.gUrls[linkId];
1089
+        if (!showdown.helper.isUndefined(globals.gTitles[linkId])) {
1090
+          title = globals.gTitles[linkId];
1091
+        }
1092
+      } else if (wholeMatch.search(/\(\s*\)$/m) > -1) {
1093
+        // Special case for explicit empty url
1094
+        url = '';
1095
+      } else {
1096
+        return wholeMatch;
1097
+      }
1098
+    }
1099
+
1100
+    url = showdown.helper.escapeCharacters(url, '*_', false);
1101
+    let result = `<a href="${url}"`;
1102
+
1103
+    if (title !== '' && title !== null) {
1104
+      title = title.replace(/"/g, '&quot;');
1105
+      title = showdown.helper.escapeCharacters(title, '*_', false);
1106
+      result += ` title="${title}"`;
1107
+    }
1108
+
1109
+    result += `>${linkText}</a>`;
1110
+
1111
+    return result;
1112
+  };
1113
+
1114
+  // First, handle reference-style links: [link text] [id]
1115
+  /*
1116
+   text = text.replace(/
1117
+   (							// wrap whole match in $1
1118
+   \[
1119
+   (
1120
+   (?:
1121
+   \[[^\]]*\]		// allow brackets nested one level
1122
+   |
1123
+   [^\[]			// or anything else
1124
+   )*
1125
+   )
1126
+   \]
1127
+
1128
+   [ ]?					// one optional space
1129
+   (?:\n[ ]*)?				// one optional newline followed by spaces
1130
+
1131
+   \[
1132
+   (.*?)					// id = $3
1133
+   \]
1134
+   )()()()()					// pad remaining backreferences
1135
+   /g,_DoAnchors_callback);
1136
+   */
1137
+  text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g, writeAnchorTag);
1138
+
1139
+  //
1140
+  // Next, inline-style links: [link text](url "optional title")
1141
+  //
1142
+
1143
+  /*
1144
+   text = text.replace(/
1145
+   (						// wrap whole match in $1
1146
+   \[
1147
+   (
1148
+   (?:
1149
+   \[[^\]]*\]	// allow brackets nested one level
1150
+   |
1151
+   [^\[\]]			// or anything else
1152
+   )
1153
+   )
1154
+   \]
1155
+   \(						// literal paren
1156
+   [ \t]*
1157
+   ()						// no id, so leave $3 empty
1158
+   <?(.*?)>?				// href = $4
1159
+   [ \t]*
1160
+   (						// $5
1161
+   (['"])				// quote char = $6
1162
+   (.*?)				// Title = $7
1163
+   \6					// matching quote
1164
+   [ \t]*				// ignore any spaces/tabs between closing quote and )
1165
+   )?						// title is optional
1166
+   \)
1167
+   )
1168
+   /g,writeAnchorTag);
1169
+   */
1170
+  text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,
1171
+    writeAnchorTag);
1172
+
1173
+  //
1174
+  // Last, handle reference-style shortcuts: [link text]
1175
+  // These must come last in case you've also got [link test][1]
1176
+  // or [link test](/foo)
1177
+  //
1178
+
1179
+  /*
1180
+   text = text.replace(/
1181
+   (                // wrap whole match in $1
1182
+   \[
1183
+   ([^\[\]]+)       // link text = $2; can't contain '[' or ']'
1184
+   \]
1185
+   )()()()()()      // pad rest of backreferences
1186
+   /g, writeAnchorTag);
1187
+   */
1188
+  text = text.replace(/(\[([^\[\]]+)])()()()()()/g, writeAnchorTag);
1189
+
1190
+  text = globals.converter._dispatch('anchors.after', text, options, globals);
1191
+  return text;
1192
+});
1193
+
1194
+showdown.subParser('autoLinks', (text, options, globals) => {
1195
+  text = globals.converter._dispatch('autoLinks.before', text, options, globals);
1196
+
1197
+  const simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi;
1198
+  const delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi;
1199
+  const simpleMailRegex = /(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi;
1200
+  const delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
1201
+
1202
+  text = text.replace(delimUrlRegex, replaceLink);
1203
+  text = text.replace(delimMailRegex, replaceMail);
1204
+  // simpleURLRegex  = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
1205
+  // Email addresses: <address@domain.foo>
1206
+
1207
+  if (options.simplifiedAutoLink) {
1208
+    text = text.replace(simpleURLRegex, replaceLink);
1209
+    text = text.replace(simpleMailRegex, replaceMail);
1210
+  }
1211
+
1212
+  function replaceLink(wm, link) {
1213
+    const lnkTxt = link;
1214
+    if (/^www\./i.test(link)) {
1215
+      link = link.replace(/^www\./i, 'http://www.');
1216
+    }
1217
+    return `<a href="${link}">${lnkTxt}</a>`;
1218
+  }
1219
+
1220
+  function replaceMail(wholeMatch, m1) {
1221
+    const unescapedStr = showdown.subParser('unescapeSpecialChars')(m1);
1222
+    return showdown.subParser('encodeEmailAddress')(unescapedStr);
1223
+  }
1224
+
1225
+  text = globals.converter._dispatch('autoLinks.after', text, options, globals);
1226
+
1227
+  return text;
1228
+});
1229
+
1230
+/**
1231
+ * These are all the transformations that form block-level
1232
+ * tags like paragraphs, headers, and list items.
1233
+ */
1234
+showdown.subParser('blockGamut', (text, options, globals) => {
1235
+  text = globals.converter._dispatch('blockGamut.before', text, options, globals);
1236
+
1237
+  // we parse blockquotes first so that we can have headings and hrs
1238
+  // inside blockquotes
1239
+  text = showdown.subParser('blockQuotes')(text, options, globals);
1240
+  text = showdown.subParser('headers')(text, options, globals);
1241
+
1242
+  // Do Horizontal Rules:
1243
+  const key = showdown.subParser('hashBlock')('<hr />', options, globals);
1244
+  text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key);
1245
+  text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key);
1246
+  text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key);
1247
+
1248
+  text = showdown.subParser('lists')(text, options, globals);
1249
+  text = showdown.subParser('codeBlocks')(text, options, globals);
1250
+  text = showdown.subParser('tables')(text, options, globals);
1251
+
1252
+  // We already ran _HashHTMLBlocks() before, in Markdown(), but that
1253
+  // was to escape raw HTML in the original Markdown source. This time,
1254
+  // we're escaping the markup we've just created, so that we don't wrap
1255
+  // <p> tags around block-level tags.
1256
+  text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
1257
+  text = showdown.subParser('paragraphs')(text, options, globals);
1258
+
1259
+  text = globals.converter._dispatch('blockGamut.after', text, options, globals);
1260
+
1261
+  return text;
1262
+});
1263
+
1264
+showdown.subParser('blockQuotes', (text, options, globals) => {
1265
+  text = globals.converter._dispatch('blockQuotes.before', text, options, globals);
1266
+  /*
1267
+   text = text.replace(/
1268
+   (								// Wrap whole match in $1
1269
+   (
1270
+   ^[ \t]*>[ \t]?			// '>' at the start of a line
1271
+   .+\n					// rest of the first line
1272
+   (.+\n)*					// subsequent consecutive lines
1273
+   \n*						// blanks
1274
+   )+
1275
+   )
1276
+   /gm, function(){...});
1277
+   */
1278
+
1279
+  text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, (wholeMatch, m1) => {
1280
+    let bq = m1;
1281
+
1282
+    // attacklab: hack around Konqueror 3.5.4 bug:
1283
+    // "----------bug".replace(/^-/g,"") == "bug"
1284
+    bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting
1285
+
1286
+    // attacklab: clean up hack
1287
+    bq = bq.replace(/~0/g, '');
1288
+
1289
+    bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
1290
+    bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
1291
+    bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
1292
+
1293
+    bq = bq.replace(/(^|\n)/g, '$1  ');
1294
+    // These leading spaces screw with <pre> content, so we need to fix that:
1295
+    bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, (wholeMatch, m1) => {
1296
+      let pre = m1;
1297
+      // attacklab: hack around Konqueror 3.5.4 bug:
1298
+      pre = pre.replace(/^ {2}/mg, '~0');
1299
+      pre = pre.replace(/~0/g, '');
1300
+      return pre;
1301
+    });
1302
+
1303
+    return showdown.subParser('hashBlock')(`<blockquote>\n${bq}\n</blockquote>`, options, globals);
1304
+  });
1305
+
1306
+  text = globals.converter._dispatch('blockQuotes.after', text, options, globals);
1307
+  return text;
1308
+});
1309
+
1310
+/**
1311
+ * Process Markdown `<pre><code>` blocks.
1312
+ */
1313
+showdown.subParser('codeBlocks', (text, options, globals) => {
1314
+  text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
1315
+  /*
1316
+   text = text.replace(text,
1317
+   /(?:\n\n|^)
1318
+   (								// $1 = the code block -- one or more lines, starting with a space/tab
1319
+   (?:
1320
+   (?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
1321
+   .*\n+
1322
+   )+
1323
+   )
1324
+   (\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
1325
+   /g,function(){...});
1326
+   */
1327
+
1328
+  // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
1329
+  text += '~0';
1330
+
1331
+  const pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
1332
+  text = text.replace(pattern, (wholeMatch, m1, m2) => {
1333
+    let codeblock = m1;
1334
+    const nextChar = m2;
1335
+    let end = '\n';
1336
+
1337
+    codeblock = showdown.subParser('outdent')(codeblock);
1338
+    codeblock = showdown.subParser('encodeCode')(codeblock);
1339
+    codeblock = showdown.subParser('detab')(codeblock);
1340
+    codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
1341
+    codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
1342
+
1343
+    if (options.omitExtraWLInCodeBlocks) {
1344
+      end = '';
1345
+    }
1346
+
1347
+    codeblock = `<pre><code>${codeblock}${end}</code></pre>`;
1348
+
1349
+    return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
1350
+  });
1351
+
1352
+  // attacklab: strip sentinel
1353
+  text = text.replace(/~0/, '');
1354
+
1355
+  text = globals.converter._dispatch('codeBlocks.after', text, options, globals);
1356
+  return text;
1357
+});
1358
+
1359
+/**
1360
+ *
1361
+ *   *  Backtick quotes are used for <code></code> spans.
1362
+ *
1363
+ *   *  You can use multiple backticks as the delimiters if you want to
1364
+ *     include literal backticks in the code span. So, this input:
1365
+ *
1366
+ *         Just type ``foo `bar` baz`` at the prompt.
1367
+ *
1368
+ *       Will translate to:
1369
+ *
1370
+ *         <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
1371
+ *
1372
+ *    There's no arbitrary limit to the number of backticks you
1373
+ *    can use as delimters. If you need three consecutive backticks
1374
+ *    in your code, use four for delimiters, etc.
1375
+ *
1376
+ *  *  You can use spaces to get literal backticks at the edges:
1377
+ *
1378
+ *         ... type `` `bar` `` ...
1379
+ *
1380
+ *       Turns to:
1381
+ *
1382
+ *         ... type <code>`bar`</code> ...
1383
+ */
1384
+showdown.subParser('codeSpans', (text, options, globals) => {
1385
+  text = globals.converter._dispatch('codeSpans.before', text, options, globals);
1386
+
1387
+  /*
1388
+   text = text.replace(/
1389
+   (^|[^\\])					// Character before opening ` can't be a backslash
1390
+   (`+)						// $2 = Opening run of `
1391
+   (							// $3 = The code block
1392
+   [^\r]*?
1393
+   [^`]					// attacklab: work around lack of lookbehind
1394
+   )
1395
+   \2							// Matching closer
1396
+   (?!`)
1397
+   /gm, function(){...});
1398
+   */
1399
+
1400
+  if (typeof (text) === 'undefined') {
1401
+    text = '';
1402
+  }
1403
+  text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
1404
+    (wholeMatch, m1, m2, m3) => {
1405
+      let c = m3;
1406
+      c = c.replace(/^([ \t]*)/g, '');	// leading whitespace
1407
+      c = c.replace(/[ \t]*$/g, '');	// trailing whitespace
1408
+      c = showdown.subParser('encodeCode')(c);
1409
+      return `${m1}<code>${c}</code>`;
1410
+    });
1411
+
1412
+  text = globals.converter._dispatch('codeSpans.after', text, options, globals);
1413
+  return text;
1414
+});
1415
+
1416
+/**
1417
+ * Convert all tabs to spaces
1418
+ */
1419
+showdown.subParser('detab', (text) => {
1420
+  // expand first n-1 tabs
1421
+  text = text.replace(/\t(?=\t)/g, '    '); // g_tab_width
1422
+
1423
+  // replace the nth with two sentinels
1424
+  text = text.replace(/\t/g, '~A~B');
1425
+
1426
+  // use the sentinel to anchor our regex so it doesn't explode
1427
+  text = text.replace(/~B(.+?)~A/g, (wholeMatch, m1) => {
1428
+    let leadingText = m1;
1429
+    const numSpaces = 4 - leadingText.length % 4; // g_tab_width
1430
+
1431
+    // there *must* be a better way to do this:
1432
+    for (let i = 0; i < numSpaces; i++) {
1433
+      leadingText += ' ';
1434
+    }
1435
+
1436
+    return leadingText;
1437
+  });
1438
+
1439
+  // clean up sentinels
1440
+  text = text.replace(/~A/g, '    '); // g_tab_width
1441
+  text = text.replace(/~B/g, '');
1442
+
1443
+  return text;
1444
+});
1445
+
1446
+/**
1447
+ * Smart processing for ampersands and angle brackets that need to be encoded.
1448
+ */
1449
+showdown.subParser('encodeAmpsAndAngles', (text) => {
1450
+  // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1451
+  // http://bumppo.net/projects/amputator/
1452
+  text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&amp;');
1453
+
1454
+  // Encode naked <'s
1455
+  text = text.replace(/<(?![a-z\/?\$!])/gi, '&lt;');
1456
+
1457
+  return text;
1458
+});
1459
+
1460
+/**
1461
+ * Returns the string, with after processing the following backslash escape sequences.
1462
+ *
1463
+ * attacklab: The polite way to do this is with the new escapeCharacters() function:
1464
+ *
1465
+ *    text = escapeCharacters(text,"\\",true);
1466
+ *    text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
1467
+ *
1468
+ * ...but we're sidestepping its use of the (slow) RegExp constructor
1469
+ * as an optimization for Firefox.  This function gets called a LOT.
1470
+ */
1471
+showdown.subParser('encodeBackslashEscapes', (text) => {
1472
+  text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
1473
+  text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback);
1474
+  return text;
1475
+});
1476
+
1477
+/**
1478
+ * Encode/escape certain characters inside Markdown code runs.
1479
+ * The point is that in code, these characters are literals,
1480
+ * and lose their special Markdown meanings.
1481
+ */
1482
+showdown.subParser('encodeCode', (text) => {
1483
+  // Encode all ampersands; HTML entities are not
1484
+  // entities within a Markdown code span.
1485
+  text = text.replace(/&/g, '&amp;');
1486
+
1487
+  // Do the angle bracket song and dance:
1488
+  text = text.replace(/</g, '&lt;');
1489
+  text = text.replace(/>/g, '&gt;');
1490
+
1491
+  // Now, escape characters that are magic in Markdown:
1492
+  text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false);
1493
+
1494
+  // jj the line above breaks this:
1495
+  //---
1496
+  //* Item
1497
+  //   1. Subitem
1498
+  //            special char: *
1499
+  // ---
1500
+
1501
+  return text;
1502
+});
1503
+
1504
+/**
1505
+ *  Input: an email address, e.g. "foo@example.com"
1506
+ *
1507
+ *  Output: the email address as a mailto link, with each character
1508
+ *    of the address encoded as either a decimal or hex entity, in
1509
+ *    the hopes of foiling most address harvesting spam bots. E.g.:
1510
+ *
1511
+ *    <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1512
+ *       x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1513
+ *       &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
1514
+ *
1515
+ *  Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
1516
+ *  mailing list: <http://tinyurl.com/yu7ue>
1517
+ *
1518
+ */
1519
+showdown.subParser('encodeEmailAddress', (addr) => {
1520
+  const encode = [
1521
+    function (ch) {
1522
+      return `&#${ch.charCodeAt(0)};`;
1523
+    },
1524
+    function (ch) {
1525
+      return `&#x${ch.charCodeAt(0).toString(16)};`;
1526
+    },
1527
+    function (ch) {
1528
+      return ch;
1529
+    },
1530
+  ];
1531
+
1532
+  addr = `mailto:${addr}`;
1533
+
1534
+  addr = addr.replace(/./g, (ch) => {
1535
+    if (ch === '@') {
1536
+      // this *must* be encoded. I insist.
1537
+      ch = encode[Math.floor(Math.random() * 2)](ch);
1538
+    } else if (ch !== ':') {
1539
+      // leave ':' alone (to spot mailto: later)
1540
+      const r = Math.random();
1541
+      // roughly 10% raw, 45% hex, 45% dec
1542
+      ch = (
1543
+        r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)
1544
+      );
1545
+    }
1546
+    return ch;
1547
+  });
1548
+
1549
+  addr = `<a href="${addr}">${addr}</a>`;
1550
+  addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part
1551
+
1552
+  return addr;
1553
+});
1554
+
1555
+/**
1556
+ * Within tags -- meaning between < and > -- encode [\ ` * _] so they
1557
+ * don't conflict with their use in Markdown for code, italics and strong.
1558
+ */
1559
+showdown.subParser('escapeSpecialCharsWithinTagAttributes', (text) => {
1560
+  // Build a regex to find HTML tags and comments.  See Friedl's
1561
+  // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
1562
+  const regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
1563
+
1564
+  text = text.replace(regex, (wholeMatch) => {
1565
+    let tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`');
1566
+    tag = showdown.helper.escapeCharacters(tag, '\\`*_', false);
1567
+    return tag;
1568
+  });
1569
+
1570
+  return text;
1571
+});
1572
+
1573
+/**
1574
+ * Handle github codeblocks prior to running HashHTML so that
1575
+ * HTML contained within the codeblock gets escaped properly
1576
+ * Example:
1577
+ * ```ruby
1578
+ *     def hello_world(x)
1579
+ *       puts "Hello, #{x}"
1580
+ *     end
1581
+ * ```
1582
+ */
1583
+showdown.subParser('githubCodeBlocks', (text, options, globals) => {
1584
+  // early exit if option is not enabled
1585
+  if (!options.ghCodeBlocks) {
1586
+    return text;
1587
+  }
1588
+
1589
+  text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
1590
+
1591
+  text += '~0';
1592
+
1593
+  text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, (wholeMatch, language, codeblock) => {
1594
+    const end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
1595
+
1596
+    // First parse the github code block
1597
+    codeblock = showdown.subParser('encodeCode')(codeblock);
1598
+    codeblock = showdown.subParser('detab')(codeblock);
1599
+    codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
1600
+    codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
1601
+
1602
+    codeblock = `<pre><code${language ? ` class="${language} language-${language}"` : ''}>${codeblock}${end}</code></pre>`;
1603
+
1604
+    codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
1605
+
1606
+    // Since GHCodeblocks can be false positives, we need to
1607
+    // store the primitive text and the parsed text in a global var,
1608
+    // and then return a token
1609
+    return `\n\n~G${globals.ghCodeBlocks.push({ text: wholeMatch, codeblock }) - 1}G\n\n`;
1610
+  });
1611
+
1612
+  // attacklab: strip sentinel
1613
+  text = text.replace(/~0/, '');
1614
+
1615
+  return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
1616
+});
1617
+
1618
+showdown.subParser('hashBlock', (text, options, globals) => {
1619
+  text = text.replace(/(^\n+|\n+$)/g, '');
1620
+  return `\n\n~K${globals.gHtmlBlocks.push(text) - 1}K\n\n`;
1621
+});
1622
+
1623
+showdown.subParser('hashElement', (text, options, globals) => function (wholeMatch, m1) {
1624
+  let blockText = m1;
1625
+
1626
+  // Undo double lines
1627
+  blockText = blockText.replace(/\n\n/g, '\n');
1628
+  blockText = blockText.replace(/^\n/, '');
1629
+
1630
+  // strip trailing blank lines
1631
+  blockText = blockText.replace(/\n+$/g, '');
1632
+
1633
+  // Replace the element text with a marker ("~KxK" where x is its key)
1634
+  blockText = `\n\n~K${globals.gHtmlBlocks.push(blockText) - 1}K\n\n`;
1635
+
1636
+  return blockText;
1637
+});
1638
+
1639
+showdown.subParser('hashHTMLBlocks', (text, options, globals) => {
1640
+  const blockTags = [
1641
+    'pre',
1642
+    'div',
1643
+    'h1',
1644
+    'h2',
1645
+    'h3',
1646
+    'h4',
1647
+    'h5',
1648
+    'h6',
1649
+    'blockquote',
1650
+    'table',
1651
+    'dl',
1652
+    'ol',
1653
+    'ul',
1654
+    'script',
1655
+    'noscript',
1656
+    'form',
1657
+    'fieldset',
1658
+    'iframe',
1659
+    'math',
1660
+    'style',
1661
+    'section',
1662
+    'header',
1663
+    'footer',
1664
+    'nav',
1665
+    'article',
1666
+    'aside',
1667
+    'address',
1668
+    'audio',
1669
+    'canvas',
1670
+    'figure',
1671
+    'hgroup',
1672
+    'output',
1673
+    'video',
1674
+    'p',
1675
+  ];
1676
+  const repFunc = function (wholeMatch, match, left, right) {
1677
+    let txt = wholeMatch;
1678
+    // check if this html element is marked as markdown
1679
+    // if so, it's contents should be parsed as markdown
1680
+    if (left.search(/\bmarkdown\b/) !== -1) {
1681
+      txt = left + globals.converter.makeHtml(match) + right;
1682
+    }
1683
+    return `\n\n~K${globals.gHtmlBlocks.push(txt) - 1}K\n\n`;
1684
+  };
1685
+
1686
+  for (let i = 0; i < blockTags.length; ++i) {
1687
+    text = showdown.helper.replaceRecursiveRegExp(text, repFunc, `^(?: |\\t){0,3}<${blockTags[i]}\\b[^>]*>`, `</${blockTags[i]}>`, 'gim');
1688
+  }
1689
+
1690
+  // HR SPECIAL CASE
1691
+  text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
1692
+    showdown.subParser('hashElement')(text, options, globals));
1693
+
1694
+  // Special case for standalone HTML comments:
1695
+  text = text.replace(/(<!--[\s\S]*?-->)/g,
1696
+    showdown.subParser('hashElement')(text, options, globals));
1697
+
1698
+  // PHP and ASP-style processor instructions (<?...?> and <%...%>)
1699
+  text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
1700
+    showdown.subParser('hashElement')(text, options, globals));
1701
+  return text;
1702
+});
1703
+
1704
+/**
1705
+ * Hash span elements that should not be parsed as markdown
1706
+ */
1707
+showdown.subParser('hashHTMLSpans', (text, config, globals) => {
1708
+  const matches = showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');
1709
+
1710
+  for (let i = 0; i < matches.length; ++i) {
1711
+    text = text.replace(matches[i][0], `~L${globals.gHtmlSpans.push(matches[i][0]) - 1}L`);
1712
+  }
1713
+  return text;
1714
+});
1715
+
1716
+/**
1717
+ * Unhash HTML spans
1718
+ */
1719
+showdown.subParser('unhashHTMLSpans', (text, config, globals) => {
1720
+  for (let i = 0; i < globals.gHtmlSpans.length; ++i) {
1721
+    text = text.replace(`~L${i}L`, globals.gHtmlSpans[i]);
1722
+  }
1723
+
1724
+  return text;
1725
+});
1726
+
1727
+/**
1728
+ * Hash span elements that should not be parsed as markdown
1729
+ */
1730
+showdown.subParser('hashPreCodeTags', (text, config, globals) => {
1731
+  const repFunc = function (wholeMatch, match, left, right) {
1732
+    // encode html entities
1733
+    const codeblock = left + showdown.subParser('encodeCode')(match) + right;
1734
+    return `\n\n~G${globals.ghCodeBlocks.push({ text: wholeMatch, codeblock }) - 1}G\n\n`;
1735
+  };
1736
+
1737
+  text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^(?: |\\t){0,3}</code>\\s*</pre>', 'gim');
1738
+  return text;
1739
+});
1740
+
1741
+showdown.subParser('headers', (text, options, globals) => {
1742
+  text = globals.converter._dispatch('headers.before', text, options, globals);
1743
+
1744
+  let prefixHeader = options.prefixHeaderId;
1745
+  const headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart);
1746
+
1747
+  // Set text-style headers:
1748
+  //	Header 1
1749
+  //	========
1750
+  //
1751
+  //	Header 2
1752
+  //	--------
1753
+  //
1754
+  const setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm;
1755
+  const setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
1756
+
1757
+  text = text.replace(setextRegexH1, (wholeMatch, m1) => {
1758
+    const spanGamut = showdown.subParser('spanGamut')(m1, options, globals);
1759
+    const hID = (options.noHeaderId) ? '' : ` id="${headerId(m1)}"`;
1760
+    const hLevel = headerLevelStart;
1761
+    const hashBlock = `<h${hLevel}${hID}>${spanGamut}</h${hLevel}>`;
1762
+    return showdown.subParser('hashBlock')(hashBlock, options, globals);
1763
+  });
1764
+
1765
+  text = text.replace(setextRegexH2, (matchFound, m1) => {
1766
+    const spanGamut = showdown.subParser('spanGamut')(m1, options, globals);
1767
+    const hID = (options.noHeaderId) ? '' : ` id="${headerId(m1)}"`;
1768
+    const hLevel = headerLevelStart + 1;
1769
+    const hashBlock = `<h${hLevel}${hID}>${spanGamut}</h${hLevel}>`;
1770
+    return showdown.subParser('hashBlock')(hashBlock, options, globals);
1771
+  });
1772
+
1773
+  // atx-style headers:
1774
+  //  # Header 1
1775
+  //  ## Header 2
1776
+  //  ## Header 2 with closing hashes ##
1777
+  //  ...
1778
+  //  ###### Header 6
1779
+  //
1780
+  text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, (wholeMatch, m1, m2) => {
1781
+    const span = showdown.subParser('spanGamut')(m2, options, globals);
1782
+    const hID = (options.noHeaderId) ? '' : ` id="${headerId(m2)}"`;
1783
+    const hLevel = headerLevelStart - 1 + m1.length;
1784
+    const header = `<h${hLevel}${hID}>${span}</h${hLevel}>`;
1785
+
1786
+    return showdown.subParser('hashBlock')(header, options, globals);
1787
+  });
1788
+
1789
+  function headerId(m) {
1790
+    let title; const
1791
+      escapedId = m.replace(/[^\w]/g, '').toLowerCase();
1792
+
1793
+    if (globals.hashLinkCounts[escapedId]) {
1794
+      title = `${escapedId}-${globals.hashLinkCounts[escapedId]++}`;
1795
+    } else {
1796
+      title = escapedId;
1797
+      globals.hashLinkCounts[escapedId] = 1;
1798
+    }
1799
+
1800
+    // Prefix id to prevent causing inadvertent pre-existing style matches.
1801
+    if (prefixHeader === true) {
1802
+      prefixHeader = 'section';
1803
+    }
1804
+
1805
+    if (showdown.helper.isString(prefixHeader)) {
1806
+      return prefixHeader + title;
1807
+    }
1808
+    return title;
1809
+  }
1810
+
1811
+  text = globals.converter._dispatch('headers.after', text, options, globals);
1812
+  return text;
1813
+});
1814
+
1815
+/**
1816
+ * Turn Markdown image shortcuts into <img> tags.
1817
+ */
1818
+showdown.subParser('images', (text, options, globals) => {
1819
+  text = globals.converter._dispatch('images.before', text, options, globals);
1820
+
1821
+  const inlineRegExp = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g;
1822
+  const referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g;
1823
+
1824
+  function writeImageTag(wholeMatch, altText, linkId, url, width, height, m5, title) {
1825
+    const { gUrls } = globals;
1826
+    const { gTitles } = globals;
1827
+    const gDims = globals.gDimensions;
1828
+
1829
+    linkId = linkId.toLowerCase();
1830
+
1831
+    if (!title) {
1832
+      title = '';
1833
+    }
1834
+
1835
+    if (url === '' || url === null) {
1836
+      if (linkId === '' || linkId === null) {
1837
+        // lower-case and turn embedded newlines into spaces
1838
+        linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
1839
+      }
1840
+      url = `#${linkId}`;
1841
+
1842
+      if (!showdown.helper.isUndefined(gUrls[linkId])) {
1843
+        url = gUrls[linkId];
1844
+        if (!showdown.helper.isUndefined(gTitles[linkId])) {
1845
+          title = gTitles[linkId];
1846
+        }
1847
+        if (!showdown.helper.isUndefined(gDims[linkId])) {
1848
+          width = gDims[linkId].width;
1849
+          height = gDims[linkId].height;
1850
+        }
1851
+      } else {
1852
+        return wholeMatch;
1853
+      }
1854
+    }
1855
+
1856
+    altText = altText.replace(/"/g, '&quot;');
1857
+    altText = showdown.helper.escapeCharacters(altText, '*_', false);
1858
+    url = showdown.helper.escapeCharacters(url, '*_', false);
1859
+    let result = `<img src="${url}" alt="${altText}"`;
1860
+
1861
+    if (title) {
1862
+      title = title.replace(/"/g, '&quot;');
1863
+      title = showdown.helper.escapeCharacters(title, '*_', false);
1864
+      result += ` title="${title}"`;
1865
+    }
1866
+
1867
+    if (width && height) {
1868
+      width = (width === '*') ? 'auto' : width;
1869
+      height = (height === '*') ? 'auto' : height;
1870
+
1871
+      result += ` width="${width}"`;
1872
+      result += ` height="${height}"`;
1873
+    }
1874
+
1875
+    result += ' />';
1876
+    return result;
1877
+  }
1878
+
1879
+  // First, handle reference-style labeled images: ![alt text][id]
1880
+  text = text.replace(referenceRegExp, writeImageTag);
1881
+
1882
+  // Next, handle inline images:  ![alt text](url =<width>x<height> "optional title")
1883
+  text = text.replace(inlineRegExp, writeImageTag);
1884
+
1885
+  text = globals.converter._dispatch('images.after', text, options, globals);
1886
+  return text;
1887
+});
1888
+
1889
+showdown.subParser('italicsAndBold', (text, options, globals) => {
1890
+  text = globals.converter._dispatch('italicsAndBold.before', text, options, globals);
1891
+
1892
+  if (options.literalMidWordUnderscores) {
1893
+    // underscores
1894
+    // Since we are consuming a \s character, we need to add it
1895
+    text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>');
1896
+    text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>');
1897
+    // asterisks
1898
+    text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '<strong>$2</strong>');
1899
+    text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
1900
+  } else {
1901
+    // <strong> must go first:
1902
+    text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '<strong>$2</strong>');
1903
+    text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
1904
+  }
1905
+
1906
+  text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
1907
+  return text;
1908
+});
1909
+
1910
+/**
1911
+ * Form HTML ordered (numbered) and unordered (bulleted) lists.
1912
+ */
1913
+showdown.subParser('lists', (text, options, globals) => {
1914
+  text = globals.converter._dispatch('lists.before', text, options, globals);
1915
+  /**
1916
+   * Process the contents of a single ordered or unordered list, splitting it
1917
+   * into individual list items.
1918
+   * @param {string} listStr
1919
+   * @param {boolean} trimTrailing
1920
+   * @returns {string}
1921
+   */
1922
+  function processListItems(listStr, trimTrailing) {
1923
+    // The $g_list_level global keeps track of when we're inside a list.
1924
+    // Each time we enter a list, we increment it; when we leave a list,
1925
+    // we decrement. If it's zero, we're not in a list anymore.
1926
+    //
1927
+    // We do this because when we're not inside a list, we want to treat
1928
+    // something like this:
1929
+    //
1930
+    //    I recommend upgrading to version
1931
+    //    8. Oops, now this line is treated
1932
+    //    as a sub-list.
1933
+    //
1934
+    // As a single paragraph, despite the fact that the second line starts
1935
+    // with a digit-period-space sequence.
1936
+    //
1937
+    // Whereas when we're inside a list (or sub-list), that line will be
1938
+    // treated as the start of a sub-list. What a kludge, huh? This is
1939
+    // an aspect of Markdown's syntax that's hard to parse perfectly
1940
+    // without resorting to mind-reading. Perhaps the solution is to
1941
+    // change the syntax rules such that sub-lists must start with a
1942
+    // starting cardinal number; e.g. "1." or "a.".
1943
+    globals.gListLevel++;
1944
+
1945
+    // trim trailing blank lines:
1946
+    listStr = listStr.replace(/\n{2,}$/, '\n');
1947
+
1948
+    // attacklab: add sentinel to emulate \z
1949
+    listStr += '~0';
1950
+
1951
+    const rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm;
1952
+    const isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr));
1953
+
1954
+    listStr = listStr.replace(rgx, (wholeMatch, m1, m2, m3, m4, taskbtn, checked) => {
1955
+      checked = (checked && checked.trim() !== '');
1956
+      let item = showdown.subParser('outdent')(m4, options, globals);
1957
+      let bulletStyle = '';
1958
+
1959
+      // Support for github tasklists
1960
+      if (taskbtn && options.tasklists) {
1961
+        bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
1962
+        item = item.replace(/^[ \t]*\[(x|X| )?]/m, () => {
1963
+          let otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
1964
+          if (checked) {
1965
+            otp += ' checked';
1966
+          }
1967
+          otp += '>';
1968
+          return otp;
1969
+        });
1970
+      }
1971
+      // m1 - Leading line or
1972
+      // Has a double return (multi paragraph) or
1973
+      // Has sublist
1974
+      if (m1 || (item.search(/\n{2,}/) > -1)) {
1975
+        item = showdown.subParser('githubCodeBlocks')(item, options, globals);
1976
+        item = showdown.subParser('blockGamut')(item, options, globals);
1977
+      } else {
1978
+        // Recursion for sub-lists:
1979
+        item = showdown.subParser('lists')(item, options, globals);
1980
+        item = item.replace(/\n$/, ''); // chomp(item)
1981
+        if (isParagraphed) {
1982
+          item = showdown.subParser('paragraphs')(item, options, globals);
1983
+        } else {
1984
+          item = showdown.subParser('spanGamut')(item, options, globals);
1985
+        }
1986
+      }
1987
+      item = `\n<li${bulletStyle}>${item}</li>\n`;
1988
+      return item;
1989
+    });
1990
+
1991
+    // attacklab: strip sentinel
1992
+    listStr = listStr.replace(/~0/g, '');
1993
+
1994
+    globals.gListLevel--;
1995
+
1996
+    if (trimTrailing) {
1997
+      listStr = listStr.replace(/\s+$/, '');
1998
+    }
1999
+
2000
+    return listStr;
2001
+  }
2002
+
2003
+  /**
2004
+   * Check and parse consecutive lists (better fix for issue #142)
2005
+   * @param {string} list
2006
+   * @param {string} listType
2007
+   * @param {boolean} trimTrailing
2008
+   * @returns {string}
2009
+   */
2010
+  function parseConsecutiveLists(list, listType, trimTrailing) {
2011
+    // check if we caught 2 or more consecutive lists by mistake
2012
+    // we use the counterRgx, meaning if listType is UL we look for UL and vice versa
2013
+    let counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
2014
+    const subLists = [];
2015
+    let result = '';
2016
+
2017
+    if (list.search(counterRxg) !== -1) {
2018
+      (function parseCL(txt) {
2019
+        const pos = txt.search(counterRxg);
2020
+        if (pos !== -1) {
2021
+          // slice
2022
+          result += `\n\n<${listType}>${processListItems(txt.slice(0, pos), !!trimTrailing)}</${listType}>\n\n`;
2023
+
2024
+          // invert counterType and listType
2025
+          listType = (listType === 'ul') ? 'ol' : 'ul';
2026
+          counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
2027
+
2028
+          // recurse
2029
+          parseCL(txt.slice(pos));
2030
+        } else {
2031
+          result += `\n\n<${listType}>${processListItems(txt, !!trimTrailing)}</${listType}>\n\n`;
2032
+        }
2033
+      }(list));
2034
+      for (let i = 0; i < subLists.length; ++i) {
2035
+
2036
+      }
2037
+    } else {
2038
+      result = `\n\n<${listType}>${processListItems(list, !!trimTrailing)}</${listType}>\n\n`;
2039
+    }
2040
+
2041
+    return result;
2042
+  }
2043
+
2044
+  // attacklab: add sentinel to hack around khtml/safari bug:
2045
+  // http://bugs.webkit.org/show_bug.cgi?id=11231
2046
+  text += '~0';
2047
+
2048
+  // Re-usable pattern to match any entire ul or ol list:
2049
+  let wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
2050
+
2051
+  if (globals.gListLevel) {
2052
+    text = text.replace(wholeList, (wholeMatch, list, m2) => {
2053
+      const listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2054
+      return parseConsecutiveLists(list, listType, true);
2055
+    });
2056
+  } else {
2057
+    wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
2058
+    // wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g;
2059
+    text = text.replace(wholeList, (wholeMatch, m1, list, m3) => {
2060
+      const listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2061
+      return parseConsecutiveLists(list, listType);
2062
+    });
2063
+  }
2064
+
2065
+  // attacklab: strip sentinel
2066
+  text = text.replace(/~0/, '');
2067
+
2068
+  text = globals.converter._dispatch('lists.after', text, options, globals);
2069
+  return text;
2070
+});
2071
+
2072
+/**
2073
+ * Remove one level of line-leading tabs or spaces
2074
+ */
2075
+showdown.subParser('outdent', (text) => {
2076
+  // attacklab: hack around Konqueror 3.5.4 bug:
2077
+  // "----------bug".replace(/^-/g,"") == "bug"
2078
+  text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width
2079
+
2080
+  // attacklab: clean up hack
2081
+  text = text.replace(/~0/g, '');
2082
+
2083
+  return text;
2084
+});
2085
+
2086
+/**
2087
+ *
2088
+ */
2089
+showdown.subParser('paragraphs', (text, options, globals) => {
2090
+  text = globals.converter._dispatch('paragraphs.before', text, options, globals);
2091
+  // Strip leading and trailing lines:
2092
+  text = text.replace(/^\n+/g, '');
2093
+  text = text.replace(/\n+$/g, '');
2094
+
2095
+  const grafs = text.split(/\n{2,}/g);
2096
+  const grafsOut = [];
2097
+  let end = grafs.length; // Wrap <p> tags
2098
+
2099
+  for (var i = 0; i < end; i++) {
2100
+    let str = grafs[i];
2101
+    // if this is an HTML marker, copy it
2102
+    if (str.search(/~(K|G)(\d+)\1/g) >= 0) {
2103
+      grafsOut.push(str);
2104
+    } else {
2105
+      str = showdown.subParser('spanGamut')(str, options, globals);
2106
+      str = str.replace(/^([ \t]*)/g, '<p>');
2107
+      str += '</p>';
2108
+      grafsOut.push(str);
2109
+    }
2110
+  }
2111
+
2112
+  /** Unhashify HTML blocks */
2113
+  end = grafsOut.length;
2114
+  for (i = 0; i < end; i++) {
2115
+    let blockText = '';
2116
+    let grafsOutIt = grafsOut[i];
2117
+    let codeFlag = false;
2118
+    // if this is a marker for an html block...
2119
+    while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) {
2120
+      const delim = RegExp.$1;
2121
+      const num = RegExp.$2;
2122
+
2123
+      if (delim === 'K') {
2124
+        blockText = globals.gHtmlBlocks[num];
2125
+      } else {
2126
+        // we need to check if ghBlock is a false positive
2127
+        if (codeFlag) {
2128
+          // use encoded version of all text
2129
+          blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text);
2130
+        } else {
2131
+          blockText = globals.ghCodeBlocks[num].codeblock;
2132
+        }
2133
+      }
2134
+      blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
2135
+
2136
+      grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText);
2137
+      // Check if grafsOutIt is a pre->code
2138
+      if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
2139
+        codeFlag = true;
2140
+      }
2141
+    }
2142
+    grafsOut[i] = grafsOutIt;
2143
+  }
2144
+  text = grafsOut.join('\n\n');
2145
+  // Strip leading and trailing lines:
2146
+  text = text.replace(/^\n+/g, '');
2147
+  text = text.replace(/\n+$/g, '');
2148
+  return globals.converter._dispatch('paragraphs.after', text, options, globals);
2149
+});
2150
+
2151
+/**
2152
+ * Run extension
2153
+ */
2154
+showdown.subParser('runExtension', (ext, text, options, globals) => {
2155
+  if (ext.filter) {
2156
+    text = ext.filter(text, globals.converter, options);
2157
+  } else if (ext.regex) {
2158
+    // TODO remove this when old extension loading mechanism is deprecated
2159
+    let re = ext.regex;
2160
+    if (!re instanceof RegExp) {
2161
+      re = new RegExp(re, 'g');
2162
+    }
2163
+    text = text.replace(re, ext.replace);
2164
+  }
2165
+
2166
+  return text;
2167
+});
2168
+
2169
+/**
2170
+ * These are all the transformations that occur *within* block-level
2171
+ * tags like paragraphs, headers, and list items.
2172
+ */
2173
+showdown.subParser('spanGamut', (text, options, globals) => {
2174
+  text = globals.converter._dispatch('spanGamut.before', text, options, globals);
2175
+  text = showdown.subParser('codeSpans')(text, options, globals);
2176
+  text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
2177
+  text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
2178
+
2179
+  // Process anchor and image tags. Images must come first,
2180
+  // because ![foo][f] looks like an anchor.
2181
+  text = showdown.subParser('images')(text, options, globals);
2182
+  text = showdown.subParser('anchors')(text, options, globals);
2183
+
2184
+  // Make links out of things like `<http://example.com/>`
2185
+  // Must come after _DoAnchors(), because you can use < and >
2186
+  // delimiters in inline links like [this](<url>).
2187
+  text = showdown.subParser('autoLinks')(text, options, globals);
2188
+  text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
2189
+  text = showdown.subParser('italicsAndBold')(text, options, globals);
2190
+  text = showdown.subParser('strikethrough')(text, options, globals);
2191
+
2192
+  // Do hard breaks:
2193
+  text = text.replace(/  +\n/g, ' <br />\n');
2194
+
2195
+  text = globals.converter._dispatch('spanGamut.after', text, options, globals);
2196
+  return text;
2197
+});
2198
+
2199
+showdown.subParser('strikethrough', (text, options, globals) => {
2200
+  if (options.strikethrough) {
2201
+    text = globals.converter._dispatch('strikethrough.before', text, options, globals);
2202
+    text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '<del>$1</del>');
2203
+    text = globals.converter._dispatch('strikethrough.after', text, options, globals);
2204
+  }
2205
+
2206
+  return text;
2207
+});
2208
+
2209
+/**
2210
+ * Strip any lines consisting only of spaces and tabs.
2211
+ * This makes subsequent regexs easier to write, because we can
2212
+ * match consecutive blank lines with /\n+/ instead of something
2213
+ * contorted like /[ \t]*\n+/
2214
+ */
2215
+showdown.subParser('stripBlankLines', (text) => text.replace(/^[ \t]+$/mg, ''));
2216
+
2217
+/**
2218
+ * Strips link definitions from text, stores the URLs and titles in
2219
+ * hash references.
2220
+ * Link defs are in the form: ^[id]: url "optional title"
2221
+ *
2222
+ * ^[ ]{0,3}\[(.+)\]: // id = $1  attacklab: g_tab_width - 1
2223
+ * [ \t]*
2224
+ * \n?                  // maybe *one* newline
2225
+ * [ \t]*
2226
+ * <?(\S+?)>?          // url = $2
2227
+ * [ \t]*
2228
+ * \n?                // maybe one newline
2229
+ * [ \t]*
2230
+ * (?:
2231
+ * (\n*)              // any lines skipped = $3 attacklab: lookbehind removed
2232
+ * ["(]
2233
+ * (.+?)              // title = $4
2234
+ * [")]
2235
+ * [ \t]*
2236
+ * )?                 // title is optional
2237
+ * (?:\n+|$)
2238
+ * /gm,
2239
+ * function(){...});
2240
+ *
2241
+ */
2242
+showdown.subParser('stripLinkDefinitions', (text, options, globals) => {
2243
+  const regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm;
2244
+
2245
+  // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
2246
+  text += '~0';
2247
+
2248
+  text = text.replace(regex, (wholeMatch, linkId, url, width, height, blankLines, title) => {
2249
+    linkId = linkId.toLowerCase();
2250
+    globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive
2251
+
2252
+    if (blankLines) {
2253
+      // Oops, found blank lines, so it's not a title.
2254
+      // Put back the parenthetical statement we stole.
2255
+      return blankLines + title;
2256
+    }
2257
+    if (title) {
2258
+      globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
2259
+    }
2260
+    if (options.parseImgDimensions && width && height) {
2261
+      globals.gDimensions[linkId] = {
2262
+        width,
2263
+        height,
2264
+      };
2265
+    }
2266
+
2267
+    // Completely remove the definition from the text
2268
+    return '';
2269
+  });
2270
+
2271
+  // attacklab: strip sentinel
2272
+  text = text.replace(/~0/, '');
2273
+
2274
+  return text;
2275
+});
2276
+
2277
+showdown.subParser('tables', (text, options, globals) => {
2278
+  if (!options.tables) {
2279
+    return text;
2280
+  }
2281
+
2282
+  const tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm;
2283
+
2284
+  function parseStyles(sLine) {
2285
+    if (/^:[ \t]*--*$/.test(sLine)) {
2286
+      return ' style="text-align:left;"';
2287
+    } if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
2288
+      return ' style="text-align:right;"';
2289
+    } if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
2290
+      return ' style="text-align:center;"';
2291
+    }
2292
+    return '';
2293
+  }
2294
+
2295
+  function parseHeaders(header, style) {
2296
+    let id = '';
2297
+    header = header.trim();
2298
+    if (options.tableHeaderId) {
2299
+      id = ` id="${header.replace(/ /g, '_').toLowerCase()}"`;
2300
+    }
2301
+    header = showdown.subParser('spanGamut')(header, options, globals);
2302
+
2303
+    return `<th${id}${style}>${header}</th>\n`;
2304
+  }
2305
+
2306
+  function parseCells(cell, style) {
2307
+    const subText = showdown.subParser('spanGamut')(cell, options, globals);
2308
+    return `<td${style}>${subText}</td>\n`;
2309
+  }
2310
+
2311
+  function buildTable(headers, cells) {
2312
+    let tb = '<table>\n<thead>\n<tr>\n';
2313
+    const tblLgn = headers.length;
2314
+
2315
+    for (var i = 0; i < tblLgn; ++i) {
2316
+      tb += headers[i];
2317
+    }
2318
+    tb += '</tr>\n</thead>\n<tbody>\n';
2319
+
2320
+    for (i = 0; i < cells.length; ++i) {
2321
+      tb += '<tr>\n';
2322
+      for (let ii = 0; ii < tblLgn; ++ii) {
2323
+        tb += cells[i][ii];
2324
+      }
2325
+      tb += '</tr>\n';
2326
+    }
2327
+    tb += '</tbody>\n</table>\n';
2328
+    return tb;
2329
+  }
2330
+
2331
+  text = globals.converter._dispatch('tables.before', text, options, globals);
2332
+
2333
+  text = text.replace(tableRgx, (rawTable) => {
2334
+    let i; const
2335
+      tableLines = rawTable.split('\n');
2336
+
2337
+    // strip wrong first and last column if wrapped tables are used
2338
+    for (i = 0; i < tableLines.length; ++i) {
2339
+      if (/^[ \t]{0,3}\|/.test(tableLines[i])) {
2340
+        tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, '');
2341
+      }
2342
+      if (/\|[ \t]*$/.test(tableLines[i])) {
2343
+        tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
2344
+      }
2345
+    }
2346
+
2347
+    const rawHeaders = tableLines[0].split('|').map((s) => s.trim());
2348
+    const rawStyles = tableLines[1].split('|').map((s) => s.trim());
2349
+    const rawCells = [];
2350
+    const headers = [];
2351
+    const styles = [];
2352
+    const cells = [];
2353
+
2354
+    tableLines.shift();
2355
+    tableLines.shift();
2356
+
2357
+    for (i = 0; i < tableLines.length; ++i) {
2358
+      if (tableLines[i].trim() === '') {
2359
+        continue;
2360
+      }
2361
+      rawCells.push(
2362
+        tableLines[i]
2363
+          .split('|')
2364
+          .map((s) => s.trim()),
2365
+      );
2366
+    }
2367
+
2368
+    if (rawHeaders.length < rawStyles.length) {
2369
+      return rawTable;
2370
+    }
2371
+
2372
+    for (i = 0; i < rawStyles.length; ++i) {
2373
+      styles.push(parseStyles(rawStyles[i]));
2374
+    }
2375
+
2376
+    for (i = 0; i < rawHeaders.length; ++i) {
2377
+      if (showdown.helper.isUndefined(styles[i])) {
2378
+        styles[i] = '';
2379
+      }
2380
+      headers.push(parseHeaders(rawHeaders[i], styles[i]));
2381
+    }
2382
+
2383
+    for (i = 0; i < rawCells.length; ++i) {
2384
+      const row = [];
2385
+      for (let ii = 0; ii < headers.length; ++ii) {
2386
+        if (showdown.helper.isUndefined(rawCells[i][ii])) {
2387
+
2388
+        }
2389
+        row.push(parseCells(rawCells[i][ii], styles[ii]));
2390
+      }
2391
+      cells.push(row);
2392
+    }
2393
+
2394
+    return buildTable(headers, cells);
2395
+  });
2396
+
2397
+  text = globals.converter._dispatch('tables.after', text, options, globals);
2398
+
2399
+  return text;
2400
+});
2401
+
2402
+/**
2403
+ * Swap back in all the special characters we've hidden.
2404
+ */
2405
+showdown.subParser('unescapeSpecialChars', (text) => {
2406
+  text = text.replace(/~E(\d+)E/g, (wholeMatch, m1) => {
2407
+    const charCodeToReplace = parseInt(m1);
2408
+    return String.fromCharCode(charCodeToReplace);
2409
+  });
2410
+  return text;
2411
+});
2412
+module.exports = showdown;

+ 206 - 0
src/utils/wxParse/wxDiscode.js

@@ -0,0 +1,206 @@
1
+// HTML 支持的数学符号
2
+function strNumDiscode(str) {
3
+  str = str.replace(/&forall;/g, '∀');
4
+  str = str.replace(/&part;/g, '∂');
5
+  str = str.replace(/&exists;/g, '∃');
6
+  str = str.replace(/&empty;/g, '∅');
7
+  str = str.replace(/&nabla;/g, '∇');
8
+  str = str.replace(/&isin;/g, '∈');
9
+  str = str.replace(/&notin;/g, '∉');
10
+  str = str.replace(/&ni;/g, '∋');
11
+  str = str.replace(/&prod;/g, '∏');
12
+  str = str.replace(/&sum;/g, '∑');
13
+  str = str.replace(/&minus;/g, '−');
14
+  str = str.replace(/&lowast;/g, '∗');
15
+  str = str.replace(/&radic;/g, '√');
16
+  str = str.replace(/&prop;/g, '∝');
17
+  str = str.replace(/&infin;/g, '∞');
18
+  str = str.replace(/&ang;/g, '∠');
19
+  str = str.replace(/&and;/g, '∧');
20
+  str = str.replace(/&or;/g, '∨');
21
+  str = str.replace(/&cap;/g, '∩');
22
+  str = str.replace(/&cap;/g, '∪');
23
+  str = str.replace(/&int;/g, '∫');
24
+  str = str.replace(/&there4;/g, '∴');
25
+  str = str.replace(/&sim;/g, '∼');
26
+  str = str.replace(/&cong;/g, '≅');
27
+  str = str.replace(/&asymp;/g, '≈');
28
+  str = str.replace(/&ne;/g, '≠');
29
+  str = str.replace(/&le;/g, '≤');
30
+  str = str.replace(/&ge;/g, '≥');
31
+  str = str.replace(/&sub;/g, '⊂');
32
+  str = str.replace(/&sup;/g, '⊃');
33
+  str = str.replace(/&nsub;/g, '⊄');
34
+  str = str.replace(/&sube;/g, '⊆');
35
+  str = str.replace(/&supe;/g, '⊇');
36
+  str = str.replace(/&oplus;/g, '⊕');
37
+  str = str.replace(/&otimes;/g, '⊗');
38
+  str = str.replace(/&perp;/g, '⊥');
39
+  str = str.replace(/&sdot;/g, '⋅');
40
+  return str;
41
+}
42
+
43
+// HTML 支持的希腊字母
44
+function strGreeceDiscode(str) {
45
+  str = str.replace(/&Alpha;/g, 'Α');
46
+  str = str.replace(/&Beta;/g, 'Β');
47
+  str = str.replace(/&Gamma;/g, 'Γ');
48
+  str = str.replace(/&Delta;/g, 'Δ');
49
+  str = str.replace(/&Epsilon;/g, 'Ε');
50
+  str = str.replace(/&Zeta;/g, 'Ζ');
51
+  str = str.replace(/&Eta;/g, 'Η');
52
+  str = str.replace(/&Theta;/g, 'Θ');
53
+  str = str.replace(/&Iota;/g, 'Ι');
54
+  str = str.replace(/&Kappa;/g, 'Κ');
55
+  str = str.replace(/&Lambda;/g, 'Λ');
56
+  str = str.replace(/&Mu;/g, 'Μ');
57
+  str = str.replace(/&Nu;/g, 'Ν');
58
+  str = str.replace(/&Xi;/g, 'Ν');
59
+  str = str.replace(/&Omicron;/g, 'Ο');
60
+  str = str.replace(/&Pi;/g, 'Π');
61
+  str = str.replace(/&Rho;/g, 'Ρ');
62
+  str = str.replace(/&Sigma;/g, 'Σ');
63
+  str = str.replace(/&Tau;/g, 'Τ');
64
+  str = str.replace(/&Upsilon;/g, 'Υ');
65
+  str = str.replace(/&Phi;/g, 'Φ');
66
+  str = str.replace(/&Chi;/g, 'Χ');
67
+  str = str.replace(/&Psi;/g, 'Ψ');
68
+  str = str.replace(/&Omega;/g, 'Ω');
69
+
70
+  str = str.replace(/&alpha;/g, 'α');
71
+  str = str.replace(/&beta;/g, 'β');
72
+  str = str.replace(/&gamma;/g, 'γ');
73
+  str = str.replace(/&delta;/g, 'δ');
74
+  str = str.replace(/&epsilon;/g, 'ε');
75
+  str = str.replace(/&zeta;/g, 'ζ');
76
+  str = str.replace(/&eta;/g, 'η');
77
+  str = str.replace(/&theta;/g, 'θ');
78
+  str = str.replace(/&iota;/g, 'ι');
79
+  str = str.replace(/&kappa;/g, 'κ');
80
+  str = str.replace(/&lambda;/g, 'λ');
81
+  str = str.replace(/&mu;/g, 'μ');
82
+  str = str.replace(/&nu;/g, 'ν');
83
+  str = str.replace(/&xi;/g, 'ξ');
84
+  str = str.replace(/&omicron;/g, 'ο');
85
+  str = str.replace(/&pi;/g, 'π');
86
+  str = str.replace(/&rho;/g, 'ρ');
87
+  str = str.replace(/&sigmaf;/g, 'ς');
88
+  str = str.replace(/&sigma;/g, 'σ');
89
+  str = str.replace(/&tau;/g, 'τ');
90
+  str = str.replace(/&upsilon;/g, 'υ');
91
+  str = str.replace(/&phi;/g, 'φ');
92
+  str = str.replace(/&chi;/g, 'χ');
93
+  str = str.replace(/&psi;/g, 'ψ');
94
+  str = str.replace(/&omega;/g, 'ω');
95
+  str = str.replace(/&thetasym;/g, 'ϑ');
96
+  str = str.replace(/&upsih;/g, 'ϒ');
97
+  str = str.replace(/&piv;/g, 'ϖ');
98
+  str = str.replace(/&middot;/g, '·');
99
+  return str;
100
+}
101
+
102
+//
103
+
104
+function strcharacterDiscode(str) {
105
+  // 加入常用解析
106
+  str = str.replace(/&nbsp;/g, ' ');
107
+  str = str.replace(/&quot;/g, "'");
108
+  str = str.replace(/&amp;/g, '&');
109
+  // str = str.replace(/&lt;/g, '‹');
110
+  // str = str.replace(/&gt;/g, '›');
111
+
112
+  str = str.replace(/&lt;/g, '<');
113
+  str = str.replace(/&gt;/g, '>');
114
+  str = str.replace(/&#8226;/g, '•');
115
+
116
+  return str;
117
+}
118
+
119
+// HTML 支持的其他实体
120
+function strOtherDiscode(str) {
121
+  str = str.replace(/&OElig;/g, 'Œ');
122
+  str = str.replace(/&oelig;/g, 'œ');
123
+  str = str.replace(/&Scaron;/g, 'Š');
124
+  str = str.replace(/&scaron;/g, 'š');
125
+  str = str.replace(/&Yuml;/g, 'Ÿ');
126
+  str = str.replace(/&fnof;/g, 'ƒ');
127
+  str = str.replace(/&circ;/g, 'ˆ');
128
+  str = str.replace(/&tilde;/g, '˜');
129
+  str = str.replace(/&ensp;/g, '');
130
+  str = str.replace(/&emsp;/g, '');
131
+  str = str.replace(/&thinsp;/g, '');
132
+  str = str.replace(/&zwnj;/g, '');
133
+  str = str.replace(/&zwj;/g, '');
134
+  str = str.replace(/&lrm;/g, '');
135
+  str = str.replace(/&rlm;/g, '');
136
+  str = str.replace(/&ndash;/g, '–');
137
+  str = str.replace(/&mdash;/g, '—');
138
+  str = str.replace(/&lsquo;/g, '‘');
139
+  str = str.replace(/&rsquo;/g, '’');
140
+  str = str.replace(/&sbquo;/g, '‚');
141
+  str = str.replace(/&ldquo;/g, '“');
142
+  str = str.replace(/&rdquo;/g, '”');
143
+  str = str.replace(/&bdquo;/g, '„');
144
+  str = str.replace(/&dagger;/g, '†');
145
+  str = str.replace(/&Dagger;/g, '‡');
146
+  str = str.replace(/&bull;/g, '•');
147
+  str = str.replace(/&hellip;/g, '…');
148
+  str = str.replace(/&permil;/g, '‰');
149
+  str = str.replace(/&prime;/g, '′');
150
+  str = str.replace(/&Prime;/g, '″');
151
+  str = str.replace(/&lsaquo;/g, '‹');
152
+  str = str.replace(/&rsaquo;/g, '›');
153
+  str = str.replace(/&oline;/g, '‾');
154
+  str = str.replace(/&euro;/g, '€');
155
+  str = str.replace(/&trade;/g, '™');
156
+
157
+  str = str.replace(/&larr;/g, '←');
158
+  str = str.replace(/&uarr;/g, '↑');
159
+  str = str.replace(/&rarr;/g, '→');
160
+  str = str.replace(/&darr;/g, '↓');
161
+  str = str.replace(/&harr;/g, '↔');
162
+  str = str.replace(/&crarr;/g, '↵');
163
+  str = str.replace(/&lceil;/g, '⌈');
164
+  str = str.replace(/&rceil;/g, '⌉');
165
+
166
+  str = str.replace(/&lfloor;/g, '⌊');
167
+  str = str.replace(/&rfloor;/g, '⌋');
168
+  str = str.replace(/&loz;/g, '◊');
169
+  str = str.replace(/&spades;/g, '♠');
170
+  str = str.replace(/&clubs;/g, '♣');
171
+  str = str.replace(/&hearts;/g, '♥');
172
+
173
+  str = str.replace(/&diams;/g, '♦');
174
+  str = str.replace(/&#39;/g, '\'');
175
+  return str;
176
+}
177
+
178
+function strMoreDiscode(str) {
179
+  str = str.replace(/\r\n/g, '');
180
+  str = str.replace(/\n/g, '');
181
+
182
+  str = str.replace(/code/g, 'wxxxcode-style');
183
+  return str;
184
+}
185
+
186
+function strDiscode(str) {
187
+  str = strNumDiscode(str);
188
+  str = strGreeceDiscode(str);
189
+  str = strcharacterDiscode(str);
190
+  str = strOtherDiscode(str);
191
+  str = strMoreDiscode(str);
192
+  return str;
193
+}
194
+function urlToHttpUrl(url, rep) {
195
+  const patt1 = new RegExp('^//');
196
+  const result = patt1.test(url);
197
+  if (result) {
198
+    url = `${rep}:${url}`;
199
+  }
200
+  return url;
201
+}
202
+
203
+module.exports = {
204
+  strDiscode,
205
+  urlToHttpUrl,
206
+};

+ 159 - 0
src/utils/wxParse/wxParse.js

@@ -0,0 +1,159 @@
1
+/**
2
+ * author: Di (微信小程序开发工程师)
3
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
4
+ *               垂直微信小程序开发交流社区
5
+ *
6
+ * github地址: https://github.com/icindy/wxParse
7
+ *
8
+ * for: 微信小程序富文本解析
9
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
10
+ */
11
+
12
+/**
13
+ * utils函数引入
14
+ * */
15
+import showdown from './showdown.js';
16
+import HtmlToJson from './html2json.js';
17
+/**
18
+ * 配置及公有属性
19
+ * */
20
+let realWindowWidth = 0;
21
+let realWindowHeight = 0;
22
+wx.getSystemInfo({
23
+  success(res) {
24
+    realWindowWidth = res.windowWidth;
25
+    realWindowHeight = res.windowHeight;
26
+  },
27
+});
28
+/**
29
+ * 主函数入口区
30
+ * */
31
+function wxParse(bindName = 'wxParseData', type = 'html', data = '<div class="color:red;">数据不能为空</div>', target, imagePadding) {
32
+  const that = target;
33
+  let transData = {};// 存放转化后的数据
34
+  if (type == 'html') {
35
+    transData = HtmlToJson.html2json(data, bindName);
36
+    console.log(JSON.stringify(transData, ' ', ' '));
37
+  } else if (type == 'md' || type == 'markdown') {
38
+    const converter = new showdown.Converter();
39
+    const html = converter.makeHtml(data);
40
+    transData = HtmlToJson.html2json(html, bindName);
41
+    console.log(JSON.stringify(transData, ' ', ' '));
42
+  }
43
+  transData.view = {};
44
+  transData.view.imagePadding = 0;
45
+  if (typeof (imagePadding) !== 'undefined') {
46
+    transData.view.imagePadding = imagePadding;
47
+  }
48
+  const bindData = {};
49
+  bindData[bindName] = transData;
50
+  that.setData(bindData);
51
+  that.wxParseImgLoad = wxParseImgLoad;
52
+  that.wxParseImgTap = wxParseImgTap;
53
+}
54
+// 图片点击事件
55
+function wxParseImgTap(e) {
56
+  const that = this;
57
+  const nowImgUrl = e.target.dataset.src;
58
+  const tagFrom = e.target.dataset.from;
59
+  if (typeof (tagFrom) !== 'undefined' && tagFrom.length > 0) {
60
+    wx.previewImage({
61
+      current: nowImgUrl, // 当前显示图片的http链接
62
+      urls: that.data[tagFrom].imageUrls, // 需要预览的图片http链接列表
63
+    });
64
+  }
65
+}
66
+
67
+/**
68
+ * 图片视觉宽高计算函数区
69
+ * */
70
+function wxParseImgLoad(e) {
71
+  const that = this;
72
+  const tagFrom = e.target.dataset.from;
73
+  const { idx } = e.target.dataset;
74
+  if (typeof (tagFrom) !== 'undefined' && tagFrom.length > 0) {
75
+    calMoreImageInfo(e, idx, that, tagFrom);
76
+  }
77
+}
78
+// 假循环获取计算图片视觉最佳宽高
79
+function calMoreImageInfo(e, idx, that, bindName) {
80
+  const temData = that.data[bindName];
81
+  if (!temData || temData.images.length == 0) {
82
+    return;
83
+  }
84
+  const temImages = temData.images;
85
+  // 因为无法获取view宽度 需要自定义padding进行计算,稍后处理
86
+  const recal = wxAutoImageCal(e.detail.width, e.detail.height, that, bindName);
87
+  // temImages[idx].width = recal.imageWidth;
88
+  // temImages[idx].height = recal.imageheight;
89
+  // temData.images = temImages;
90
+  // var bindData = {};
91
+  // bindData[bindName] = temData;
92
+  // that.setData(bindData);
93
+  const { index } = temImages[idx];
94
+  let key = `${bindName}`;
95
+  for (const i of index.split('.')) key += `.nodes[${i}]`;
96
+  const keyW = `${key}.width`;
97
+  const keyH = `${key}.height`;
98
+  that.setData({
99
+    [keyW]: recal.imageWidth,
100
+    [keyH]: recal.imageheight,
101
+  });
102
+}
103
+
104
+// 计算视觉优先的图片宽高
105
+function wxAutoImageCal(originalWidth, originalHeight, that, bindName) {
106
+  // 获取图片的原始长宽
107
+  let windowWidth = 0; let
108
+    windowHeight = 0;
109
+  let autoWidth = 0; let
110
+    autoHeight = 0;
111
+  const results = {};
112
+  const padding = that.data[bindName].view.imagePadding;
113
+  windowWidth = realWindowWidth - 2 * padding;
114
+  windowHeight = realWindowHeight;
115
+  // 判断按照那种方式进行缩放
116
+  // console.log("windowWidth" + windowWidth);
117
+  if (originalWidth > windowWidth) { // 在图片width大于手机屏幕width时候
118
+    autoWidth = windowWidth;
119
+    // console.log("autoWidth" + autoWidth);
120
+    autoHeight = (autoWidth * originalHeight) / originalWidth;
121
+    // console.log("autoHeight" + autoHeight);
122
+    results.imageWidth = autoWidth;
123
+    results.imageheight = autoHeight;
124
+  } else { // 否则展示原来的数据
125
+    results.imageWidth = originalWidth;
126
+    results.imageheight = originalHeight;
127
+  }
128
+  return results;
129
+}
130
+
131
+function wxParseTemArray(temArrayName, bindNameReg, total, that) {
132
+  const array = [];
133
+  const temData = that.data;
134
+  let obj = null;
135
+  for (let i = 0; i < total; i++) {
136
+    const simArr = temData[bindNameReg + i].nodes;
137
+    array.push(simArr);
138
+  }
139
+
140
+  temArrayName = temArrayName || 'wxParseTemArray';
141
+  obj = JSON.parse(`{"${temArrayName}":""}`);
142
+  obj[temArrayName] = array;
143
+  that.setData(obj);
144
+}
145
+
146
+/**
147
+ * 配置emojis
148
+ *
149
+ */
150
+
151
+function emojisInit(reg = '', baseSrc = '/wxParse/emojis/', emojis) {
152
+  HtmlToJson.emojisInit(reg, baseSrc, emojis);
153
+}
154
+
155
+module.exports = {
156
+  wxParse,
157
+  wxParseTemArray,
158
+  emojisInit,
159
+};

+ 967 - 0
src/utils/wxParse/wxParse.wxml

@@ -0,0 +1,967 @@
1
+<!--**
2
+ * author: Di (微信小程序开发工程师)
3
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
4
+ *               垂直微信小程序开发交流社区
5
+ * 
6
+ * github地址: https://github.com/icindy/wxParse
7
+ * 
8
+ * for: 微信小程序富文本解析
9
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
10
+ */-->
11
+
12
+<!--基础元素-->
13
+<template name="wxParseVideo">
14
+  <!--增加video标签支持,并循环添加-->
15
+  <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
16
+    <video class="{{item.classStr}} wxParse-{{item.tag}}-video" src="{{item.attr.src}}"></video>
17
+  </view>
18
+</template>
19
+
20
+<template name="wxParseImg">
21
+  <image class="{{item.classStr}} wxParse-{{item.tag}}" data-from="{{item.from}}" data-src="{{item.attr.src}}" data-idx="{{item.imgIndex}}" src="{{item.attr.src}}" bindload="wxParseImgLoad" bindtap="wxParseImgTap" mode="widthFix" style="width:100%;"
22
+  />
23
+</template>
24
+
25
+<template name="WxEmojiView">
26
+  <view class="WxEmojiView wxParse-inline" style="{{item.styleStr}}">
27
+    <block wx:for="{{item.textArray}}" wx:key="">
28
+      <block class="{{item.text == '\\n' ? 'wxParse-hide':''}}" wx:if="{{item.node == 'text'}}">{{item.text}}</block>
29
+      <block wx:elif="{{item.node == 'element'}}">
30
+        <image class="wxEmoji" src="{{item.baseSrc}}{{item.text}}" />
31
+      </block>
32
+    </block>
33
+  </view>
34
+</template>
35
+
36
+<template name="WxParseBr">
37
+  <text>\n</text>
38
+</template>
39
+<!--入口模版-->
40
+
41
+<template name="wxParse">
42
+  <block wx:for="{{wxParseData}}" wx:key="">
43
+    <template is="wxParse0" data="{{item}}" />
44
+  </block>
45
+</template>
46
+
47
+
48
+<!--循环模版-->
49
+<template name="wxParse0">
50
+  <!--<template is="wxParse1" data="{{item}}" />-->
51
+  <!--判断是否是标签节点-->
52
+  <block wx:if="{{item.node == 'element'}}">
53
+    <block wx:if="{{item.tag == 'button'}}">
54
+      <button type="default" size="mini">
55
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
56
+          <template is="wxParse1" data="{{item}}" />
57
+        </block>
58
+      </button>
59
+    </block>
60
+    <!--li类型-->
61
+    <block wx:elif="{{item.tag == 'li'}}">
62
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
63
+        <view class="{{item.classStr}} wxParse-li-inner">
64
+          <view class="{{item.classStr}} wxParse-li-text">
65
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
66
+          </view>
67
+          <view class="{{item.classStr}} wxParse-li-text">
68
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
69
+              <template is="wxParse1" data="{{item}}" />
70
+            </block>
71
+          </view>
72
+        </view>
73
+      </view>
74
+    </block>
75
+
76
+    <!--video类型-->
77
+    <block wx:elif="{{item.tag == 'video'}}">
78
+      <template is="wxParseVideo" data="{{item}}" />
79
+    </block>
80
+
81
+    <!--img类型-->
82
+    <block wx:elif="{{item.tag == 'img'}}">
83
+      <template is="wxParseImg" data="{{item}}" />
84
+    </block>
85
+
86
+    <!--a类型-->
87
+    <block wx:elif="{{item.tag == 'a'}}">
88
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
89
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
90
+          <template is="wxParse1" data="{{item}}" />
91
+        </block>
92
+      </view>
93
+    </block>
94
+    <block wx:elif="{{item.tag == 'table'}}">
95
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
96
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
97
+          <template is="wxParse1" data="{{item}}" />
98
+        </block>
99
+      </view>
100
+    </block>
101
+
102
+    <block wx:elif="{{item.tag == 'br'}}">
103
+      <template is="WxParseBr"></template>
104
+    </block>
105
+    <!--其他块级标签-->
106
+    <block wx:elif="{{item.tagType == 'block'}}">
107
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
108
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
109
+          <template is="wxParse1" data="{{item}}" />
110
+        </block>
111
+      </view>
112
+    </block>
113
+
114
+    <!--内联标签-->
115
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
116
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
117
+        <template is="wxParse1" data="{{item}}" />
118
+      </block>
119
+    </view>
120
+
121
+  </block>
122
+
123
+  <!--判断是否是文本节点-->
124
+  <block wx:elif="{{item.node == 'text'}}">
125
+    <!--如果是,直接进行-->
126
+    <template is="WxEmojiView" data="{{item}}" />
127
+  </block>
128
+
129
+</template>
130
+
131
+
132
+
133
+<!--循环模版-->
134
+<template name="wxParse1">
135
+  <!--<template is="wxParse2" data="{{item}}" />-->
136
+  <!--判断是否是标签节点-->
137
+  <block wx:if="{{item.node == 'element'}}">
138
+    <block wx:if="{{item.tag == 'button'}}">
139
+      <button type="default" size="mini">
140
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
141
+          <template is="wxParse2" data="{{item}}" />
142
+        </block>
143
+      </button>
144
+    </block>
145
+    <!--li类型-->
146
+    <block wx:elif="{{item.tag == 'li'}}">
147
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
148
+        <view class="{{item.classStr}} wxParse-li-inner">
149
+          <view class="{{item.classStr}} wxParse-li-text">
150
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
151
+          </view>
152
+          <view class="{{item.classStr}} wxParse-li-text">
153
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
154
+              <template is="wxParse2" data="{{item}}" />
155
+            </block>
156
+          </view>
157
+        </view>
158
+      </view>
159
+    </block>
160
+
161
+    <!--video类型-->
162
+    <block wx:elif="{{item.tag == 'video'}}">
163
+      <template is="wxParseVideo" data="{{item}}" />
164
+    </block>
165
+
166
+    <!--img类型-->
167
+    <block wx:elif="{{item.tag == 'img'}}">
168
+      <template is="wxParseImg" data="{{item}}" />
169
+    </block>
170
+
171
+    <!--a类型-->
172
+    <block wx:elif="{{item.tag == 'a'}}">
173
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
174
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
175
+          <template is="wxParse2" data="{{item}}" />
176
+        </block>
177
+      </view>
178
+    </block>
179
+
180
+    <block wx:elif="{{item.tag == 'br'}}">
181
+      <template is="WxParseBr"></template>
182
+    </block>
183
+    <!--其他块级标签-->
184
+    <block wx:elif="{{item.tagType == 'block'}}">
185
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
186
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
187
+          <template is="wxParse2" data="{{item}}" />
188
+        </block>
189
+      </view>
190
+    </block>
191
+
192
+    <!--内联标签-->
193
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
194
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
195
+        <template is="wxParse2" data="{{item}}" />
196
+      </block>
197
+    </view>
198
+
199
+  </block>
200
+
201
+  <!--判断是否是文本节点-->
202
+  <block wx:elif="{{item.node == 'text'}}">
203
+    <!--如果是,直接进行-->
204
+    <template is="WxEmojiView" data="{{item}}" />
205
+  </block>
206
+
207
+</template>
208
+
209
+
210
+<!--循环模版-->
211
+<template name="wxParse2">
212
+  <!--<template is="wxParse3" data="{{item}}" />-->
213
+  <!--判断是否是标签节点-->
214
+  <block wx:if="{{item.node == 'element'}}">
215
+    <block wx:if="{{item.tag == 'button'}}">
216
+      <button type="default" size="mini">
217
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
218
+          <template is="wxParse3" data="{{item}}" />
219
+        </block>
220
+      </button>
221
+    </block>
222
+    <!--li类型-->
223
+    <block wx:elif="{{item.tag == 'li'}}">
224
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
225
+        <view class="{{item.classStr}} wxParse-li-inner">
226
+          <view class="{{item.classStr}} wxParse-li-text">
227
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
228
+          </view>
229
+          <view class="{{item.classStr}} wxParse-li-text">
230
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
231
+              <template is="wxParse3" data="{{item}}" />
232
+            </block>
233
+          </view>
234
+        </view>
235
+      </view>
236
+    </block>
237
+
238
+    <!--video类型-->
239
+    <block wx:elif="{{item.tag == 'video'}}">
240
+      <template is="wxParseVideo" data="{{item}}" />
241
+    </block>
242
+
243
+    <!--img类型-->
244
+    <block wx:elif="{{item.tag == 'img'}}">
245
+      <template is="wxParseImg" data="{{item}}" />
246
+    </block>
247
+
248
+    <!--a类型-->
249
+    <block wx:elif="{{item.tag == 'a'}}">
250
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
251
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
252
+          <template is="wxParse3" data="{{item}}" />
253
+        </block>
254
+      </view>
255
+    </block>
256
+
257
+    <block wx:elif="{{item.tag == 'br'}}">
258
+      <template is="WxParseBr"></template>
259
+    </block>
260
+    <!--其他块级标签-->
261
+    <block wx:elif="{{item.tagType == 'block'}}">
262
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
263
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
264
+          <template is="wxParse3" data="{{item}}" />
265
+        </block>
266
+      </view>
267
+    </block>
268
+
269
+    <!--内联标签-->
270
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
271
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
272
+        <template is="wxParse3" data="{{item}}" />
273
+      </block>
274
+    </view>
275
+
276
+  </block>
277
+
278
+  <!--判断是否是文本节点-->
279
+  <block wx:elif="{{item.node == 'text'}}">
280
+    <!--如果是,直接进行-->
281
+    <template is="WxEmojiView" data="{{item}}" />
282
+  </block>
283
+
284
+</template>
285
+
286
+<!--循环模版-->
287
+<template name="wxParse3">
288
+  <!--<template is="wxParse4" data="{{item}}" />-->
289
+  <!--判断是否是标签节点-->
290
+  <block wx:if="{{item.node == 'element'}}">
291
+    <block wx:if="{{item.tag == 'button'}}">
292
+      <button type="default" size="mini">
293
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
294
+          <template is="wxParse4" data="{{item}}" />
295
+        </block>
296
+      </button>
297
+    </block>
298
+    <!--li类型-->
299
+    <block wx:elif="{{item.tag == 'li'}}">
300
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
301
+        <view class="{{item.classStr}} wxParse-li-inner">
302
+          <view class="{{item.classStr}} wxParse-li-text">
303
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
304
+          </view>
305
+          <view class="{{item.classStr}} wxParse-li-text">
306
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
307
+              <template is="wxParse4" data="{{item}}" />
308
+            </block>
309
+          </view>
310
+        </view>
311
+      </view>
312
+    </block>
313
+
314
+    <!--video类型-->
315
+    <block wx:elif="{{item.tag == 'video'}}">
316
+      <template is="wxParseVideo" data="{{item}}" />
317
+    </block>
318
+
319
+    <!--img类型-->
320
+    <block wx:elif="{{item.tag == 'img'}}">
321
+      <template is="wxParseImg" data="{{item}}" />
322
+    </block>
323
+
324
+    <!--a类型-->
325
+    <block wx:elif="{{item.tag == 'a'}}">
326
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
327
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
328
+          <template is="wxParse4" data="{{item}}" />
329
+        </block>
330
+      </view>
331
+    </block>
332
+
333
+    <block wx:elif="{{item.tag == 'br'}}">
334
+      <template is="WxParseBr"></template>
335
+    </block>
336
+    <!--其他块级标签-->
337
+    <block wx:elif="{{item.tagType == 'block'}}">
338
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
339
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
340
+          <template is="wxParse4" data="{{item}}" />
341
+        </block>
342
+      </view>
343
+    </block>
344
+
345
+    <!--内联标签-->
346
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
347
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
348
+        <template is="wxParse4" data="{{item}}" />
349
+      </block>
350
+    </view>
351
+
352
+  </block>
353
+
354
+  <!--判断是否是文本节点-->
355
+  <block wx:elif="{{item.node == 'text'}}">
356
+    <!--如果是,直接进行-->
357
+    <template is="WxEmojiView" data="{{item}}" />
358
+  </block>
359
+
360
+</template>
361
+
362
+<!--循环模版-->
363
+<template name="wxParse4">
364
+  <!--<template is="wxParse5" data="{{item}}" />-->
365
+  <!--判断是否是标签节点-->
366
+  <block wx:if="{{item.node == 'element'}}">
367
+    <block wx:if="{{item.tag == 'button'}}">
368
+      <button type="default" size="mini">
369
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
370
+          <template is="wxParse5" data="{{item}}" />
371
+        </block>
372
+      </button>
373
+    </block>
374
+    <!--li类型-->
375
+    <block wx:elif="{{item.tag == 'li'}}">
376
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
377
+        <view class="{{item.classStr}} wxParse-li-inner">
378
+          <view class="{{item.classStr}} wxParse-li-text">
379
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
380
+          </view>
381
+          <view class="{{item.classStr}} wxParse-li-text">
382
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
383
+              <template is="wxParse5" data="{{item}}" />
384
+            </block>
385
+          </view>
386
+        </view>
387
+      </view>
388
+    </block>
389
+
390
+    <!--video类型-->
391
+    <block wx:elif="{{item.tag == 'video'}}">
392
+      <template is="wxParseVideo" data="{{item}}" />
393
+    </block>
394
+
395
+    <!--img类型-->
396
+    <block wx:elif="{{item.tag == 'img'}}">
397
+      <template is="wxParseImg" data="{{item}}" />
398
+    </block>
399
+
400
+    <!--a类型-->
401
+    <block wx:elif="{{item.tag == 'a'}}">
402
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
403
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
404
+          <template is="wxParse5" data="{{item}}" />
405
+        </block>
406
+      </view>
407
+    </block>
408
+
409
+    <block wx:elif="{{item.tag == 'br'}}">
410
+      <template is="WxParseBr"></template>
411
+    </block>
412
+    <!--其他块级标签-->
413
+    <block wx:elif="{{item.tagType == 'block'}}">
414
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
415
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
416
+          <template is="wxParse5" data="{{item}}" />
417
+        </block>
418
+      </view>
419
+    </block>
420
+
421
+    <!--内联标签-->
422
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
423
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
424
+        <template is="wxParse5" data="{{item}}" />
425
+      </block>
426
+    </view>
427
+
428
+  </block>
429
+
430
+  <!--判断是否是文本节点-->
431
+  <block wx:elif="{{item.node == 'text'}}">
432
+    <!--如果是,直接进行-->
433
+    <template is="WxEmojiView" data="{{item}}" />
434
+  </block>
435
+
436
+</template>
437
+
438
+<!--循环模版-->
439
+<template name="wxParse5">
440
+  <!--<template is="wxParse6" data="{{item}}" />-->
441
+  <!--判断是否是标签节点-->
442
+  <block wx:if="{{item.node == 'element'}}">
443
+    <block wx:if="{{item.tag == 'button'}}">
444
+      <button type="default" size="mini">
445
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
446
+          <template is="wxParse6" data="{{item}}" />
447
+        </block>
448
+      </button>
449
+    </block>
450
+    <!--li类型-->
451
+    <block wx:elif="{{item.tag == 'li'}}">
452
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
453
+        <view class="{{item.classStr}} wxParse-li-inner">
454
+          <view class="{{item.classStr}} wxParse-li-text">
455
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
456
+          </view>
457
+          <view class="{{item.classStr}} wxParse-li-text">
458
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
459
+              <template is="wxParse6" data="{{item}}" />
460
+            </block>
461
+          </view>
462
+        </view>
463
+      </view>
464
+    </block>
465
+
466
+    <!--video类型-->
467
+    <block wx:elif="{{item.tag == 'video'}}">
468
+      <template is="wxParseVideo" data="{{item}}" />
469
+    </block>
470
+
471
+    <!--img类型-->
472
+    <block wx:elif="{{item.tag == 'img'}}">
473
+      <template is="wxParseImg" data="{{item}}" />
474
+    </block>
475
+
476
+    <!--a类型-->
477
+    <block wx:elif="{{item.tag == 'a'}}">
478
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
479
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
480
+          <template is="wxParse6" data="{{item}}" />
481
+        </block>
482
+      </view>
483
+    </block>
484
+
485
+    <block wx:elif="{{item.tag == 'br'}}">
486
+      <template is="WxParseBr"></template>
487
+    </block>
488
+    <!--其他块级标签-->
489
+    <block wx:elif="{{item.tagType == 'block'}}">
490
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
491
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
492
+          <template is="wxParse6" data="{{item}}" />
493
+        </block>
494
+      </view>
495
+    </block>
496
+
497
+    <!--内联标签-->
498
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
499
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
500
+        <template is="wxParse6" data="{{item}}" />
501
+      </block>
502
+    </view>
503
+
504
+  </block>
505
+
506
+  <!--判断是否是文本节点-->
507
+  <block wx:elif="{{item.node == 'text'}}">
508
+    <!--如果是,直接进行-->
509
+    <template is="WxEmojiView" data="{{item}}" />
510
+  </block>
511
+
512
+</template>
513
+
514
+<!--循环模版-->
515
+<template name="wxParse6">
516
+  <!--<template is="wxParse7" data="{{item}}" />-->
517
+  <!--判断是否是标签节点-->
518
+  <block wx:if="{{item.node == 'element'}}">
519
+    <block wx:if="{{item.tag == 'button'}}">
520
+      <button type="default" size="mini">
521
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
522
+          <template is="wxParse7" data="{{item}}" />
523
+        </block>
524
+      </button>
525
+    </block>
526
+    <!--li类型-->
527
+    <block wx:elif="{{item.tag == 'li'}}">
528
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
529
+        <view class="{{item.classStr}} wxParse-li-inner">
530
+          <view class="{{item.classStr}} wxParse-li-text">
531
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
532
+          </view>
533
+          <view class="{{item.classStr}} wxParse-li-text">
534
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
535
+              <template is="wxParse7" data="{{item}}" />
536
+            </block>
537
+          </view>
538
+        </view>
539
+      </view>
540
+    </block>
541
+
542
+    <!--video类型-->
543
+    <block wx:elif="{{item.tag == 'video'}}">
544
+      <template is="wxParseVideo" data="{{item}}" />
545
+    </block>
546
+
547
+    <!--img类型-->
548
+    <block wx:elif="{{item.tag == 'img'}}">
549
+      <template is="wxParseImg" data="{{item}}" />
550
+    </block>
551
+
552
+    <!--a类型-->
553
+    <block wx:elif="{{item.tag == 'a'}}">
554
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
555
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
556
+          <template is="wxParse7" data="{{item}}" />
557
+        </block>
558
+      </view>
559
+    </block>
560
+
561
+    <block wx:elif="{{item.tag == 'br'}}">
562
+      <template is="WxParseBr"></template>
563
+    </block>
564
+    <!--其他块级标签-->
565
+    <block wx:elif="{{item.tagType == 'block'}}">
566
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
567
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
568
+          <template is="wxParse7" data="{{item}}" />
569
+        </block>
570
+      </view>
571
+    </block>
572
+
573
+    <!--内联标签-->
574
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
575
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
576
+        <template is="wxParse7" data="{{item}}" />
577
+      </block>
578
+    </view>
579
+
580
+  </block>
581
+
582
+  <!--判断是否是文本节点-->
583
+  <block wx:elif="{{item.node == 'text'}}">
584
+    <!--如果是,直接进行-->
585
+    <template is="WxEmojiView" data="{{item}}" />
586
+  </block>
587
+
588
+</template>
589
+<!--循环模版-->
590
+<template name="wxParse7">
591
+  <!--<template is="wxParse8" data="{{item}}" />-->
592
+  <!--判断是否是标签节点-->
593
+  <block wx:if="{{item.node == 'element'}}">
594
+    <block wx:if="{{item.tag == 'button'}}">
595
+      <button type="default" size="mini">
596
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
597
+          <template is="wxParse8" data="{{item}}" />
598
+        </block>
599
+      </button>
600
+    </block>
601
+    <!--li类型-->
602
+    <block wx:elif="{{item.tag == 'li'}}">
603
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
604
+        <view class="{{item.classStr}} wxParse-li-inner">
605
+          <view class="{{item.classStr}} wxParse-li-text">
606
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
607
+          </view>
608
+          <view class="{{item.classStr}} wxParse-li-text">
609
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
610
+              <template is="wxParse8" data="{{item}}" />
611
+            </block>
612
+          </view>
613
+        </view>
614
+      </view>
615
+    </block>
616
+
617
+    <!--video类型-->
618
+    <block wx:elif="{{item.tag == 'video'}}">
619
+      <template is="wxParseVideo" data="{{item}}" />
620
+    </block>
621
+
622
+    <!--img类型-->
623
+    <block wx:elif="{{item.tag == 'img'}}">
624
+      <template is="wxParseImg" data="{{item}}" />
625
+    </block>
626
+
627
+    <!--a类型-->
628
+    <block wx:elif="{{item.tag == 'a'}}">
629
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
630
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
631
+          <template is="wxParse8" data="{{item}}" />
632
+        </block>
633
+      </view>
634
+    </block>
635
+
636
+    <block wx:elif="{{item.tag == 'br'}}">
637
+      <template is="WxParseBr"></template>
638
+    </block>
639
+    <!--其他块级标签-->
640
+    <block wx:elif="{{item.tagType == 'block'}}">
641
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
642
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
643
+          <template is="wxParse8" data="{{item}}" />
644
+        </block>
645
+      </view>
646
+    </block>
647
+
648
+    <!--内联标签-->
649
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
650
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
651
+        <template is="wxParse8" data="{{item}}" />
652
+      </block>
653
+    </view>
654
+
655
+  </block>
656
+
657
+  <!--判断是否是文本节点-->
658
+  <block wx:elif="{{item.node == 'text'}}">
659
+    <!--如果是,直接进行-->
660
+    <template is="WxEmojiView" data="{{item}}" />
661
+  </block>
662
+
663
+</template>
664
+
665
+<!--循环模版-->
666
+<template name="wxParse8">
667
+  <!--<template is="wxParse9" data="{{item}}" />-->
668
+  <!--判断是否是标签节点-->
669
+  <block wx:if="{{item.node == 'element'}}">
670
+    <block wx:if="{{item.tag == 'button'}}">
671
+      <button type="default" size="mini">
672
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
673
+          <template is="wxParse9" data="{{item}}" />
674
+        </block>
675
+      </button>
676
+    </block>
677
+    <!--li类型-->
678
+    <block wx:elif="{{item.tag == 'li'}}">
679
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
680
+        <view class="{{item.classStr}} wxParse-li-inner">
681
+          <view class="{{item.classStr}} wxParse-li-text">
682
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
683
+          </view>
684
+          <view class="{{item.classStr}} wxParse-li-text">
685
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
686
+              <template is="wxParse9" data="{{item}}" />
687
+            </block>
688
+          </view>
689
+        </view>
690
+      </view>
691
+    </block>
692
+
693
+    <!--video类型-->
694
+    <block wx:elif="{{item.tag == 'video'}}">
695
+      <template is="wxParseVideo" data="{{item}}" />
696
+    </block>
697
+
698
+    <!--img类型-->
699
+    <block wx:elif="{{item.tag == 'img'}}">
700
+      <template is="wxParseImg" data="{{item}}" />
701
+    </block>
702
+
703
+    <!--a类型-->
704
+    <block wx:elif="{{item.tag == 'a'}}">
705
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
706
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
707
+          <template is="wxParse9" data="{{item}}" />
708
+        </block>
709
+      </view>
710
+    </block>
711
+
712
+    <block wx:elif="{{item.tag == 'br'}}">
713
+      <template is="WxParseBr"></template>
714
+    </block>
715
+    <!--其他块级标签-->
716
+    <block wx:elif="{{item.tagType == 'block'}}">
717
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
718
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
719
+          <template is="wxParse9" data="{{item}}" />
720
+        </block>
721
+      </view>
722
+    </block>
723
+
724
+    <!--内联标签-->
725
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
726
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
727
+        <template is="wxParse9" data="{{item}}" />
728
+      </block>
729
+    </view>
730
+
731
+  </block>
732
+
733
+  <!--判断是否是文本节点-->
734
+  <block wx:elif="{{item.node == 'text'}}">
735
+    <!--如果是,直接进行-->
736
+    <template is="WxEmojiView" data="{{item}}" />
737
+  </block>
738
+
739
+</template>
740
+
741
+<!--循环模版-->
742
+<template name="wxParse9">
743
+  <!--<template is="wxParse10" data="{{item}}" />-->
744
+  <!--判断是否是标签节点-->
745
+  <block wx:if="{{item.node == 'element'}}">
746
+    <block wx:if="{{item.tag == 'button'}}">
747
+      <button type="default" size="mini">
748
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
749
+          <template is="wxParse10" data="{{item}}" />
750
+        </block>
751
+      </button>
752
+    </block>
753
+    <!--li类型-->
754
+    <block wx:elif="{{item.tag == 'li'}}">
755
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
756
+        <view class="{{item.classStr}} wxParse-li-inner">
757
+          <view class="{{item.classStr}} wxParse-li-text">
758
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
759
+          </view>
760
+          <view class="{{item.classStr}} wxParse-li-text">
761
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
762
+              <template is="wxParse10" data="{{item}}" />
763
+            </block>
764
+          </view>
765
+        </view>
766
+      </view>
767
+    </block>
768
+
769
+    <!--video类型-->
770
+    <block wx:elif="{{item.tag == 'video'}}">
771
+      <template is="wxParseVideo" data="{{item}}" />
772
+    </block>
773
+
774
+    <!--img类型-->
775
+    <block wx:elif="{{item.tag == 'img'}}">
776
+      <template is="wxParseImg" data="{{item}}" />
777
+    </block>
778
+
779
+    <!--a类型-->
780
+    <block wx:elif="{{item.tag == 'a'}}">
781
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
782
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
783
+          <template is="wxParse10" data="{{item}}" />
784
+        </block>
785
+      </view>
786
+    </block>
787
+
788
+    <block wx:elif="{{item.tag == 'br'}}">
789
+      <template is="WxParseBr"></template>
790
+    </block>
791
+    <!--其他块级标签-->
792
+    <block wx:elif="{{item.tagType == 'block'}}">
793
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
794
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
795
+          <template is="wxParse10" data="{{item}}" />
796
+        </block>
797
+      </view>
798
+    </block>
799
+
800
+    <!--内联标签-->
801
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
802
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
803
+        <template is="wxParse10" data="{{item}}" />
804
+      </block>
805
+    </view>
806
+
807
+  </block>
808
+
809
+  <!--判断是否是文本节点-->
810
+  <block wx:elif="{{item.node == 'text'}}">
811
+    <!--如果是,直接进行-->
812
+    <template is="WxEmojiView" data="{{item}}" />
813
+  </block>
814
+
815
+</template>
816
+
817
+<!--循环模版-->
818
+<template name="wxParse10">
819
+  <!--<template is="wxParse11" data="{{item}}" />-->
820
+  <!--判断是否是标签节点-->
821
+  <block wx:if="{{item.node == 'element'}}">
822
+    <block wx:if="{{item.tag == 'button'}}">
823
+      <button type="default" size="mini">
824
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
825
+          <template is="wxParse11" data="{{item}}" />
826
+        </block>
827
+      </button>
828
+    </block>
829
+    <!--li类型-->
830
+    <block wx:elif="{{item.tag == 'li'}}">
831
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
832
+        <view class="{{item.classStr}} wxParse-li-inner">
833
+          <view class="{{item.classStr}} wxParse-li-text">
834
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
835
+          </view>
836
+          <view class="{{item.classStr}} wxParse-li-text">
837
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
838
+              <template is="wxParse11" data="{{item}}" />
839
+            </block>
840
+          </view>
841
+        </view>
842
+      </view>
843
+    </block>
844
+
845
+    <!--video类型-->
846
+    <block wx:elif="{{item.tag == 'video'}}">
847
+      <template is="wxParseVideo" data="{{item}}" />
848
+    </block>
849
+
850
+    <!--img类型-->
851
+    <block wx:elif="{{item.tag == 'img'}}">
852
+      <template is="wxParseImg" data="{{item}}" />
853
+    </block>
854
+
855
+    <!--a类型-->
856
+    <block wx:elif="{{item.tag == 'a'}}">
857
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
858
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
859
+          <template is="wxParse11" data="{{item}}" />
860
+        </block>
861
+      </view>
862
+    </block>
863
+
864
+    <block wx:elif="{{item.tag == 'br'}}">
865
+      <template is="WxParseBr"></template>
866
+    </block>
867
+    <!--其他块级标签-->
868
+    <block wx:elif="{{item.tagType == 'block'}}">
869
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
870
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
871
+          <template is="wxParse11" data="{{item}}" />
872
+        </block>
873
+      </view>
874
+    </block>
875
+
876
+    <!--内联标签-->
877
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
878
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
879
+        <template is="wxParse11" data="{{item}}" />
880
+      </block>
881
+    </view>
882
+
883
+  </block>
884
+
885
+  <!--判断是否是文本节点-->
886
+  <block wx:elif="{{item.node == 'text'}}">
887
+    <!--如果是,直接进行-->
888
+    <template is="WxEmojiView" data="{{item}}" />
889
+  </block>
890
+
891
+</template>
892
+
893
+<!--循环模版-->
894
+<template name="wxParse11">
895
+  <!--<template is="wxParse12" data="{{item}}" />-->
896
+  <!--判断是否是标签节点-->
897
+  <block wx:if="{{item.node == 'element'}}">
898
+    <block wx:if="{{item.tag == 'button'}}">
899
+      <button type="default" size="mini">
900
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
901
+          <template is="wxParse12" data="{{item}}" />
902
+        </block>
903
+      </button>
904
+    </block>
905
+    <!--li类型-->
906
+    <block wx:elif="{{item.tag == 'li'}}">
907
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
908
+        <view class="{{item.classStr}} wxParse-li-inner">
909
+          <view class="{{item.classStr}} wxParse-li-text">
910
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
911
+          </view>
912
+          <view class="{{item.classStr}} wxParse-li-text">
913
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
914
+              <template is="wxParse12" data="{{item}}" />
915
+            </block>
916
+          </view>
917
+        </view>
918
+      </view>
919
+    </block>
920
+
921
+    <!--video类型-->
922
+    <block wx:elif="{{item.tag == 'video'}}">
923
+      <template is="wxParseVideo" data="{{item}}" />
924
+    </block>
925
+
926
+    <!--img类型-->
927
+    <block wx:elif="{{item.tag == 'img'}}">
928
+      <template is="wxParseImg" data="{{item}}" />
929
+    </block>
930
+
931
+    <!--a类型-->
932
+    <block wx:elif="{{item.tag == 'a'}}">
933
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
934
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
935
+          <template is="wxParse12" data="{{item}}" />
936
+        </block>
937
+      </view>
938
+    </block>
939
+
940
+    <block wx:elif="{{item.tag == 'br'}}">
941
+      <template is="WxParseBr"></template>
942
+    </block>
943
+    <!--其他块级标签-->
944
+    <block wx:elif="{{item.tagType == 'block'}}">
945
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
946
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
947
+          <template is="wxParse12" data="{{item}}" />
948
+        </block>
949
+      </view>
950
+    </block>
951
+
952
+    <!--内联标签-->
953
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
954
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
955
+        <template is="wxParse12" data="{{item}}" />
956
+      </block>
957
+    </view>
958
+
959
+  </block>
960
+
961
+  <!--判断是否是文本节点-->
962
+  <block wx:elif="{{item.node == 'text'}}">
963
+    <!--如果是,直接进行-->
964
+    <template is="WxEmojiView" data="{{item}}" />
965
+  </block>
966
+
967
+</template>

+ 270 - 0
src/utils/wxParse/wxParse.wxss

@@ -0,0 +1,270 @@
1
+/**
2
+ * author: Di (微信小程序开发工程师)
3
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
4
+ *               垂直微信小程序开发交流社区
5
+ * 
6
+ * github地址: https://github.com/icindy/wxParse
7
+ * 
8
+ * for: 微信小程序富文本解析
9
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
10
+ */
11
+
12
+.wxParse {
13
+  margin: 0 5px;
14
+  font-family: Helvetica, sans-serif;
15
+  font-size: 28rpx;
16
+  color: #666;
17
+  line-height: 1.8;
18
+}
19
+view {
20
+  word-break: break-all;
21
+}
22
+.wxParse-inline {
23
+  display: inline;
24
+  margin: 0;
25
+  padding: 0;
26
+}
27
+/*//标题 */
28
+.wxParse-div {
29
+  margin: 0;
30
+  padding: 0;
31
+}
32
+.wxParse-h1 {
33
+  font-size: 2em;
34
+  margin: 0.67em 0;
35
+}
36
+.wxParse-h2 {
37
+  font-size: 1.5em;
38
+  margin: 0.75em 0;
39
+}
40
+.wxParse-h3 {
41
+  font-size: 1.17em;
42
+  margin: 0.83em 0;
43
+}
44
+.wxParse-h4 {
45
+  margin: 1.12em 0;
46
+}
47
+.wxParse-h5 {
48
+  font-size: 0.83em;
49
+  margin: 1.5em 0;
50
+}
51
+.wxParse-h6 {
52
+  font-size: 0.75em;
53
+  margin: 1.67em 0;
54
+}
55
+
56
+.wxParse-h1 {
57
+  font-size: 18px;
58
+  font-weight: 400;
59
+  margin-bottom: 0.9em;
60
+}
61
+.wxParse-h2 {
62
+  font-size: 16px;
63
+  font-weight: 400;
64
+  margin-bottom: 0.34em;
65
+}
66
+.wxParse-h3 {
67
+  font-weight: 400;
68
+  font-size: 15px;
69
+  margin-bottom: 0.34em;
70
+}
71
+.wxParse-h4 {
72
+  font-weight: 400;
73
+  font-size: 14px;
74
+  margin-bottom: 0.24em;
75
+}
76
+.wxParse-h5 {
77
+  font-weight: 400;
78
+  font-size: 13px;
79
+  margin-bottom: 0.14em;
80
+}
81
+.wxParse-h6 {
82
+  font-weight: 400;
83
+  font-size: 12px;
84
+  margin-bottom: 0.04em;
85
+}
86
+
87
+.wxParse-h1,
88
+.wxParse-h2,
89
+.wxParse-h3,
90
+.wxParse-h4,
91
+.wxParse-h5,
92
+.wxParse-h6,
93
+.wxParse-b,
94
+.wxParse-strong {
95
+  font-weight: bolder;
96
+}
97
+
98
+.wxParse-i,
99
+.wxParse-cite,
100
+.wxParse-em,
101
+.wxParse-var,
102
+.wxParse-address {
103
+  font-style: italic;
104
+}
105
+.wxParse-pre,
106
+.wxParse-tt,
107
+.wxParse-code,
108
+.wxParse-kbd,
109
+.wxParse-samp {
110
+  font-family: monospace;
111
+}
112
+.wxParse-pre {
113
+  white-space: pre;
114
+}
115
+.wxParse-big {
116
+  font-size: 1.17em;
117
+}
118
+.wxParse-small,
119
+.wxParse-sub,
120
+.wxParse-sup {
121
+  font-size: 0.83em;
122
+}
123
+.wxParse-sub {
124
+  vertical-align: sub;
125
+}
126
+.wxParse-sup {
127
+  vertical-align: super;
128
+}
129
+.wxParse-s,
130
+.wxParse-strike,
131
+.wxParse-del {
132
+  text-decoration: line-through;
133
+}
134
+/*wxparse-自定义个性化的css样式*/
135
+/*增加video的css样式*/
136
+.wxParse-strong,
137
+.wxParse-s {
138
+  display: inline;
139
+}
140
+.wxParse-a {
141
+  color: #576b95;
142
+  text-decoration: underline;
143
+  word-break: break-all;
144
+  overflow: auto;
145
+}
146
+
147
+.wxParse-a-hover {
148
+  opacity: 0.8;
149
+}
150
+
151
+.wxParse-video {
152
+  text-align: center;
153
+  margin: 10px 0;
154
+}
155
+
156
+.wxParse-video-video {
157
+  width: 100%;
158
+}
159
+
160
+.wxParse-img {
161
+  /*background-color: #efefef;*/
162
+  overflow: hidden;
163
+}
164
+
165
+.wxParse-blockquote {
166
+  margin: 0;
167
+  padding: 10px 0 10px 5px;
168
+  font-family: Courier, Calibri, '宋体';
169
+  background: #f5f5f5;
170
+  border-left: 3px solid #dbdbdb;
171
+}
172
+
173
+.wxParse-code,
174
+.wxParse-wxxxcode-style {
175
+  display: inline;
176
+  background: #f5f5f5;
177
+}
178
+.wxParse-ul {
179
+  margin: 20rpx 10rpx;
180
+}
181
+
182
+.wxParse-li,
183
+.wxParse-li-inner {
184
+  display: flex;
185
+  align-items: baseline;
186
+}
187
+.wxParse-li-text {
188
+  align-items: center;
189
+}
190
+
191
+.wxParse-li-circle {
192
+  display: inline-flex;
193
+  width: 6px;
194
+  height: 6px;
195
+  border-radius: 3px;
196
+  background-color: #333;
197
+  margin-right: 5px;
198
+}
199
+
200
+.wxParse-li-square {
201
+  display: inline-flex;
202
+  width: 10rpx;
203
+  height: 10rpx;
204
+  background-color: #333;
205
+  margin-right: 5px;
206
+}
207
+.wxParse-li-ring {
208
+  display: inline-flex;
209
+  width: 10rpx;
210
+  height: 10rpx;
211
+  border: 2rpx solid #333;
212
+  border-radius: 50%;
213
+  background-color: #fff;
214
+  margin-right: 5px;
215
+}
216
+
217
+/*.wxParse-table{
218
+    width: 100%;
219
+    height: 400px;
220
+}
221
+.wxParse-thead,.wxParse-tfoot,.wxParse-tr{
222
+    display: flex;
223
+    flex-direction: row;
224
+}
225
+.wxParse-th,.wxParse-td{
226
+    display: flex;
227
+    width: 580px;
228
+    overflow: auto;
229
+}*/
230
+
231
+.wxParse-u {
232
+  text-decoration: underline;
233
+}
234
+.wxParse-hide {
235
+  display: none;
236
+}
237
+.WxEmojiView {
238
+  align-items: center;
239
+}
240
+.wxEmoji {
241
+  width: 16px;
242
+  height: 16px;
243
+}
244
+.wxParse-tr {
245
+  display: flex;
246
+  border-right: 1px solid #e0e0e0;
247
+  border-bottom: 1px solid #e0e0e0;
248
+  border-top: 1px solid #e0e0e0;
249
+}
250
+.wxParse-th,
251
+.wxParse-td {
252
+  flex: 1;
253
+  padding: 5px;
254
+  font-size: 28rpx;
255
+  border-left: 1px solid #e0e0e0;
256
+  word-break: break-all;
257
+}
258
+.wxParse-td:last {
259
+  border-top: 1px solid #e0e0e0;
260
+}
261
+.wxParse-th {
262
+  background: #f0f0f0;
263
+  border-top: 1px solid #e0e0e0;
264
+}
265
+.wxParse-del {
266
+  display: inline;
267
+}
268
+.wxParse-figure {
269
+  overflow: hidden;
270
+}

+ 10 - 0
src/wxs/stringFilter.wxs

@@ -0,0 +1,10 @@
1
+var filter = function (text) {
2
+  if (text) {
3
+    var pattern = '\\\\n';
4
+    var target = '\n';
5
+    var reg = getRegExp(pattern, 'g');
6
+    return text.replace(reg, target);
7
+  }
8
+};
9
+
10
+module.exports.filter = filter;