Browse Source

医美小程序第一版

yuhao 1 year ago
commit
b2629ff52d
100 changed files with 10432 additions and 0 deletions
  1. 16 0
      .gitignore
  2. 2 0
      .vscode/settings.json
  3. 20 0
      README.md
  4. 253 0
      app.js
  5. 168 0
      app.json
  6. 28 0
      app.wxs
  7. 147 0
      app.wxss
  8. 382 0
      components/dist/animation-group/index.js
  9. 3 0
      components/dist/animation-group/index.json
  10. 3 0
      components/dist/animation-group/index.wxml
  11. 206 0
      components/dist/animation-group/index.wxss
  12. 62 0
      components/dist/backdrop/index.js
  13. 6 0
      components/dist/backdrop/index.json
  14. 1 0
      components/dist/backdrop/index.wxml
  15. 15 0
      components/dist/backdrop/index.wxss
  16. 199 0
      components/dist/circle/index.js
  17. 3 0
      components/dist/circle/index.json
  18. 6 0
      components/dist/circle/index.wxml
  19. 13 0
      components/dist/circle/index.wxss
  20. 176 0
      components/dist/countdown/index.js
  21. 220 0
      components/dist/countup/index.js
  22. 24 0
      components/dist/helpers/arrayTreeFilter.js
  23. 77 0
      components/dist/helpers/baseComponent.js
  24. 29 0
      components/dist/helpers/checkIPhoneX.js
  25. 39 0
      components/dist/helpers/classNames.js
  26. 27 0
      components/dist/helpers/colors.js
  27. 27 0
      components/dist/helpers/compareVersion.js
  28. 48 0
      components/dist/helpers/computedBehavior.js
  29. 75 0
      components/dist/helpers/createFieldsStore.js
  30. 56 0
      components/dist/helpers/debounce.js
  31. 53 0
      components/dist/helpers/eventsMixin.js
  32. 97 0
      components/dist/helpers/funcBehavior.js
  33. 50 0
      components/dist/helpers/gestures.js
  34. 19 0
      components/dist/helpers/isEmpty.js
  35. 17 0
      components/dist/helpers/mergeOptionsToData.js
  36. 314 0
      components/dist/helpers/popupMixin.js
  37. 67 0
      components/dist/helpers/relationsBehavior.js
  38. 46 0
      components/dist/helpers/safeAreaBehavior.js
  39. 57 0
      components/dist/helpers/safeSetDataBehavior.js
  40. 65 0
      components/dist/helpers/shallowEqual.js
  41. 139 0
      components/dist/helpers/styleToCssString.js
  42. 67 0
      components/dist/index.js
  43. 66 0
      components/dist/landscape/index.js
  44. 6 0
      components/dist/landscape/index.json
  45. 22 0
      components/dist/landscape/index.wxml
  46. 36 0
      components/dist/landscape/index.wxss
  47. 194 0
      components/dist/popup/index.js
  48. 7 0
      components/dist/popup/index.json
  49. 26 0
      components/dist/popup/index.wxml
  50. 121 0
      components/dist/popup/index.wxss
  51. 103 0
      components/dist/progress/index.js
  52. 3 0
      components/dist/progress/index.json
  53. 8 0
      components/dist/progress/index.wxml
  54. 51 0
      components/dist/progress/index.wxss
  55. 203 0
      components/dist/search-bar/index.js
  56. 3 0
      components/dist/search-bar/index.json
  57. 35 0
      components/dist/search-bar/index.wxml
  58. 135 0
      components/dist/search-bar/index.wxss
  59. 77 0
      components/dist/spin/index.js
  60. 6 0
      components/dist/spin/index.json
  61. 14 0
      components/dist/spin/index.wxml
  62. 168 0
      components/dist/spin/index.wxss
  63. 106 0
      components/local/change-env/change-env.js
  64. 6 0
      components/local/change-env/change-env.json
  65. 15 0
      components/local/change-env/change-env.wxml
  66. 53 0
      components/local/change-env/change-env.wxss
  67. 124 0
      components/local/couponItem/couponItem.js
  68. 4 0
      components/local/couponItem/couponItem.json
  69. 35 0
      components/local/couponItem/couponItem.wxml
  70. 274 0
      components/local/couponItem/couponItem.wxss
  71. 250 0
      components/local/ec-canvas/ec-canvas.js
  72. 4 0
      components/local/ec-canvas/ec-canvas.json
  73. 9 0
      components/local/ec-canvas/ec-canvas.wxml
  74. 4 0
      components/local/ec-canvas/ec-canvas.wxss
  75. 16 0
      components/local/ec-canvas/echarts.js
  76. 105 0
      components/local/ec-canvas/wx-canvas.js
  77. 63 0
      components/local/home-skeleton/home-skeleton.js
  78. 4 0
      components/local/home-skeleton/home-skeleton.json
  79. 21 0
      components/local/home-skeleton/home-skeleton.wxml
  80. 72 0
      components/local/home-skeleton/home-skeleton.wxss
  81. 25 0
      components/local/navigation/navigation.js
  82. 4 0
      components/local/navigation/navigation.json
  83. 14 0
      components/local/navigation/navigation.wxml
  84. 19 0
      components/local/navigation/navigation.wxss
  85. 319 0
      components/local/wxParse/html2json.js
  86. 192 0
      components/local/wxParse/htmlparser.js
  87. 2532 0
      components/local/wxParse/showdown.js
  88. 207 0
      components/local/wxParse/wxDiscode.js
  89. 158 0
      components/local/wxParse/wxParse.js
  90. 967 0
      components/local/wxParse/wxParse.wxml
  91. 224 0
      components/local/wxParse/wxParse.wxss
  92. BIN
      images/add.png
  93. BIN
      images/all_carding.png
  94. BIN
      images/black_location.png
  95. BIN
      images/car-icon.png
  96. BIN
      images/card.png
  97. BIN
      images/card_img.png
  98. BIN
      images/clearMessage.png
  99. BIN
      images/delete-gray.png
  100. 0 0
      images/display.png

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
1
+# Windows
2
+[Dd]esktop.ini
3
+Thumbs.db
4
+$RECYCLE.BIN/
5
+
6
+# macOS
7
+.DS_Store
8
+.fseventsd
9
+.Spotlight-V100
10
+.TemporaryItems
11
+.Trashes
12
+.idea
13
+
14
+# Node.js
15
+miniprogram_npm
16
+node_modules/

+ 2 - 0
.vscode/settings.json

@@ -0,0 +1,2 @@
1
+{
2
+}

+ 20 - 0
README.md

@@ -0,0 +1,20 @@
1
+一:项目结构
2
+   "pages/index/index",   首页
3
+    "pages/logs/logs",  登录
4
+    "pages/my/index”,我的
5
+    "pages/my/vip", 我的vip
6
+    "pages/index/details", 预约项目详情
7
+    "pages/index/select_shop”,选择其他店铺
8
+    "pages/my/disclaimer”,免责声明
9
+    "pages/indent/unpaid”,订单详情(复用)
10
+    "pages/indent/list”,订单列表
11
+    "pages/my/buy_vip”,已购会员卡
12
+    "pages/my/coupon”我的优惠卷
13
+二:项目架构
14
+	使用npm的 Vant Weapp进行辅助画图,
15
+	使用地址 	https://youzan.github.io/vant-weapp/#/quickstart
16
+	目前使用了van-grid ,van-cell。,van-popup。,van-grid
17
+三:备注
18
+  原型设计稿
19
+  https://v5.modao.cc/app/073ae6dcb639121eb7dc8d1c4af35975f8681a1c?simulator_type=device&sticky
20
+  ui给的是本地文件,需要找相关人员

+ 253 - 0
app.js

@@ -0,0 +1,253 @@
1
+// app.js
2
+const ald = require('./utils/ald-stat.js')
3
+import { post, get, login } from './utils/http'
4
+App({
5
+  globalData: {
6
+    navBarData: {
7
+      navBarHeight: 0, // 导航栏高度
8
+      menuBotton: 0, // 胶囊距底部间距(保持底部间距一致)
9
+      menuRight: 0, // 胶囊距右方间距(方保持左、右间距一致)
10
+      menuHeight: 0 // 胶囊高度(自定义内容可与胶囊高度保证一致)
11
+    },
12
+    userInfo: null, 
13
+    couponPopup: false,
14
+    coupon: null,
15
+    systemInfo: {},
16
+    sceneData: {},
17
+    memberRules: '', // 会员守则
18
+    storeData: {},
19
+    showGoods:false, //点击商品或者按钮跳转到下单页面是否直接显示美妆产品
20
+    toViewIndex:'', //记录首页跳转到项目页面的滚动下标
21
+    shareUserID:'',//分享人ID
22
+    isH5:false, //是否是h5商城进来的
23
+    isSkinPlan:false, //测肤推荐方案标识
24
+    isTestSkin:false,//肤质下单标识
25
+    isCardBag:false, //卡包更多次卡跳转
26
+  },
27
+  invite_id:"null",//邀请人id
28
+  //记录登录状态相关的配置
29
+  loginConfig: {
30
+    code: '',
31
+    codeTime: ''
32
+  },
33
+  address:{},
34
+  onLaunch(options) {
35
+    // 肤质 测肤方案埋点
36
+    let RECORD_WHITE = ['pages/projectDetail/projectDetail','pages/reserveCar/reserveCar','pages/orderBy/orderBy']
37
+    wx.onAppRoute((route) => {
38
+    if(this.globalData.isSkinPlan||this.globalData.isTestSkin){
39
+        if(!RECORD_WHITE.includes(route.path)){
40
+          this.globalData.isSkinPlan = false
41
+          this.globalData.isTestSkin = false
42
+        }
43
+      }
44
+      });
45
+    let WHITE_LIST=['pages/login/login','pages/home/home']
46
+    if(!WHITE_LIST.includes(options.path)){
47
+      if(wx.getStorageSync('userInfo')==''){
48
+        wx.navigateTo({
49
+          url: '/pages/login/login',
50
+        })
51
+      }
52
+    }
53
+    let that = this
54
+    this.getPermission(that); //获取当前定位  
55
+    that.setNavBarInfo()
56
+    // 查询是否首次购买
57
+    if (wx.getStorageSync('token')) {
58
+      get('api/order/check_first', {}, (res) => {
59
+        if (res.data.order_status == 1) {
60
+          wx.setStorageSync('agree', true)
61
+        }
62
+      })
63
+    }
64
+    that.initData()
65
+    // this.getPermission()
66
+  },
67
+  onShow(opts) {
68
+    this.globalData.sceneData = {
69
+      path: opts.path,
70
+      query: opts.query
71
+    }
72
+    if (!this.globalData.userInfo) {
73
+      this.initData()
74
+    }
75
+    if (opts.path == 'pages/deviceLogin/deviceLogin') {
76
+      return
77
+    }
78
+    // if (!wx.getStorageSync('token') || !wx.getStorageSync('userInfo')) {
79
+    //   login(opts)
80
+    // }
81
+  },
82
+
83
+  /**
84
+   * 初始化数据
85
+   */
86
+  initData() {
87
+    let that = this
88
+    // 获取用户信息
89
+    if (wx.getStorageSync('token')) {
90
+      get('api/user', {}, (res) => {
91
+        if (res.data) {
92
+          that.globalData.userInfo = res.data
93
+          wx.setStorageSync('userInfo', res.data)
94
+        }
95
+      })
96
+    }
97
+
98
+    return
99
+    // 获取店铺信息
100
+    let storeId = wx.getStorageSync('store_id')
101
+    if (storeId) {
102
+      get(
103
+        'api/store/info',
104
+        {
105
+          store_id: storeId
106
+        },
107
+        (res) => {
108
+          this.globalData.storeData = res.data
109
+        }
110
+      )
111
+    }
112
+  },
113
+
114
+  /**
115
+   * @description 设置导航栏信息
116
+   */
117
+  setNavBarInfo() {
118
+    // 获取系统信息
119
+    const systemInfo = wx.getSystemInfoSync()
120
+    this.globalData.systemInfo = systemInfo
121
+    // 胶囊按钮位置信息
122
+    const menuButtonInfo = wx.getMenuButtonBoundingClientRect()
123
+    this.systemInfo = systemInfo
124
+    // 导航栏高度 = 状态栏到胶囊的间距(胶囊距上距离-状态栏高度) * 2 + 胶囊高度 + 状态栏高度
125
+    let navBarData = {
126
+      navBarHeight:
127
+        (menuButtonInfo.top - systemInfo.statusBarHeight) * 2 +
128
+        menuButtonInfo.height +
129
+        systemInfo.statusBarHeight,
130
+      menuBotton: menuButtonInfo.top - systemInfo.statusBarHeight,
131
+      menuRight: systemInfo.screenWidth - menuButtonInfo.right,
132
+      menuHeight: menuButtonInfo.height
133
+    }
134
+    this.globalData.navBarData = navBarData
135
+  },
136
+    /**
137
+   * 获取当前定位
138
+   * @param {*} obj 
139
+   */
140
+  getPermission: function (obj) {
141
+    let that = this
142
+    wx.getLocation({
143
+      success: function (res) {
144
+        that.getStore()
145
+        that.globalData.address = res
146
+        // console.log(that.address, 'that.addressthat.address', res);
147
+      },
148
+      fail: function () {
149
+        wx.getSetting({
150
+          success: function (res) {
151
+            var statu = res.authSetting;
152
+            if (!statu['scope.userLocation']) {
153
+              wx.showModal({
154
+                title: '是否授权当前位置',
155
+                content: '需要获取您的地理位置,请确认授权,否则无法定位最近店铺',
156
+                success: function (tip) {
157
+                  if (tip.confirm) {
158
+                    wx.openSetting({
159
+                      success: function (data) {
160
+                        if (data.authSetting["scope.userLocation"] === true) {
161
+                          wx.showToast({
162
+                            title: '授权成功',
163
+                            icon: 'success',
164
+                            duration: 1000
165
+                          })
166
+                          //授权成功之后,再调用chooseLocation选择地方
167
+                          wx.getLocation({
168
+                            success: function (res) {
169
+                              that.getStore()
170
+                              app.address = res
171
+                            },
172
+                          })
173
+                        } else {
174
+                          wx.showToast({
175
+                            title: '授权失败',
176
+                            icon: 'success',
177
+                            duration: 1000
178
+                          })
179
+                        }
180
+                      }
181
+                    })
182
+                  }
183
+                }
184
+              })
185
+            }
186
+          },
187
+          fail: function (res) {
188
+            wx.showToast({
189
+              title: '调用授权窗口失败',
190
+              icon: 'success',
191
+              duration: 1000
192
+            })
193
+          }
194
+        })
195
+      }
196
+    })
197
+  },
198
+    /**
199
+   * 获取店铺接口
200
+   * /api/store
201
+   */
202
+  getStore() {
203
+    get(
204
+      'api/store', {
205
+        page: 1,
206
+        limit: 100,
207
+        keyword: ""
208
+      },
209
+      (res) => {
210
+        let lat1 = this.globalData.address.latitude,
211
+          lng1 = this.globalData.address.longitude,
212
+          storeList = res.data.list,
213
+          distanceList = []
214
+        //循环遍历计算获取当前定位到各门店的距离
215
+        storeList.forEach(item => {
216
+          let distanceInfo = {}
217
+          distanceInfo["id"] = item.id
218
+          distanceInfo["dist"] = this.getDistance(lat1, lng1, item.latitude, item.longitude)
219
+          distanceList.push(distanceInfo)
220
+        })
221
+        //近到远排序
222
+        let _distanceList = distanceList.sort(function (a, b) {
223
+          return a.dist - b.dist
224
+        })
225
+        // //切换门店
226
+        // this.getStoreInfo(String(_distanceList[0].id))
227
+        wx.setStorageSync('store_id', String(_distanceList[0].id))
228
+      }
229
+    )
230
+  },
231
+    /**
232
+   * 两点之间距离计算
233
+   * @param {*} lat1 
234
+   * @param {*} lng1 
235
+   * @param {*} lat2 
236
+   * @param {*} lng2 
237
+   */
238
+  getDistance(lat1, lng1, lat2, lng2) {
239
+    lat1 = lat1 || 0;
240
+    lng1 = lng1 || 0;
241
+    lat2 = lat2 || 0;
242
+    lng2 = lng2 || 0;
243
+
244
+    var rad1 = lat1 * Math.PI / 180.0;
245
+    var rad2 = lat2 * Math.PI / 180.0;
246
+    var a = rad1 - rad2;
247
+    var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
248
+    var r = 6378137;
249
+    var distance = r * 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(rad1) * Math.cos(rad2) * Math.pow(Math.sin(b / 2), 2)));
250
+
251
+    return distance;
252
+  },
253
+})

+ 168 - 0
app.json

@@ -0,0 +1,168 @@
1
+{
2
+  "pages": [
3
+    "pages/home/home",
4
+    "pages/activePage2/activePage2",
5
+    "pages/couponCenter/couponCenter",
6
+    "pages/faceDetectionDetails/faceDetectionDetails",
7
+    "pages/recommendRule/recommendRule",
8
+    "pages/actDetails/actDetails",
9
+    "pages/authOrder/authOrder",
10
+    "pages/confirmProjectOrder/confirmProjectOrder",
11
+    "pages/orderDetailsProject/orderDetailsProject",
12
+    "pages/orderBySuccess/orderBySuccess",
13
+    "pages/paySuccess/paySuccess",
14
+    "pages/reserveCar/reserveCar",
15
+    "pages/member/member",
16
+    "pages/orderBy/orderBy",
17
+    "pages/vip/vip",
18
+    "pages/vipCard/vipCard",
19
+    "pages/coupon/coupon",
20
+    "pages/disclaimer/disclaimer",
21
+    "pages/order/order",
22
+    "pages/orderDetail/orderDetail",
23
+    "pages/projectDetail/projectDetail",
24
+    "pages/orderConfirm/orderConfirm",
25
+    "pages/goodsOrderConfirm/goodsOrderConfirm",
26
+    "pages/store/store",
27
+    "pages/login/login",
28
+    "pages/agreement/agreement",
29
+    "pages/deviceLogin/deviceLogin",
30
+    "pages/caseList/caseList",
31
+    "pages/article/article",
32
+    "pages/dossier/dossier",
33
+    "pages/phone/phone",
34
+    "pages/contact/contact",
35
+    "pages/caseDetail/caseDetail",
36
+    "pages/articleDetail/articleDetail",
37
+    "pages/name/name",
38
+    "pages/skinRecord/skinRecord",
39
+    "pages/test/test",
40
+    "pages/skinRecordList/skinRecordList",
41
+    "pages/beautyPlan/beautyPlan",
42
+    "pages/beautyPlanList/beautyPlanList",
43
+    "pages/reserveProject/reserveProject",
44
+    "pages/reserveProduct/reserveProduct",
45
+    "pages/feedback/feedback",
46
+    "pages/addFeedback/addFeedback",
47
+    "pages/goods/orderList/orderList",
48
+    "pages/goods/orderDetail/orderDetail",
49
+    "pages/member/investigation/investigation",
50
+    "pages/storedValue/index",
51
+    "pages/rechange/index",
52
+    "pages/toolDetails/index",
53
+    "pages/guildMessage/index",
54
+    "pages/plan/plan",
55
+    "pages/invitationDetails/invitationDetails",
56
+    "pages/invitationDetails/poster/poster",
57
+    "pages/invitees/invitees",
58
+    "pages/sucReception/sucReception",
59
+    "pages/couponHistory/couponHistory",
60
+    "pages/sendFriend/sendFriend",
61
+    "pages/sendGetCoupon/sendGetCoupon",
62
+    "pages/shoppingMall/shoppingMall",
63
+    "pages/moreClassify/moreClassify"
64
+  ],
65
+  "subpackages": [
66
+    {
67
+      "root": "subPackages",
68
+      "name": "分包A",
69
+      "pages": [
70
+        "pages/cardBag/cardBag",
71
+        "pages/subCardDetails/subCardDetails",
72
+        "pages/couponHistory/couponHistory"
73
+      ]
74
+    },
75
+    {
76
+      "root": "subPackagesB",
77
+      "name": "分包B",
78
+      "pages": [
79
+        "pages/joinGroup/joinGroup",
80
+        "pages/groupRecord/groupRecord",
81
+        "pages/groupOrder/groupOrder",
82
+        "pages/groupFlow/groupFlow",
83
+        "pages/groupDetail/groupDetail",
84
+        "pages/successOrder/successOrder",
85
+        "pages/GroupConfirm/GroupConfirm",
86
+        "pages/groupExplain/groupExplain"
87
+      ]
88
+    },
89
+    {
90
+      "root": "subPackagesC",
91
+      "name": "分包C",
92
+      "pages": [
93
+        "pages/messageCenter/messageCenter",
94
+        "pages/messageDetail/messageDetail"
95
+      ]
96
+    },
97
+    {
98
+      "root": "subPackagesD",
99
+      "name": "分包D",
100
+      "pages": [
101
+        "pages/testSkin/testSkin",
102
+        "pages/skinHistory/skinHistory",
103
+        "pages/skinLoading/skinLoading",
104
+        "pages/skinPlan/skinPlan"
105
+      ]
106
+    },
107
+    {
108
+      "root": "subPackagesE",
109
+      "name": "分包E",
110
+      "pages": [
111
+        "pages/giftBag/giftBag",
112
+        "pages/giftBagSize/giftBagSize",
113
+        "pages/giftBagOrder/giftBagOrder"
114
+      ]
115
+    },
116
+    {
117
+      "root": "subPackagesF",
118
+      "name": "分包F",
119
+      "pages": [
120
+        "pages/seckillActivity/seckillActivity",
121
+        "pages/seckillDetail/seckillDetail",
122
+        "pages/seckillOrder/seckillOrder"
123
+      ]
124
+    }
125
+  ],
126
+  "tabBar": {
127
+    "color": "#666666",
128
+    "selectedColor": "#333333",
129
+    "borderStyle": "white",
130
+    "backgroundColor": "#fff",
131
+    "list": [
132
+      {
133
+        "pagePath": "pages/home/home",
134
+        "iconPath": "images/nav/首页-未选中@2x.png",
135
+        "selectedIconPath": "images/nav/首页-选中@2x.png",
136
+        "text": "首页"
137
+      },
138
+      {
139
+        "pagePath": "pages/shoppingMall/shoppingMall",
140
+        "iconPath": "images/nav/商城-未选中@2x.png",
141
+        "selectedIconPath": "images/nav/商城-选中@2x.png",
142
+        "text": "商城"
143
+      },
144
+      {
145
+        "pagePath": "pages/member/member",
146
+        "iconPath": "images/nav/我的-未选中@2x.png",
147
+        "selectedIconPath": "images/nav/我的-选中@2x.png",
148
+        "text": "我的"
149
+      }
150
+    ]
151
+  },
152
+  "window": {
153
+    "backgroundTextStyle": "light",
154
+    "navigationBarBackgroundColor": "#fff",
155
+    "navigationBarTitleText": "Weixin",
156
+    "navigationBarTextStyle": "black"
157
+  },
158
+  "permission": {
159
+    "scope.userLocation": {
160
+      "desc": "你的位置信息将用于小程序位置接口的效果展示"
161
+    }
162
+  },
163
+  "requiredPrivateInfos": [
164
+    "getLocation"
165
+  ],
166
+  "usingComponents": {},
167
+  "sitemapLocation": "sitemap.json"
168
+}

+ 28 - 0
app.wxs

@@ -0,0 +1,28 @@
1
+var replaceStr = function (str) {
2
+  if (str) {
3
+    return str.replace(getRegExp('-', 'g'), '.')
4
+  }
5
+}
6
+
7
+var indexOf = function (args, param) {
8
+  if (args.indexOf(param) > -1) {
9
+    return true
10
+  }
11
+  return false
12
+}
13
+var mysub = function(str,start,length){
14
+  //起始位置
15
+  var offset = start || 0;
16
+  var len=length || 70;
17
+  if(str.length<=len){
18
+    return str;
19
+  }
20
+  return str.substring(offset,len)+'...';
21
+}
22
+
23
+
24
+module.exports = {
25
+  replaceStr: replaceStr,
26
+  indexOf: indexOf,
27
+  mysub:mysub
28
+}

+ 147 - 0
app.wxss

@@ -0,0 +1,147 @@
1
+/**app.wxss**/
2
+page {
3
+  /* background: #F9F9F9; */
4
+  background: #F7F8FA;
5
+  /* background: linear-gradient(180deg, #FA7D22 0%, rgba(255, 202, 224, 0) 100%);
6
+  background-repeat: no-repeat; */
7
+}
8
+
9
+.container {
10
+  height: 100%;
11
+  display: flex;
12
+  flex-direction: column;
13
+  align-items: center;
14
+  justify-content: space-between;
15
+  padding: 200rpx 0;
16
+  box-sizing: border-box;
17
+}
18
+
19
+.flex {
20
+  display: flex;
21
+}
22
+
23
+.flex-start {
24
+  align-items: flex-start;
25
+  justify-content: flex-start;
26
+}
27
+
28
+.flex-align-center {
29
+  display: flex;
30
+  align-items: center;
31
+}
32
+
33
+.flex-align-baseline {
34
+  display: flex;
35
+  align-items: baseline;
36
+}
37
+
38
+.flex-justify-center {
39
+  display: flex;
40
+  justify-content: center;
41
+}
42
+
43
+.flex-justify-space-around {
44
+  display: flex;
45
+  justify-content: space-around;
46
+}
47
+
48
+.flex-justify-space-between {
49
+  display: flex;
50
+  justify-content: space-between;
51
+}
52
+
53
+.flex-center {
54
+  display: flex;
55
+  align-items: center;
56
+  justify-content: center;
57
+}
58
+
59
+.flex-column {
60
+  display: flex;
61
+  flex-direction: column;
62
+  /* align-items: flex-start;
63
+  justify-content: flex-start; */
64
+}
65
+
66
+.flex1 {
67
+  flex: 1;
68
+}
69
+
70
+.radius {
71
+  border-radius: 8rpx;
72
+  box-sizing: border-box;
73
+  overflow: hidden;
74
+}
75
+
76
+.popup-radius .wux-popup__content {
77
+  border-top-left-radius: 40rpx;
78
+  border-top-right-radius: 40rpx;
79
+}
80
+.ellipsis {
81
+  white-space: nowrap;
82
+  overflow: hidden;
83
+  text-overflow: ellipsis;
84
+}
85
+
86
+.ellipsis2 {
87
+  /* overflow: hidden;
88
+  text-overflow: ellipsis;
89
+  display: -webkit-box;
90
+  -webkit-line-clamp: 2;
91
+  -webkit-box-orient: vertical; */
92
+
93
+  text-overflow: ellipsis;
94
+  display: -webkit-box;
95
+  -webkit-line-clamp: 2;
96
+  -webkit-box-orient: vertical;
97
+  display: -moz-box;
98
+  -moz-line-clamp: 2;
99
+  -moz-box-orient: vertical;
100
+  word-wrap: break-word;
101
+  word-break: break-all;
102
+  white-space: normal !important;
103
+  overflow: hidden;
104
+}
105
+
106
+.ellipsis3 {
107
+  overflow: hidden;
108
+  text-overflow: ellipsis;
109
+  display: -webkit-box;
110
+  -webkit-line-clamp: 3;
111
+  -webkit-box-orient: vertical;
112
+}
113
+
114
+text {
115
+  font-size: 32rpx;
116
+  color: #333333;
117
+  /* color: #FFFFFF; */
118
+  font-family: PingFangSC-Regular;
119
+}
120
+
121
+.btm-text {
122
+  font-size: 28rpx;
123
+  color: #999999;
124
+  text-align: center;
125
+  margin: 42rpx 0;
126
+}
127
+
128
+.no-data {
129
+  text-align: center;
130
+  margin-top: 400rpx;
131
+  color: #666;
132
+}
133
+
134
+
135
+.no-more-data {
136
+  text-align: center;
137
+  margin-top: 28rpx;
138
+  color: #666;
139
+}
140
+
141
+rich-text .richImg {
142
+  max-width: 100%;
143
+  max-height: 100%;
144
+  vertical-align: middle;
145
+  height: auto !important;
146
+  width: auto !important;
147
+}

+ 382 - 0
components/dist/animation-group/index.js

@@ -0,0 +1,382 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+import styleToCssString from '../helpers/styleToCssString'
3
+
4
+const ENTER = 'enter'
5
+const ENTERING = 'entering'
6
+const ENTERED = 'entered'
7
+const EXIT = 'exit'
8
+const EXITING = 'exiting'
9
+const EXITED = 'exited'
10
+const UNMOUNTED = 'unmounted'
11
+
12
+const TRANSITION = 'transition'
13
+const ANIMATION = 'animation'
14
+
15
+const TIMEOUT = 1000 / 60
16
+
17
+const defaultClassNames = {
18
+    enter: '', // 进入过渡的开始状态,在过渡过程完成之后移除
19
+    enterActive: '', // 进入过渡的结束状态,在过渡过程完成之后移除
20
+    enterDone: '', // 进入过渡的完成状态
21
+    exit: '', // 离开过渡的开始状态,在过渡过程完成之后移除
22
+    exitActive: '', // 离开过渡的结束状态,在过渡过程完成之后移除
23
+    exitDone: '', // 离开过渡的完成状态
24
+}
25
+
26
+baseComponent({
27
+    properties: {
28
+        // 触发组件进入或离开过渡的状态
29
+        in: {
30
+            type: Boolean,
31
+            value: false,
32
+            observer(newVal) {
33
+                if (this.data.isMounting) {
34
+                    this.updated(newVal)
35
+                }
36
+            },
37
+        },
38
+        // 过渡的类名
39
+        classNames: {
40
+            type: null,
41
+            value: defaultClassNames,
42
+        },
43
+        // 过渡持续时间
44
+        duration: {
45
+            type: null,
46
+            value: null,
47
+        },
48
+        // 过渡动效的类型
49
+        type: {
50
+            type: String,
51
+            value: TRANSITION,
52
+        },
53
+        // 首次挂载时是否触发进入过渡
54
+        appear: {
55
+            type: Boolean,
56
+            value: false,
57
+        },
58
+        // 是否启用进入过渡
59
+        enter: {
60
+            type: Boolean,
61
+            value: true,
62
+        },
63
+        // 是否启用离开过渡
64
+        exit: {
65
+            type: Boolean,
66
+            value: true,
67
+        },
68
+        // 首次进入过渡时是否懒挂载组件
69
+        mountOnEnter: {
70
+            type: Boolean,
71
+            value: true,
72
+        },
73
+        // 离开过渡完成时是否卸载组件
74
+        unmountOnExit: {
75
+            type: Boolean,
76
+            value: true,
77
+        },
78
+        // 自定义类名
79
+        wrapCls: {
80
+            type: String,
81
+            value: '',
82
+        },
83
+        // 自定义样式
84
+        wrapStyle: {
85
+            type: [String, Object],
86
+            value: '',
87
+            observer(newVal) {
88
+                this.setData({
89
+                    extStyle: styleToCssString(newVal),
90
+                })
91
+            },
92
+        },
93
+        disableScroll: {
94
+            type: Boolean,
95
+            value: false,
96
+        },
97
+    },
98
+    data: {
99
+        animateCss: '', // 动画样式
100
+        animateStatus: EXITED, // 动画状态,可选值 entering、entered、exiting、exited
101
+        isMounting: false, // 是否首次挂载
102
+        extStyle: '', // 组件样式
103
+    },
104
+    methods: {
105
+        /**
106
+         * 监听过渡或动画的回调函数
107
+         */
108
+        addEventListener() {
109
+            const { animateStatus } = this.data
110
+            const { enter, exit } = this.getTimeouts()
111
+
112
+            if (animateStatus === ENTERING && !enter && this.data.enter) {
113
+                this.performEntered()
114
+            }
115
+
116
+            if (animateStatus === EXITING && !exit && this.data.exit) {
117
+                this.performExited()
118
+            }
119
+        },
120
+        /**
121
+         * 会在 WXSS transition 或 wx.createAnimation 动画结束后触发
122
+         */
123
+        onTransitionEnd() {
124
+            if (this.data.type === TRANSITION) {
125
+                this.addEventListener()
126
+            }
127
+        },
128
+        /**
129
+         * 会在一个 WXSS animation 动画完成时触发
130
+         */
131
+        onAnimationEnd() {
132
+            if (this.data.type === ANIMATION) {
133
+                this.addEventListener()
134
+            }
135
+        },
136
+        /**
137
+         * 更新组件状态
138
+         * @param {String} nextStatus 下一状态,ENTERING 或 EXITING
139
+         * @param {Boolean} mounting 是否首次挂载
140
+         */
141
+        updateStatus(nextStatus, mounting = false) {
142
+            if (nextStatus !== null) {
143
+                this.cancelNextCallback()
144
+                this.isAppearing = mounting
145
+
146
+                if (nextStatus === ENTERING) {
147
+                    this.performEnter()
148
+                } else {
149
+                    this.performExit()
150
+                }
151
+            }
152
+        },
153
+        /**
154
+         * 进入过渡
155
+         */
156
+        performEnter() {
157
+            const { className, activeClassName } = this.getClassNames(ENTER)
158
+            const { enter } = this.getTimeouts()
159
+            const enterParams = {
160
+                animateStatus: ENTER,
161
+                animateCss: className,
162
+            }
163
+            const enteringParams = {
164
+                animateStatus: ENTERING,
165
+                animateCss: `${className} ${activeClassName}`,
166
+            }
167
+
168
+            // 若已禁用进入过渡,则更新状态至 ENTERED
169
+            if (!this.isAppearing && !this.data.enter) {
170
+                return this.performEntered()
171
+            }
172
+
173
+            // 第一阶段:设置进入过渡的开始状态,并触发 ENTER 事件
174
+            // 第二阶段:延迟一帧后,设置进入过渡的结束状态,并触发 ENTERING 事件
175
+            // 第三阶段:若已设置过渡的持续时间,则延迟指定时间后触发进入过渡完成 performEntered,否则等待触发 onTransitionEnd 或 onAnimationEnd
176
+            this.safeSetData(enterParams, () => {
177
+                this.triggerEvent('change', { animateStatus: ENTER })
178
+                this.triggerEvent(ENTER, { isAppearing: this.isAppearing })
179
+
180
+                // 由于有些时候不能正确的触发动画完成的回调,具体原因未知
181
+                // 所以采用延迟一帧的方式来确保可以触发回调
182
+                this.delayHandler(TIMEOUT, () => {
183
+                    this.safeSetData(enteringParams, () => {
184
+                        this.triggerEvent('change', { animateStatus: ENTERING })
185
+                        this.triggerEvent(ENTERING, { isAppearing: this.isAppearing })
186
+
187
+                        if (enter) {
188
+                            this.delayHandler(enter, this.performEntered)
189
+                        }
190
+                    })
191
+                })
192
+            })
193
+        },
194
+        /**
195
+         * 进入过渡完成
196
+         */
197
+        performEntered() {
198
+            const { doneClassName } = this.getClassNames(ENTER)
199
+            const enteredParams = {
200
+                animateStatus: ENTERED,
201
+                animateCss: doneClassName,
202
+            }
203
+
204
+            // 第三阶段:设置进入过渡的完成状态,并触发 ENTERED 事件
205
+            this.safeSetData(enteredParams, () => {
206
+                this.triggerEvent('change', { animateStatus: ENTERED })
207
+                this.triggerEvent(ENTERED, { isAppearing: this.isAppearing })
208
+            })
209
+        },
210
+        /**
211
+         * 离开过渡
212
+         */
213
+        performExit() {
214
+            const { className, activeClassName } = this.getClassNames(EXIT)
215
+            const { exit } = this.getTimeouts()
216
+            const exitParams = {
217
+                animateStatus: EXIT,
218
+                animateCss: className,
219
+            }
220
+            const exitingParams = {
221
+                animateStatus: EXITING,
222
+                animateCss: `${className} ${activeClassName}`,
223
+            }
224
+
225
+            // 若已禁用离开过渡,则更新状态至 EXITED
226
+            if (!this.data.exit) {
227
+                return this.performExited()
228
+            }
229
+
230
+            // 第一阶段:设置离开过渡的开始状态,并触发 EXIT 事件
231
+            // 第二阶段:延迟一帧后,设置离开过渡的结束状态,并触发 EXITING 事件
232
+            // 第三阶段:若已设置过渡的持续时间,则延迟指定时间后触发离开过渡完成 performExited,否则等待触发 onTransitionEnd 或 onAnimationEnd
233
+            this.safeSetData(exitParams, () => {
234
+                this.triggerEvent('change', { animateStatus: EXIT })
235
+                this.triggerEvent(EXIT)
236
+
237
+                this.delayHandler(TIMEOUT, () => {
238
+                    this.safeSetData(exitingParams, () => {
239
+                        this.triggerEvent('change', { animateStatus: EXITING })
240
+                        this.triggerEvent(EXITING)
241
+
242
+                        if (exit) {
243
+                            this.delayHandler(exit, this.performExited)
244
+                        }
245
+                    })
246
+                })
247
+            })
248
+        },
249
+        /**
250
+         * 离开过渡完成
251
+         */
252
+        performExited() {
253
+            const { doneClassName } = this.getClassNames(EXIT)
254
+            const exitedParams = {
255
+                animateStatus: EXITED,
256
+                animateCss: doneClassName,
257
+            }
258
+
259
+            // 第三阶段:设置离开过渡的完成状态,并触发 EXITED 事件
260
+            this.safeSetData(exitedParams, () => {
261
+                this.triggerEvent('change', { animateStatus: EXITED })
262
+                this.triggerEvent(EXITED)
263
+
264
+                // 判断离开过渡完成时是否卸载组件
265
+                if (this.data.unmountOnExit) {
266
+                    this.setData({ animateStatus: UNMOUNTED }, () => {
267
+                        this.triggerEvent('change', { animateStatus: UNMOUNTED })
268
+                    })
269
+                }
270
+            })
271
+        },
272
+        /**
273
+         * 获取指定状态下的类名
274
+         * @param {String} type 过渡类型,enter 或 exit
275
+         */
276
+        getClassNames(type) {
277
+            const { classNames } = this.data
278
+            const className = typeof classNames !== 'string' ? classNames[type] : `${classNames}-${type}`
279
+            const activeClassName = typeof classNames !== 'string' ? classNames[`${type}Active`] : `${classNames}-${type}-active`
280
+            const doneClassName = typeof classNames !== 'string' ? classNames[`${type}Done`] : `${classNames}-${type}-done`
281
+
282
+            return {
283
+                className,
284
+                activeClassName,
285
+                doneClassName,
286
+            }
287
+        },
288
+        /**
289
+         * 获取过渡持续时间
290
+         */
291
+        getTimeouts() {
292
+            const { duration } = this.data
293
+
294
+            if (duration !== null && typeof duration === 'object') {
295
+                return {
296
+                    enter: duration.enter,
297
+                    exit: duration.exit,
298
+                }
299
+            } else if (typeof duration === 'number') {
300
+                return {
301
+                    enter: duration,
302
+                    exit: duration,
303
+                }
304
+            }
305
+
306
+            return {}
307
+        },
308
+        /**
309
+         * 属性值 in 被更改时的响应函数
310
+         * @param {Boolean} newVal 触发组件进入或离开过渡的状态
311
+         */
312
+        updated(newVal) {
313
+            let { animateStatus } = this.pendingData || this.data
314
+            let nextStatus = null
315
+
316
+            if (newVal) {
317
+                if (animateStatus === UNMOUNTED) {
318
+                    animateStatus = EXITED
319
+                    this.setData({ animateStatus: EXITED }, () => {
320
+                        this.triggerEvent('change', { animateStatus: EXITED })
321
+                    })
322
+                }
323
+                if (animateStatus !== ENTER && animateStatus !== ENTERING && animateStatus !== ENTERED) {
324
+                    nextStatus = ENTERING
325
+                }
326
+            } else {
327
+                if (animateStatus === ENTER || animateStatus === ENTERING || animateStatus === ENTERED) {
328
+                    nextStatus = EXITING
329
+                }
330
+            }
331
+
332
+            this.updateStatus(nextStatus)
333
+        },
334
+        /**
335
+         * 延迟一段时间触发回调
336
+         * @param {Number} timeout 延迟时间
337
+         * @param {Function} handler 回调函数
338
+         */
339
+        delayHandler(timeout, handler) {
340
+            if (timeout) {
341
+                this.setNextCallback(handler)
342
+                setTimeout(this.nextCallback, timeout)
343
+            }
344
+        },
345
+        /**
346
+         * 点击事件
347
+         */
348
+        onTap() {
349
+            this.triggerEvent('click')
350
+        },
351
+        /**
352
+         * 阻止移动触摸
353
+         */
354
+        noop() {},
355
+    },
356
+    attached() {
357
+        let animateStatus = null
358
+        let appearStatus = null
359
+
360
+        if (this.data.in) {
361
+            if (this.data.appear) {
362
+                animateStatus = EXITED
363
+                appearStatus = ENTERING
364
+            } else {
365
+                animateStatus = ENTERED
366
+            }
367
+        } else {
368
+            if (this.data.unmountOnExit || this.data.mountOnEnter) {
369
+                animateStatus = UNMOUNTED
370
+            } else {
371
+                animateStatus = EXITED
372
+            }
373
+        }
374
+
375
+        // 由于小程序组件首次挂载时 observer 事件总是优先于 attached 事件
376
+        // 所以使用 isMounting 来强制优先触发 attached 事件
377
+        this.safeSetData({ animateStatus, isMounting: true }, () => {
378
+            this.triggerEvent('change', { animateStatus })
379
+            this.updateStatus(appearStatus, true)
380
+        })
381
+    },
382
+})

+ 3 - 0
components/dist/animation-group/index.json

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

+ 3 - 0
components/dist/animation-group/index.wxml

@@ -0,0 +1,3 @@
1
+<view class="wux-class {{ wrapCls }} {{ animateCss }}" bindtap="onTap" catchtouchmove="{{ disableScroll ? 'noop' : '' }}" bindtransitionend="onTransitionEnd" bindanimationend="onAnimationEnd" wx:if="{{ animateStatus !== 'unmounted' }}" style="{{ extStyle }}">
2
+    <slot></slot>
3
+</view>

+ 206 - 0
components/dist/animation-group/index.wxss

@@ -0,0 +1,206 @@
1
+.wux-animate--fadeIn-enter {
2
+  transition: opacity .3s;
3
+  opacity: 0
4
+}
5
+.wux-animate--fadeIn-enter-active,
6
+.wux-animate--fadeIn-enter-done {
7
+  opacity: 1
8
+}
9
+.wux-animate--fadeIn-exit {
10
+  transition: opacity .3s;
11
+  opacity: 1
12
+}
13
+.wux-animate--fadeIn-exit-active,
14
+.wux-animate--fadeIn-exit-done {
15
+  opacity: 0
16
+}
17
+.wux-animate--fadeInDown-enter {
18
+  transition: opacity .3s,transform .3s;
19
+  opacity: 0;
20
+  transform: translate3d(0,-100%,0)
21
+}
22
+.wux-animate--fadeInDown-enter-active,
23
+.wux-animate--fadeInDown-enter-done {
24
+  opacity: 1;
25
+  transform: none
26
+}
27
+.wux-animate--fadeInDown-exit {
28
+  transition: opacity .3s,transform .3s;
29
+  opacity: 1;
30
+  transform: none
31
+}
32
+.wux-animate--fadeInDown-exit-active,
33
+.wux-animate--fadeInDown-exit-done {
34
+  opacity: 0;
35
+  transform: translate3d(0,-100%,0)
36
+}
37
+.wux-animate--fadeInLeft-enter {
38
+  transition: opacity .3s,transform .3s;
39
+  opacity: 0;
40
+  transform: translate3d(-100%,0,0)
41
+}
42
+.wux-animate--fadeInLeft-enter-active,
43
+.wux-animate--fadeInLeft-enter-done {
44
+  opacity: 1;
45
+  transform: none
46
+}
47
+.wux-animate--fadeInLeft-exit {
48
+  transition: opacity .3s,transform .3s;
49
+  opacity: 1;
50
+  transform: none
51
+}
52
+.wux-animate--fadeInLeft-exit-active,
53
+.wux-animate--fadeInLeft-exit-done {
54
+  opacity: 0;
55
+  transform: translate3d(-100%,0,0)
56
+}
57
+.wux-animate--fadeInRight-enter {
58
+  transition: opacity .3s,transform .3s;
59
+  opacity: 0;
60
+  transform: translate3d(100%,0,0)
61
+}
62
+.wux-animate--fadeInRight-enter-active,
63
+.wux-animate--fadeInRight-enter-done {
64
+  opacity: 1;
65
+  transform: none
66
+}
67
+.wux-animate--fadeInRight-exit {
68
+  transition: opacity .3s,transform .3s;
69
+  opacity: 1;
70
+  transform: none
71
+}
72
+.wux-animate--fadeInRight-exit-active,
73
+.wux-animate--fadeInRight-exit-done {
74
+  opacity: 0;
75
+  transform: translate3d(100%,0,0)
76
+}
77
+.wux-animate--fadeInUp-enter {
78
+  transition: opacity .3s,transform .3s;
79
+  opacity: 0;
80
+  transform: translate3d(0,100%,0)
81
+}
82
+.wux-animate--fadeInUp-enter-active,
83
+.wux-animate--fadeInUp-enter-done {
84
+  opacity: 1;
85
+  transform: none
86
+}
87
+.wux-animate--fadeInUp-exit {
88
+  transition: opacity .3s,transform .3s;
89
+  opacity: 1;
90
+  transform: none
91
+}
92
+.wux-animate--fadeInUp-exit-active,
93
+.wux-animate--fadeInUp-exit-done {
94
+  opacity: 0;
95
+  transform: translate3d(0,100%,0)
96
+}
97
+.wux-animate--slideInUp-enter {
98
+  transition: transform .3s;
99
+  transform: translate3d(0,100%,0);
100
+  visibility: visible
101
+}
102
+.wux-animate--slideInUp-enter-active,
103
+.wux-animate--slideInUp-enter-done {
104
+  transform: translateZ(0)
105
+}
106
+.wux-animate--slideInUp-exit {
107
+  transition: transform .3s;
108
+  transform: translateZ(0)
109
+}
110
+.wux-animate--slideInUp-exit-active,
111
+.wux-animate--slideInUp-exit-done {
112
+  transform: translate3d(0,100%,0);
113
+  visibility: visible
114
+}
115
+.wux-animate--slideInDown-enter {
116
+  transition: transform .3s;
117
+  transform: translate3d(0,-100%,0);
118
+  visibility: visible
119
+}
120
+.wux-animate--slideInDown-enter-active,
121
+.wux-animate--slideInDown-enter-done {
122
+  transform: translateZ(0)
123
+}
124
+.wux-animate--slideInDown-exit {
125
+  transition: transform .3s;
126
+  transform: translateZ(0)
127
+}
128
+.wux-animate--slideInDown-exit-active,
129
+.wux-animate--slideInDown-exit-done {
130
+  transform: translate3d(0,-100%,0);
131
+  visibility: visible
132
+}
133
+.wux-animate--slideInLeft-enter {
134
+  transition: transform .3s;
135
+  transform: translate3d(-100%,0,0);
136
+  visibility: visible
137
+}
138
+.wux-animate--slideInLeft-enter-active,
139
+.wux-animate--slideInLeft-enter-done {
140
+  transform: translateZ(0)
141
+}
142
+.wux-animate--slideInLeft-exit {
143
+  transition: transform .3s;
144
+  transform: translateZ(0)
145
+}
146
+.wux-animate--slideInLeft-exit-active,
147
+.wux-animate--slideInLeft-exit-done {
148
+  transform: translate3d(-100%,0,0);
149
+  visibility: visible
150
+}
151
+.wux-animate--slideInRight-enter {
152
+  transition: transform .3s;
153
+  transform: translate3d(100%,0,0);
154
+  visibility: visible
155
+}
156
+.wux-animate--slideInRight-enter-active,
157
+.wux-animate--slideInRight-enter-done {
158
+  transform: none
159
+}
160
+.wux-animate--slideInRight-exit {
161
+  transition: transform .3s;
162
+  transform: none
163
+}
164
+.wux-animate--slideInRight-exit-active,
165
+.wux-animate--slideInRight-exit-done {
166
+  transform: translate3d(100%,0,0);
167
+  visibility: visible
168
+}
169
+.wux-animate--zoom-enter {
170
+  transition: all .3s cubic-bezier(.215,.61,.355,1);
171
+  opacity: .01;
172
+  transform: scale(.75)
173
+}
174
+.wux-animate--zoom-enter-active,
175
+.wux-animate--zoom-enter-done {
176
+  opacity: 1;
177
+  transform: none
178
+}
179
+.wux-animate--zoom-exit {
180
+  transition: all .25s linear;
181
+  transform: none
182
+}
183
+.wux-animate--zoom-exit-active,
184
+.wux-animate--zoom-exit-done {
185
+  opacity: .01;
186
+  transform: scale(.75)
187
+}
188
+.wux-animate--punch-enter {
189
+  transition: all .3s cubic-bezier(.215,.61,.355,1);
190
+  opacity: .01;
191
+  transform: scale(1.35)
192
+}
193
+.wux-animate--punch-enter-active,
194
+.wux-animate--punch-enter-done {
195
+  opacity: 1;
196
+  transform: none
197
+}
198
+.wux-animate--punch-exit {
199
+  transition: all .25s linear;
200
+  transform: none
201
+}
202
+.wux-animate--punch-exit-active,
203
+.wux-animate--punch-exit-done {
204
+  opacity: .01;
205
+  transform: scale(1.35)
206
+}

+ 62 - 0
components/dist/backdrop/index.js

@@ -0,0 +1,62 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+
3
+baseComponent({
4
+    properties: {
5
+        prefixCls: {
6
+            type: String,
7
+            value: 'wux-backdrop',
8
+        },
9
+        transparent: {
10
+            type: Boolean,
11
+            value: false,
12
+        },
13
+        zIndex: {
14
+            type: Number,
15
+            value: 1000,
16
+        },
17
+        classNames: {
18
+            type: null,
19
+            value: 'wux-animate--fadeIn',
20
+        },
21
+    },
22
+    computed: {
23
+        classes: ['prefixCls, transparent', function(prefixCls, transparent) {
24
+            const wrap = transparent ? `${prefixCls}--transparent` : prefixCls
25
+
26
+            return {
27
+                wrap,
28
+            }
29
+        }],
30
+    },
31
+    methods: {
32
+        /**
33
+         * 保持锁定
34
+         */
35
+        retain() {
36
+            if (typeof this.backdropHolds !== 'number' || !this.backdropHolds) {
37
+                this.backdropHolds = 0
38
+            }
39
+
40
+            this.backdropHolds = this.backdropHolds + 1
41
+
42
+            if (this.backdropHolds === 1) {
43
+                this.setData({ in: true })
44
+            }
45
+        },
46
+        /**
47
+         * 释放锁定
48
+         */
49
+        release() {
50
+            if (this.backdropHolds === 1) {
51
+                this.setData({ in: false })
52
+            }
53
+            this.backdropHolds = Math.max(0, this.backdropHolds - 1)
54
+        },
55
+        /**
56
+         * 点击事件
57
+         */
58
+        onClick() {
59
+            this.triggerEvent('click')
60
+        },
61
+    },
62
+})

+ 6 - 0
components/dist/backdrop/index.json

@@ -0,0 +1,6 @@
1
+{
2
+    "component": true,
3
+    "usingComponents": {
4
+        "wux-animation-group": "../animation-group/index"
5
+    }
6
+}

+ 1 - 0
components/dist/backdrop/index.wxml

@@ -0,0 +1 @@
1
+<wux-animation-group wux-class="{{ classes.wrap }}" in="{{ in }}" classNames="{{ classNames }}" bind:click="onClick" wrapStyle="{{ { zIndex } }}" disableScroll />

+ 15 - 0
components/dist/backdrop/index.wxss

@@ -0,0 +1,15 @@
1
+.wux-backdrop {
2
+  background: rgba(0,0,0,.4)
3
+}
4
+.wux-backdrop,
5
+.wux-backdrop--transparent {
6
+  position: fixed;
7
+  z-index: 1000;
8
+  top: 0;
9
+  right: 0;
10
+  left: 0;
11
+  bottom: 0
12
+}
13
+.wux-backdrop--transparent {
14
+  background: 0 0
15
+}

+ 199 - 0
components/dist/circle/index.js

@@ -0,0 +1,199 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+import classNames from '../helpers/classNames'
3
+
4
+const toAngle = (a) => a / 180 * Math.PI
5
+const percent = (a) => toAngle(a / 100 * 360)
6
+const easeInOutCubic = (a, b, c, d) => {
7
+    a /= d / 2
8
+    if (a < 1) return c / 2 * a * a * a + b
9
+    a -= 2
10
+    return c / 2 * (a * a * a + 2) + b
11
+}
12
+
13
+baseComponent({
14
+    properties: {
15
+        prefixCls: {
16
+            type: String,
17
+            value: 'wux-circle',
18
+        },
19
+        percent: {
20
+            type: Number,
21
+            value: 0,
22
+            observer: 'redraw',
23
+        },
24
+        strokeWidth: {
25
+            type: Number,
26
+            value: 10,
27
+        },
28
+        size: {
29
+            type: Number,
30
+            value: 120,
31
+            observer: 'updateStyle',
32
+        },
33
+        lineCap: {
34
+            type: String,
35
+            value: 'round',
36
+        },
37
+        backgroundColor: {
38
+            type: String,
39
+            value: '#f3f3f3',
40
+        },
41
+        color: {
42
+            type: String,
43
+            value: '#33cd5f',
44
+        },
45
+        sAngle: {
46
+            type: Number,
47
+            value: 0,
48
+            observer(newVal) {
49
+                this.setData({
50
+                    beginAngle: toAngle(newVal),
51
+                })
52
+            },
53
+        },
54
+        counterclockwise: {
55
+            type: Boolean,
56
+            value: false,
57
+        },
58
+        speed: {
59
+            type: Number,
60
+            value: 2000,
61
+        },
62
+        animate: {
63
+            type: Boolean,
64
+            value: true,
65
+        },
66
+        background: {
67
+            type: Boolean,
68
+            value: true,
69
+        },
70
+    },
71
+    data: {
72
+        beginAngle: 0,
73
+        startAngle: 0,
74
+        endAngle: 0,
75
+        currentAngle: 0,
76
+    },
77
+    computed: {
78
+        classes: ['prefixCls', function(prefixCls) {
79
+            const wrap = classNames(prefixCls)
80
+            const inner = `${prefixCls}__inner`
81
+
82
+            return {
83
+                wrap,
84
+                inner,
85
+            }
86
+        }],
87
+    },
88
+    methods: {
89
+        /**
90
+         * 更新样式
91
+         */
92
+        updateStyle(size = this.data.size) {
93
+            const style = `width: ${size}px; height: ${size}px;`
94
+
95
+            this.setData({
96
+                style,
97
+            })
98
+        },
99
+        /**
100
+         * 着帧绘制 canvas
101
+         */
102
+        redraw(value = this.data.percent) {
103
+            const endAngle = percent(value)
104
+            const now = Date.now()
105
+            const decrease = this.data.currentAngle > endAngle
106
+            const startAngle = !decrease ? this.data.currentAngle : this.data.endAngle
107
+
108
+            this.cancelNextCallback()
109
+            this.clearTimer()
110
+
111
+            this.safeSetData({ startAngle, endAngle }, () => {
112
+                this.animate(now, now, decrease)
113
+            })
114
+        },
115
+        /**
116
+         * 绘制 canvas
117
+         */
118
+        draw(line = true) {
119
+            const { lineCap, backgroundColor, color, size, strokeWidth, counterclockwise, background } = this.data
120
+            const position = size / 2
121
+            const radius = position - strokeWidth / 2
122
+            const p = 2 * Math.PI
123
+            const startAngle = counterclockwise ? p - this.data.beginAngle : this.data.beginAngle
124
+            const endAngle = counterclockwise ? p - (this.data.beginAngle + this.data.currentAngle) : this.data.beginAngle + this.data.currentAngle
125
+
126
+            // 创建 canvas 绘图上下文
127
+            this.ctx = this.ctx || wx.createCanvasContext('circle', this)
128
+
129
+            // 清除画布
130
+            this.ctx.clearRect(0, 0, size, size)
131
+
132
+            // 绘制背景
133
+            if (background) {
134
+                this.ctx.beginPath()
135
+                this.ctx.arc(position, position, radius, 0, 2 * Math.PI)
136
+                this.ctx.setLineWidth(strokeWidth)
137
+                this.ctx.setStrokeStyle(backgroundColor)
138
+                this.ctx.stroke()
139
+            }
140
+
141
+            // 绘制进度
142
+            if (line) {
143
+                this.ctx.beginPath()
144
+                this.ctx.arc(position, position, radius, startAngle, endAngle)
145
+                this.ctx.setLineWidth(strokeWidth)
146
+                this.ctx.setStrokeStyle(color)
147
+                this.ctx.setLineCap(lineCap)
148
+                this.ctx.stroke()
149
+            }
150
+
151
+            // 绘制完成
152
+            this.ctx.draw(false, () => {
153
+                this.triggerEvent('change', { value: this.data.currentAngle })
154
+            })
155
+        },
156
+        /**
157
+         * 开始动画
158
+         */
159
+        animate(c, d, e) {
160
+            const now = Date.now()
161
+            const f = now - c < 1 ? 1 : now - c
162
+            const { animate, speed, startAngle, endAngle } = this.data
163
+            const isEnd = !e && 1000 * this.data.currentAngle <= Math.floor(1000 * endAngle) || e && 1000 * this.data.currentAngle >= Math.floor(1000 * endAngle)
164
+
165
+            if (animate && c - d < 1.05 * speed && isEnd) {
166
+                const value = easeInOutCubic((c - d) / f, startAngle, endAngle - startAngle, speed / f)
167
+                const currentAngle = value < 0 ? 0 : value
168
+
169
+                c = Date.now()
170
+
171
+                this.safeSetData({ currentAngle }, () => {
172
+                    this.draw(currentAngle !== 0)
173
+                    this.timer = setTimeout(() => this.animate(c, d, e), 1000 / 60)
174
+                })
175
+            } else {
176
+                this.safeSetData({ currentAngle: endAngle }, () => this.draw(endAngle !== 0))
177
+            }
178
+        },
179
+        /**
180
+         * 清除定时器
181
+         */
182
+        clearTimer() {
183
+            if (this.timer) {
184
+                clearTimeout(this.timer)
185
+                this.timer = null
186
+            }
187
+        },
188
+    },
189
+    attached() {
190
+        this.updateStyle()
191
+        if (this.data.percent === 0) {
192
+            this.draw(false)
193
+        }
194
+    },
195
+    detached() {
196
+        this.ctx = null
197
+        this.clearTimer()
198
+    },
199
+})

+ 3 - 0
components/dist/circle/index.json

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

+ 6 - 0
components/dist/circle/index.wxml

@@ -0,0 +1,6 @@
1
+<view class="wux-class {{ classes.wrap }}" style="{{ style }}">
2
+    <canvas style="{{ style }}" canvas-id="circle" />
3
+    <view class="{{ classes.inner }}">
4
+    	<slot></slot>
5
+    </view>
6
+</view>

+ 13 - 0
components/dist/circle/index.wxss

@@ -0,0 +1,13 @@
1
+.wux-circle {
2
+  display: inline-block;
3
+  position: relative
4
+}
5
+.wux-circle__inner {
6
+  width: 100%;
7
+  text-align: center;
8
+  position: absolute;
9
+  left: 0;
10
+  top: 50%;
11
+  transform: translate3d(0,-50%,0);
12
+  line-height: 1
13
+}

+ 176 - 0
components/dist/countdown/index.js

@@ -0,0 +1,176 @@
1
+class Countdown {
2
+    constructor(options = {}, page = getCurrentPages()[getCurrentPages().length - 1]) {
3
+        Object.assign(this, {
4
+            page,
5
+            options,
6
+        })
7
+        this.__init()
8
+    }
9
+
10
+    /**
11
+     * 初始化
12
+     */
13
+    __init() {
14
+        this.setData = this.page.setData.bind(this.page)
15
+        this.restart(this.options)
16
+    }
17
+
18
+    /**
19
+     * 默认参数
20
+     */
21
+    setDefaults() {
22
+        return {
23
+            date: 'June 7, 2087 15:03:25',
24
+            refresh: 1000,
25
+            offset: 0,
26
+            onEnd() {},
27
+            render(date) {},
28
+        }
29
+    }
30
+
31
+    /**
32
+     * 合并参数
33
+     */
34
+    mergeOptions(options) {
35
+        const defaultOptions = this.setDefaults()
36
+
37
+        for (let i in defaultOptions) {
38
+            if (defaultOptions.hasOwnProperty(i)) {
39
+                this.options[i] = typeof options[i] !== 'undefined' ? options[i] : defaultOptions[i]
40
+
41
+                if (i === 'date' && typeof this.options.date !== 'object') {
42
+                    this.options.date = new Date(this.options.date)
43
+                }
44
+
45
+                if (typeof this.options[i] === 'function') {
46
+                    this.options[i] = this.options[i].bind(this)
47
+                }
48
+            }
49
+        }
50
+
51
+        if (typeof this.options.date !== 'object') {
52
+            this.options.date = new Date(this.options.date)
53
+        }
54
+    }
55
+
56
+    /**
57
+     * 计算日期差
58
+     */
59
+    getDiffDate() {
60
+        let diff = (this.options.date.getTime() - Date.now() + this.options.offset) / 1000
61
+
62
+        let dateData = {
63
+            years: 0,
64
+            days: 0,
65
+            hours: 0,
66
+            min: 0,
67
+            sec: 0,
68
+            millisec: 0,
69
+        }
70
+
71
+        if (diff <= 0) {
72
+            if (this.interval) {
73
+                this.stop()
74
+                this.options.onEnd()
75
+            }
76
+            return dateData
77
+        }
78
+
79
+        if (diff >= (365.25 * 86400)) {
80
+            dateData.years = Math.floor(diff / (365.25 * 86400))
81
+            diff -= dateData.years * 365.25 * 86400
82
+        }
83
+
84
+        if (diff >= 86400) {
85
+            dateData.days = Math.floor(diff / 86400)
86
+            diff -= dateData.days * 86400
87
+        }
88
+
89
+        if (diff >= 3600) {
90
+            dateData.hours = Math.floor(diff / 3600)
91
+            diff -= dateData.hours * 3600
92
+        }
93
+
94
+        if (diff >= 60) {
95
+            dateData.min = Math.floor(diff / 60)
96
+            diff -= dateData.min * 60
97
+        }
98
+
99
+        dateData.sec = Math.round(diff)
100
+
101
+        dateData.millisec = diff % 1 * 1000
102
+
103
+        return dateData
104
+    }
105
+
106
+    /**
107
+     * 补零
108
+     */
109
+    leadingZeros(num, length = 2) {
110
+        num = String(num)
111
+        if (num.length > length) return num
112
+        return (Array(length + 1).join('0') + num).substr(-length)
113
+    }
114
+
115
+    /**
116
+     * 更新组件
117
+     */
118
+    update(newDate) {
119
+        this.options.date = typeof newDate !== 'object' ? new Date(newDate) : newDate
120
+        this.render()
121
+        return this
122
+    }
123
+
124
+    /**
125
+     * 停止倒计时
126
+     */
127
+    stop() {
128
+        if (this.interval) {
129
+            clearInterval(this.interval)
130
+            this.interval = !1
131
+        }
132
+        return this
133
+    }
134
+
135
+    /**
136
+     * 渲染组件
137
+     */
138
+    render() {
139
+        this.options.render(this.getDiffDate())
140
+        return this
141
+    }
142
+
143
+    /**
144
+     * 启动倒计时
145
+     */
146
+    start() {
147
+        if (this.interval) return !1
148
+        this.render()
149
+        if (this.options.refresh) {
150
+            this.interval = setInterval(() => {
151
+                this.render()
152
+            }, this.options.refresh)
153
+        }
154
+        return this
155
+    }
156
+
157
+    /**
158
+     * 更新offset
159
+     */
160
+    updateOffset(offset) {
161
+        this.options.offset = offset
162
+        return this
163
+    }
164
+
165
+    /**
166
+     * 重启倒计时
167
+     */
168
+    restart(options = {}) {
169
+        this.mergeOptions(options)
170
+        this.interval = !1
171
+        this.start()
172
+        return this
173
+    }
174
+}
175
+
176
+export default Countdown

+ 220 - 0
components/dist/countup/index.js

@@ -0,0 +1,220 @@
1
+class CountUp {
2
+    constructor(startVal, endVal, decimals, duration, options = {}, page = getCurrentPages()[getCurrentPages().length - 1]) {
3
+        Object.assign(this, {
4
+            page,
5
+            startVal,
6
+            endVal,
7
+            decimals,
8
+            duration,
9
+            options,
10
+        })
11
+        this.__init()
12
+    }
13
+
14
+    /**
15
+     * 初始化
16
+     */
17
+    __init() {
18
+        this.setData = this.page.setData.bind(this.page)
19
+
20
+        this.lastTime = 0
21
+
22
+        // merge options
23
+        this.mergeOptions(this.options)
24
+
25
+        this.startVal = Number(this.startVal)
26
+        this.cacheVal = this.startVal
27
+        this.endVal = Number(this.endVal)
28
+        this.countDown = (this.startVal > this.endVal)
29
+        this.frameVal = this.startVal
30
+        this.decimals = Math.max(0, this.decimals || 0)
31
+        this.dec = Math.pow(10, this.decimals)
32
+        this.duration = Number(this.duration) * 1000 || 2000
33
+
34
+        // format startVal on initialization
35
+        this.printValue(this.formattingFn(this.startVal))
36
+    }
37
+
38
+    /**
39
+     * 默认参数
40
+     */
41
+    setDefaultOptions() {
42
+        return {
43
+            useEasing: true, // toggle easing
44
+            useGrouping: true, // 1,000,000 vs 1000000
45
+            separator: ',', // character to use as a separator
46
+            decimal: '.', // character to use as a decimal
47
+            easingFn: null, // optional custom easing closure function, default is Robert Penner's easeOutExpo
48
+            formattingFn: null, // optional custom formatting function, default is this.formatNumber below
49
+            printValue(value) {}, // printValue
50
+        }
51
+    }
52
+
53
+    /**
54
+     * 合并参数
55
+     */
56
+    mergeOptions(options) {
57
+        const defaultOptions = this.setDefaultOptions()
58
+
59
+        // extend default options with passed options object
60
+        for (let key in defaultOptions) {
61
+            if (defaultOptions.hasOwnProperty(key)) {
62
+                this.options[key] = typeof options[key] !== 'undefined' ? options[key] : defaultOptions[key]
63
+                if (typeof this.options[key] === 'function') {
64
+                    this.options[key] = this.options[key].bind(this)
65
+                }
66
+            }
67
+        }
68
+
69
+        if (this.options.separator === '') { this.options.useGrouping = !1 }
70
+        if (!this.options.prefix) this.options.prefix = ''
71
+        if (!this.options.suffix) this.options.suffix = ''
72
+
73
+        this.easingFn = this.options.easingFn ? this.options.easingFn : this.easeOutExpo
74
+        this.formattingFn = this.options.formattingFn ? this.options.formattingFn : this.formatNumber
75
+        this.printValue = this.options.printValue ? this.options.printValue : function() {}
76
+    }
77
+
78
+    /**
79
+     * 创建定时器
80
+     */
81
+    requestAnimationFrame(callback) {
82
+        let currTime = new Date().getTime()
83
+        let timeToCall = Math.max(0, 16 - (currTime - this.lastTime))
84
+        let timeout = setTimeout(() => {
85
+            callback.bind(this)(currTime + timeToCall)
86
+        }, timeToCall)
87
+        this.lastTime = currTime + timeToCall
88
+        return timeout
89
+    }
90
+
91
+    /**
92
+     * 清空定时器
93
+     */
94
+    cancelAnimationFrame(timeout) {
95
+        clearTimeout(timeout)
96
+    }
97
+
98
+    /**
99
+     * 格式化数字
100
+     */
101
+    formatNumber(nStr) {
102
+        nStr = nStr.toFixed(this.decimals)
103
+        nStr += ''
104
+        let x, x1, x2, rgx
105
+        x = nStr.split('.')
106
+        x1 = x[0]
107
+        x2 = x.length > 1 ? this.options.decimal + x[1] : ''
108
+        rgx = /(\d+)(\d{3})/
109
+        if (this.options.useGrouping) {
110
+            while (rgx.test(x1)) {
111
+                x1 = x1.replace(rgx, '$1' + this.options.separator + '$2')
112
+            }
113
+        }
114
+        return this.options.prefix + x1 + x2 + this.options.suffix
115
+    }
116
+
117
+    /**
118
+     * 过渡效果
119
+     */
120
+    easeOutExpo(t, b, c, d) {
121
+        return c * (-Math.pow(2, -10 * t / d) + 1) * 1024 / 1023 + b
122
+    }
123
+
124
+    /**
125
+     * 计数函数
126
+     */
127
+    count(timestamp) {
128
+        if (!this.startTime) { this.startTime = timestamp }
129
+
130
+        this.timestamp = timestamp
131
+        const progress = timestamp - this.startTime
132
+        this.remaining = this.duration - progress
133
+
134
+        // to ease or not to ease
135
+        if (this.options.useEasing) {
136
+            if (this.countDown) {
137
+                this.frameVal = this.startVal - this.easingFn(progress, 0, this.startVal - this.endVal, this.duration)
138
+            } else {
139
+                this.frameVal = this.easingFn(progress, this.startVal, this.endVal - this.startVal, this.duration)
140
+            }
141
+        } else {
142
+            if (this.countDown) {
143
+                this.frameVal = this.startVal - ((this.startVal - this.endVal) * (progress / this.duration))
144
+            } else {
145
+                this.frameVal = this.startVal + (this.endVal - this.startVal) * (progress / this.duration)
146
+            }
147
+        }
148
+
149
+        // don't go past endVal since progress can exceed duration in the last frame
150
+        if (this.countDown) {
151
+            this.frameVal = (this.frameVal < this.endVal) ? this.endVal : this.frameVal
152
+        } else {
153
+            this.frameVal = (this.frameVal > this.endVal) ? this.endVal : this.frameVal
154
+        }
155
+
156
+        // decimal
157
+        this.frameVal = Math.round(this.frameVal * this.dec) / this.dec
158
+
159
+        // format and print value
160
+        this.printValue(this.formattingFn(this.frameVal))
161
+
162
+        // whether to continue
163
+        if (progress < this.duration) {
164
+            this.rAF = this.requestAnimationFrame(this.count)
165
+        } else {
166
+            if (this.callback) { this.callback() }
167
+        }
168
+    }
169
+
170
+    /**
171
+     * 启动计数器
172
+     */
173
+    start(callback) {
174
+        this.callback = callback
175
+        this.rAF = this.requestAnimationFrame(this.count)
176
+        return !1
177
+    }
178
+
179
+    /**
180
+     * 停止计数器
181
+     */
182
+    pauseResume() {
183
+        if (!this.paused) {
184
+            this.paused = !0
185
+            this.cancelAnimationFrame(this.rAF)
186
+        } else {
187
+            this.paused = !1
188
+            delete this.startTime
189
+            this.duration = this.remaining
190
+            this.startVal = this.frameVal
191
+            this.requestAnimationFrame(this.count)
192
+        }
193
+    }
194
+
195
+    /**
196
+     * 重置计数器
197
+     */
198
+    reset() {
199
+        this.paused = !1
200
+        delete this.startTime
201
+        this.startVal = this.cacheVal
202
+        this.cancelAnimationFrame(this.rAF)
203
+        this.printValue(this.formattingFn(this.startVal))
204
+    }
205
+
206
+    /**
207
+     * 更新计数器
208
+     */
209
+    update(newEndVal) {
210
+        this.cancelAnimationFrame(this.rAF)
211
+        this.paused = !1
212
+        delete this.startTime
213
+        this.startVal = this.frameVal
214
+        this.endVal = Number(newEndVal)
215
+        this.countDown = (this.startVal > this.endVal)
216
+        this.rAF = this.requestAnimationFrame(this.count)
217
+    }
218
+}
219
+
220
+export default CountUp

+ 24 - 0
components/dist/helpers/arrayTreeFilter.js

@@ -0,0 +1,24 @@
1
+/**
2
+ * https://github.com/afc163/array-tree-filter
3
+ */
4
+function arrayTreeFilter(data, filterFn, options) {
5
+    options = options || {}
6
+    options.childrenKeyName = options.childrenKeyName || 'children'
7
+    let children = data || []
8
+    const result = []
9
+    let level = 0
10
+    do {
11
+        const foundItem = children.filter(function(item) {
12
+            return filterFn(item, level)
13
+        })[0]
14
+        if (!foundItem) {
15
+            break
16
+        }
17
+        result.push(foundItem)
18
+        children = foundItem[options.childrenKeyName] || []
19
+        level += 1
20
+    } while (children.length > 0)
21
+    return result
22
+}
23
+
24
+export default arrayTreeFilter

+ 77 - 0
components/dist/helpers/baseComponent.js

@@ -0,0 +1,77 @@
1
+import computedBehavior from './computedBehavior'
2
+import relationsBehavior from './relationsBehavior'
3
+import safeAreaBehavior from './safeAreaBehavior'
4
+import safeSetDataBehavior from './safeSetDataBehavior'
5
+import funcBehavior from './funcBehavior'
6
+import compareVersion from './compareVersion'
7
+
8
+const { platform, SDKVersion } = wx.getSystemInfoSync()
9
+const libVersion = '2.6.6'
10
+
11
+// check SDKVersion
12
+if (platform === 'devtools' && compareVersion(SDKVersion, libVersion) < 0) {
13
+    if (wx && wx.showModal) {
14
+        wx.showModal({
15
+            title: '提示',
16
+            content: `当前基础库版本(${SDKVersion})过低,无法使用 Wux Weapp 组件库,请更新基础库版本 >=${libVersion} 后重试。`,
17
+        })
18
+    }
19
+}
20
+
21
+const baseComponent = (options = {}) => {
22
+    // add default externalClasses
23
+    options.externalClasses = [
24
+        'wux-class',
25
+        'wux-hover-class',
26
+        ...(options.externalClasses = options.externalClasses || []),
27
+    ]
28
+
29
+    // add default behaviors
30
+    options.behaviors = [
31
+        relationsBehavior,
32
+        safeSetDataBehavior,
33
+        ...(options.behaviors = options.behaviors || []),
34
+        computedBehavior, // make sure it's triggered
35
+    ]
36
+
37
+    // use safeArea
38
+    if (options.useSafeArea) {
39
+        options.behaviors = [...options.behaviors, safeAreaBehavior]
40
+        delete options.useSafeArea
41
+    }
42
+
43
+    // use func
44
+    if (options.useFunc) {
45
+        options.behaviors = [...options.behaviors, funcBehavior]
46
+        delete options.useFunc
47
+    }
48
+
49
+    // use field
50
+    if (options.useField) {
51
+        options.behaviors = [...options.behaviors, 'wx://form-field']
52
+        delete options.useField
53
+    }
54
+
55
+    // use export
56
+    if (options.useExport) {
57
+        options.behaviors = [...options.behaviors, 'wx://component-export']
58
+        options.methods = {
59
+            export () {
60
+                return this
61
+            },
62
+            ...options.methods,
63
+        }
64
+        delete options.useExport
65
+    }
66
+
67
+    // add default options
68
+    options.options = {
69
+        multipleSlots: true,
70
+        addGlobalClass: true,
71
+        ...options.options,
72
+    }
73
+
74
+    return Component(options)
75
+}
76
+
77
+export default baseComponent

+ 29 - 0
components/dist/helpers/checkIPhoneX.js

@@ -0,0 +1,29 @@
1
+/**
2
+ * 获取系统信息
3
+ */
4
+
5
+let systemInfo = null
6
+
7
+export const getSystemInfo = (isForce) => {
8
+    if (!systemInfo || isForce) {
9
+        try {
10
+            systemInfo = wx.getSystemInfoSync()
11
+        } catch(e) { /* Ignore */ }
12
+    }
13
+
14
+    return systemInfo
15
+}
16
+
17
+// iPhoneX 竖屏安全区域
18
+export const safeAreaInset = {
19
+    top: 88, // StatusBar & NavBar
20
+    left: 0,
21
+    right: 0,
22
+    bottom: 34, // Home Indicator
23
+}
24
+
25
+const isIPhoneX = ({ model, platform }) => {
26
+    return /iPhone X/.test(model) && platform === 'ios'
27
+}
28
+
29
+export const checkIPhoneX = (isForce) => isIPhoneX(getSystemInfo(isForce))

+ 39 - 0
components/dist/helpers/classNames.js

@@ -0,0 +1,39 @@
1
+/*!
2
+  Copyright (c) 2018 Jed Watson.
3
+  Licensed under the MIT License (MIT), see
4
+  http://jedwatson.github.io/classnames
5
+*/
6
+/* global define */
7
+'use strict';
8
+
9
+var hasOwn = {}.hasOwnProperty;
10
+
11
+function classNames() {
12
+    var classes = [];
13
+
14
+    for (var i = 0; i < arguments.length; i++) {
15
+        var arg = arguments[i];
16
+        if (!arg) continue;
17
+
18
+        var argType = typeof arg;
19
+
20
+        if (argType === 'string' || argType === 'number') {
21
+            classes.push(arg);
22
+        } else if (Array.isArray(arg) && arg.length) {
23
+            var inner = classNames.apply(null, arg);
24
+            if (inner) {
25
+                classes.push(inner);
26
+            }
27
+        } else if (argType === 'object') {
28
+            for (var key in arg) {
29
+                if (hasOwn.call(arg, key) && arg[key]) {
30
+                    classes.push(key);
31
+                }
32
+            }
33
+        }
34
+    }
35
+
36
+    return classes.join(' ');
37
+}
38
+
39
+export default classNames

+ 27 - 0
components/dist/helpers/colors.js

@@ -0,0 +1,27 @@
1
+/**
2
+ * Don't modify this file!
3
+ * Colors generated by themes!
4
+ */
5
+
6
+/* eslint-disable */
7
+
8
+export const colors = {
9
+    'light': '#ddd',
10
+    'stable': '#b2b2b2',
11
+    'positive': '#387ef5',
12
+    'calm': '#11c1f3',
13
+    'balanced': '#33cd5f',
14
+    'energized': '#ffc900',
15
+    'assertive': '#ef473a',
16
+    'royal': '#886aea',
17
+    'dark': '#444',
18
+}
19
+
20
+export const isPresetColor = (color) => {
21
+    if (!color) {
22
+        return false
23
+    }
24
+    return colors[color] ? colors[color] : color
25
+}
26
+
27
+/* eslint-enable */

+ 27 - 0
components/dist/helpers/compareVersion.js

@@ -0,0 +1,27 @@
1
+function compareVersion(v1, v2) {
2
+    const $v1 = v1.split('.')
3
+    const $v2 = v2.split('.')
4
+    const len = Math.max($v1.length, $v2.length)
5
+
6
+    while ($v1.length < len) {
7
+        $v1.push('0')
8
+    }
9
+    while ($v2.length < len) {
10
+        $v2.push('0')
11
+    }
12
+
13
+    for (let i = 0; i < len; i++) {
14
+        const num1 = parseInt($v1[i])
15
+        const num2 = parseInt($v2[i])
16
+
17
+        if (num1 > num2) {
18
+            return 1
19
+        } else if (num1 < num2) {
20
+            return -1
21
+        }
22
+    }
23
+
24
+    return 0
25
+}
26
+
27
+export default compareVersion

+ 48 - 0
components/dist/helpers/computedBehavior.js

@@ -0,0 +1,48 @@
1
+import isEmpty from './isEmpty'
2
+import shallowEqual from './shallowEqual'
3
+
4
+const ALL_DATA_KEY = '**'
5
+
6
+const trim = (str = '') => str.replace(/\s/g, '')
7
+
8
+export default Behavior({
9
+    lifetimes: {
10
+        attached() {
11
+            this.initComputed()
12
+        },
13
+    },
14
+    definitionFilter(defFields) {
15
+        const { computed = {} } = defFields
16
+        const observers = Object.keys(computed).reduce((acc, name) => {
17
+            const [field, getter] = Array.isArray(computed[name]) ? computed[name] : [ALL_DATA_KEY, computed[name]]
18
+            return {
19
+                ...acc,
20
+                [field]: function(...args) {
21
+                    if (typeof getter === 'function') {
22
+                        const newValue = getter.apply(this, args)
23
+                        const oldValue = this.data[name]
24
+                        if (!isEmpty(newValue) && !shallowEqual(newValue, oldValue)) {
25
+                            this.setData({ [name]: newValue })
26
+                        }
27
+                    }
28
+                },
29
+            }
30
+        }, {})
31
+
32
+        Object.assign(defFields.observers = (defFields.observers || {}), observers)
33
+        Object.assign(defFields.methods = (defFields.methods || {}), {
34
+            initComputed: function(data = {}, isForce = false) {
35
+                if (!this.runInitComputed || isForce) {
36
+                    this.runInitComputed = false
37
+                    const context = this
38
+                    const result = { ...this.data, ...data }
39
+                    Object.keys(observers).forEach((key) => {
40
+                        const values = trim(key).split(',').reduce((acc, name) => ([...acc, result[name]]), [])
41
+                        observers[key].apply(context, values)
42
+                    })
43
+                    this.runInitComputed = true
44
+                }
45
+            },
46
+        })
47
+    },
48
+})

+ 75 - 0
components/dist/helpers/createFieldsStore.js

@@ -0,0 +1,75 @@
1
+class FieldsStore {
2
+    constructor(fields = {}) {
3
+        this.fields = fields
4
+    }
5
+
6
+    setFields(fields) {
7
+        Object.assign(this.fields, fields)
8
+    }
9
+
10
+    updateFields(fields) {
11
+        this.fields = fields
12
+    }
13
+
14
+    clearField(name) {
15
+        delete this.fields[name]
16
+    }
17
+
18
+    getValueFromFields(name, fields) {
19
+        const field = fields[name]
20
+        if (field && 'value' in field) {
21
+            return field.value
22
+        }
23
+        return field.initialValue
24
+    }
25
+
26
+    getAllFieldsName() {
27
+        const { fields } = this
28
+        return fields ? Object.keys(fields) : []
29
+    }
30
+
31
+    getField(name) {
32
+        return {
33
+            ...this.fields[name],
34
+            name,
35
+        }
36
+    }
37
+
38
+    getFieldValuePropValue(fieldOption) {
39
+        const { name, valuePropName } = fieldOption
40
+        const field = this.getField(name)
41
+        const fieldValue = 'value' in field ? field.value : field.initialValue
42
+
43
+        return {
44
+            [valuePropName]: fieldValue,
45
+        }
46
+    }
47
+
48
+    getFieldValue(name) {
49
+        return this.getValueFromFields(name, this.fields)
50
+    }
51
+
52
+    getFieldsValue(names) {
53
+        const fields = names || this.getAllFieldsName()
54
+        return fields.reduce((acc, name) => {
55
+            acc[name] = this.getFieldValue(name)
56
+            return acc
57
+        }, {})
58
+    }
59
+
60
+    resetFields(ns) {
61
+        const { fields } = this
62
+        const names = ns || this.getAllFieldsName()
63
+        return names.reduce((acc, name) => {
64
+            const field = fields[name]
65
+            if (field) {
66
+                acc[name] = field.initialValue
67
+            }
68
+            return acc
69
+        }, {})
70
+    }
71
+}
72
+
73
+export default function createFieldsStore(fields) {
74
+    return new FieldsStore(fields)
75
+}

+ 56 - 0
components/dist/helpers/debounce.js

@@ -0,0 +1,56 @@
1
+export default function debounce(func, wait, immediate) {
2
+    let timeout,
3
+        args,
4
+        context,
5
+        timestamp,
6
+        result
7
+
8
+    function later() {
9
+        const last = +(new Date()) - timestamp
10
+        if (last < wait && last >= 0) {
11
+            timeout = setTimeout(later, wait - last)
12
+        } else {
13
+            timeout = undefined
14
+            if (!immediate) {
15
+                result = func.apply(context, args)
16
+                if (!timeout) {
17
+                    context = undefined
18
+                    args = undefined
19
+                }
20
+            }
21
+        }
22
+    }
23
+
24
+    function debounced() {
25
+        context = this
26
+        args = arguments
27
+        timestamp = +(new Date())
28
+
29
+        const callNow = immediate && !timeout
30
+        if (!timeout) {
31
+            timeout = setTimeout(later, wait)
32
+        }
33
+
34
+        if (callNow) {
35
+            result = func.apply(context, args)
36
+            context = undefined
37
+            args = undefined
38
+        }
39
+
40
+        return result
41
+    }
42
+
43
+    function cancel() {
44
+        if (timeout !== undefined) {
45
+            clearTimeout(timeout)
46
+            timeout = undefined
47
+        }
48
+
49
+        context = undefined
50
+        args = undefined
51
+    }
52
+
53
+    debounced.cancel = cancel
54
+
55
+    return debounced
56
+}

+ 53 - 0
components/dist/helpers/eventsMixin.js

@@ -0,0 +1,53 @@
1
+const defaultEvents = {
2
+    onChange() {},
3
+}
4
+
5
+export default function eventsMixin(params = { defaultEvents }) {
6
+    return Behavior({
7
+        lifetimes: {
8
+            created () {
9
+                this._oriTriggerEvent = this.triggerEvent
10
+                this.triggerEvent = this._triggerEvent
11
+            },
12
+        },
13
+        properties: {
14
+            events: {
15
+                type: Object,
16
+                value: defaultEvents,
17
+            },
18
+        },
19
+        data: {
20
+            inputEvents: defaultEvents,
21
+        },
22
+        definitionFilter(defFields) {
23
+            // set default data
24
+            Object.assign(defFields.data = (defFields.data || {}), {
25
+                inputEvents: Object.assign({}, defaultEvents, defFields.inputEvents),
26
+            })
27
+
28
+            // set default methods
29
+            Object.assign(defFields.methods = (defFields.methods || {}), {
30
+                _triggerEvent(name, params, runCallbacks = true, option) {
31
+                    const { inputEvents } = this.data
32
+                    const method = `on${name[0].toUpperCase()}${name.slice(1)}`
33
+                    const func = inputEvents[method]
34
+
35
+                    if (runCallbacks && typeof func === 'function') {
36
+                        func.call(this, params)
37
+                    }
38
+
39
+                    this._oriTriggerEvent(name, params, option)
40
+                },
41
+            })
42
+
43
+            // set default observers
44
+            Object.assign(defFields.observers = (defFields.observers || {}), {
45
+                events(newVal) {
46
+                    this.setData({
47
+                        inputEvents: Object.assign({}, defaultEvents, this.data.inputEvents, newVal),
48
+                    })
49
+                },
50
+            })
51
+        },
52
+    })
53
+}

+ 97 - 0
components/dist/helpers/funcBehavior.js

@@ -0,0 +1,97 @@
1
+/**
2
+ * 过滤对象的函数属性
3
+ * @param {Object} opts
4
+ */
5
+const mergeOptionsToData = (opts = {}) => {
6
+    const options = Object.assign({}, opts)
7
+
8
+    for (const key in options) {
9
+        if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
10
+            delete options[key]
11
+        }
12
+    }
13
+
14
+    return options
15
+}
16
+
17
+/**
18
+ * Simple bind, faster than native
19
+ *
20
+ * @param {Function} fn
21
+ * @param {Object} ctx
22
+ * @return {Function}
23
+ */
24
+const bind = (fn, ctx) => {
25
+    return (...args) => {
26
+        return args.length ? fn.apply(ctx, args) : fn.call(ctx)
27
+    }
28
+}
29
+
30
+/**
31
+ * Object assign
32
+ */
33
+const assign = (...args) => Object.assign({}, ...args)
34
+
35
+export default Behavior({
36
+    definitionFilter(defFields) {
37
+        defFields.data = mergeOptionsToData(defFields.data)
38
+        defFields.data.in = false
39
+        defFields.data.visible = false
40
+    },
41
+    methods: {
42
+        /**
43
+         * 过滤对象的函数属性
44
+         * @param {Object} opts
45
+         */
46
+        $$mergeOptionsToData: mergeOptionsToData,
47
+        /**
48
+         * 合并参数并绑定方法
49
+         *
50
+         * @param {Object} opts 参数对象
51
+         * @param {Object} fns 方法挂载的属性
52
+         */
53
+        $$mergeOptionsAndBindMethods (opts = {}, fns = this.fns) {
54
+            const options = Object.assign({}, opts)
55
+
56
+            for (const key in options) {
57
+                if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
58
+                    fns[key] = bind(options[key], this)
59
+                    delete options[key]
60
+                }
61
+            }
62
+
63
+            return options
64
+        },
65
+        /**
66
+         * Promise setData
67
+         * @param {Array} args 参数对象
68
+         */
69
+        $$setData (...args) {
70
+            const params = assign({}, ...args)
71
+
72
+            return new Promise((resolve) => {
73
+                this.setData(params, resolve)
74
+            })
75
+        },
76
+        /**
77
+         * 延迟指定时间执行回调函数
78
+         * @param {Function} callback 回调函数
79
+         * @param {Number} timeout 延迟时间
80
+         */
81
+        $$requestAnimationFrame (callback = () => {}, timeout = 1000 / 60) {
82
+            return new Promise((resolve) => setTimeout(resolve, timeout)).then(callback)
83
+        },
84
+    },
85
+    /**
86
+     * 组件生命周期函数,在组件实例进入页面节点树时执行
87
+     */
88
+    created () {
89
+        this.fns = {}
90
+    },
91
+    /**
92
+     * 组件生命周期函数,在组件实例被从页面节点树移除时执行
93
+     */
94
+    detached () {
95
+        this.fns = {}
96
+    },
97
+})

+ 50 - 0
components/dist/helpers/gestures.js

@@ -0,0 +1,50 @@
1
+/**
2
+ * 获取触摸点位置信息
3
+ */
4
+export const getTouchPoints = (nativeEvent, index = 0) => {
5
+    const touches = nativeEvent.touches
6
+    const changedTouches = nativeEvent.changedTouches
7
+    const hasTouches = touches && touches.length > 0
8
+    const hasChangedTouches = changedTouches && changedTouches.length > 0
9
+    const points = !hasTouches && hasChangedTouches ? changedTouches[index] : hasTouches ? touches[index] : nativeEvent
10
+
11
+    return {
12
+        x: points.pageX,
13
+        y: points.pageY,
14
+    }
15
+}
16
+
17
+/**
18
+ * 获取触摸点个数
19
+ */
20
+export const getPointsNumber = (e) => e.touches && e.touches.length || e.changedTouches && e.changedTouches.length
21
+
22
+/**
23
+ * 判断是否为同一点
24
+ */
25
+export const isEqualPoints = (p1, p2) => p1.x === p2.x && p1.y === p2.y
26
+
27
+/**
28
+ * 判断是否为相近的两点
29
+ */
30
+export const isNearbyPoints = (p1, p2, DOUBLE_TAP_RADIUS = 25) => {
31
+    const xMove = Math.abs(p1.x - p2.x)
32
+    const yMove = Math.abs(p1.y - p2.y)
33
+    return xMove < DOUBLE_TAP_RADIUS & yMove < DOUBLE_TAP_RADIUS
34
+}
35
+
36
+/**
37
+ * 获取两点之间的距离
38
+ */
39
+export const getPointsDistance = (p1, p2) => {
40
+    const xMove = Math.abs(p1.x - p2.x)
41
+    const yMove = Math.abs(p1.y - p2.y)
42
+    return Math.sqrt(xMove * xMove + yMove * yMove)
43
+}
44
+
45
+/**
46
+ * 获取触摸移动方向
47
+ */
48
+export const getSwipeDirection = (x1, x2, y1, y2) => {
49
+    return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
50
+}

+ 19 - 0
components/dist/helpers/isEmpty.js

@@ -0,0 +1,19 @@
1
+/**
2
+ * Checks if a value is empty.
3
+ */
4
+function isEmpty(value) {
5
+    if (Array.isArray(value)) {
6
+        return value.length === 0
7
+    } else if (typeof value === 'object') {
8
+        if (value) {
9
+            for (const _ in value) {
10
+                return false
11
+            }
12
+        }
13
+        return true
14
+    } else {
15
+        return !value
16
+    }
17
+}
18
+
19
+export default isEmpty

+ 17 - 0
components/dist/helpers/mergeOptionsToData.js

@@ -0,0 +1,17 @@
1
+/**
2
+ * 过滤对象的函数属性
3
+ * @param {Object} opts
4
+ */
5
+const mergeOptionsToData = (opts = {}) => {
6
+    const options = Object.assign({}, opts)
7
+
8
+    for (const key in options) {
9
+        if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
10
+            delete options[key]
11
+        }
12
+    }
13
+
14
+    return options
15
+}
16
+
17
+export default mergeOptionsToData

+ 314 - 0
components/dist/helpers/popupMixin.js

@@ -0,0 +1,314 @@
1
+import classNames from './classNames'
2
+import eventsMixin from './eventsMixin'
3
+
4
+const DEFAULT_TRIGGER = 'onClick'
5
+const CELL_NAME = '../cell/index'
6
+const FIELD_NAME = '../field/index'
7
+
8
+const defaultToolbar = {
9
+    title: '请选择',
10
+    cancelText: '取消',
11
+    confirmText: '确定',
12
+}
13
+
14
+const defaultEvents = {
15
+    onChange() {},
16
+    onConfirm() {},
17
+    onCancel() {},
18
+    onVisibleChange() {},
19
+    onValueChange() {},
20
+}
21
+
22
+const defaultPlatformProps = {
23
+    labelPropName: 'label',
24
+    format(values, props) {
25
+        return Array.isArray(values.displayValue) ? values.displayValue.join(',') : values.displayValue
26
+    },
27
+}
28
+
29
+const defaultFieldNames = {
30
+    label: 'label',
31
+    value: 'value',
32
+    children: 'children',
33
+}
34
+
35
+export default function popupMixin(selector = '#wux-picker', platformProps = defaultPlatformProps) {
36
+    return Behavior({
37
+        behaviors: [eventsMixin({ defaultEvents })],
38
+        properties: {
39
+            toolbar: {
40
+                type: Object,
41
+                value: defaultToolbar,
42
+            },
43
+            trigger: {
44
+                type: String,
45
+                value: DEFAULT_TRIGGER,
46
+            },
47
+            defaultVisible: {
48
+                type: Boolean,
49
+                value: false,
50
+            },
51
+            visible: {
52
+                type: Boolean,
53
+                value: false,
54
+            },
55
+            controlled: {
56
+                type: Boolean,
57
+                value: false,
58
+            },
59
+            disabled: {
60
+                type: Boolean,
61
+                value: false,
62
+            },
63
+        },
64
+        data: {
65
+            mounted: false,
66
+            popupVisible: false,
67
+            inputValue: [],
68
+        },
69
+        methods: {
70
+            /**
71
+             * 设置组件显示或隐藏
72
+             */
73
+            setVisibleState(popupVisible, callback = () => {}) {
74
+                if (this.data.popupVisible !== popupVisible) {
75
+                    const params = {
76
+                        mounted: true,
77
+                        inputValue: this.data.value, // forceUpdate
78
+                        popupVisible,
79
+                    }
80
+                    this.setData(popupVisible ? params : { popupVisible }, () => {
81
+                        // collect field component & forceUpdate
82
+                        if (popupVisible && this.hasFieldDecorator) {
83
+                            const field = this.getFieldElem()
84
+                            if (field) {
85
+                                field.changeValue(field.data.value)
86
+                            }
87
+                        }
88
+                        callback()
89
+                    })
90
+                }
91
+            },
92
+            /**
93
+             * 触发 visibleChange 事件
94
+             */
95
+            fireVisibleChange(popupVisible) {
96
+                if (this.data.popupVisible !== popupVisible) {
97
+                    if (!this.data.controlled) {
98
+                        this.setVisibleState(popupVisible)
99
+                    }
100
+                    this.setScrollValue(undefined)
101
+                    this.triggerEvent('visibleChange', { visible: popupVisible })
102
+                }
103
+            },
104
+            /**
105
+             * 打开
106
+             */
107
+            open() {
108
+                this.fireVisibleChange(true)
109
+            },
110
+            /**
111
+             * 关闭
112
+             */
113
+            close(callback) {
114
+                if (typeof callback === 'function') {
115
+                    const values = this.getPickerValue(this.scrollValue || this.data.inputValue)
116
+                    callback.call(this, this.formatPickerValue(values))
117
+                }
118
+                this.fireVisibleChange(false)
119
+            },
120
+            /**
121
+             * 组件关闭时重置其内部数据
122
+             */
123
+            onClosed() {
124
+                this.picker = null
125
+                this.setData({ mounted: false, inputValue: null })
126
+            },
127
+            /**
128
+             * 点击确定按钮时的回调函数
129
+             */
130
+            onConfirm() {
131
+                this.close((values) => {
132
+                    this.triggerEvent('change', values) // collect field component
133
+                    this.triggerEvent('confirm', values)
134
+                })
135
+            },
136
+            /**
137
+             * 点击取消按钮时的回调函数
138
+             */
139
+            onCancel() {
140
+                this.close((values) => this.triggerEvent('cancel', values))
141
+            },
142
+            /**
143
+             * 每列数据选择变化后的回调函数
144
+             */
145
+            onValueChange(e) {
146
+                if (!this.data.mounted) return
147
+                const { value } = e.detail
148
+                if (this.data.cascade) {
149
+                    this.setCasecadeScrollValue(value)
150
+                } else {
151
+                    this.setScrollValue(value)
152
+                }
153
+
154
+                this.updated(value, true)
155
+                this.triggerEvent('valueChange', this.formatPickerValue(e.detail))
156
+            },
157
+            /**
158
+             * 获取当前 picker 的值
159
+             */
160
+            getPickerValue(value = this.data.inputValue) {
161
+                this.picker = this.picker || this.selectComponent(selector)
162
+                return this.picker && this.picker.getValue(value)
163
+            },
164
+            /**
165
+             * 格式化 picker 返回值
166
+             */
167
+            formatPickerValue(values) {
168
+                return {
169
+                    ...values,
170
+                    [platformProps.labelPropName]: platformProps.format(values, this.data),
171
+                }
172
+            },
173
+            /**
174
+             * 获取 field 父元素
175
+             */
176
+            getFieldElem() {
177
+                return this.field = (this.field || this.getRelationNodes(FIELD_NAME)[0])
178
+            },
179
+            /**
180
+             * 设置子元素 props
181
+             */
182
+            setChildProps() {
183
+                if (this.data.disabled) return
184
+                const elements = this.getRelationNodes(CELL_NAME)
185
+                const { trigger = DEFAULT_TRIGGER } = this.data
186
+                if (elements.length > 0) {
187
+                    elements.forEach((inputElem) => {
188
+                        const { inputEvents } = inputElem.data
189
+                        const oriInputEvents = inputElem.data.oriInputEvents || { ...inputEvents }
190
+                        inputEvents[trigger] = (...args) => {
191
+                            if (oriInputEvents && oriInputEvents[trigger]) {
192
+                                oriInputEvents[trigger](...args)
193
+                            }
194
+                            this.onTriggerClick()
195
+                        }
196
+                        inputElem.setData({ oriInputEvents, inputEvents })
197
+                    })
198
+                }
199
+            },
200
+            /**
201
+             * 触发事件
202
+             */
203
+            onTriggerClick() {
204
+                this.fireVisibleChange(!this.data.popupVisible)
205
+            },
206
+            /**
207
+             * 阻止移动触摸
208
+             */
209
+            noop() {},
210
+            /**
211
+             * 更新值
212
+             */
213
+            updated(inputValue, isForce) {
214
+                if (!this.hasFieldDecorator || isForce) {
215
+                    if (this.data.inputValue !== inputValue) {
216
+                        this.setData({ inputValue })
217
+                    }
218
+                }
219
+            },
220
+            /**
221
+             * 记录每列数据的变化值
222
+             */
223
+            setScrollValue(value) {
224
+                this.scrollValue = value
225
+            },
226
+            /**
227
+             * 记录每列数据的变化值 - 针对于级联
228
+             */
229
+            setCasecadeScrollValue(value) {
230
+                if (value && this.scrollValue) {
231
+                    const length = this.scrollValue.length
232
+                    if (length === value.length && this.scrollValue[length - 1] === value[length - 1]) {
233
+                        return
234
+                    }
235
+                }
236
+                this.setScrollValue(value)
237
+            },
238
+        },
239
+        lifetimes: {
240
+            ready() {
241
+                const { defaultVisible, visible, controlled, value } = this.data
242
+                const popupVisible = controlled ? visible : defaultVisible
243
+
244
+                // 若 defaultFieldNames 存在,则缓存之,并传递给子组件,只生效一次
245
+                if ('defaultFieldNames' in this.data) {
246
+                    this.setData({
247
+                        fieldNames: Object.assign({}, defaultFieldNames, this.data.defaultFieldNames),
248
+                    })
249
+                }
250
+
251
+                this.mounted = true
252
+                this.scrollValue = undefined
253
+                this.setVisibleState(popupVisible)
254
+                this.setChildProps()
255
+            },
256
+            detached() {
257
+                this.mounted = false
258
+            },
259
+        },
260
+        definitionFilter(defFields) {
261
+            // set default child
262
+            Object.assign(defFields.relations = (defFields.relations || {}), {
263
+                [CELL_NAME]: {
264
+                    type: 'child',
265
+                    observer() {
266
+                        this.setChildProps()
267
+                    },
268
+                },
269
+                [FIELD_NAME]: {
270
+                    type: 'ancestor',
271
+                },
272
+            })
273
+
274
+            // set default classes
275
+            Object.assign(defFields.computed = (defFields.computed || {}), {
276
+                classes: ['prefixCls', function(prefixCls) {
277
+                    const wrap = classNames(prefixCls)
278
+                    const toolbar = `${prefixCls}__toolbar`
279
+                    const inner = `${prefixCls}__inner`
280
+                    const cancel = classNames(`${prefixCls}__button`, {
281
+                        [`${prefixCls}__button--cancel`]: true,
282
+                    })
283
+                    const confirm = classNames(`${prefixCls}__button`, {
284
+                        [`${prefixCls}__button--confirm`]: true,
285
+                    })
286
+                    const hover = `${prefixCls}__button--hover`
287
+                    const title = `${prefixCls}__title`
288
+
289
+                    return {
290
+                        wrap,
291
+                        toolbar,
292
+                        inner,
293
+                        cancel,
294
+                        confirm,
295
+                        hover,
296
+                        title,
297
+                    }
298
+                }],
299
+            })
300
+
301
+            // set default observers
302
+            Object.assign(defFields.observers = (defFields.observers || {}), {
303
+                visible(popupVisible) {
304
+                    if (this.data.controlled) {
305
+                        this.setVisibleState(popupVisible)
306
+                    }
307
+                },
308
+                value(value) {
309
+                    this.updated(value)
310
+                },
311
+            })
312
+        },
313
+    })
314
+}

+ 67 - 0
components/dist/helpers/relationsBehavior.js

@@ -0,0 +1,67 @@
1
+import isEmpty from './isEmpty'
2
+import debounce from './debounce'
3
+
4
+/**
5
+ * bind func to obj
6
+ */
7
+function bindFunc(obj, method, observer) {
8
+    const oldFn = obj[method]
9
+    obj[method] = function(target) {
10
+        if (observer) {
11
+            observer.call(this, target, {
12
+                [method]: true,
13
+            })
14
+        }
15
+        if (oldFn) {
16
+            oldFn.call(this, target)
17
+        }
18
+    }
19
+}
20
+
21
+// default methods
22
+const methods = ['linked', 'linkChanged', 'unlinked']
23
+
24
+// extra props
25
+const extProps = ['observer']
26
+
27
+export default Behavior({
28
+    lifetimes: {
29
+        created() {
30
+            this._debounce = null
31
+        },
32
+        detached() {
33
+            if (this._debounce && this._debounce.cancel) {
34
+                this._debounce.cancel()
35
+            }
36
+        },
37
+    },
38
+    definitionFilter(defFields) {
39
+        const { relations } = defFields
40
+
41
+        if (!isEmpty(relations)) {
42
+            for (const key in relations) {
43
+                const relation = relations[key]
44
+
45
+                // bind func
46
+                methods.forEach((method) => bindFunc(relation, method, relation.observer))
47
+
48
+                // delete extProps
49
+                extProps.forEach((prop) => delete relation[prop])
50
+            }
51
+        }
52
+
53
+        Object.assign(defFields.methods = (defFields.methods || {}), {
54
+            getRelationsName: function(types = ['parent', 'child', 'ancestor', 'descendant']) {
55
+                return Object.keys(relations || {}).map((key) => {
56
+                    if (relations[key] && types.includes(relations[key].type)) {
57
+                        return key
58
+                    }
59
+                    return null
60
+                }).filter((v) => !!v)
61
+            },
62
+            debounce: function(func, wait = 0, immediate = false) {
63
+                return (this._debounce = this._debounce || debounce(func.bind(this), wait, immediate)).call(this)
64
+            },
65
+        })
66
+    },
67
+})

+ 46 - 0
components/dist/helpers/safeAreaBehavior.js

@@ -0,0 +1,46 @@
1
+import { getSystemInfo, checkIPhoneX } from './checkIPhoneX'
2
+
3
+const defaultSafeArea = {
4
+    top: false,
5
+    bottom: false,
6
+}
7
+
8
+const setSafeArea = (params) => {
9
+    if (typeof params === 'boolean') {
10
+        return Object.assign({}, defaultSafeArea, {
11
+            top: params,
12
+            bottom: params,
13
+        })
14
+    } else if (params !== null && typeof params === 'object') {
15
+        return Object.assign({}, defaultSafeArea)
16
+    } else if (typeof params === 'string') {
17
+        return Object.assign({}, defaultSafeArea, {
18
+            [params]: true,
19
+        })
20
+    }
21
+    return defaultSafeArea
22
+}
23
+
24
+export default Behavior({
25
+    properties: {
26
+        safeArea: {
27
+            type: [Boolean, String, Object],
28
+            value: false,
29
+        },
30
+    },
31
+    observers: {
32
+        safeArea(newVal) {
33
+            this.setData({ safeAreaConfig: setSafeArea(newVal) })
34
+        },
35
+    },
36
+    definitionFilter(defFields) {
37
+        const { statusBarHeight } = getSystemInfo() || {}
38
+        const isIPhoneX = checkIPhoneX()
39
+
40
+        Object.assign(defFields.data = (defFields.data || {}), {
41
+            safeAreaConfig: defaultSafeArea,
42
+            statusBarHeight,
43
+            isIPhoneX,
44
+        })
45
+    },
46
+})

+ 57 - 0
components/dist/helpers/safeSetDataBehavior.js

@@ -0,0 +1,57 @@
1
+export default Behavior({
2
+    lifetimes: {
3
+        created() {
4
+            this.nextCallback = null
5
+        },
6
+        detached() {
7
+            this.cancelNextCallback()
8
+        },
9
+    },
10
+    methods: {
11
+        /**
12
+         * safeSetData
13
+         * @param {Object} nextData 数据对象
14
+         * @param {Function} callback 回调函数
15
+         */
16
+        safeSetData(nextData, callback) {
17
+            this.pendingData = Object.assign({}, this.data, nextData)
18
+            callback = this.setNextCallback(callback)
19
+
20
+            this.setData(nextData, () => {
21
+                this.pendingData = null
22
+                callback()
23
+            })
24
+        },
25
+        /**
26
+         * 设置下一回调函数
27
+         * @param {Function} callback 回调函数
28
+         */
29
+        setNextCallback(callback) {
30
+            let active = true
31
+
32
+            this.nextCallback = (event) => {
33
+                if (active) {
34
+                    active = false
35
+                    this.nextCallback = null
36
+
37
+                    callback.call(this, event)
38
+                }
39
+            }
40
+
41
+            this.nextCallback.cancel = () => {
42
+                active = false
43
+            }
44
+
45
+            return this.nextCallback
46
+        },
47
+        /**
48
+         * 取消下一回调函数
49
+         */
50
+        cancelNextCallback() {
51
+            if (this.nextCallback !== null) {
52
+                this.nextCallback.cancel()
53
+                this.nextCallback = null
54
+            }
55
+        },
56
+    },
57
+})

+ 65 - 0
components/dist/helpers/shallowEqual.js

@@ -0,0 +1,65 @@
1
+/**
2
+ * Copyright (c) 2013-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @typechecks
8
+ * 
9
+ */
10
+
11
+/*eslint-disable no-self-compare */
12
+
13
+'use strict';
14
+
15
+var hasOwnProperty = Object.prototype.hasOwnProperty;
16
+
17
+/**
18
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
19
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
20
+ */
21
+function is(x, y) {
22
+    // SameValue algorithm
23
+    if (x === y) {
24
+        // Steps 1-5, 7-10
25
+        // Steps 6.b-6.e: +0 != -0
26
+        // Added the nonzero y check to make Flow happy, but it is redundant
27
+        return x !== 0 || y !== 0 || 1 / x === 1 / y;
28
+    } else {
29
+        // Step 6.a: NaN == NaN
30
+        return x !== x && y !== y;
31
+    }
32
+}
33
+
34
+/**
35
+ * Performs equality by iterating through keys on an object and returning false
36
+ * when any key has values which are not strictly equal between the arguments.
37
+ * Returns true when the values of all keys are strictly equal.
38
+ */
39
+function shallowEqual(objA, objB) {
40
+    if (is(objA, objB)) {
41
+        return true;
42
+    }
43
+
44
+    if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
45
+        return false;
46
+    }
47
+
48
+    var keysA = Object.keys(objA);
49
+    var keysB = Object.keys(objB);
50
+
51
+    if (keysA.length !== keysB.length) {
52
+        return false;
53
+    }
54
+
55
+    // Test for A's keys different from B.
56
+    for (var i = 0; i < keysA.length; i++) {
57
+        if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
58
+            return false;
59
+        }
60
+    }
61
+
62
+    return true;
63
+}
64
+
65
+export default shallowEqual

+ 139 - 0
components/dist/helpers/styleToCssString.js

@@ -0,0 +1,139 @@
1
+'use strict';
2
+
3
+/**
4
+ * CSS properties which accept numbers but are not in units of "px".
5
+ */
6
+var isUnitlessNumber = {
7
+    boxFlex: true,
8
+    boxFlexGroup: true,
9
+    columnCount: true,
10
+    flex: true,
11
+    flexGrow: true,
12
+    flexPositive: true,
13
+    flexShrink: true,
14
+    flexNegative: true,
15
+    fontWeight: true,
16
+    lineClamp: true,
17
+    lineHeight: true,
18
+    opacity: true,
19
+    order: true,
20
+    orphans: true,
21
+    widows: true,
22
+    zIndex: true,
23
+    zoom: true,
24
+
25
+    // SVG-related properties
26
+    fillOpacity: true,
27
+    strokeDashoffset: true,
28
+    strokeOpacity: true,
29
+    strokeWidth: true
30
+};
31
+
32
+/**
33
+ * @param {string} prefix vendor-specific prefix, eg: Webkit
34
+ * @param {string} key style name, eg: transitionDuration
35
+ * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
36
+ * WebkitTransitionDuration
37
+ */
38
+function prefixKey(prefix, key) {
39
+    return prefix + key.charAt(0).toUpperCase() + key.substring(1);
40
+}
41
+
42
+/**
43
+ * Support style names that may come passed in prefixed by adding permutations
44
+ * of vendor prefixes.
45
+ */
46
+var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
47
+
48
+// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
49
+// infinite loop, because it iterates over the newly added props too.
50
+Object.keys(isUnitlessNumber).forEach(function(prop) {
51
+    prefixes.forEach(function(prefix) {
52
+        isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
53
+    });
54
+});
55
+
56
+var msPattern = /^ms-/;
57
+
58
+var _uppercasePattern = /([A-Z])/g;
59
+
60
+/**
61
+ * Hyphenates a camelcased string, for example:
62
+ *
63
+ *   > hyphenate('backgroundColor')
64
+ *   < "background-color"
65
+ *
66
+ * For CSS style names, use `hyphenateStyleName` instead which works properly
67
+ * with all vendor prefixes, including `ms`.
68
+ *
69
+ * @param {string} string
70
+ * @return {string}
71
+ */
72
+function hyphenate(string) {
73
+    return string.replace(_uppercasePattern, '-$1').toLowerCase();
74
+}
75
+
76
+/**
77
+ * Hyphenates a camelcased CSS property name, for example:
78
+ *
79
+ *   > hyphenateStyleName('backgroundColor')
80
+ *   < "background-color"
81
+ *   > hyphenateStyleName('MozTransition')
82
+ *   < "-moz-transition"
83
+ *   > hyphenateStyleName('msTransition')
84
+ *   < "-ms-transition"
85
+ *
86
+ * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
87
+ * is converted to `-ms-`.
88
+ *
89
+ * @param {string} string
90
+ * @return {string}
91
+ */
92
+function hyphenateStyleName(string) {
93
+    return hyphenate(string).replace(msPattern, '-ms-');
94
+}
95
+
96
+var isArray = Array.isArray;
97
+var keys = Object.keys;
98
+
99
+var counter = 1;
100
+// Follows syntax at https://developer.mozilla.org/en-US/docs/Web/CSS/content,
101
+// including multiple space separated values.
102
+var unquotedContentValueRegex = /^(normal|none|(\b(url\([^)]*\)|chapter_counter|attr\([^)]*\)|(no-)?(open|close)-quote|inherit)((\b\s*)|$|\s+))+)$/;
103
+
104
+function buildRule(key, value) {
105
+    if (!isUnitlessNumber[key] && typeof value === 'number') {
106
+        value = '' + value + 'px';
107
+    } else if (key === 'content' && !unquotedContentValueRegex.test(value)) {
108
+        value = "'" + value.replace(/'/g, "\\'") + "'";
109
+    }
110
+
111
+    return hyphenateStyleName(key) + ': ' + value + ';  ';
112
+}
113
+
114
+function styleToCssString(rules) {
115
+    var result = ''
116
+    if (typeof rules === 'string') {
117
+        rules = rules.trim()
118
+        return rules.slice(-1) === ";" ? `${rules} ` : `${rules}; `
119
+    }
120
+    if (!rules || keys(rules).length === 0) {
121
+        return result;
122
+    }
123
+    var styleKeys = keys(rules);
124
+    for (var j = 0, l = styleKeys.length; j < l; j++) {
125
+        var styleKey = styleKeys[j];
126
+        var value = rules[styleKey];
127
+
128
+        if (isArray(value)) {
129
+            for (var i = 0, len = value.length; i < len; i++) {
130
+                result += buildRule(styleKey, value[i]);
131
+            }
132
+        } else {
133
+            result += buildRule(styleKey, value);
134
+        }
135
+    }
136
+    return result;
137
+}
138
+
139
+export default styleToCssString

+ 67 - 0
components/dist/index.js

@@ -0,0 +1,67 @@
1
+/**
2
+ * Wux Weapp 3.8.7
3
+ * 一套组件化、可复用、易扩展的微信小程序 UI 组件库
4
+ * https://github.com/wux-weapp/wux-weapp#readme
5
+ *
6
+ * Copyright 2017-2021 skyvow
7
+ *
8
+ * Released under the MIT License
9
+ *
10
+ * Released on: 2021-4-12
11
+ */
12
+
13
+import $wuxCountDown from './countdown/index'
14
+import $wuxCountUp from './countup/index'
15
+
16
+/**
17
+ * 使用选择器选择组件实例节点,返回匹配到的第一个组件实例对象
18
+ * @param {String} selector 节点选择器
19
+ * @param {Object} ctx 页面栈或组件的实例,默认为当前页面栈实例
20
+ */
21
+export const getCtx = (selector, ctx = getCurrentPages()[getCurrentPages().length - 1]) => {
22
+    const componentCtx = ctx.selectComponent(selector)
23
+
24
+    if (!componentCtx) {
25
+        throw new Error('无法找到对应的组件,请按文档说明使用组件')
26
+    }
27
+
28
+    return componentCtx
29
+}
30
+
31
+const version = '3.8.7'
32
+const $wuxActionSheet = (selector = '#wux-actionsheet', ctx) => getCtx(selector, ctx)
33
+const $wuxBackdrop = (selector = '#wux-backdrop', ctx) => getCtx(selector, ctx)
34
+const $wuxCalendar = (selector = '#wux-calendar', ctx) => getCtx(selector, ctx)
35
+const $wuxDialog = (selector = '#wux-dialog', ctx) => getCtx(selector, ctx)
36
+const $wuxForm = (selector = '#wux-form', ctx) => getCtx(selector, ctx)
37
+const $wuxGallery = (selector = '#wux-gallery', ctx) => getCtx(selector, ctx)
38
+const $wuxKeyBoard = (selector = '#wux-keyboard', ctx) => getCtx(selector, ctx)
39
+const $wuxLoading = (selector = '#wux-loading', ctx) => getCtx(selector, ctx)
40
+const $wuxNotification = (selector = '#wux-notification', ctx) => getCtx(selector, ctx)
41
+const $startWuxRefresher = (selector = '#wux-refresher', ctx) => getCtx(selector, ctx).triggerRefresh()
42
+const $stopWuxRefresher = (selector = '#wux-refresher', ctx) => getCtx(selector, ctx).finishPullToRefresh()
43
+const $stopWuxLoader = (selector = '#wux-refresher', ctx, isEnd) => getCtx(selector, ctx).finishLoadmore(isEnd)
44
+const $wuxSelect = (selector = '#wux-select', ctx) => getCtx(selector, ctx)
45
+const $wuxToast = (selector = '#wux-toast', ctx) => getCtx(selector, ctx)
46
+const $wuxToptips = (selector = '#wux-toptips', ctx) => getCtx(selector, ctx)
47
+
48
+export {
49
+    version,
50
+	$wuxCountDown,
51
+	$wuxCountUp,
52
+	$wuxActionSheet,
53
+	$wuxBackdrop,
54
+	$wuxCalendar,
55
+	$wuxDialog,
56
+	$wuxForm,
57
+	$wuxGallery,
58
+	$wuxKeyBoard,
59
+	$wuxLoading,
60
+	$wuxNotification,
61
+	$startWuxRefresher,
62
+	$stopWuxRefresher,
63
+	$stopWuxLoader,
64
+	$wuxSelect,
65
+	$wuxToast,
66
+	$wuxToptips
67
+}

+ 66 - 0
components/dist/landscape/index.js

@@ -0,0 +1,66 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+import classNames from '../helpers/classNames'
3
+
4
+baseComponent({
5
+    properties: {
6
+        prefixCls: {
7
+            type: String,
8
+            value: 'wux-landscape',
9
+        },
10
+        newPrefixCls: {
11
+            type: String,
12
+            value: '',
13
+        },
14
+        visible: {
15
+            type: Boolean,
16
+            value: false,
17
+        },
18
+        mask: {
19
+            type: Boolean,
20
+            value: true,
21
+            observer(newVal) {
22
+                this.setData({ showMask: newVal })
23
+            },
24
+        },
25
+        maskClosable: {
26
+            type: Boolean,
27
+            value: false,
28
+        },
29
+        closable: {
30
+            type: Boolean,
31
+            value: true,
32
+        },
33
+    },
34
+    data: {
35
+        showMask: true,
36
+    },
37
+    computed: {
38
+        classes: ['prefixCls, showMask', function(prefixCls, showMask) {
39
+            const wrap = classNames(prefixCls, {
40
+                [`${prefixCls}--has-mask`]: showMask,
41
+            })
42
+            const popup = `${prefixCls}__popup`
43
+            const popupBody = `${prefixCls}__popup-body`
44
+            const popupClose = `${prefixCls}__popup-close`
45
+            const inner = `${prefixCls}__inner`
46
+            const close = `${prefixCls}__close`
47
+            const x = `${prefixCls}__close-x`
48
+
49
+            return {
50
+                wrap,
51
+                popup,
52
+                popupBody,
53
+                popupClose,
54
+                inner,
55
+                close,
56
+                x,
57
+            }
58
+        }],
59
+    },
60
+    methods: {
61
+        onClose() {
62
+            this.triggerEvent('close', { visible: !this.data.visible })
63
+        },
64
+    },
65
+    attached() {},
66
+})

+ 6 - 0
components/dist/landscape/index.json

@@ -0,0 +1,6 @@
1
+{
2
+    "component": true,
3
+    "usingComponents": {
4
+        "wux-popup": "../popup/index"
5
+    }
6
+}

+ 22 - 0
components/dist/landscape/index.wxml

@@ -0,0 +1,22 @@
1
+<wux-popup
2
+    wux-content-class="{{ classes.popup }}"
3
+    wux-body-class="{{ classes.popupBody }}"
4
+    wux-close-class="{{ classes.popupClose }}"
5
+    visible="{{ visible }}"
6
+    hasHeader="{{ false }}"
7
+    hasFooter="{{ false }}"
8
+    mask="{{ showMask }}"
9
+    maskClosable="{{ maskClosable }}"
10
+    bind:close="onClose"
11
+    newPrefixCls="{{ newPrefixCls }}"
12
+>
13
+	<view class="wux-class {{ classes.wrap }}">
14
+	    <view class="{{ classes.inner }}">
15
+	    	<slot></slot>
16
+	    </view>
17
+        <view class="{{ classes.close }}" wx:if="{{ closable }}" bindtap="onClose">
18
+            <!-- <text class="{{ classes.x }}"></text> -->
19
+            <image class="close" src="/images/home_popup_close.png"></image>
20
+        </view>
21
+	</view>
22
+</wux-popup>

+ 36 - 0
components/dist/landscape/index.wxss

@@ -0,0 +1,36 @@
1
+.wux-landscape__popup {
2
+  background-color: transparent!important
3
+}
4
+.wux-landscape__popup-body {
5
+  padding: 0!important
6
+}
7
+.wux-landscape__inner {
8
+  padding: 30rpx;
9
+  font-size: 30rpx;
10
+  line-height: 1.5;
11
+  color: rgba(0,0,0,.65)
12
+}
13
+.wux-landscape__inner > image {
14
+  width: 100%;
15
+  max-width: 100%
16
+}
17
+.wux-landscape__close {
18
+  position: relative;
19
+  display: inline-block;
20
+  margin-top: 10rpx
21
+}
22
+.wux-landscape__close image {
23
+  width: 96rpx;
24
+  height: 96rpx;
25
+}
26
+.wux-landscape__close-x {
27
+  display: inline-block;
28
+  width: 48rpx;
29
+  height: 48rpx;
30
+  background-repeat: no-repeat;
31
+  background-size: cover;
32
+  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20width%3D%2230%22%20height%3D%2230%22%20viewBox%3D%220%200%2030%2030%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20fill%3D%22%23888%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M1.414%200l28.284%2028.284-1.414%201.414L0%201.414z%22%2F%3E%3Cpath%20d%3D%22M28.284%200L0%2028.284l1.414%201.414L29.698%201.414z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
33
+}
34
+.wux-landscape--has-mask .wux-landscape__close-x {
35
+  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20width%3D%2230%22%20height%3D%2230%22%20viewBox%3D%220%200%2030%2030%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20fill%3D%22%23fff%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M1.414%200l28.284%2028.284-1.414%201.414L0%201.414z%22%2F%3E%3Cpath%20d%3D%22M28.284%200L0%2028.284l1.414%201.414L29.698%201.414z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
36
+}

+ 194 - 0
components/dist/popup/index.js

@@ -0,0 +1,194 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+import classNames from '../helpers/classNames'
3
+import styleToCssString from '../helpers/styleToCssString'
4
+import { $wuxBackdrop } from '../index'
5
+
6
+baseComponent({
7
+    useSafeArea: true,
8
+    externalClasses: ['wux-content-class', 'wux-header-class', 'wux-body-class', 'wux-footer-class', 'wux-close-class'],
9
+    properties: {
10
+        prefixCls: {
11
+            type: String,
12
+            value: 'wux-popup',
13
+        },
14
+        newPrefixCls: {
15
+            type: String,
16
+            value: '',
17
+        },
18
+        animationPrefixCls: {
19
+            type: String,
20
+            value: 'wux-animate',
21
+        },
22
+        title: {
23
+            type: String,
24
+            value: '',
25
+        },
26
+        content: {
27
+            type: String,
28
+            value: '',
29
+        },
30
+        extra: {
31
+            type: String,
32
+            value: '',
33
+        },
34
+        position: {
35
+            type: String,
36
+            value: 'center',
37
+            observer: 'getTransitionName',
38
+        },
39
+        wrapStyle: {
40
+            type: [String, Object],
41
+            value: '',
42
+            observer(newVal) {
43
+                this.setData({
44
+                    extStyle: styleToCssString(newVal),
45
+                })
46
+            },
47
+        },
48
+        closable: {
49
+            type: Boolean,
50
+            value: false,
51
+        },
52
+        mask: {
53
+            type: Boolean,
54
+            value: true,
55
+        },
56
+        maskClosable: {
57
+            type: Boolean,
58
+            value: true,
59
+        },
60
+        visible: {
61
+            type: Boolean,
62
+            value: false,
63
+            observer: 'setPopupVisible',
64
+        },
65
+        zIndex: {
66
+            type: Number,
67
+            value: 1000,
68
+        },
69
+        hasHeader: {
70
+            type: Boolean,
71
+            value: true,
72
+        },
73
+        hasFooter: {
74
+            type: Boolean,
75
+            value: true,
76
+        },
77
+        mountOnEnter: {
78
+            type: Boolean,
79
+            value: true,
80
+        },
81
+        unmountOnExit: {
82
+            type: Boolean,
83
+            value: true,
84
+        },
85
+    },
86
+    data: {
87
+        transitionName: '',
88
+        popupVisible: false,
89
+        extStyle: '',
90
+    },
91
+    computed: {
92
+        classes: ['prefixCls, position, safeAreaConfig, isIPhoneX', function(prefixCls, position, safeAreaConfig, isIPhoneX) {
93
+            const wrap = classNames(`${prefixCls}-position`, {
94
+                [`${prefixCls}-position--${position}`]: position,
95
+                [`${prefixCls}-position--is-iphonex`]: safeAreaConfig.bottom && isIPhoneX,
96
+            })
97
+            const content = `${prefixCls}__content`
98
+            const hd = `${prefixCls}__hd`
99
+            const title = `${prefixCls}__title`
100
+            const bd = `${prefixCls}__bd`
101
+            const ft = `${prefixCls}__ft`
102
+            const extra = `${prefixCls}__extra`
103
+            const close = `${prefixCls}__close`
104
+            const x = `${prefixCls}__close-x`
105
+
106
+            return {
107
+                wrap,
108
+                content,
109
+                hd,
110
+                title,
111
+                bd,
112
+                ft,
113
+                extra,
114
+                close,
115
+                x,
116
+            }
117
+        }],
118
+    },
119
+    methods: {
120
+        /**
121
+         * 点击关闭按钮事件
122
+         */
123
+        close() {
124
+            this.triggerEvent('close')
125
+        },
126
+        /**
127
+         * 点击蒙层事件
128
+         */
129
+        onMaskClick() {
130
+            if (this.data.maskClosable) {
131
+                this.close()
132
+            }
133
+        },
134
+        /**
135
+         * 组件关闭后的回调函数
136
+         */
137
+        onExited() {
138
+            this.triggerEvent('closed')
139
+        },
140
+        /**
141
+         * 获取过渡的类名
142
+         */
143
+        getTransitionName(value = this.data.position) {
144
+            const { animationPrefixCls } = this.data
145
+            let transitionName = ''
146
+
147
+            switch (value) {
148
+                    case 'top':
149
+                        transitionName = `${animationPrefixCls}--slideInDown`
150
+                        break
151
+                    case 'right':
152
+                        transitionName = `${animationPrefixCls}--slideInRight`
153
+                        break
154
+                    case 'bottom':
155
+                        transitionName = `${animationPrefixCls}--slideInUp`
156
+                        break
157
+                    case 'left':
158
+                        transitionName = `${animationPrefixCls}--slideInLeft`
159
+                        break
160
+                    default:
161
+                        transitionName = `${animationPrefixCls}--fadeIn`
162
+                        break
163
+            }
164
+
165
+            this.setData({ transitionName })
166
+        },
167
+        /**
168
+         * 设置 popup 组件的显示隐藏
169
+         */
170
+        setPopupVisible(popupVisible) {
171
+            if (this.data.popupVisible !== popupVisible) {
172
+                this.setData({ popupVisible })
173
+                this.setBackdropVisible(popupVisible)
174
+            }
175
+        },
176
+        /**
177
+         * 设置 backdrop 组件的显示隐藏
178
+         */
179
+        setBackdropVisible(visible) {
180
+            if (this.data.mask && this.$wuxBackdrop) {
181
+                this.$wuxBackdrop[visible ? 'retain' : 'release']()
182
+            }
183
+        },
184
+    },
185
+    created() {
186
+        if (this.data.mask) {
187
+            this.$wuxBackdrop = $wuxBackdrop('#wux-backdrop', this)
188
+        }
189
+    },
190
+    attached() {
191
+        this.setPopupVisible(this.data.visible)
192
+        this.getTransitionName()
193
+    },
194
+})

+ 7 - 0
components/dist/popup/index.json

@@ -0,0 +1,7 @@
1
+{
2
+    "component": true,
3
+    "usingComponents": {
4
+        "wux-animation-group": "../animation-group/index",
5
+        "wux-backdrop": "../backdrop/index"
6
+    }
7
+}

+ 26 - 0
components/dist/popup/index.wxml

@@ -0,0 +1,26 @@
1
+<wux-backdrop id="wux-backdrop" wx:if="{{ mask }}" bind:click="onMaskClick" zIndex="{{ zIndex }}" />
2
+<view class="wux-class {{ classes.wrap }}" style="{{ extStyle }}">
3
+    <wux-animation-group wux-class="{{ prefixCls }} {{ newPrefixCls }}" in="{{ popupVisible }}" classNames="{{ transitionName }}" bind:exited="onExited" wrapStyle="{{ { zIndex } }}" mountOnEnter="{{ mountOnEnter }}" unmountOnExit="{{ unmountOnExit }}">
4
+        <view class="wux-content-class {{ classes.content }}">
5
+            <view class="wux-header-class {{ classes.hd }}" wx:if="{{ hasHeader }}">
6
+                <view class="{{ classes.title }}" wx:if="{{ title }}">{{ title }}</view>
7
+                <block wx:else>
8
+                    <slot name="header"></slot>
9
+                </block>
10
+            </view>
11
+            <view class="wux-body-class {{ classes.bd }}">
12
+                <view wx:if="{{ content }}">{{ content }}</view>
13
+                <slot></slot>
14
+            </view>
15
+            <view class="wux-footer-class {{ classes.ft }}" wx:if="{{ hasFooter }}">
16
+                <view class="{{ classes.extra }}" wx:if="{{ extra }}">{{ extra }}</view>
17
+                <block wx:else>
18
+                    <slot name="footer"></slot>
19
+                </block>
20
+            </view>
21
+            <view class="wux-close-class {{ classes.close }}" wx:if="{{ closable }}" bindtap="close">
22
+                <text class="{{ classes.x }}"></text>
23
+            </view>
24
+        </view>
25
+    </wux-animation-group>
26
+</view>

+ 121 - 0
components/dist/popup/index.wxss

@@ -0,0 +1,121 @@
1
+.wux-popup {
2
+  position: fixed;
3
+  z-index: 1000;
4
+  width: 80%;
5
+  max-width: 600rpx
6
+}
7
+
8
+.wux-popup-widtn {
9
+  width: 100%;
10
+  max-width: 100%
11
+}
12
+
13
+.wux-popup-position.wux-popup-position--center .wux-popup {
14
+  top: 50%;
15
+  left: 50%;
16
+  transform: translate(-50%,-50%)
17
+}
18
+.wux-popup-position.wux-popup-position--center .wux-popup__content {
19
+  border-radius: 8rpx
20
+}
21
+.wux-popup-position.wux-popup-position--center .wux-popup__hd {
22
+  padding: 1.3em 1.6em .5em
23
+}
24
+.wux-popup-position.wux-popup-position--center .wux-popup__bd {
25
+  padding: 0 1.6em .8em
26
+}
27
+.wux-popup-position.wux-popup-position--center .wux-popup__ft::after {
28
+  content: " ";
29
+  position: absolute;
30
+  left: 0;
31
+  top: 0;
32
+  right: 0;
33
+  height: 1PX;
34
+  border-top: 1PX solid #d9d9d9;
35
+  color: #d9d9d9;
36
+  transform-origin: 0 0;
37
+  transform: scaleY(.5)
38
+}
39
+.wux-popup-position.wux-popup-position--top .wux-popup {
40
+  position: fixed;
41
+  left: 0;
42
+  top: 0;
43
+  width: 100%;
44
+  max-width: 100%
45
+}
46
+.wux-popup-position.wux-popup-position--right .wux-popup {
47
+  position: fixed;
48
+  top: 0;
49
+  right: 0;
50
+  width: 80%;
51
+  max-width: 100%;
52
+  height: 100%;
53
+  max-height: 100%
54
+}
55
+.wux-popup-position.wux-popup-position--bottom .wux-popup {
56
+  position: fixed;
57
+  left: 0;
58
+  bottom: 0;
59
+  width: 100%;
60
+  max-width: 100%
61
+}
62
+.wux-popup-position.wux-popup-position--left .wux-popup {
63
+  position: fixed;
64
+  left: 0;
65
+  top: 0;
66
+  width: 80%;
67
+  max-width: 100%;
68
+  height: 100%;
69
+  max-height: 100%
70
+}
71
+.wux-popup-position.wux-popup-position--is-iphonex .wux-popup__content {
72
+  padding-bottom: 68rpx
73
+}
74
+.wux-popup__content {
75
+  position: relative;
76
+  background-color: #fff;
77
+  border: 0;
78
+  background-clip: padding-box;
79
+  height: 100%;
80
+  text-align: center;
81
+  overflow: hidden
82
+}
83
+.wux-popup__title {
84
+  font-weight: 400;
85
+  font-size: 36rpx;
86
+  color: rgba(0,0,0,.85)
87
+}
88
+.wux-popup__bd {
89
+  min-height: 80rpx;
90
+  font-size: 30rpx;
91
+  line-height: 1.3;
92
+  word-wrap: break-word;
93
+  word-break: break-all;
94
+  color: rgba(0,0,0,.45)
95
+}
96
+.wux-popup__ft {
97
+  position: relative;
98
+  line-height: 96rpx;
99
+  font-size: 36rpx;
100
+  display: -ms-flexbox;
101
+  display: flex
102
+}
103
+.wux-popup__close {
104
+  border: 0;
105
+  padding: 6rpx;
106
+  background-color: transparent;
107
+  outline: 0;
108
+  position: absolute;
109
+  top: 12rpx;
110
+  right: 12rpx;
111
+  height: 42rpx;
112
+  width: 42rpx
113
+}
114
+.wux-popup__close-x {
115
+  display: inline-block;
116
+  width: 30rpx;
117
+  height: 30rpx;
118
+  background-repeat: no-repeat;
119
+  background-size: cover;
120
+  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20width%3D%2230%22%20height%3D%2230%22%20viewBox%3D%220%200%2030%2030%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20fill%3D%22%23888%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M1.414%200l28.284%2028.284-1.414%201.414L0%201.414z%22%2F%3E%3Cpath%20d%3D%22M28.284%200L0%2028.284l1.414%201.414L29.698%201.414z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
121
+}

+ 103 - 0
components/dist/progress/index.js

@@ -0,0 +1,103 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+import classNames from '../helpers/classNames'
3
+import styleToCssString from '../helpers/styleToCssString'
4
+import { colors } from '../helpers/colors'
5
+
6
+const defaultColors = {
7
+    normal: colors.positive,
8
+    progress: colors.positive,
9
+    error: colors.assertive,
10
+    success: colors.balanced,
11
+}
12
+
13
+baseComponent({
14
+    properties: {
15
+        prefixCls: {
16
+            type: String,
17
+            value: 'wux-progress',
18
+        },
19
+        percent: {
20
+            type: Number,
21
+            value: 0,
22
+            observer: 'updateStyle',
23
+        },
24
+        strokeWidth: {
25
+            type: Number,
26
+            value: 10,
27
+            observer: 'updateStyle',
28
+        },
29
+        activeColor: {
30
+            type: String,
31
+            value: '',
32
+            observer: 'updateStyle',
33
+        },
34
+        backgroundColor: {
35
+            type: String,
36
+            value: '#f3f3f3',
37
+        },
38
+        status: {
39
+            type: String,
40
+            value: 'normal',
41
+            observer: 'updateStyle',
42
+        },
43
+        shape: {
44
+            type: String,
45
+            value: 'round',
46
+        },
47
+        barStyle: {
48
+            type: [String, Object],
49
+            value: '',
50
+            observer(newVal) {
51
+                this.setData({
52
+                    extStyle: styleToCssString(newVal),
53
+                })
54
+            },
55
+        },
56
+        showInfo: {
57
+            type: Boolean,
58
+            value: false,
59
+        },
60
+    },
61
+    data: {
62
+        width: 0,
63
+        style: '',
64
+        extStyle: '',
65
+    },
66
+    computed: {
67
+        classes: ['prefixCls, shape, status', function(prefixCls, shape, status) {
68
+            const wrap = classNames(prefixCls, {
69
+                [`${prefixCls}--${shape}`]: shape,
70
+                [`${prefixCls}--${status}`]: status,
71
+            })
72
+            const outer = `${prefixCls}__outer`
73
+            const inner = `${prefixCls}__inner`
74
+            const bar = `${prefixCls}__bar`
75
+            const text = `${prefixCls}__text`
76
+
77
+            return {
78
+                wrap,
79
+                outer,
80
+                inner,
81
+                bar,
82
+                text,
83
+            }
84
+        }],
85
+    },
86
+    methods: {
87
+        updateStyle(opts = {}) {
88
+            const { percent, strokeWidth, activeColor, status } = Object.assign({}, this.data, opts)
89
+            const width = percent < 0 ? 0 : percent > 100 ? 100 : percent
90
+            const height = strokeWidth > 0 ? strokeWidth : 10
91
+            const backgroundColor = activeColor ? activeColor : (defaultColors[status] || defaultColors['normal'])
92
+            const style = `background-color: ${backgroundColor}; width: ${width}%; height: ${height}px;`
93
+
94
+            this.setData({
95
+                width,
96
+                style,
97
+            })
98
+        },
99
+    },
100
+    attached() {
101
+        this.updateStyle()
102
+    },
103
+})

+ 3 - 0
components/dist/progress/index.json

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

+ 8 - 0
components/dist/progress/index.wxml

@@ -0,0 +1,8 @@
1
+<view class="wux-class {{ classes.wrap }}">
2
+    <view class="{{ classes.outer }}">
3
+    	<view class="{{ classes.inner }}" style="background-color: {{ backgroundColor }}">
4
+    		<view class="{{ classes.bar }}" style="{{ style }} {{ extStyle }}"></view>
5
+    	</view>
6
+    </view>
7
+    <view class="{{ classes.text }}" wx:if="{{ showInfo }}">{{ width + '%' }}</view>
8
+</view>

+ 51 - 0
components/dist/progress/index.wxss

@@ -0,0 +1,51 @@
1
+.wux-progress {
2
+  display: -ms-flexbox;
3
+  display: flex;
4
+  -ms-flex-align: center;
5
+  align-items: center
6
+}
7
+.wux-progress__outer {
8
+  -ms-flex: 1;
9
+  flex: 1
10
+}
11
+.wux-progress__inner {
12
+  background-color: #f3f3f3
13
+}
14
+.wux-progress__bar {
15
+  background-color: #33cd5f;
16
+  transition: all .3s linear 0s
17
+}
18
+.wux-progress__text {
19
+  margin-left: 10rpx;
20
+  text-align: left;
21
+  vertical-align: middle
22
+}
23
+.wux-progress--round .wux-progress__bar,
24
+.wux-progress--round .wux-progress__inner {
25
+  border-radius: 200rpx
26
+}
27
+.wux-progress--progress .wux-progress__bar {
28
+  position: relative
29
+}
30
+.wux-progress--progress .wux-progress__bar::before {
31
+  content: '';
32
+  opacity: 0;
33
+  position: absolute;
34
+  top: 0;
35
+  left: 0;
36
+  right: 0;
37
+  bottom: 0;
38
+  background: #fff;
39
+  border-radius: 20rpx;
40
+  animation: progress 2s ease-in-out infinite
41
+}
42
+@keyframes progress {
43
+  0% {
44
+    opacity: .3;
45
+    width: 0
46
+  }
47
+  100% {
48
+    opacity: 0;
49
+    width: 100%
50
+  }
51
+}

+ 203 - 0
components/dist/search-bar/index.js

@@ -0,0 +1,203 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+import classNames from '../helpers/classNames'
3
+import styleToCssString from '../helpers/styleToCssString'
4
+
5
+baseComponent({
6
+    properties: {
7
+        prefixCls: {
8
+            type: String,
9
+            value: 'wux-search-bar',
10
+        },
11
+        defaultValue: {
12
+            type: String,
13
+            value: '',
14
+        },
15
+        value: {
16
+            type: String,
17
+            value: '',
18
+            observer(newVal) {
19
+                if (this.data.controlled) {
20
+                    this.updated(newVal)
21
+                }
22
+            },
23
+        },
24
+        placeholder: {
25
+            type: String,
26
+            value: '搜索',
27
+        },
28
+        placeholderStyle: {
29
+            type: [String, Object],
30
+            value: '',
31
+            observer(newVal) {
32
+                this.setData({
33
+                    extStyle: styleToCssString(newVal),
34
+                })
35
+            },
36
+        },
37
+        placeholderClass: {
38
+            type: String,
39
+            value: 'input-placeholder',
40
+        },
41
+        disabled: {
42
+            type: Boolean,
43
+            value: false,
44
+        },
45
+        maxlength: {
46
+            type: Number,
47
+            value: 140,
48
+        },
49
+        cursorSpacing: {
50
+            type: Number,
51
+            value: 11,
52
+        },
53
+        focus: {
54
+            type: Boolean,
55
+            value: false,
56
+            observer(newVal) {
57
+                this.setData({
58
+                    inputFocus: newVal,
59
+                })
60
+            },
61
+        },
62
+        confirmType: {
63
+            type: String,
64
+            value: 'search',
65
+        },
66
+        confirmHold: {
67
+            type: Boolean,
68
+            value: false,
69
+        },
70
+        cursor: {
71
+            type: Number,
72
+            value: -1,
73
+        },
74
+        selectionStart: {
75
+            type: Number,
76
+            value: -1,
77
+        },
78
+        selectionEnd: {
79
+            type: Number,
80
+            value: -1,
81
+        },
82
+        adjustPosition: {
83
+            type: Boolean,
84
+            value: true,
85
+        },
86
+        clear: {
87
+            type: Boolean,
88
+            value: false,
89
+        },
90
+        cancelText: {
91
+            type: String,
92
+            value: '取消',
93
+        },
94
+        showCancel: {
95
+            type: Boolean,
96
+            value: false,
97
+        },
98
+        controlled: {
99
+            type: Boolean,
100
+            value: false,
101
+        },
102
+    },
103
+    data: {
104
+        inputValue: '',
105
+        inputFocus: false,
106
+        extStyle: '',
107
+    },
108
+    computed: {
109
+        classes: ['prefixCls, disabled, inputFocus', function(prefixCls, disabled, inputFocus) {
110
+            const wrap = classNames(prefixCls, {
111
+                [`${prefixCls}--focus`]: inputFocus,
112
+                [`${prefixCls}--disabled`]: disabled,
113
+            })
114
+            const form = `${prefixCls}__form`
115
+            const box = `${prefixCls}__box`
116
+            const search = `${prefixCls}__search`
117
+            const input = `${prefixCls}__input`
118
+            const clear = `${prefixCls}__clear`
119
+            const label = `${prefixCls}__label`
120
+            const icon = `${prefixCls}__icon`
121
+            const text = `${prefixCls}__text`
122
+            const cancel = `${prefixCls}__cancel`
123
+
124
+            return {
125
+                wrap,
126
+                form,
127
+                box,
128
+                search,
129
+                input,
130
+                clear,
131
+                label,
132
+                icon,
133
+                text,
134
+                cancel,
135
+            }
136
+        }],
137
+    },
138
+    methods: {
139
+        updated(inputValue) {
140
+            if (this.data.inputValue !== inputValue) {
141
+                this.setData({
142
+                    inputValue,
143
+                })
144
+            }
145
+        },
146
+        onChange(e) {
147
+            if (!this.data.controlled) {
148
+                this.updated(e.detail.value)
149
+            }
150
+
151
+            if (!this.data.inputFocus) {
152
+                this.setData({
153
+                    inputFocus: true,
154
+                })
155
+            }
156
+
157
+            this.triggerEvent('change', e.detail)
158
+        },
159
+        onFocus(e) {
160
+            this.setData({
161
+                inputFocus: true,
162
+            })
163
+
164
+            this.triggerEvent('focus', e.detail)
165
+        },
166
+        onBlur(e) {
167
+            this.setData({
168
+                inputFocus: false,
169
+            })
170
+
171
+            this.triggerEvent('blur', e.detail)
172
+        },
173
+        onConfirm(e) {
174
+            this.triggerEvent('confirm', e.detail)
175
+        },
176
+        onClear() {
177
+            const { controlled, inputValue } = this.data
178
+
179
+            this.setData({
180
+                inputValue: controlled ? inputValue : '',
181
+                inputFocus: true,
182
+            })
183
+
184
+            this.triggerEvent('clear', { value: '' })
185
+        },
186
+        onCancel() {
187
+            this.triggerEvent('cancel', { value: this.data.inputValue })
188
+        },
189
+        onClick() {
190
+            if (this.data.disabled) return
191
+
192
+            this.setData({
193
+                inputFocus: true,
194
+            })
195
+        },
196
+    },
197
+    attached() {
198
+        const { defaultValue, value, controlled } = this.data
199
+        const inputValue = controlled ? value : defaultValue
200
+
201
+        this.updated(inputValue)
202
+    },
203
+})

+ 3 - 0
components/dist/search-bar/index.json

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

+ 35 - 0
components/dist/search-bar/index.wxml

@@ -0,0 +1,35 @@
1
+<view class="wux-class wux-class-flex {{ classes.wrap }}">
2
+    <view class="{{ classes.form }}">
3
+        <view class="{{ classes.box }}" wx:if="{{ showCancel || inputFocus || (inputValue && inputValue.length > 0) }}">
4
+            <icon class="{{ classes.search }}" type="search" color="#B2B2B2" size="14" />
5
+            <input
6
+            	type="text"
7
+            	class="{{ classes.input }}"
8
+            	value="{{ inputValue }}"
9
+            	placeholder="{{ placeholder }}"
10
+                placeholder-style="{{ extStyle }}"
11
+                placeholder-class="{{ placeholderClass }}"
12
+                disabled="{{ disabled }}"
13
+                maxlength="{{ maxlength }}"
14
+                cursor-spacing="{{ cursorSpacing }}"
15
+                focus="{{ inputFocus }}"
16
+                confirm-type="{{ confirmType }}"
17
+                confirm-hold="{{ confirmHold }}"
18
+                cursor="{{ cursor }}"
19
+                selection-start="{{ selectionStart }}"
20
+                selection-end="{{ selectionEnd }}"
21
+                adjust-position="{{ adjustPosition }}"
22
+            	bindinput="onChange"
23
+            	bindfocus="onFocus"
24
+            	bindblur="onBlur"
25
+            	bindconfirm="onConfirm"
26
+            />
27
+            <icon class="{{ classes.clear }}" type="clear" color="#B2B2B2" size="14" wx:if="{{ clear && !disabled && inputValue && inputValue.length > 0 }}" bindtap="onClear" />
28
+        </view>
29
+        <label class="{{ classes.label }}" wx:if="{{ !inputFocus && !inputValue }}" bindtap="onClick">
30
+            <icon class="{{ classes.icon }}" type="search" color="#B2B2B2" size="14" />
31
+            <text class="{{ classes.text }}">{{ placeholder }}</text>
32
+        </label>
33
+    </view>
34
+    <view class="{{ classes.cancel }}" wx:if="{{ showCancel || inputFocus || (inputValue && inputValue.length > 0) }}" bindtap="onCancel">{{ cancelText }}</view>
35
+</view>

+ 135 - 0
components/dist/search-bar/index.wxss

@@ -0,0 +1,135 @@
1
+.wux-class-flex {
2
+  display: flex;
3
+  align-items: center;
4
+}
5
+
6
+.wux-search-bar {
7
+  position: relative;
8
+  padding: 16rpx 20rpx;
9
+  display: -ms-flexbox;
10
+  display: flex;
11
+  box-sizing: border-box;
12
+  background-color: #efeff4
13
+}
14
+
15
+.wux-search-bar::before {
16
+  content: " ";
17
+  position: absolute;
18
+  left: 0;
19
+  top: 0;
20
+  right: 0;
21
+  height: 2rPX;
22
+  /* border-top: 2rPX solid #d9d9d9; */
23
+  color: #d9d9d9;
24
+  transform-origin: 0 0;
25
+  transform: scaleY(.5)
26
+}
27
+
28
+.wux-search-bar::after {
29
+  content: " ";
30
+  position: absolute;
31
+  left: 0;
32
+  bottom: 0;
33
+  right: 0;
34
+  height: 2rPX;
35
+  border-bottom: 2rPX solid #d9d9d9;
36
+  color: #d9d9d9;
37
+  transform-origin: 0 100%;
38
+  transform: scaleY(.5)
39
+}
40
+
41
+.wux-search-bar__form {
42
+  position: relative;
43
+  -ms-flex: 1;
44
+  flex: 1;
45
+  width: 100%;
46
+  height: 56rpx;
47
+  overflow: hidden;
48
+  background-color: #fff;
49
+  background-clip: padding-box;
50
+  border-radius: 8rpx
51
+}
52
+
53
+.wux-search-bar__box {
54
+  position: relative;
55
+  padding-left: 60rpx;
56
+  padding-right: 60rpx;
57
+  height: 100%;
58
+  width: 100%;
59
+  box-sizing: border-box;
60
+  z-index: 1
61
+}
62
+
63
+.wux-search-bar__input {
64
+  padding: 8rpx 0;
65
+  width: 100%;
66
+  height: 56rpx;
67
+  min-height: inherit;
68
+  border: 0;
69
+  font-size: 28rpx;
70
+  line-height: 56rpx;
71
+  box-sizing: border-box;
72
+  background: 0 0
73
+}
74
+
75
+.wux-search-bar__label {
76
+  display: -ms-flexbox;
77
+  display: flex;
78
+  -ms-flex-pack: center;
79
+  justify-content: center;
80
+  -ms-flex-align: center;
81
+  align-items: center;
82
+  position: absolute;
83
+  top: 2rpx;
84
+  right: 2rpx;
85
+  bottom: 2rpx;
86
+  left: 2rpx;
87
+  z-index: 2;
88
+  border-radius: 8rpx;
89
+  text-align: center;
90
+  color: rgba(0, 0, 0, .45);
91
+  background: #fff
92
+}
93
+
94
+.wux-search-bar__text {
95
+  display: inline-block;
96
+  font-size: 28rpx;
97
+  vertical-align: middle;
98
+  margin-left: 10rpx
99
+}
100
+
101
+.wux-search-bar__cancel {
102
+  margin-left: 20rpx;
103
+  line-height: 56rpx;
104
+  /* color: #33cd5f; */
105
+  white-space: nowrap;
106
+  font-size: 36rpx;
107
+  font-family: PingFangSC-Medium;
108
+  color: #FA7D22;
109
+}
110
+
111
+.wux-search-bar__icon {
112
+  vertical-align: middle
113
+}
114
+
115
+.wux-search-bar__search {
116
+  position: absolute;
117
+  left: 20rpx;
118
+  top: 16rpx
119
+}
120
+
121
+.wux-search-bar__clear {
122
+  display: none;
123
+  position: absolute;
124
+  top: 16rpx;
125
+  right: 0;
126
+  padding: 0 20rpx
127
+}
128
+
129
+.wux-search-bar--focus .wux-search-bar__clear {
130
+  display: block
131
+}
132
+
133
+.wux-search-bar--disabled {
134
+  opacity: .3
135
+}

+ 77 - 0
components/dist/spin/index.js

@@ -0,0 +1,77 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+import classNames from '../helpers/classNames'
3
+
4
+baseComponent({
5
+    properties: {
6
+        prefixCls: {
7
+            type: String,
8
+            value: 'wux-spin',
9
+        },
10
+        classNames: {
11
+            type: null,
12
+            value: 'wux-animate--fadeIn',
13
+        },
14
+        tip: {
15
+            type: String,
16
+            value: '',
17
+        },
18
+        size: {
19
+            type: String,
20
+            value: 'default',
21
+        },
22
+        spinning: {
23
+            type: Boolean,
24
+            value: true,
25
+            observer: 'updated',
26
+        },
27
+        nested: {
28
+            type: Boolean,
29
+            value: false,
30
+        },
31
+    },
32
+    data: {
33
+        spinVisible: true,
34
+    },
35
+    computed: {
36
+        classes: ['prefixCls, size, nested, tip, spinVisible', function(prefixCls, size, nested, showText, spinVisible) {
37
+            const wrap = classNames(prefixCls, {
38
+                [`${prefixCls}--${size}`]: size,
39
+                [`${prefixCls}--nested`]: nested,
40
+                [`${prefixCls}--show-text`]: showText,
41
+            })
42
+            const anim = !nested ? `${prefixCls}__spinning` : `${prefixCls}__spinning--nested`
43
+            const dots = `${prefixCls}__dots`
44
+            const dot = `${prefixCls}__dot`
45
+            const tip = `${prefixCls}__tip`
46
+            const container = classNames(`${prefixCls}__container`, {
47
+                [`${prefixCls}__container--blur`]: spinVisible,
48
+            })
49
+
50
+            return {
51
+                wrap,
52
+                anim,
53
+                dots,
54
+                dot,
55
+                tip,
56
+                container,
57
+            }
58
+        }],
59
+    },
60
+    methods: {
61
+        updated(spinVisible) {
62
+            if (this.data.nested) {
63
+                if(spinVisible) {
64
+                    this.setData({
65
+                        spinVisible,
66
+                    }) 
67
+                } else {
68
+                    setTimeout(() => {
69
+                        this.setData({
70
+                            spinVisible,
71
+                        })
72
+                    }, 500);
73
+                }
74
+            }
75
+        },
76
+    },
77
+})

+ 6 - 0
components/dist/spin/index.json

@@ -0,0 +1,6 @@
1
+{
2
+    "component": true,
3
+    "usingComponents": {
4
+        "wux-animation-group": "../animation-group/index"
5
+    }
6
+}

+ 14 - 0
components/dist/spin/index.wxml

@@ -0,0 +1,14 @@
1
+<view class="wux-class {{ classes.wrap }}">
2
+    <wux-animation-group wux-class="{{ classes.anim }}" in="{{ spinVisible }}" classNames="{{ classNames }}">
3
+        <view class="{{ classes.dots }}">
4
+            <view class="{{ classes.dot }}">dot</view>
5
+            <view class="{{ classes.dot }}">dot</view>
6
+            <view class="{{ classes.dot }}">dot</view>
7
+            <view class="{{ classes.dot }}">dot</view>
8
+        </view>
9
+        <view class="{{ classes.tip }}" wx:if="{{ tip }}">{{ tip }}</view>
10
+    </wux-animation-group>
11
+    <view class="{{ classes.container }}" wx:if="{{ nested }}">
12
+        <slot></slot>
13
+    </view>
14
+</view>

+ 168 - 0
components/dist/spin/index.wxss

@@ -0,0 +1,168 @@
1
+.wux-spin {
2
+  position: relative;
3
+  display: inline-block
4
+}
5
+
6
+.wux-spin__spinning {
7
+  opacity: 1;
8
+  position: static;
9
+  display: inline-block;
10
+  vertical-align: middle;
11
+  text-align: center;
12
+  font-size: 28rpx;
13
+  line-height: 1.5;
14
+  color: #FA7D22
15
+}
16
+
17
+.wux-spin__spinning--nested {
18
+  vertical-align: middle;
19
+  text-align: center;
20
+  font-size: 28rpx;
21
+  line-height: 1.5;
22
+  color: #FA7D22;
23
+  display: block;
24
+  position: absolute;
25
+  height: 100%;
26
+  max-height: 640rpx;
27
+  width: 100%;
28
+  z-index: 4
29
+}
30
+
31
+.wux-spin--nested {
32
+  display: block;
33
+  height: 100%;
34
+}
35
+
36
+.wux-spin--nested .wux-spin__tip {
37
+  position: absolute;
38
+  top: 50%;
39
+  width: 100%;
40
+  padding-top: 12rpx;
41
+  text-shadow: 0 2rpx 4rpx #fff
42
+}
43
+
44
+.wux-spin__dots {
45
+  width: 40rpx;
46
+  height: 40rpx;
47
+  overflow: hidden;
48
+  display: inline-block;
49
+  transform: rotate(45deg);
50
+  animation: rotate 1.2s infinite linear
51
+}
52
+
53
+.wux-spin--nested .wux-spin__dots {
54
+  position: absolute;
55
+  top: 50%;
56
+  left: 50%;
57
+  margin: -20rpx
58
+}
59
+
60
+.wux-spin__dot {
61
+  width: 18rpx;
62
+  height: 18rpx;
63
+  border-radius: 100%;
64
+  background-color: #FA7D22;
65
+  transform: scale(.75);
66
+  display: block;
67
+  position: absolute;
68
+  opacity: .3;
69
+  animation: spinMove 1s infinite linear alternate;
70
+  transform-origin: 50% 50%;
71
+  text-indent: -999em
72
+}
73
+
74
+.wux-spin__dot:first-child {
75
+  left: 0;
76
+  top: 0
77
+}
78
+
79
+.wux-spin__dot:nth-child(2) {
80
+  right: 0;
81
+  top: 0;
82
+  animation-delay: .4s
83
+}
84
+
85
+.wux-spin__dot:nth-child(3) {
86
+  right: 0;
87
+  bottom: 0;
88
+  animation-delay: .8s
89
+}
90
+
91
+.wux-spin__dot:nth-child(4) {
92
+  left: 0;
93
+  bottom: 0;
94
+  animation-delay: 1.2s
95
+}
96
+
97
+.wux-spin--show-text.wux-spin--nested .wux-spin__dots {
98
+  margin-top: -40rpx
99
+}
100
+
101
+.wux-spin__container {
102
+  position: relative;
103
+  transition: opacity .3s;
104
+  zoom: 1
105
+}
106
+
107
+.wux-spin__container--blur {
108
+  overflow: hidden;
109
+  opacity: .5;
110
+  filter: blur(1rpx);
111
+  transform: translateZ(0)
112
+}
113
+
114
+.wux-spin--small .wux-spin__tip {
115
+  padding-top: 6rpx
116
+}
117
+
118
+.wux-spin--small .wux-spin__dots {
119
+  width: 28rpx;
120
+  height: 28rpx
121
+}
122
+
123
+.wux-spin--small.wux-spin--nested .wux-spin__dots {
124
+  margin: -14rpx
125
+}
126
+
127
+.wux-spin--small .wux-spin__dot {
128
+  width: 12rpx;
129
+  height: 12rpx
130
+}
131
+
132
+.wux-spin--small.wux-spin--show-text.wux-spin--nested .wux-spin__dots {
133
+  margin-top: -34rpx
134
+}
135
+
136
+.wux-spin--large .wux-spin__tip {
137
+  padding-top: 24rpx
138
+}
139
+
140
+.wux-spin--large .wux-spin__dots {
141
+  width: 64rpx;
142
+  height: 64rpx
143
+}
144
+
145
+.wux-spin--large.wux-spin--nested .wux-spin__dots {
146
+  margin: -32rpx
147
+}
148
+
149
+.wux-spin--large .wux-spin__dot {
150
+  width: 28rpx;
151
+  height: 28rpx
152
+}
153
+
154
+.wux-spin--large.wux-spin--show-text.wux-spin--nested .wux-spin__dots {
155
+  margin-top: -52rpx
156
+}
157
+
158
+@keyframes rotate {
159
+  to {
160
+    transform: rotate(405deg)
161
+  }
162
+}
163
+
164
+@keyframes spinMove {
165
+  to {
166
+    opacity: 1
167
+  }
168
+}

+ 106 - 0
components/local/change-env/change-env.js

@@ -0,0 +1,106 @@
1
+// components/local/change-env/change-env.js
2
+// app.js
3
+import {
4
+  login
5
+} from '../../../utils/http';
6
+Component({
7
+  /**
8
+   * 组件的属性列表
9
+   */
10
+  options: {
11
+    addGlobalClass: true
12
+  },
13
+  properties: {
14
+    visible: {
15
+      type: Boolean,
16
+      value: false,
17
+    },
18
+  },
19
+
20
+  /**
21
+   * 组件的初始数据
22
+   */
23
+  data: {
24
+    inputVal: "",
25
+    env: false,
26
+    items: [{
27
+        value: 'ijolijoli',
28
+        name: '正式环境',
29
+        checked: true
30
+      },
31
+      {
32
+        value: 'dev',
33
+        name: '测试环境',
34
+        checked: false
35
+      },
36
+    ]
37
+  },
38
+
39
+  /**
40
+   * 组件的方法列表
41
+   */
42
+  methods: {
43
+    // 监听输入框
44
+    onChangeInput(e) {
45
+      this.setData({
46
+        inputVal: e.detail.value
47
+      })
48
+    },
49
+
50
+    // 点击确定事件
51
+    onConfirm() {
52
+      let {
53
+        env,
54
+        items
55
+      } = this.data;
56
+      if (env) {
57
+        items.forEach((item, index) => {
58
+          if (item.checked) {
59
+            wx.setStorageSync('env', item.value)
60
+            this.setData({
61
+              visible: false,
62
+              env: false,
63
+            }, () => {
64
+              login()
65
+              wx.reLaunch({
66
+                url: '/pages/home/home',
67
+              })
68
+            })
69
+          }
70
+        })
71
+        return;
72
+      }
73
+      if (this.data.inputVal == 'ijolijoli') {
74
+        this.setData({
75
+          env: true
76
+        })
77
+      } else {
78
+        wx.showToast({
79
+          title: '密码错误',
80
+          icon: 'none'
81
+        })
82
+      }
83
+      console.log(this.data.inputVal)
84
+    },
85
+
86
+    // 点击取消事件
87
+    onCancel() {
88
+      this.setData({
89
+        visible: false,
90
+        env: false
91
+      })
92
+    },
93
+
94
+    // 监听却换环境
95
+    onChangeRadio(e) {
96
+      console.log('radio发生change事件,携带value值为:', e.detail.value)
97
+      const items = this.data.items
98
+      for (let i = 0, len = items.length; i < len; ++i) {
99
+        items[i].checked = items[i].value === e.detail.value
100
+      }
101
+      this.setData({
102
+        items
103
+      })
104
+    },
105
+  }
106
+})

+ 6 - 0
components/local/change-env/change-env.json

@@ -0,0 +1,6 @@
1
+{
2
+  "component": true,
3
+  "usingComponents": {
4
+    "wux-landscape": "../../dist/landscape/index"
5
+  }
6
+}

+ 15 - 0
components/local/change-env/change-env.wxml

@@ -0,0 +1,15 @@
1
+<wux-landscape visible="{{ visible }}"  maskClosable="{{ true }}" closable="{{ false }}" bind:close="onCancelUse">
2
+  <view class="use-popup flex-column" wx:if="{{ visible }}">
3
+    <input wx:if="{{!env}}" class="weui-input" auto-focus placeholder="请输入密码" bindinput="onChangeInput" />
4
+    <radio-group bindchange="onChangeRadio" wx:if="{{env}}">
5
+      <label class="flex" wx:for="{{items}}" wx:key="index">
6
+        <radio value="{{item.value}}" checked="{{item.checked}}"/>
7
+        <view style="margin-left: 30rpx;">{{item.name}}</view>
8
+      </label>
9
+    </radio-group>
10
+    <view class="use-btn flex">
11
+      <view class="btn cancel flex-center" bindtap="onCancel">取消</view>
12
+      <view class="btn confirm flex-center" bindtap="onConfirm">确定</view>
13
+    </view>
14
+  </view>
15
+</wux-landscape>

+ 53 - 0
components/local/change-env/change-env.wxss

@@ -0,0 +1,53 @@
1
+.use-popup {
2
+  width: 558rpx;
3
+  /* height: 350rpx; */
4
+  background: #FFFFFF;
5
+  border-radius: 20rpx;
6
+  overflow: hidden;
7
+  /* justify-content: center; */
8
+  justify-content: space-evenly;
9
+}
10
+
11
+/* .use-popup .red {
12
+  font-size: 24rpx;
13
+  font-family: PingFangSC-Regular, PingFang SC;
14
+  font-weight: 400;
15
+  color: #E95564;
16
+} */
17
+
18
+.use-popup input {
19
+  text-align: left;
20
+  padding: 30rpx;
21
+}
22
+
23
+radio-group {
24
+  padding: 30rpx 30rpx 0;
25
+}
26
+
27
+radio-group label {
28
+  margin-bottom: 30rpx;
29
+}
30
+
31
+.use-btn {
32
+  padding: 30rpx;
33
+  border-top: 1rpx solid #ccc;
34
+}
35
+
36
+.use-btn .btn {
37
+  flex: 1;
38
+  /* width: 208rpx; */
39
+  height: 92rpx;
40
+  /* margin: 0 48rpx; */
41
+  font-size: 36rpx;
42
+  color: #FA7D22;
43
+  border-radius: 52rpx;
44
+  border: 2rpx solid #FA7D22;
45
+}
46
+
47
+
48
+.use-btn .confirm {
49
+  /* margin-top: 28rpx; */
50
+  margin-left: 46rpx;
51
+  background-color: #FA7D22;
52
+  color: #fff;
53
+}

+ 124 - 0
components/local/couponItem/couponItem.js

@@ -0,0 +1,124 @@
1
+// components/local/couponItem/couponItem.js
2
+Component({
3
+  // options: {
4
+  //   addGlobalClass: true
5
+  // },
6
+  /**
7
+   * 组件的属性列表
8
+   */
9
+  properties: {
10
+    source: {
11
+      type: String,
12
+      value: '' // 场景 默认为空(简单的列表),可选值:checkBox(勾选操作)
13
+    },
14
+    item: {
15
+      type: Object,
16
+      value: {}
17
+    },
18
+    couponStatus: {
19
+      type: Number,
20
+      value: 0 // 0待使用,1已使用,2已过期
21
+    },
22
+    checkStatus: {
23
+      type: Boolean,
24
+      value: false // 是否选中 0 未选择, 1选中
25
+    }
26
+  },
27
+
28
+  /**
29
+   * 组件的初始数据
30
+   */
31
+  data: {
32
+    showRuleText: false,
33
+    marks: {
34
+      // 优惠券类型,1直接抵扣券,2满减券,3全额减免券,4卡券
35
+      1: '抵扣券',
36
+      2: '满减券',
37
+      3: '全额减免券',
38
+      4: '项目卡券'
39
+    }
40
+  },
41
+
42
+  /**
43
+   * 组件生命周期函数-在组件实例进入页面节点树时执行)
44
+   */
45
+  attached: function () {
46
+    let { item } = this.data
47
+    // 数据结构不一致,对齐参数
48
+    this.setData({
49
+      'item.coupon_type':
50
+        item.coupon_type == undefined ? item.type : item.coupon_type
51
+    })
52
+  },
53
+
54
+  // /**
55
+  //  * 组件数据字段监听器,用于监听 properties 和 data 的变化
56
+  //  */
57
+  // observers: {
58
+  //   checkStatus: function (checkStatus) {
59
+  //     checkStatus == this.data.checkStatus
60
+  //   }
61
+  // },
62
+
63
+  /**
64
+   * 组件的方法列表
65
+   */
66
+  methods: {
67
+    /**
68
+     * 点击整条优惠券
69
+     */
70
+    clickItem: function (e) {
71
+      let { source } = this.data
72
+      if (source == 'checkbox') {
73
+        this.check(e)
74
+      }
75
+    },
76
+    /**
77
+     * 展开、收起规则
78
+     */
79
+    clickRuleText: function () {
80
+      this.setData({
81
+        showRuleText: !this.data.showRuleText
82
+      })
83
+    },
84
+    /**
85
+     * 点击使用按钮、勾选按钮
86
+     */
87
+    check: function (e) {
88
+      // 判断美妆产品的优惠条件
89
+      if(this.data.item.couponSource=='goods'){
90
+        if (this.data.item.coupon_type == 2) {
91
+          if(this.data.item.allow_time!=1){
92
+            wx.showToast({
93
+              title: '抱歉,优惠券没到使用日期',
94
+              icon: 'none'
95
+            })
96
+            return
97
+          }
98
+          let full_price = this.data.item.full_price
99
+          let price = this.data.item.showMoneyGood
100
+          if (Number(price) < Number(full_price)) {
101
+            wx.showToast({
102
+              title: '订单不满足优惠条件',
103
+              icon: 'none'
104
+            })
105
+            return
106
+          }
107
+        }
108
+      }
109
+      if (this.data.couponStatus != 0) {
110
+        return
111
+      }
112
+      let checkStatus = !this.data.checkStatus
113
+      this.setData(
114
+        {
115
+          checkStatus: checkStatus
116
+        },
117
+        this.triggerEvent('check', {
118
+          id: e.currentTarget.dataset.id,
119
+          checkStatus: checkStatus
120
+        })
121
+      )
122
+    }
123
+  }
124
+})

+ 4 - 0
components/local/couponItem/couponItem.json

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

+ 35 - 0
components/local/couponItem/couponItem.wxml

@@ -0,0 +1,35 @@
1
+<view class="component-coupon-item flex-justify-center flex-column {{couponStatus == 0 ? '' : 'invalid' }}">
2
+  <view class="coupon-item-mark">{{marks[item.coupon_type]}}</view>
3
+  <view class="coupon-item-content" bindtap="clickItem" data-id="{{item.id}}">
4
+    <view class="coupon-item-money">
5
+      <view class="coupon-item-money-detail flex-row justify-between" style="margin-top: 30rpx;" wx:if="{{item.coupon_type == 3 || item.coupon_type == 4}}">
6
+        <text class="yuan" style="font-size: 44rpx">全免</text>
7
+      </view>
8
+      <view class="coupon-item-money-detail flex-row justify-between {{item.coupon_type == 2 ? 'line2' : ''}}" wx:else>
9
+        <text class="currency">¥</text>
10
+        <text class="yuan-price">{{item.price}}</text>
11
+        <!-- <text class="fen">.00</text> -->
12
+      </view>
13
+      <text class="coupon-item-limit-text ellipsis" wx:if="{{item.coupon_type == 2}}">满{{item.full_price}}元可用</text>
14
+    </view>
15
+    <view class="coupon-item-title flex-col justify-between flex1">
16
+      <text class="ellipsis">{{item.coupon_name}}</text>
17
+      <text class="coupon-time ellipsis">有效期{{item.ex_time}}</text>
18
+    </view>
19
+    <view wx:if="{{!source&&couponStatus==0}}" class="coupon-item-btn1 flex-col justify-center align-center" catchtap="check" data-id="{{item.id}}">
20
+      去使用 
21
+    </view>
22
+    <view wx:elif="{{source=='checkbox'}}" class="coupon-item-btn2 flex-col justify-center align-center" catchtap="check" data-id="{{item.id}}">
23
+      <image src="{{checkStatus ? '/images/icon_17.png' : '/images/icon_16.png'}}"></image>
24
+    </view>
25
+  </view>
26
+  <!-- 0待使用,1已使用,2已过期 -->
27
+  <image class="coupon-item-status-icon" wx:if="{{couponStatus == 1 || couponStatus == 2}}" src="{{couponStatus == 1 ? '/images/my/icon_coupon_used.png' : '/images/my/icon_coupon_expired.png'}}"></image>
28
+  <view class="coupon-item-line flex-col"></view>
29
+  <view class="coupon-item-rules flex-row flex-center" bindtap="clickRuleText">
30
+    <text class="coupon-item-rules-text {{showRuleText ? ' rules-show ' : ' ellipsis '}}">{{item.coupon_desc}}</text>
31
+    <view class="flex-center" style="justify-content: flex-end;">
32
+      <image style="{{showRuleText ? 'transform: rotate(180deg);' : ''}}" src="{{couponStatus == 0 ? '/images/icon_arrow_2.png' : '/images/icon_arrow_gray.png'}}"></image>
33
+    </view>
34
+  </view>
35
+</view>

+ 274 - 0
components/local/couponItem/couponItem.wxss

@@ -0,0 +1,274 @@
1
+/* components/local/couponItem/couponItem.wxss */
2
+.flex {
3
+  display: flex;
4
+}
5
+
6
+.flex-start {
7
+  align-items: flex-start;
8
+  justify-content: flex-start;
9
+}
10
+
11
+.flex-align-center {
12
+  display: flex;
13
+  align-items: center;
14
+}
15
+
16
+.flex-align-baseline {
17
+  display: flex;
18
+  align-items: baseline;
19
+}
20
+
21
+.flex-justify-center {
22
+  display: flex;
23
+  justify-content: center;
24
+}
25
+
26
+.flex-justify-space-around {
27
+  display: flex;
28
+  justify-content: space-around;
29
+}
30
+
31
+.flex-justify-space-between {
32
+  display: flex;
33
+  justify-content: space-between;
34
+}
35
+
36
+.flex-center {
37
+  display: flex;
38
+  align-items: center;
39
+  justify-content: center;
40
+}
41
+
42
+.flex-column {
43
+  display: flex;
44
+  flex-direction: column;
45
+  /* align-items: flex-start;
46
+  justify-content: flex-start; */
47
+}
48
+
49
+.flex1 {
50
+  flex: 1;
51
+}
52
+
53
+.ellipsis {
54
+  white-space: nowrap;
55
+  overflow: hidden;
56
+  text-overflow: ellipsis;
57
+}
58
+
59
+.component-coupon-item text {
60
+  font-family: PingFangSC-Medium, PingFang SC;
61
+}
62
+
63
+.component-coupon-item.invalid {
64
+  background: #E6E6E6 !important;
65
+}
66
+
67
+.component-coupon-item.invalid text {
68
+  color: #B1B1B1 !important;
69
+}
70
+
71
+.component-coupon-item.invalid .coupon-item-mark {
72
+  background: #B3B3B3 !important;
73
+}
74
+
75
+.component-coupon-item.invalid .coupon-item-btn1 {
76
+  background: #B3B3B3 !important;
77
+}
78
+
79
+.component-coupon-item.invalid .coupon-item-line {
80
+  border-top: 2rpx dashed #D7D7D7 !important;
81
+}
82
+
83
+.component-coupon-item .flex2 {
84
+  flex: 2;
85
+}
86
+
87
+.component-coupon-item .flex-row {
88
+  display: flex;
89
+  flex-direction: row;
90
+}
91
+
92
+.component-coupon-item .flex-col {
93
+  display: flex;
94
+  flex-direction: column;
95
+}
96
+
97
+.component-coupon-item {
98
+  position: relative;
99
+  background: #FFF4E6;
100
+  border-radius: 24rpx;
101
+  overflow: hidden;
102
+  padding: 20rpx 28rpx 12rpx;
103
+  text-align: left;
104
+}
105
+
106
+.coupon-item-mark {
107
+  position: absolute;
108
+  top: 0;
109
+  left: 0;
110
+  height: 36rpx;
111
+  background: #875617;
112
+  border-radius: 24rpx 0rpx 24rpx 0rpx;
113
+  text-align: center;
114
+  line-height: 36rpx;
115
+  font-size: 20rpx;
116
+  font-weight: 500;
117
+  color: #FFFFFF;
118
+  padding: 0 16rpx;
119
+}
120
+
121
+.coupon-item-content {
122
+  display: flex;
123
+  flex-direction: row;
124
+  align-items: center;
125
+  justify-content: space-between;
126
+}
127
+
128
+.coupon-item-money {
129
+  flex: 1.2;
130
+  overflow: hidden;
131
+}
132
+
133
+.coupon-item-money text {
134
+  font-family: PingFangSC-Medium, PingFang SC;
135
+  font-weight: 500;
136
+  color: #FF3007;
137
+}
138
+
139
+.coupon-item-money-detail {
140
+  align-items: baseline;
141
+  margin-top: 30rpx;
142
+}
143
+
144
+.coupon-item-money-detail.line2 {
145
+  margin-top: 10rpx;
146
+}
147
+
148
+.coupon-item-money-detail.line2 .yuan {
149
+  line-height: 70rpx;
150
+}
151
+
152
+.coupon-item-money-detail .currency {
153
+  font-size: 28rpx;
154
+  font-family: PingFangSC-Medium, PingFang SC;
155
+  line-height: 40rpx;
156
+}
157
+
158
+.coupon-item-money-detail .yuan {
159
+  font-size: 56rpx;
160
+  font-family: PingFangSC-Medium, PingFang SC;
161
+  line-height: 80rpx;
162
+}
163
+.coupon-item-money-detail .yuan-price{
164
+  font-size: 42rpx;
165
+  font-family: PingFangSC-Medium, PingFang SC;
166
+  line-height: 80rpx;
167
+}
168
+
169
+.coupon-item-money-detail .fen {
170
+  font-size: 28rpx;
171
+  font-family: PingFangSC-Medium, PingFang SC;
172
+  font-weight: 500;
173
+  line-height: 40rpx;
174
+}
175
+
176
+.coupon-item-limit-text {
177
+  font-size: 24rpx;
178
+  font-family: PingFangSC-Regular, PingFang SC !important;
179
+  font-weight: 400;
180
+  color: #FF3007;
181
+  line-height: 34rpx;
182
+}
183
+
184
+.coupon-item-btn1 {
185
+  width: 104rpx;
186
+  height: 50rpx;
187
+  background: #FA7D22;
188
+  border-radius: 25rpx;
189
+  font-size: 24rpx;
190
+  font-family: PingFang-SC-Medium, PingFang-SC;
191
+  font-weight: 500;
192
+  color: #FFFFFF;
193
+  line-height: 50rpx;
194
+  text-align: center;
195
+}
196
+
197
+.coupon-item-btn2 {
198
+  width: 48rpx;
199
+  height: 48rpx;
200
+  font-family: PingFang-SC-Medium, PingFang-SC;
201
+  font-weight: 500;
202
+  line-height: 50rpx;
203
+  text-align: right;
204
+}
205
+
206
+.coupon-item-btn2 image {
207
+  width: 48rpx;
208
+  height: 48rpx;
209
+}
210
+
211
+.coupon-item-title {
212
+  margin-top: 10rpx;
213
+  /* margin-left: 36rpx; */
214
+  flex: 2.5;
215
+  overflow: hidden;
216
+}
217
+
218
+.coupon-item-title text {
219
+  width: 100%;
220
+  height: 44rpx;
221
+  line-height: 44rpx;
222
+  font-size: 32rpx;
223
+  font-weight: 500;
224
+  color: #875617;
225
+  margin-top: 16rpx;
226
+}
227
+
228
+.coupon-item-title .coupon-time {
229
+  height: 34rpx;
230
+  line-height: 34rpx;
231
+  font-size: 24rpx;
232
+  font-family: PingFangSC-Regular, PingFang SC;
233
+  font-weight: 400;
234
+  color: #875617;
235
+}
236
+
237
+.coupon-item-line {
238
+  height: 2rpx;
239
+  border: none;
240
+  border-top: 2rpx dashed #FFDFB6;
241
+  margin: 16rpx 0 10rpx;
242
+}
243
+
244
+.coupon-item-rules {
245
+  justify-content: space-between;
246
+}
247
+
248
+.coupon-item-rules image {
249
+  width: 48rpx;
250
+  height: 48rpx;
251
+}
252
+
253
+.coupon-item-rules-text {
254
+  height: 32rpx;
255
+  line-height: 32rpx;
256
+  font-size: 24rpx;
257
+  font-family: PingFangSC-Regular, PingFang SC;
258
+  font-weight: 400;
259
+  color: #C38029;
260
+  margin-right: 56rpx;
261
+}
262
+
263
+.coupon-item-rules-text.rules-show {
264
+  height: auto;
265
+  margin-top: 8rpx;
266
+}
267
+
268
+.coupon-item-status-icon {
269
+  width: 144rpx;
270
+  height: 144rpx;
271
+  position: absolute;
272
+  top: 36rpx;
273
+  right: 28rpx;
274
+}

+ 250 - 0
components/local/ec-canvas/ec-canvas.js

@@ -0,0 +1,250 @@
1
+import WxCanvas from './wx-canvas';
2
+import * as echarts from './echarts';
3
+
4
+let ctx;
5
+
6
+function compareVersion(v1, v2) {
7
+  v1 = v1.split('.')
8
+  v2 = v2.split('.')
9
+  const len = Math.max(v1.length, v2.length)
10
+
11
+  while (v1.length < len) {
12
+    v1.push('0')
13
+  }
14
+  while (v2.length < len) {
15
+    v2.push('0')
16
+  }
17
+
18
+  for (let i = 0; i < len; i++) {
19
+    const num1 = parseInt(v1[i])
20
+    const num2 = parseInt(v2[i])
21
+
22
+    if (num1 > num2) {
23
+      return 1
24
+    } else if (num1 < num2) {
25
+      return -1
26
+    }
27
+  }
28
+  return 0
29
+}
30
+
31
+Component({
32
+  properties: {
33
+    canvasId: {
34
+      type: String,
35
+      value: 'ec-canvas'
36
+    },
37
+
38
+    ec: {
39
+      type: Object
40
+    },
41
+
42
+    forceUseOldCanvas: {
43
+      type: Boolean,
44
+      value: false
45
+    }
46
+  },
47
+
48
+  data: {
49
+    isUseNewCanvas: false
50
+  },
51
+
52
+  ready: function () {
53
+    // Disable prograssive because drawImage doesn't support DOM as parameter
54
+    // See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html
55
+    echarts.registerPreprocessor(option => {
56
+      if (option && option.series) {
57
+        if (option.series.length > 0) {
58
+          option.series.forEach(series => {
59
+            series.progressive = 0;
60
+          });
61
+        }
62
+        else if (typeof option.series === 'object') {
63
+          option.series.progressive = 0;
64
+        }
65
+      }
66
+    });
67
+
68
+    if (!this.data.ec) {
69
+      console.warn('组件需绑定 ec 变量,例:<ec-canvas id="mychart-dom-bar" '
70
+        + 'canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>');
71
+      return;
72
+    }
73
+
74
+    if (!this.data.ec.lazyLoad) {
75
+      this.init();
76
+    }
77
+  },
78
+
79
+  methods: {
80
+    init: function (callback) {
81
+      const version = wx.getSystemInfoSync().SDKVersion
82
+
83
+      const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0;
84
+      const forceUseOldCanvas = this.data.forceUseOldCanvas;
85
+      const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas;
86
+      this.setData({ isUseNewCanvas });
87
+
88
+      if (forceUseOldCanvas && canUseNewCanvas) {
89
+        console.warn('开发者强制使用旧canvas,建议关闭');
90
+      }
91
+
92
+      if (isUseNewCanvas) {
93
+        // console.log('微信基础库版本大于2.9.0,开始使用<canvas type="2d"/>');
94
+        // 2.9.0 可以使用 <canvas type="2d"></canvas>
95
+        this.initByNewWay(callback);
96
+      } else {
97
+        const isValid = compareVersion(version, '1.9.91') >= 0
98
+        if (!isValid) {
99
+          console.error('微信基础库版本过低,需大于等于 1.9.91。'
100
+            + '参见:https://github.com/ecomfe/echarts-for-weixin'
101
+            + '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82');
102
+          return;
103
+        } else {
104
+          console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能');
105
+          this.initByOldWay(callback);
106
+        }
107
+      }
108
+    },
109
+
110
+    initByOldWay(callback) {
111
+      // 1.9.91 <= version < 2.9.0:原来的方式初始化
112
+      ctx = wx.createCanvasContext(this.data.canvasId, this);
113
+      const canvas = new WxCanvas(ctx, this.data.canvasId, false);
114
+
115
+      echarts.setCanvasCreator(() => {
116
+        return canvas;
117
+      });
118
+      // const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr
119
+      const canvasDpr = 1
120
+      var query = wx.createSelectorQuery().in(this);
121
+      query.select('.ec-canvas').boundingClientRect(res => {
122
+        if (typeof callback === 'function') {
123
+          this.chart = callback(canvas, res.width, res.height, canvasDpr);
124
+        }
125
+        else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
126
+          this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr);
127
+        }
128
+        else {
129
+          this.triggerEvent('init', {
130
+            canvas: canvas,
131
+            width: res.width,
132
+            height: res.height,
133
+            canvasDpr: canvasDpr // 增加了dpr,可方便外面echarts.init
134
+          });
135
+        }
136
+      }).exec();
137
+    },
138
+
139
+    initByNewWay(callback) {
140
+      // version >= 2.9.0:使用新的方式初始化
141
+      const query = wx.createSelectorQuery().in(this)
142
+      query
143
+        .select('.ec-canvas')
144
+        .fields({ node: true, size: true })
145
+        .exec(res => {
146
+          const canvasNode = res[0].node
147
+          this.canvasNode = canvasNode
148
+
149
+          const canvasDpr = wx.getSystemInfoSync().pixelRatio
150
+          const canvasWidth = res[0].width
151
+          const canvasHeight = res[0].height
152
+
153
+          const ctx = canvasNode.getContext('2d')
154
+
155
+          const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode)
156
+          echarts.setCanvasCreator(() => {
157
+            return canvas
158
+          })
159
+
160
+          if (typeof callback === 'function') {
161
+            this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr)
162
+          } else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
163
+            this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr)
164
+          } else {
165
+            this.triggerEvent('init', {
166
+              canvas: canvas,
167
+              width: canvasWidth,
168
+              height: canvasHeight,
169
+              dpr: canvasDpr
170
+            })
171
+          }
172
+        })
173
+    },
174
+    canvasToTempFilePath(opt) {
175
+      if (this.data.isUseNewCanvas) {
176
+        // 新版
177
+        const query = wx.createSelectorQuery().in(this)
178
+        query
179
+          .select('.ec-canvas')
180
+          .fields({ node: true, size: true })
181
+          .exec(res => {
182
+            const canvasNode = res[0].node
183
+            opt.canvas = canvasNode
184
+            wx.canvasToTempFilePath(opt)
185
+          })
186
+      } else {
187
+        // 旧的
188
+        if (!opt.canvasId) {
189
+          opt.canvasId = this.data.canvasId;
190
+        }
191
+        ctx.draw(true, () => {
192
+          wx.canvasToTempFilePath(opt, this);
193
+        });
194
+      }
195
+    },
196
+
197
+    touchStart(e) {
198
+      if (this.chart && e.touches.length > 0) {
199
+        var touch = e.touches[0];
200
+        var handler = this.chart.getZr().handler;
201
+        handler.dispatch('mousedown', {
202
+          zrX: touch.x,
203
+          zrY: touch.y
204
+        });
205
+        handler.dispatch('mousemove', {
206
+          zrX: touch.x,
207
+          zrY: touch.y
208
+        });
209
+        handler.processGesture(wrapTouch(e), 'start');
210
+      }
211
+    },
212
+
213
+    touchMove(e) {
214
+      if (this.chart && e.touches.length > 0) {
215
+        var touch = e.touches[0];
216
+        var handler = this.chart.getZr().handler;
217
+        handler.dispatch('mousemove', {
218
+          zrX: touch.x,
219
+          zrY: touch.y
220
+        });
221
+        handler.processGesture(wrapTouch(e), 'change');
222
+      }
223
+    },
224
+
225
+    touchEnd(e) {
226
+      if (this.chart) {
227
+        const touch = e.changedTouches ? e.changedTouches[0] : {};
228
+        var handler = this.chart.getZr().handler;
229
+        handler.dispatch('mouseup', {
230
+          zrX: touch.x,
231
+          zrY: touch.y
232
+        });
233
+        handler.dispatch('click', {
234
+          zrX: touch.x,
235
+          zrY: touch.y
236
+        });
237
+        handler.processGesture(wrapTouch(e), 'end');
238
+      }
239
+    }
240
+  }
241
+});
242
+
243
+function wrapTouch(event) {
244
+  for (let i = 0; i < event.touches.length; ++i) {
245
+    const touch = event.touches[i];
246
+    touch.offsetX = touch.x;
247
+    touch.offsetY = touch.y;
248
+  }
249
+  return event;
250
+}

+ 4 - 0
components/local/ec-canvas/ec-canvas.json

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

+ 9 - 0
components/local/ec-canvas/ec-canvas.wxml

@@ -0,0 +1,9 @@
1
+<!-- 新的:接口对其了H5 -->
2
+<canvas wx:if="{{isUseNewCanvas}}" type="2d" class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init"></canvas>
3
+<!-- 旧的 -->
4
+<canvas wx:else class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init"></canvas>
5
+
6
+<!-- 新的:接口对其了H5 -->
7
+<!-- <canvas wx:if="{{isUseNewCanvas}}" type="2d" class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas> -->
8
+<!-- 旧的 -->
9
+<!-- <canvas wx:else class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas> -->

+ 4 - 0
components/local/ec-canvas/ec-canvas.wxss

@@ -0,0 +1,4 @@
1
+.ec-canvas {
2
+  width: 100%;
3
+  height: 100%;
4
+}

File diff suppressed because it is too large
+ 16 - 0
components/local/ec-canvas/echarts.js


+ 105 - 0
components/local/ec-canvas/wx-canvas.js

@@ -0,0 +1,105 @@
1
+export default class WxCanvas {
2
+  constructor(ctx, canvasId, isNew, canvasNode) {
3
+    this.ctx = ctx;
4
+    this.canvasId = canvasId;
5
+    this.chart = null;
6
+    this.isNew = isNew
7
+    if (isNew) {
8
+      this.canvasNode = canvasNode;
9
+    }
10
+    else {
11
+      this._initStyle(ctx);
12
+    }
13
+
14
+    // this._initCanvas(zrender, ctx);
15
+
16
+    this._initEvent();
17
+  }
18
+
19
+  getContext(contextType) {
20
+    if (contextType === '2d') {
21
+      return this.ctx;
22
+    }
23
+  }
24
+
25
+  // canvasToTempFilePath(opt) {
26
+  //   if (!opt.canvasId) {
27
+  //     opt.canvasId = this.canvasId;
28
+  //   }
29
+  //   return wx.canvasToTempFilePath(opt, this);
30
+  // }
31
+
32
+  setChart(chart) {
33
+    this.chart = chart;
34
+  }
35
+
36
+  attachEvent() {
37
+    // noop
38
+  }
39
+
40
+  detachEvent() {
41
+    // noop
42
+  }
43
+
44
+  _initCanvas(zrender, ctx) {
45
+    zrender.util.getContext = function () {
46
+      return ctx;
47
+    };
48
+
49
+    zrender.util.$override('measureText', function (text, font) {
50
+      ctx.font = font || '12px sans-serif';
51
+      return ctx.measureText(text);
52
+    });
53
+  }
54
+
55
+  _initStyle(ctx) {
56
+    ctx.createRadialGradient = () => {
57
+      return ctx.createCircularGradient(arguments);
58
+    };
59
+  }
60
+
61
+  _initEvent() {
62
+    this.event = {};
63
+    const eventNames = [{
64
+      wxName: 'touchStart',
65
+      ecName: 'mousedown'
66
+    }, {
67
+      wxName: 'touchMove',
68
+      ecName: 'mousemove'
69
+    }, {
70
+      wxName: 'touchEnd',
71
+      ecName: 'mouseup'
72
+    }, {
73
+      wxName: 'touchEnd',
74
+      ecName: 'click'
75
+    }];
76
+
77
+    eventNames.forEach(name => {
78
+      this.event[name.wxName] = e => {
79
+        const touch = e.touches[0];
80
+        this.chart.getZr().handler.dispatch(name.ecName, {
81
+          zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
82
+          zrY: name.wxName === 'tap' ? touch.clientY : touch.y
83
+        });
84
+      };
85
+    });
86
+  }
87
+
88
+  set width(w) {
89
+    if (this.canvasNode) this.canvasNode.width = w
90
+  }
91
+  set height(h) {
92
+    if (this.canvasNode) this.canvasNode.height = h
93
+  }
94
+
95
+  get width() {
96
+    if (this.canvasNode)
97
+      return this.canvasNode.width
98
+    return 0
99
+  }
100
+  get height() {
101
+    if (this.canvasNode)
102
+      return this.canvasNode.height
103
+    return 0
104
+  }
105
+}

+ 63 - 0
components/local/home-skeleton/home-skeleton.js

@@ -0,0 +1,63 @@
1
+// components/local/homeSkeleton/homeSkeleton.js
2
+Component({
3
+  /**
4
+   * 组件的属性列表
5
+   */
6
+  options: {
7
+    multipleSlots: true
8
+  },
9
+  properties: {
10
+    isLoading: {
11
+      type: Boolean,
12
+      value: true,
13
+      observer(newVal) {
14
+        if(newVal) {
15
+          var animation= wx.createAnimation({}) //创建一个动画实例
16
+          animation.opacity(1).step({
17
+            duration:0
18
+          });
19
+          this.setData({
20
+            showpic:animation.export(),
21
+            hidden:true
22
+          });
23
+          return;
24
+        }
25
+        this.skeletonVis();
26
+      }
27
+    },
28
+  },
29
+
30
+  /**
31
+   * 组件的初始数据
32
+   */
33
+  data: {
34
+    hidden:true
35
+  },
36
+
37
+  /**
38
+   * 组件的方法列表
39
+   */
40
+  methods: {
41
+    skeletonVis() {
42
+      var animation= wx.createAnimation({}) //创建一个动画实例
43
+      animation.opacity(0).step({
44
+        duration:500
45
+      });
46
+      // this.setData({
47
+      //   showpic:animation.export(),
48
+      // });
49
+      
50
+      setTimeout(() => {
51
+        this.setData({
52
+          showpic:animation.export(),
53
+        },() => {
54
+          setTimeout(() => {
55
+            this.setData({
56
+              hidden:false,
57
+            })
58
+          }, 500);
59
+        });
60
+      }, 500);
61
+    }
62
+  }
63
+})

+ 4 - 0
components/local/home-skeleton/home-skeleton.json

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

+ 21 - 0
components/local/home-skeleton/home-skeleton.wxml

@@ -0,0 +1,21 @@
1
+<view class="content">
2
+
3
+  <view class="home-skeleton" animation="{{showpic}}" hidden="{{!hidden}}">
4
+    <view class="home-skeleton-swiper"></view>
5
+    <view class="home-skeleton-magic">
6
+      <view class="item jgc" wx:key="index" wx:for="{{10}}"></view>
7
+    </view>
8
+    <view class="home-skeleton-img"></view>
9
+
10
+    <view class="home-skeleton-magic" style="margin-top: 40rpx;">
11
+      <view class="item hd" wx:key="index" wx:for="{{2}}"></view>
12
+    </view>
13
+    <view class="home-skeleton-magic">
14
+      <view class="item hd" wx:key="index" wx:for="{{2}}"></view>
15
+    </view>
16
+  </view>
17
+  
18
+  <view class="home-page">
19
+    <slot></slot>
20
+  </view>
21
+</view>

+ 72 - 0
components/local/home-skeleton/home-skeleton.wxss

@@ -0,0 +1,72 @@
1
+
2
+.home-skeleton {
3
+  position: fixed;
4
+  background: white;
5
+  height: 100%;
6
+  height: 100%;
7
+  z-index: 1000000000000;
8
+}
9
+
10
+.home-skeleton.hidden {
11
+  z-index: 0;
12
+}
13
+
14
+.home-page {
15
+  position: relative;
16
+  z-index: 1;
17
+}
18
+
19
+.home-skeleton view{
20
+  margin: 20rpx;
21
+  background: #eeeeee;
22
+  /* position: absolute; */
23
+  border-radius: 8rpx;
24
+  animation-duration: 2s;
25
+  animation-name: shine;
26
+  animation-iteration-count: infinite;
27
+}
28
+@keyframes shine {
29
+  0% {
30
+    opacity: .4;
31
+  }
32
+  50% {
33
+    opacity: 1;
34
+  }
35
+  100% {
36
+    opacity: .4;
37
+  }
38
+}
39
+
40
+.home-skeleton-swiper {
41
+  height: 150px;
42
+}
43
+
44
+.home-skeleton-magic {
45
+  display: flex;
46
+  flex-wrap: wrap;
47
+  justify-content: space-around;
48
+}
49
+
50
+.home-skeleton .home-skeleton-magic {
51
+  background: transparent;
52
+}
53
+
54
+.home-skeleton-magic .jgc {
55
+  width: 100rpx;
56
+  height: 100rpx;
57
+  flex-wrap: wrap;
58
+}
59
+
60
+.home-skeleton-img {
61
+  height: 100rpx;
62
+}
63
+
64
+.home-skeleton-magic .hd {
65
+  flex: 1;
66
+  height: 140rpx;
67
+  margin: 0;
68
+}
69
+
70
+.home-skeleton-magic .hd:first-child {
71
+  margin-right: 20rpx;
72
+}

+ 25 - 0
components/local/navigation/navigation.js

@@ -0,0 +1,25 @@
1
+// components/local/navigation/navigation.js
2
+const app = getApp()
3
+Component({
4
+  /**
5
+   * 组件的属性列表
6
+   */
7
+  properties: {},
8
+
9
+  /**
10
+   * 组件的初始数据
11
+   */
12
+  data: {
13
+    version: 'cover_all',
14
+    navBarHeight: app.globalData.navBarData.navBarHeight, //导航栏高度
15
+    menuRight: app.globalData.navBarData.menuRight, // 胶囊距右方间距(方保持左、右间距一致)
16
+    menuBotton: app.globalData.navBarData.menuBotton,
17
+    menuHeight: app.globalData.navBarData.menuHeight,
18
+    navBarBgClass: app.globalData.navBarData.navBarBgClass // 导航栏背景色
19
+  },
20
+
21
+  /**
22
+   * 组件的方法列表
23
+   */
24
+  methods: {}
25
+})

+ 4 - 0
components/local/navigation/navigation.json

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

+ 14 - 0
components/local/navigation/navigation.wxml

@@ -0,0 +1,14 @@
1
+<!-- components/local/navigation/navigation.wxml -->
2
+<cover-view class="nav" style="height:{{navBarHeight}}px;z-index: 999;">
3
+  <cover-view class="nav-main" wx:if="{{version == 'normal'}}">
4
+    <!-- 胶囊区域 -->
5
+    <cover-view class="capsule-box" style="height:{{menuHeight}}px; min-height:{{menuHeight}}px; line-height:{{menuHeight}}px; bottom:{{menuBotton}}px;">
6
+      <!-- 导航内容区域 -->
7
+      <slot></slot>
8
+    </cover-view>
9
+  </cover-view>
10
+  <cover-view class="nav-main" wx:elif="{{version == 'cover_all'}}">
11
+    <!-- 导航内容区域 -->
12
+    <slot></slot>
13
+  </cover-view>
14
+</cover-view>

+ 19 - 0
components/local/navigation/navigation.wxss

@@ -0,0 +1,19 @@
1
+/* components/local/navigation/navigation.wxss */
2
+.nav {
3
+  position: fixed;
4
+  top: 0;
5
+  left: 0;
6
+  width: 100vw;
7
+}
8
+
9
+.nav-main {
10
+  width: 100%;
11
+  height: 100%;
12
+  position: relative;
13
+}
14
+
15
+.nav .capsule-box {
16
+  position: absolute;
17
+  box-sizing: border-box;
18
+  width: 100%;
19
+}

+ 319 - 0
components/local/wxParse/html2json.js

@@ -0,0 +1,319 @@
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
+var __placeImgeUrlHttps = "https";
16
+var __emojisReg = '';
17
+var __emojisBaseSrc = '';
18
+var __emojis = {};
19
+var wxDiscode = require('./wxDiscode.js');
20
+var HTMLParser = require('./htmlparser.js');
21
+// Empty Elements - HTML 5
22
+var 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
+var 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
+var 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
+var 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
+var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
35
+
36
+// Special Elements (can contain anything)
37
+var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block");
38
+function makeMap(str) {
39
+    var obj = {}, items = str.split(",");
40
+    for (var i = 0; i < items.length; i++)
41
+        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
+    var bufArray = [];
72
+    var results = {
73
+        node: bindName,
74
+        nodes: [],
75
+        images:[],
76
+        imageUrls:[]
77
+    };
78
+    var index = 0;
79
+    HTMLParser(html, {
80
+        start: function (tag, attrs, unary) {
81
+            //debug(tag, attrs, unary);
82
+            // node for this element
83
+            var node = {
84
+                node: 'element',
85
+                tag: 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(function (pre, attr) {
109
+                    var name = attr.name;
110
+                    var value = attr.value;
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
+                var 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
+                var fontSize = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', '-webkit-xxx-large'];
164
+                var 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 (var key in styleAttrs) {
172
+                    if (node.attr[key]) {
173
+                        var 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: function (tag) {
200
+            //debug(tag);
201
+            // merge into parent tag
202
+            var 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
+            if (node.tag === 'p') { //当前如果是p标签
211
+              if(node.nodes){
212
+              if ( node.nodes[0].text == ' ') { //如果没有nodes就强行赋值nodes
213
+                  var nodes = [{
214
+                      attr: {
215
+                          style: ["padding:", "15rpx", "0","display:","inline-block"],
216
+                      },
217
+                      index: "0.0.0.0",
218
+                      node: "element",
219
+                      tag: "span",
220
+                      tagType: "inline",
221
+                      styleStr: "padding:15rpx 0;display:inline-block"
222
+                  }]
223
+                  node.nodes = nodes
224
+                }
225
+              }
226
+          }
227
+            if (bufArray.length === 0) {
228
+                results.nodes.push(node);
229
+            } else {
230
+                var parent = bufArray[0];
231
+                if (parent.nodes === undefined) {
232
+                    parent.nodes = [];
233
+                }
234
+                parent.nodes.push(node);
235
+            }
236
+        },
237
+        chars: function (text) {
238
+            //debug(text);
239
+            var node = {
240
+                node: 'text',
241
+                text: text,
242
+                textArray:transEmojiStr(text)
243
+            };
244
+            
245
+            if (bufArray.length === 0) {
246
+                node.index = index.toString()
247
+                index += 1
248
+                results.nodes.push(node);
249
+            } else {
250
+                var parent = bufArray[0];
251
+                if (parent.nodes === undefined) {
252
+                    parent.nodes = [];
253
+                }
254
+                node.index = parent.index + '.' + parent.nodes.length
255
+                parent.nodes.push(node);
256
+            }
257
+        },
258
+        comment: function (text) {
259
+            //debug(text);
260
+            // var node = {
261
+            //     node: 'comment',
262
+            //     text: text,
263
+            // };
264
+            // var parent = bufArray[0];
265
+            // if (parent.nodes === undefined) {
266
+            //     parent.nodes = [];
267
+            // }
268
+            // parent.nodes.push(node);
269
+        },
270
+    });
271
+    return results;
272
+};
273
+
274
+function transEmojiStr(str){
275
+  // var eReg = new RegExp("["+__reg+' '+"]");
276
+//   str = str.replace(/\[([^\[\]]+)\]/g,':$1:')
277
+  
278
+  var emojiObjs = [];
279
+  //如果正则表达式为空
280
+  if(__emojisReg.length == 0 || !__emojis){
281
+      var emojiObj = {}
282
+      emojiObj.node = "text";
283
+      emojiObj.text = str;
284
+      array = [emojiObj];
285
+      return array;
286
+  }
287
+  //这个地方需要调整
288
+  str = str.replace(/\[([^\[\]]+)\]/g,':$1:')
289
+  var eReg = new RegExp("[:]");
290
+  var array = str.split(eReg);
291
+  for(var i = 0; i < array.length; i++){
292
+    var ele = array[i];
293
+    var emojiObj = {};
294
+    if(__emojis[ele]){
295
+      emojiObj.node = "element";
296
+      emojiObj.tag = "emoji";
297
+      emojiObj.text = __emojis[ele];
298
+      emojiObj.baseSrc= __emojisBaseSrc;
299
+    }else{
300
+      emojiObj.node = "text";
301
+      emojiObj.text = ele;
302
+    }
303
+    emojiObjs.push(emojiObj);
304
+  }
305
+  
306
+  return emojiObjs;
307
+}
308
+
309
+function emojisInit(reg='',baseSrc="/wxParse/emojis/",emojis){
310
+    __emojisReg = reg;
311
+    __emojisBaseSrc=baseSrc;
312
+    __emojis=emojis;
313
+}
314
+
315
+module.exports = {
316
+    html2json: html2json,
317
+    emojisInit:emojisInit
318
+};
319
+

+ 192 - 0
components/local/wxParse/htmlparser.js

@@ -0,0 +1,192 @@
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
+var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
16
+	endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/,
17
+	attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
18
+
19
+// Empty Elements - HTML 5
20
+var 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
+var 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
+var 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
+var 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
+var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
34
+
35
+// Special Elements (can contain anything)
36
+var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block");
37
+
38
+function HTMLParser(html, handler) {
39
+	var index, chars, match, stack = [], last = html;
40
+	stack.last = function () {
41
+		return this[this.length - 1];
42
+	};
43
+
44
+	while (html) {
45
+		chars = true;
46
+
47
+		// Make sure we're not in a script or style element
48
+		if (!stack.last() || !special[stack.last()]) {
49
+
50
+			// Comment
51
+			if (html.indexOf("<!--") == 0) {
52
+				index = html.indexOf("-->");
53
+
54
+				if (index >= 0) {
55
+					if (handler.comment)
56
+						handler.comment(html.substring(4, index));
57
+					html = html.substring(index + 3);
58
+					chars = false;
59
+				}
60
+
61
+				// end tag
62
+			} else if (html.indexOf("</") == 0) {
63
+				match = html.match(endTag);
64
+
65
+				if (match) {
66
+					html = html.substring(match[0].length);
67
+					match[0].replace(endTag, parseEndTag);
68
+					chars = false;
69
+				}
70
+
71
+				// start tag
72
+			} else if (html.indexOf("<") == 0) {
73
+				match = html.match(startTag);
74
+
75
+				if (match) {
76
+					html = html.substring(match[0].length);
77
+					match[0].replace(startTag, parseStartTag);
78
+					chars = false;
79
+				}
80
+			}
81
+
82
+			if (chars) {
83
+				index = html.indexOf("<");
84
+				var text = ''
85
+				while (index === 0) {
86
+                                  text += "<";
87
+                                  html = html.substring(1);
88
+                                  index = html.indexOf("<");
89
+				}
90
+				text += index < 0 ? html : html.substring(0, index);
91
+				html = index < 0 ? "" : html.substring(index);
92
+
93
+				if (handler.chars)
94
+					handler.chars(text);
95
+			}
96
+
97
+		} else {
98
+
99
+			html = html.replace(new RegExp("([\\s\\S]*?)<\/" + stack.last() + "[^>]*>"), function (all, text) {
100
+				text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, "$1$2");
101
+				if (handler.chars)
102
+					handler.chars(text);
103
+
104
+				return "";
105
+			});
106
+
107
+
108
+			parseEndTag("", stack.last());
109
+		}
110
+
111
+		if (html == last)
112
+			throw "Parse Error: " + html;
113
+		last = html;
114
+	}
115
+
116
+	// Clean up any remaining tags
117
+	parseEndTag();
118
+
119
+	function parseStartTag(tag, tagName, rest, unary) {
120
+		tagName = tagName.toLowerCase();
121
+
122
+		if (block[tagName]) {
123
+			while (stack.last() && inline[stack.last()]) {
124
+				parseEndTag("", stack.last());
125
+			}
126
+		}
127
+
128
+		if (closeSelf[tagName] && stack.last() == tagName) {
129
+			parseEndTag("", tagName);
130
+		}
131
+
132
+		unary = empty[tagName] || !!unary;
133
+
134
+		if (!unary)
135
+			stack.push(tagName);
136
+
137
+		if (handler.start) {
138
+			var attrs = [];
139
+
140
+			rest.replace(attr, function (match, name) {
141
+				var value = arguments[2] ? arguments[2] :
142
+					arguments[3] ? arguments[3] :
143
+						arguments[4] ? arguments[4] :
144
+							fillAttrs[name] ? name : "";
145
+
146
+				attrs.push({
147
+					name: name,
148
+					value: value,
149
+					escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
150
+				});
151
+			});
152
+
153
+			if (handler.start) {
154
+				handler.start(tagName, attrs, unary);
155
+			}
156
+
157
+		}
158
+	}
159
+
160
+	function parseEndTag(tag, tagName) {
161
+		// If no tag name is provided, clean shop
162
+		if (!tagName)
163
+			var pos = 0;
164
+
165
+		// Find the closest opened tag of the same type
166
+		else {
167
+			tagName = tagName.toLowerCase();
168
+			for (var pos = stack.length - 1; pos >= 0; pos--)
169
+				if (stack[pos] == tagName)
170
+					break;
171
+		}
172
+		if (pos >= 0) {
173
+			// Close all the open elements, up the stack
174
+			for (var i = stack.length - 1; i >= pos; i--)
175
+				if (handler.end)
176
+					handler.end(stack[i]);
177
+
178
+			// Remove the open elements from the stack
179
+			stack.length = pos;
180
+		}
181
+	}
182
+};
183
+
184
+
185
+function makeMap(str) {
186
+	var obj = {}, items = str.split(",");
187
+	for (var i = 0; i < items.length; i++)
188
+		obj[items[i]] = true;
189
+	return obj;
190
+}
191
+
192
+module.exports = HTMLParser;

File diff suppressed because it is too large
+ 2532 - 0
components/local/wxParse/showdown.js


+ 207 - 0
components/local/wxParse/wxDiscode.js

@@ -0,0 +1,207 @@
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
+    
196
+    var patt1 = new RegExp("^//");
197
+    var result = patt1.test(url);
198
+    if(result){
199
+        url = rep+":"+url;
200
+    }
201
+    return  url;
202
+}
203
+
204
+module.exports = {
205
+    strDiscode:strDiscode,
206
+    urlToHttpUrl:urlToHttpUrl
207
+}

+ 158 - 0
components/local/wxParse/wxParse.js

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

+ 967 - 0
components/local/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}}" mode="aspectFit" bindload="wxParseImgLoad" bindtap="wxParseImgTap" mode="widthFix" style="width:{{350}}px;"
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="index">
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="index">
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="index">
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="index">
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}}">
89
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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="index">
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}}">
174
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
251
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
327
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
403
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
479
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
555
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
630
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
706
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
782
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
858
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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="index">
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="index">
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}}">
934
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="index">
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="index">
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="index">
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>

+ 224 - 0
components/local/wxParse/wxParse.wxss

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

BIN
images/add.png


BIN
images/all_carding.png


BIN
images/black_location.png


BIN
images/car-icon.png


BIN
images/card.png


BIN
images/card_img.png


BIN
images/clearMessage.png


BIN
images/delete-gray.png


+ 0 - 0
images/display.png


Some files were not shown because too many files changed in this diff