소스 검색

✨ 界面/对接

谢创宏 3 년 전
부모
커밋
71268f3546
100개의 변경된 파일15337개의 추가작업 그리고 69개의 파일을 삭제
  1. BIN
      .DS_Store
  2. 12 0
      app.js
  3. 14 1
      app.json
  4. BIN
      components/.DS_Store
  5. 755 0
      components/dist/calendar/index.js
  6. 6 0
      components/dist/calendar/index.json
  7. 67 0
      components/dist/calendar/index.wxml
  8. 215 0
      components/dist/calendar/index.wxss
  9. 47 0
      components/dist/icon/index.js
  10. 3 0
      components/dist/icon/index.json
  11. 1 0
      components/dist/icon/index.wxml
  12. 2820 0
      components/dist/icon/index.wxss
  13. 66 0
      components/dist/landscape/index.js
  14. 6 0
      components/dist/landscape/index.json
  15. 22 0
      components/dist/landscape/index.wxml
  16. 36 0
      components/dist/landscape/index.wxss
  17. 202 0
      components/dist/rater/index.js
  18. 6 0
      components/dist/rater/index.json
  19. 16 0
      components/dist/rater/index.wxml
  20. 52 0
      components/dist/rater/index.wxss
  21. 33 0
      components/local/calendar/func/config.js
  22. 1036 0
      components/local/calendar/func/convertSolarLunar.js
  23. 543 0
      components/local/calendar/func/day.js
  24. 375 0
      components/local/calendar/func/render.js
  25. 182 0
      components/local/calendar/func/todo.js
  26. 367 0
      components/local/calendar/func/utils.js
  27. 601 0
      components/local/calendar/func/week.js
  28. 26 0
      components/local/calendar/func/wxData.js
  29. 254 0
      components/local/calendar/index.js
  30. 3 0
      components/local/calendar/index.json
  31. 87 0
      components/local/calendar/index.wxml
  32. 214 0
      components/local/calendar/index.wxss
  33. 877 0
      components/local/calendar/main.js
  34. 29 0
      components/local/calendar/theme/iconfont.wxss
  35. 52 0
      components/local/calendar/theme/theme-default.wxss
  36. 49 0
      components/local/calendar/theme/theme-elegant.wxss
  37. 144 0
      components/local/v2/core.js
  38. 12 0
      components/local/v2/helper.js
  39. 257 0
      components/local/v2/index.js
  40. 3 0
      components/local/v2/index.json
  41. 60 0
      components/local/v2/index.wxml
  42. 214 0
      components/local/v2/index.wxss
  43. 212 0
      components/local/v2/plugins/holidays/holidays-map.js
  44. 201 0
      components/local/v2/plugins/holidays/index.js
  45. 18 0
      components/local/v2/plugins/index.js
  46. 277 0
      components/local/v2/plugins/preset/base.js
  47. 69 0
      components/local/v2/plugins/preset/get-calendar-data.js
  48. 9 0
      components/local/v2/plugins/preset/index.js
  49. 219 0
      components/local/v2/plugins/selectable.js
  50. 1036 0
      components/local/v2/plugins/solarLunar/convertSolarLunar.js
  51. 59 0
      components/local/v2/plugins/solarLunar/index.js
  52. 305 0
      components/local/v2/plugins/time-range.js
  53. 135 0
      components/local/v2/plugins/todo.js
  54. 432 0
      components/local/v2/plugins/week.js
  55. 51 0
      components/local/v2/render.js
  56. 29 0
      components/local/v2/theme/iconfont.wxss
  57. 61 0
      components/local/v2/theme/theme-default.wxss
  58. 58 0
      components/local/v2/theme/theme-elegant.wxss
  59. 285 0
      components/local/v2/utils/index.js
  60. 23 0
      components/local/v2/utils/logger.js
  61. 30 0
      components/local/v2/utils/wxData.js
  62. BIN
      images/.DS_Store
  63. BIN
      images/gzh.png
  64. BIN
      images/header.png
  65. BIN
      images/icon_01.png
  66. BIN
      images/icon_02.png
  67. BIN
      images/icon_05.png
  68. BIN
      images/icon_logo.png
  69. BIN
      images/right.png
  70. BIN
      images/wx.png
  71. 105 0
      pages/clientInfo/clientInfo.js
  72. 6 0
      pages/clientInfo/clientInfo.json
  73. 68 0
      pages/clientInfo/clientInfo.wxml
  74. 160 0
      pages/clientInfo/clientInfo.wxss
  75. 157 0
      pages/clientInfoEdit/clientInfoEdit.js
  76. 7 0
      pages/clientInfoEdit/clientInfoEdit.json
  77. 91 0
      pages/clientInfoEdit/clientInfoEdit.wxml
  78. 84 0
      pages/clientInfoEdit/clientInfoEdit.wxss
  79. 66 0
      pages/contact/contact.js
  80. 4 0
      pages/contact/contact.json
  81. 21 0
      pages/contact/contact.wxml
  82. 33 0
      pages/contact/contact.wxss
  83. 155 0
      pages/dossier/dossier.js
  84. 7 0
      pages/dossier/dossier.json
  85. 68 0
      pages/dossier/dossier.wxml
  86. 68 0
      pages/dossier/dossier.wxss
  87. 191 31
      pages/feedback/feedback.js
  88. 3 1
      pages/feedback/feedback.json
  89. 23 5
      pages/feedback/feedback.wxml
  90. 38 0
      pages/feedback/feedback.wxss
  91. 83 6
      pages/home/home.js
  92. 24 17
      pages/home/home.wxml
  93. 143 0
      pages/login/login.js
  94. 3 0
      pages/login/login.json
  95. 15 0
      pages/login/login.wxml
  96. 39 0
      pages/login/login.wxss
  97. 87 5
      pages/member/member.js
  98. 111 2
      pages/member/member.wxml
  99. 204 1
      pages/member/member.wxss
  100. 0 0
      pages/name/name.js

BIN
.DS_Store


+ 12 - 0
app.js

@@ -13,6 +13,18 @@ App({
13 13
       }
14 14
     })
15 15
   },
16
+  onShow(opts) {
17
+    this.globalData.sceneData = {
18
+      path: opts.path,
19
+      query: opts.query
20
+    }
21
+    if (!wx.getStorageSync('token') || !wx.getStorageSync('userInfo')) {
22
+      wx.reLaunch({
23
+        url: '/pages/login/login',
24
+      })
25
+      // login(opts)
26
+    }
27
+  }, 
16 28
   globalData: {
17 29
     userInfo: null
18 30
   }

+ 14 - 1
app.json

@@ -5,7 +5,20 @@
5 5
     "pages/logs/logs",
6 6
     "pages/member/member",
7 7
     "pages/orderDetail/orderDetail",
8
-    "pages/feedback/feedback"
8
+    "pages/feedback/feedback",
9
+    "pages/clientInfo/clientInfo",
10
+    "pages/clientInfoEdit/clientInfoEdit",
11
+    "pages/tagsEdit/tagsEdit",
12
+    "pages/tagsAdd/tagsAdd",
13
+    "pages/tagsCustom/tagsCustom",
14
+    "pages/recordSheet/recordSheet",
15
+    "pages/contact/contact",
16
+    "pages/dossier/dossier",
17
+    "pages/name/name",
18
+    "pages/role/role",
19
+    "pages/recordList/recordList",
20
+    "pages/test/test",
21
+    "pages/login/login"
9 22
   ],
10 23
   "tabBar": {
11 24
     "color": "#666666",

BIN
components/.DS_Store


+ 755 - 0
components/dist/calendar/index.js

@@ -0,0 +1,755 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+import classNames from '../helpers/classNames'
3
+
4
+const defaults = {
5
+    prefixCls: 'wux-calendar',
6
+    monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
7
+    monthNamesShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
8
+    dayNames: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
9
+    dayNamesShort: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
10
+    firstDay: 1, // First day of the week, Monday
11
+    weekendDays: [0, 6], // Sunday and Saturday
12
+    multiple: false,
13
+    dateFormat: 'yyyy-mm-dd',
14
+    direction: 'horizontal', // or 'vertical'
15
+    minDate: null,
16
+    maxDate: null,
17
+    touchMove: true,
18
+    animate: true,
19
+    closeOnSelect: true,
20
+    weekHeader: true,
21
+    toolbar: true,
22
+    value: [],
23
+    onMonthAdd() {},
24
+    onChange() {},
25
+    onOpen() {},
26
+    onClose() {},
27
+    onDayClick() {},
28
+    onMonthYearChangeStart() {},
29
+    onMonthYearChangeEnd() {},
30
+}
31
+
32
+// 获取手指触摸点坐标
33
+const getTouchPosition = (e) => {
34
+    const touches = e.touches[0] || e.changedTouches[0]
35
+    return {
36
+        x: touches.pageX,
37
+        y: touches.pageY,
38
+    }
39
+}
40
+
41
+// 获取元素旋转属性
42
+const getTransform = (translate, isH) => `transform: translate3d(${isH ? translate : 0}%, ${isH ? 0 : translate}%, 0)`
43
+
44
+// 判断两个日期是否在同一天
45
+const isSameDate = (a, b) => {
46
+    const prev = new Date(a)
47
+    const next = new Date(b)
48
+    return prev.getFullYear() === next.getFullYear() && prev.getMonth() === next.getMonth() && prev.getDate() === next.getDate()
49
+}
50
+
51
+baseComponent({
52
+    useFunc: true,
53
+    data: defaults,
54
+    computed: {
55
+        classes: ['prefixCls, direction', function(prefixCls, direction) {
56
+            const wrap = classNames(prefixCls, {
57
+                [`${prefixCls}--${direction}`]: direction,
58
+            })
59
+            const content = `${prefixCls}__content`
60
+            const hd = `${prefixCls}__hd`
61
+            const toolbar = `${prefixCls}__toolbar`
62
+            const picker = `${prefixCls}__picker`
63
+            const link = `${prefixCls}__link`
64
+            const prev = classNames(`${prefixCls}__icon`, {
65
+                [`${prefixCls}__icon--prev`]: true,
66
+            })
67
+            const next = classNames(`${prefixCls}__icon`, {
68
+                [`${prefixCls}__icon--next`]: true,
69
+            })
70
+            const value = `${prefixCls}__value`
71
+            const bd = `${prefixCls}__bd`
72
+            const weekdays = `${prefixCls}__weekdays`
73
+            const weekday = `${prefixCls}__weekday`
74
+            const months = `${prefixCls}__months`
75
+            const monthsContent = `${prefixCls}__months-content`
76
+            const month = `${prefixCls}__month`
77
+            const days = `${prefixCls}__days`
78
+            const day = `${prefixCls}__day`
79
+            const text = `${prefixCls}__text`
80
+
81
+            return {
82
+                wrap,
83
+                content,
84
+                hd,
85
+                toolbar,
86
+                picker,
87
+                link,
88
+                prev,
89
+                next,
90
+                value,
91
+                bd,
92
+                weekdays,
93
+                weekday,
94
+                months,
95
+                monthsContent,
96
+                month,
97
+                days,
98
+                day,
99
+                text,
100
+            }
101
+        }],
102
+    },
103
+    methods: {
104
+        /**
105
+         * 打开日历
106
+         * @param {Object} opts
107
+         */
108
+        open(opts = {}) {
109
+            const options = this.$$mergeOptionsAndBindMethods(Object.assign({}, defaults, opts))
110
+
111
+            this.monthsTranslate = 0
112
+            this.isH = options.direction === 'horizontal'
113
+
114
+            this.$$setData({ in: true, ...options }).then(() => this.init())
115
+            this.setValue(options.value)
116
+
117
+            if (typeof this.fns.onOpen === 'function') {
118
+                this.fns.onOpen.call(this)
119
+            }
120
+        },
121
+        /**
122
+         * 关闭日历
123
+         */
124
+        close() {
125
+            this.$$setData({ in: false })
126
+
127
+            if (typeof this.fns.onClose === 'function') {
128
+                this.fns.onClose.call(this)
129
+            }
130
+        },
131
+        /**
132
+         * 初始化
133
+         */
134
+        init() {
135
+            const weeks = this.setWeekHeader()
136
+            const months = this.setMonthsHTML()
137
+            const monthsTranslate = this.setMonthsTranslate()
138
+
139
+            if (typeof this.fns.onMonthAdd === 'function') {
140
+                months.forEach((month) => this.fns.onMonthAdd.call(this, month))
141
+            }
142
+
143
+            return this.$$setData({ weeks, months, monthsTranslate, wrapperTranslate: '' }).then(() => this.$$setData({...this.updateCurrentMonthYear() }))
144
+        },
145
+        /**
146
+         * 设置月份的位置信息
147
+         * @param {Number} translate
148
+         */
149
+        setMonthsTranslate(translate = this.monthsTranslate) {
150
+            const prevMonthTranslate = -(translate + 1) * 100
151
+            const currentMonthTranslate = -translate * 100
152
+            const nextMonthTranslate = -(translate - 1) * 100
153
+
154
+            return [
155
+                getTransform(prevMonthTranslate, this.isH),
156
+                getTransform(currentMonthTranslate, this.isH),
157
+                getTransform(nextMonthTranslate, this.isH),
158
+            ]
159
+        },
160
+        /**
161
+         * 更新当前年月
162
+         * @param {String} dir 方向
163
+         */
164
+        updateCurrentMonthYear(dir) {
165
+            const { months, monthNames } = this.data
166
+
167
+            if (typeof dir === 'undefined') {
168
+                const currentMonth = parseInt(months[1].month, 10)
169
+                const currentYear = parseInt(months[1].year, 10)
170
+                const currentMonthName = monthNames[currentMonth]
171
+
172
+                return {
173
+                    currentMonth,
174
+                    currentYear,
175
+                    currentMonthName,
176
+                }
177
+            }
178
+
179
+            const currentMonth = parseInt(months[dir === 'next' ? (months.length - 1) : 0].month, 10)
180
+            const currentYear = parseInt(months[dir === 'next' ? (months.length - 1) : 0].year, 10)
181
+            const currentMonthName = monthNames[currentMonth]
182
+
183
+            return {
184
+                currentMonth,
185
+                currentYear,
186
+                currentMonthName,
187
+            }
188
+        },
189
+        /**
190
+         * 手指触摸动作开始
191
+         * @param {Object} e 事件对象
192
+         */
193
+        onTouchStart(e) {
194
+            if (!this.data.touchMove || this.isMoved || this.isRendered) return
195
+
196
+            this.start = getTouchPosition(e)
197
+            this.move = {}
198
+            this.touchesDiff = 0
199
+            this.allowItemClick = true
200
+            this.isMoved = false
201
+        },
202
+        /**
203
+         * 手指触摸后移动
204
+         * @param {Object} e 事件对象
205
+         */
206
+        onTouchMove(e) {
207
+            if (!this.data.touchMove || this.isRendered) return
208
+
209
+            this.allowItemClick = false
210
+
211
+            if (!this.isMoved) {
212
+                this.isMoved = true
213
+            }
214
+
215
+            this.$$setData({ swiping: true })
216
+
217
+            const { prefixCls } = this.data
218
+            const query = wx.createSelectorQuery().in(this)
219
+            query.select(`.${prefixCls}__months-content`).boundingClientRect((rect) => {
220
+
221
+                // 由于 boundingClientRect 为异步方法,某些情况下其回调函数在 onTouchEnd 之后触发,导致 wrapperTranslate 计算错误
222
+                // 所以判断 this.isMoved = false 时阻止回调函数的执行
223
+                if (!rect || !this.isMoved) return
224
+
225
+                this.move = getTouchPosition(e)
226
+                this.touchesDiff = this.isH ? this.move.x - this.start.x : this.move.y - this.start.y
227
+
228
+                const { width, height } = rect
229
+                const percentage = this.touchesDiff / (this.isH ? width : height)
230
+                const currentTranslate = (this.monthsTranslate + percentage) * 100
231
+                const transform = getTransform(currentTranslate, this.isH)
232
+
233
+                this.$$setData({
234
+                    wrapperTranslate: `transition-duration: 0s; ${transform}`,
235
+                })
236
+            })
237
+            query.exec()
238
+        },
239
+        /**
240
+         * 手指触摸动作结束
241
+         */
242
+        onTouchEnd() {
243
+            if (!this.data.touchMove || !this.isMoved || this.isRendered) return
244
+
245
+            this.isMoved = false
246
+            this.$$setData({ swiping: false })
247
+
248
+            if (Math.abs(this.touchesDiff) < 30) {
249
+                this.resetMonth()
250
+            } else if (this.touchesDiff >= 30) {
251
+                this.prevMonth()
252
+            } else {
253
+                this.nextMonth()
254
+            }
255
+
256
+            // Allow click
257
+            setTimeout(() => (this.allowItemClick = true), 100)
258
+        },
259
+        /**
260
+         * 日期的点击事件
261
+         * @param {Object} e 事件对象
262
+         */
263
+        onDayClick(e) {
264
+            if (this.allowItemClick) {
265
+                const dataset = e.currentTarget.dataset
266
+                const dateYear = dataset.year
267
+                const dateMonth = dataset.month
268
+                const dateDay = dataset.day
269
+                const dateType = dataset.type
270
+
271
+                if (dateType.selected && !this.data.multiple) return
272
+                if (dateType.disabled) return
273
+                if (dateType.next) this.nextMonth()
274
+                if (dateType.prev) this.prevMonth()
275
+
276
+                if (typeof this.fns.onDayClick === 'function') {
277
+                    this.fns.onDayClick.call(this, dateYear, dateMonth, dateDay)
278
+                }
279
+
280
+                this.addValue(new Date(dateYear, dateMonth, dateDay).getTime())
281
+
282
+                if (this.data.closeOnSelect && !this.data.multiple) {
283
+                    this.close()
284
+                }
285
+            }
286
+        },
287
+        /**
288
+         * 重置月份的位置信息
289
+         */
290
+        resetMonth() {
291
+            const translate = this.monthsTranslate * 100
292
+            const transform = getTransform(translate, this.isH)
293
+
294
+            this.$$setData({
295
+                wrapperTranslate: `transition-duration: 0s; ${transform}`,
296
+            })
297
+        },
298
+        /**
299
+         * 设置年月
300
+         * @param {String} year 年份
301
+         * @param {String} month 月份
302
+         */
303
+        setYearMonth(year = this.data.currentYear, month = this.data.currentMonth) {
304
+            const { months, monthsTranslate, maxDate, minDate, currentYear, currentMonth } = this.data
305
+            const targetDate = year < currentYear ? new Date(year, month + 1, -1).getTime() : new Date(year, month).getTime()
306
+
307
+            // 判断是否存在最大日期
308
+            if (maxDate && targetDate > new Date(maxDate).getTime()) return
309
+
310
+            // 判断是否存在最小日期
311
+            if (minDate && targetDate < new Date(minDate).getTime()) return
312
+
313
+            const currentDate = new Date(currentYear, currentMonth).getTime()
314
+            const dir = targetDate > currentDate ? 'next' : 'prev'
315
+            const newMonthHTML = this.monthHTML(new Date(year, month))
316
+
317
+            const prevTranslate = this.monthsTranslate = this.monthsTranslate || 0
318
+
319
+            if (targetDate > currentDate) {
320
+                this.monthsTranslate = this.monthsTranslate - 1
321
+
322
+                const translate = -(prevTranslate - 1) * 100
323
+                const nextMonthTranslate = getTransform(translate, this.isH)
324
+
325
+                this.$$setData({
326
+                    months: [months[1], months[2], newMonthHTML],
327
+                    monthsTranslate: [monthsTranslate[1], monthsTranslate[2], nextMonthTranslate],
328
+                })
329
+            } else {
330
+                this.monthsTranslate = this.monthsTranslate + 1
331
+
332
+                const translate = -(prevTranslate + 1) * 100
333
+                const prevMonthTranslate = getTransform(translate, this.isH)
334
+
335
+                this.$$setData({
336
+                    months: [newMonthHTML, months[0], months[1]],
337
+                    monthsTranslate: [prevMonthTranslate, monthsTranslate[0], monthsTranslate[1]],
338
+                })
339
+            }
340
+
341
+            this.onMonthChangeStart(dir)
342
+
343
+            const transform = getTransform(this.monthsTranslate * 100, this.isH)
344
+            const duration = this.data.animate ? .3 : 0
345
+            const wrapperTranslate = `transition-duration: ${duration}s; ${transform}`
346
+
347
+            this.$$setData({
348
+                wrapperTranslate,
349
+            })
350
+
351
+            setTimeout(() => this.onMonthChangeEnd(dir, true), duration)
352
+        },
353
+        /**
354
+         * 下一年
355
+         */
356
+        nextYear() {
357
+            this.setYearMonth(this.data.currentYear + 1)
358
+        },
359
+        /**
360
+         * 上一年
361
+         */
362
+        prevYear() {
363
+            this.setYearMonth(this.data.currentYear - 1)
364
+        },
365
+        /**
366
+         * 下一月
367
+         */
368
+        nextMonth() {
369
+            const { months, monthsTranslate, maxDate, currentMonth } = this.data
370
+            const nextMonth = parseInt(months[months.length - 1].month, 10)
371
+            const nextYear = parseInt(months[months.length - 1].year, 10)
372
+            const nextDate = new Date(nextYear, nextMonth)
373
+            const nextDateTime = nextDate.getTime()
374
+
375
+            // 判断是否存在最大日期
376
+            if (maxDate && nextDateTime > new Date(maxDate).getTime()) {
377
+                return this.resetMonth()
378
+            }
379
+
380
+            this.monthsTranslate = this.monthsTranslate - 1
381
+
382
+            if (nextMonth === currentMonth) {
383
+                const translate = -(this.monthsTranslate) * 100
384
+                const nextMonthHTML = this.monthHTML(nextDateTime, 'next')
385
+                const nextMonthTranslate = getTransform(translate, this.isH)
386
+                const months = [this.data.months[1], this.data.months[2], nextMonthHTML]
387
+
388
+                this.$$setData({
389
+                    months,
390
+                    monthsTranslate: [monthsTranslate[1], monthsTranslate[2], nextMonthTranslate],
391
+                })
392
+
393
+                if (typeof this.fns.onMonthAdd === 'function') {
394
+                    this.fns.onMonthAdd.call(this, months[months.length - 1])
395
+                }
396
+            }
397
+
398
+            this.onMonthChangeStart('next')
399
+
400
+            const transform = getTransform(this.monthsTranslate * 100, this.isH)
401
+            const duration = this.data.animate ? .3 : 0
402
+            const wrapperTranslate = `transition-duration: ${duration}s; ${transform}`
403
+
404
+            this.$$setData({
405
+                wrapperTranslate,
406
+            })
407
+
408
+            setTimeout(() => this.onMonthChangeEnd('next'), duration)
409
+        },
410
+        /**
411
+         * 上一月
412
+         */
413
+        prevMonth() {
414
+            const { months, monthsTranslate, minDate, currentMonth } = this.data
415
+            const prevMonth = parseInt(months[0].month, 10)
416
+            const prevYear = parseInt(months[0].year, 10)
417
+            const prevDate = new Date(prevYear, prevMonth + 1, -1)
418
+            const prevDateTime = prevDate.getTime()
419
+
420
+            // 判断是否存在最小日期
421
+            if (minDate && prevDateTime < new Date(minDate).getTime()) {
422
+                return this.resetMonth()
423
+            }
424
+
425
+            this.monthsTranslate = this.monthsTranslate + 1
426
+
427
+            if (prevMonth === currentMonth) {
428
+                const translate = -(this.monthsTranslate) * 100
429
+                const prevMonthHTML = this.monthHTML(prevDateTime, 'prev')
430
+                const prevMonthTranslate = getTransform(translate, this.isH)
431
+                const months = [prevMonthHTML, this.data.months[0], this.data.months[1]]
432
+
433
+                this.$$setData({
434
+                    months,
435
+                    monthsTranslate: [prevMonthTranslate, monthsTranslate[0], monthsTranslate[1]],
436
+                })
437
+
438
+                if (typeof this.fns.onMonthAdd === 'function') {
439
+                    this.fns.onMonthAdd.call(this, months[0])
440
+                }
441
+            }
442
+
443
+            this.onMonthChangeStart('prev')
444
+
445
+            const transform = getTransform(this.monthsTranslate * 100, this.isH)
446
+            const duration = this.data.animate ? .3 : 0
447
+            const wrapperTranslate = `transition-duration: ${duration}s; ${transform}`
448
+
449
+            this.$$setData({
450
+                wrapperTranslate,
451
+            })
452
+
453
+            setTimeout(() => this.onMonthChangeEnd('prev'), duration)
454
+        },
455
+        /**
456
+         * 月份变化开始时的回调函数
457
+         * @param {String} dir 方向
458
+         */
459
+        onMonthChangeStart(dir) {
460
+            const params = this.updateCurrentMonthYear(dir)
461
+
462
+            this.$$setData(params)
463
+
464
+            if (typeof this.fns.onMonthYearChangeStart === 'function') {
465
+                this.fns.onMonthYearChangeStart.call(this, params.currentYear, params.currentMonth)
466
+            }
467
+        },
468
+        /**
469
+         * 月份变化完成时的回调函数
470
+         * @param {String} dir 方向
471
+         * @param {Boolean} rebuildBoth 重置
472
+         */
473
+        onMonthChangeEnd(dir = 'next', rebuildBoth = false) {
474
+            const { currentYear, currentMonth } = this.data
475
+            let nextMonthHTML, prevMonthHTML, newMonthHTML, months = [...this.data.months]
476
+
477
+            if (!rebuildBoth) {
478
+                newMonthHTML = this.monthHTML(new Date(currentYear, currentMonth), dir)
479
+                if (dir === 'next') {
480
+                    months = [months[1], months[2], newMonthHTML]
481
+                } else if (dir === 'prev') {
482
+                    months = [newMonthHTML, months[0], months[1]]
483
+                }
484
+            } else {
485
+                prevMonthHTML = this.monthHTML(new Date(currentYear, currentMonth), 'prev')
486
+                nextMonthHTML = this.monthHTML(new Date(currentYear, currentMonth), 'next')
487
+                months = [prevMonthHTML, months[dir === 'next' ? months.length - 1 : 0], nextMonthHTML]
488
+            }
489
+
490
+            const monthsTranslate = this.setMonthsTranslate(this.monthsTranslate)
491
+
492
+            this.isRendered = true
493
+            this.$$setData({ months, monthsTranslate }).then(() => (this.isRendered = false))
494
+
495
+            if (typeof this.fns.onMonthAdd === 'function') {
496
+                this.fns.onMonthAdd.call(this, dir === 'next' ? months[months.length - 1] : months[0])
497
+            }
498
+
499
+            if (typeof this.fns.onMonthYearChangeEnd === 'function') {
500
+                this.fns.onMonthYearChangeEnd.call(this, currentYear, currentMonth)
501
+            }
502
+        },
503
+        /**
504
+         * 设置星期
505
+         */
506
+        setWeekHeader() {
507
+            const { weekHeader, firstDay, dayNamesShort, weekendDays } = this.data
508
+            const weeks = []
509
+
510
+            if (weekHeader) {
511
+                for (let i = 0; i < 7; i++) {
512
+                    const weekDayIndex = (i + firstDay > 6) ? (i - 7 + firstDay) : (i + firstDay)
513
+                    const dayName = dayNamesShort[weekDayIndex]
514
+                    const weekend = weekendDays.indexOf(weekDayIndex) >= 0
515
+
516
+                    weeks.push({
517
+                        weekend,
518
+                        dayName,
519
+                    })
520
+                }
521
+            }
522
+
523
+            return weeks
524
+        },
525
+        /**
526
+         * 判断日期是否存在
527
+         */
528
+        daysInMonth(date) {
529
+            const d = new Date(date)
530
+            return new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate()
531
+        },
532
+        /**
533
+         * 设置月份数据
534
+         */
535
+        monthHTML(date, offset) {
536
+            date = new Date(date)
537
+            let year = date.getFullYear(),
538
+                month = date.getMonth(),
539
+                time = date.getTime()
540
+
541
+            const monthHTML = {
542
+                year,
543
+                month,
544
+                time,
545
+                items: [],
546
+            }
547
+
548
+            if (offset === 'next') {
549
+                if (month === 11) date = new Date(year + 1, 0)
550
+                else date = new Date(year, month + 1, 1)
551
+            }
552
+
553
+            if (offset === 'prev') {
554
+                if (month === 0) date = new Date(year - 1, 11)
555
+                else date = new Date(year, month - 1, 1)
556
+            }
557
+
558
+            if (offset === 'next' || offset === 'prev') {
559
+                month = date.getMonth()
560
+                year = date.getFullYear()
561
+                time = date.getTime()
562
+            }
563
+
564
+            let daysInPrevMonth = this.daysInMonth(new Date(date.getFullYear(), date.getMonth()).getTime() - 10 * 24 * 60 * 60 * 1000),
565
+                daysInMonth = this.daysInMonth(date),
566
+                firstDayOfMonthIndex = new Date(date.getFullYear(), date.getMonth()).getDay()
567
+            if (firstDayOfMonthIndex === 0) firstDayOfMonthIndex = 7
568
+
569
+            let dayDate, currentValues = [],
570
+                i, j,
571
+                rows = 6,
572
+                cols = 7,
573
+                dayIndex = 0 + (this.data.firstDay - 1),
574
+                today = new Date().setHours(0, 0, 0, 0),
575
+                minDate = this.data.minDate ? new Date(this.data.minDate).getTime() : null,
576
+                maxDate = this.data.maxDate ? new Date(this.data.maxDate).getTime() : null
577
+
578
+            if (this.data.value && this.data.value.length) {
579
+                for (let i = 0; i < this.data.value.length; i++) {
580
+                    currentValues.push(new Date(this.data.value[i]).setHours(0, 0, 0, 0))
581
+                }
582
+            }
583
+
584
+            for (let i = 1; i <= rows; i++) {
585
+                let rowHTML = []
586
+                let row = i
587
+
588
+                for (let j = 1; j <= cols; j++) {
589
+                    let col = j
590
+                    dayIndex++
591
+                    let dayNumber = dayIndex - firstDayOfMonthIndex
592
+                    let type = {}
593
+
594
+                    if (dayNumber < 0) {
595
+                        dayNumber = daysInPrevMonth + dayNumber + 1
596
+                        type.prev = true
597
+                        dayDate = new Date(month - 1 < 0 ? year - 1 : year, month - 1 < 0 ? 11 : month - 1, dayNumber).getTime()
598
+                    } else {
599
+                        dayNumber = dayNumber + 1
600
+                        if (dayNumber > daysInMonth) {
601
+                            dayNumber = dayNumber - daysInMonth
602
+                            type.next = true
603
+                            dayDate = new Date(month + 1 > 11 ? year + 1 : year, month + 1 > 11 ? 0 : month + 1, dayNumber).getTime()
604
+                        } else {
605
+                            dayDate = new Date(year, month, dayNumber).getTime()
606
+                        }
607
+                    }
608
+
609
+                    // Today
610
+                    if (dayDate === today) type.today = true
611
+
612
+                    // Selected
613
+                    if (currentValues.indexOf(dayDate) >= 0) type.selected = true
614
+
615
+                    // Weekend
616
+                    if (this.data.weekendDays.indexOf(col - 1) >= 0) {
617
+                        type.weekend = true
618
+                    }
619
+
620
+                    // Disabled
621
+                    if ((minDate && dayDate < minDate) || (maxDate && dayDate > maxDate)) {
622
+                        type.disabled = true
623
+                    }
624
+
625
+                    dayDate = new Date(dayDate)
626
+                    const dayYear = dayDate.getFullYear()
627
+                    const dayMonth = dayDate.getMonth()
628
+
629
+                    rowHTML.push({
630
+                        type,
631
+                        year: dayYear,
632
+                        month: dayMonth,
633
+                        day: dayNumber,
634
+                        date: `${dayYear}-${dayMonth + 1}-${dayNumber}`,
635
+                    })
636
+                }
637
+
638
+                monthHTML.year = year
639
+                monthHTML.month = month
640
+                monthHTML.time = time
641
+
642
+                monthHTML.items.push(rowHTML)
643
+            }
644
+
645
+            return monthHTML
646
+        },
647
+        /**
648
+         * 设置月份
649
+         */
650
+        setMonthsHTML() {
651
+            const layoutDate = this.data.value && this.data.value.length ? this.data.value[0] : new Date().setHours(0, 0, 0, 0)
652
+            const prevMonthHTML = this.monthHTML(layoutDate, 'prev')
653
+            const currentMonthHTML = this.monthHTML(layoutDate)
654
+            const nextMonthHTML = this.monthHTML(layoutDate, 'next')
655
+
656
+            return [prevMonthHTML, currentMonthHTML, nextMonthHTML]
657
+        },
658
+        /**
659
+         * 格式化日期
660
+         */
661
+        formatDate(date) {
662
+            date = new Date(date)
663
+            const year = date.getFullYear()
664
+            const month = date.getMonth()
665
+            const month1 = month + 1
666
+            const day = date.getDate()
667
+            const weekDay = date.getDay()
668
+
669
+            return this.data.dateFormat
670
+                .replace(/yyyy/g, year)
671
+                .replace(/yy/g, (year + '').substring(2))
672
+                .replace(/mm/g, month1 < 10 ? '0' + month1 : month1)
673
+                .replace(/m/g, month1)
674
+                .replace(/MM/g, this.data.monthNames[month])
675
+                .replace(/M/g, this.data.monthNamesShort[month])
676
+                .replace(/dd/g, day < 10 ? '0' + day : day)
677
+                .replace(/d/g, day)
678
+                .replace(/DD/g, this.data.dayNames[weekDay])
679
+                .replace(/D/g, this.data.dayNamesShort[weekDay])
680
+        },
681
+        /**
682
+         * 添加选中值
683
+         */
684
+        addValue(value) {
685
+            if (this.data.multiple) {
686
+                let arrValues = this.data.value || []
687
+                let inValuesIndex = -1
688
+
689
+                for (let i = 0; i < arrValues.length; i++) {
690
+                    if (isSameDate(value, arrValues[i])) {
691
+                        inValuesIndex = i
692
+                    }
693
+                }
694
+
695
+                if (inValuesIndex === -1) {
696
+                    arrValues.push(value)
697
+                } else {
698
+                    arrValues.splice(inValuesIndex, 1)
699
+                }
700
+
701
+                this.setValue(arrValues)
702
+            } else {
703
+                this.setValue([value])
704
+            }
705
+        },
706
+        /**
707
+         * 设置选择值
708
+         */
709
+        setValue(value) {
710
+            this.$$setData({ value }).then(() => this.updateValue())
711
+        },
712
+        /**
713
+         * 更新日历
714
+         */
715
+        updateValue() {
716
+            const changedPath = {}
717
+
718
+            this.data.months.forEach((n, i) => {
719
+                n.items.forEach((v, k) => {
720
+                    v.forEach((p, j) => {
721
+                        if (p.type.selected) {
722
+                            changedPath[`months[${i}].items[${k}][${j}].type.selected`] = false
723
+                        }
724
+                    })
725
+                })
726
+            })
727
+
728
+            for (let ii = 0; ii < this.data.value.length; ii++) {
729
+                const valueDate = new Date(this.data.value[ii])
730
+                const valueYear = valueDate.getFullYear()
731
+                const valueMonth = valueDate.getMonth()
732
+                const valueDay = valueDate.getDate()
733
+
734
+                this.data.months.forEach((n, i) => {
735
+                    if (n.year === valueYear && n.month === valueMonth) {
736
+                        n.items.forEach((v, k) => {
737
+                            v.forEach((p, j) => {
738
+                                if (p.year === valueYear && p.month === valueMonth && p.day === valueDay) {
739
+                                    changedPath[`months[${i}].items[${k}][${j}].type.selected`] = true
740
+                                }
741
+                            })
742
+                        })
743
+                    }
744
+                })
745
+            }
746
+
747
+            this.$$setData(changedPath)
748
+
749
+            if (typeof this.fns.onChange === 'function') {
750
+                this.fns.onChange.call(this, this.data.value, this.data.value.map((n) => this.formatDate(n)))
751
+            }
752
+        },
753
+        noop() {},
754
+    },
755
+})

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

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

+ 67 - 0
components/dist/calendar/index.wxml

@@ -0,0 +1,67 @@
1
+<wux-popup position="bottom" visible="{{ in }}" zIndex="1010" safeArea="bottom" bind:close="close">
2
+    <view class="wux-class {{ classes.wrap }}">
3
+        <view class="{{ classes.content }}">
4
+            <view class="{{ classes.hd }}" wx:if="{{ toolbar }}">
5
+                <view class="{{ classes.toolbar }}">
6
+                    <view class="{{ classes.picker }}">
7
+                        <view class="{{ classes.link }}" bindtap="prevMonth">
8
+                            <view class="{{ classes.prev }}"></view>
9
+                        </view>
10
+                        <view class="{{ classes.value }}">{{ currentMonthName }}</view>
11
+                        <view class="{{ classes.link }}" bindtap="nextMonth">
12
+                            <view class="{{ classes.next }}"></view>
13
+                        </view>
14
+                    </view>
15
+                    <view class="{{ classes.picker }}">
16
+                        <view class="{{ classes.link }}" bindtap="prevYear">
17
+                            <view class="{{ classes.prev }}"></view>
18
+                        </view>
19
+                        <text class="{{ classes.value }}">{{ currentYear }}</text>
20
+                        <view class="{{ classes.link }}" bindtap="nextYear">
21
+                            <view class="{{ classes.next }}"></view>
22
+                        </view>
23
+                    </view>
24
+                </view>
25
+            </view>
26
+            <view class="{{ classes.bd }}">
27
+                <view class="{{ classes.weekdays }}" wx:if="{{ weekHeader }}">
28
+                    <block wx:for="{{ weeks }}" wx:for-item="week" wx:for-index="weekIndex" wx:key="weekIndex">
29
+                        <view class="{{ classes.weekday }} {{ week.weekend ? prefixCls + '__weekday--weekend' : '' }}">
30
+                            {{ week.dayName }}
31
+                        </view>
32
+                    </block>
33
+                </view>
34
+                <view class="{{ classes.months }}">
35
+                    <view class="{{ classes.monthsContent }}" bindtouchstart="onTouchStart" catchtouchmove="{{ swiping ? 'noop' : '' }}" capture-bind:touchmove="onTouchMove" bindtouchend="onTouchEnd" style="{{ wrapperTranslate }}">
36
+                        <block wx:for="{{ months }}" wx:for-item="month" wx:for-index="monthIndex" wx:key="monthIndex">
37
+                            <view
38
+                                data-year="{{ month.year }}"
39
+                                data-month="{{ month.month }}"
40
+                                class="{{ classes.month }} {{ monthIndex === 0 ? (prefixCls + '__month--prev') : monthIndex === 1 ? (prefixCls + '__month--current') : (prefixCls + '__month--next') }}"
41
+                                style="{{ monthsTranslate[monthIndex] }}"
42
+                            >
43
+                                <block wx:for="{{ month.items }}" wx:for-item="row" wx:for-index="rowIndex" wx:key="rowIndex">
44
+                                    <view class="{{ classes.days }}">
45
+                                        <block wx:for="{{ row }}" wx:for-item="col" wx:for-index="colIndex" wx:key="colIndex">
46
+                                            <view
47
+                                            data-year="{{ col.year }}"
48
+                                            data-month="{{ col.month }}"
49
+                                            data-day="{{ col.day }}"
50
+                                            data-date="{{ col.date }}"
51
+                                            data-type="{{ col.type }}"
52
+                                            class="{{ classes.day }} {{ col.type.prev ? prefixCls + '__day--prev' : '' }} {{ col.type.next ? prefixCls + '__day--next' : '' }} {{ col.type.today ? prefixCls + '__day--today' : '' }} {{ col.type.selected ? prefixCls + '__day--selected' : '' }} {{ col.type.weekend ? prefixCls + '__day--weekend' : '' }} {{ col.type.disabled ? prefixCls + '__day--disabled' : '' }}"
53
+                                            bindtap="onDayClick"
54
+                                            >
55
+                                                <text class="{{ classes.text }}">{{ col.day }}</text>
56
+                                            </view>
57
+                                        </block>
58
+                                    </view>
59
+                                </block>
60
+                            </view>
61
+                        </block>
62
+                    </view>
63
+                </view>
64
+            </view>
65
+        </view>
66
+    </view>
67
+</wux-popup>

+ 215 - 0
components/dist/calendar/index.wxss

@@ -0,0 +1,215 @@
1
+.wux-calendar {
2
+  position: relative;
3
+  background: #fff;
4
+  height: 600rpx;
5
+  width: 100%;
6
+  overflow: hidden
7
+}
8
+.wux-calendar__content {
9
+  position: relative;
10
+  width: 100%;
11
+  height: 100%;
12
+  transition: transform .3s
13
+}
14
+.wux-calendar__bd {
15
+  height: 100%;
16
+  position: relative;
17
+  overflow: hidden
18
+}
19
+.wux-calendar__hd {
20
+  position: relative;
21
+  width: 100%
22
+}
23
+.wux-calendar__hd::before {
24
+  content: " ";
25
+  position: absolute;
26
+  left: 0;
27
+  top: 0;
28
+  right: 0;
29
+  height: 1PX;
30
+  border-top: 1PX solid #d9d9d9;
31
+  color: #d9d9d9;
32
+  transform-origin: 0 0;
33
+  transform: scaleY(.5)
34
+}
35
+.wux-calendar__hd + .wux-calendar__bd {
36
+  height: calc(97.8%)
37
+}
38
+.wux-calendar__toolbar {
39
+  height: 2.2rem;
40
+  display: -ms-flexbox;
41
+  display: flex;
42
+  text-align: center
43
+}
44
+.wux-calendar__picker {
45
+  display: -ms-flexbox;
46
+  display: flex;
47
+  -ms-flex-align: center;
48
+  align-items: center;
49
+  -ms-flex-pack: justify;
50
+  justify-content: space-between;
51
+  width: 50%;
52
+  max-width: 400rpx;
53
+  -ms-flex-negative: 10;
54
+  flex-shrink: 10;
55
+  display: block;
56
+  line-height: 2.2rem
57
+}
58
+.wux-calendar__link {
59
+  float: left;
60
+  width: 25%;
61
+  height: 2.2rem;
62
+  line-height: 2rem;
63
+  min-width: 72rpx
64
+}
65
+.wux-calendar__icon {
66
+  display: inline-block;
67
+  vertical-align: middle;
68
+  background-size: 100% auto;
69
+  background-position: center
70
+}
71
+.wux-calendar__icon--next,
72
+.wux-calendar__icon--prev {
73
+  width: .75rem;
74
+  height: .75rem
75
+}
76
+.wux-calendar__icon--next {
77
+  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2015%2015'%3E%3Cg%3E%3Cpath%20fill%3D'%23007aff'%20d%3D'M1%2C1.6l11.8%2C5.8L1%2C13.4V1.6%20M0%2C0v15l15-7.6L0%2C0L0%2C0z'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
78
+}
79
+.wux-calendar__icon--prev {
80
+  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2015%2015'%3E%3Cg%3E%3Cpath%20fill%3D'%23007aff'%20d%3D'M14%2C1.6v11.8L2.2%2C7.6L14%2C1.6%20M15%2C0L0%2C7.6L15%2C15V0L15%2C0z'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
81
+}
82
+.wux-calendar__value {
83
+  -ms-flex-negative: 1;
84
+  flex-shrink: 1;
85
+  position: relative;
86
+  overflow: hidden;
87
+  text-overflow: ellipsis;
88
+  float: left;
89
+  width: 50%;
90
+  height: 2.2rem
91
+}
92
+.wux-calendar__weekdays {
93
+  height: 36rpx;
94
+  background: #f7f7f8;
95
+  display: -ms-flexbox;
96
+  display: flex;
97
+  font-size: 22rpx;
98
+  box-sizing: border-box;
99
+  position: relative
100
+}
101
+.wux-calendar__weekdays::after {
102
+  content: " ";
103
+  position: absolute;
104
+  left: 0;
105
+  bottom: 0;
106
+  right: 0;
107
+  height: 1PX;
108
+  border-bottom: 1PX solid #d9d9d9;
109
+  color: #d9d9d9;
110
+  transform-origin: 0 100%;
111
+  transform: scaleY(.5)
112
+}
113
+.wux-calendar__weekdays + .wux-calendar__months {
114
+  height: calc(82%)
115
+}
116
+.wux-calendar__weekday {
117
+  -ms-flex-negative: 1;
118
+  flex-shrink: 1;
119
+  width: 14.28571429%;
120
+  width: calc(14.28571429%);
121
+  line-height: 34rpx;
122
+  text-align: center
123
+}
124
+.wux-calendar__months {
125
+  width: 100%;
126
+  height: 100%;
127
+  overflow: hidden;
128
+  position: relative
129
+}
130
+.wux-calendar__months-content {
131
+  width: 100%;
132
+  height: 100%;
133
+  display: -ms-flexbox;
134
+  display: flex;
135
+  position: relative;
136
+  -webkit-backface-visibility: hidden;
137
+  transform: translate3d(0,0,0)
138
+}
139
+.wux-calendar__month {
140
+  display: -ms-flexbox;
141
+  display: flex;
142
+  -ms-flex-direction: column;
143
+  flex-direction: column;
144
+  width: 100%;
145
+  height: 100%;
146
+  position: absolute;
147
+  left: 0;
148
+  top: 0
149
+}
150
+.wux-calendar__days {
151
+  height: 16.66666667%;
152
+  height: calc(16.66666667%);
153
+  display: -ms-flexbox;
154
+  display: flex;
155
+  -ms-flex-negative: 1;
156
+  flex-shrink: 1;
157
+  width: 100%;
158
+  position: relative
159
+}
160
+.wux-calendar__days::after {
161
+  content: " ";
162
+  position: absolute;
163
+  left: 0;
164
+  bottom: 0;
165
+  right: 0;
166
+  height: 1PX;
167
+  border-bottom: 1PX solid #d9d9d9;
168
+  color: #d9d9d9;
169
+  transform-origin: 0 100%;
170
+  transform: scaleY(.5)
171
+}
172
+.wux-calendar__days:last-child::after {
173
+  display: none
174
+}
175
+.wux-calendar__day {
176
+  -ms-flex-negative: 1;
177
+  flex-shrink: 1;
178
+  display: -ms-flexbox;
179
+  display: flex;
180
+  -ms-flex-pack: center;
181
+  justify-content: center;
182
+  -ms-flex-align: center;
183
+  align-items: center;
184
+  box-sizing: border-box;
185
+  width: 14.28571429%;
186
+  width: calc(14.28571429%);
187
+  text-align: center;
188
+  color: #3d4145;
189
+  font-size: 30rpx;
190
+  cursor: pointer
191
+}
192
+.wux-calendar__day--prev {
193
+  color: #ccc
194
+}
195
+.wux-calendar__day--next {
196
+  color: #ccc
197
+}
198
+.wux-calendar__day--disabled {
199
+  color: #d4d4d4;
200
+  cursor: auto
201
+}
202
+.wux-calendar__day--today .wux-calendar__text {
203
+  background: #e3e3e3
204
+}
205
+.wux-calendar__day--selected .wux-calendar__text {
206
+  background: #0894ec;
207
+  color: #fff
208
+}
209
+.wux-calendar__text {
210
+  display: inline-block;
211
+  border-radius: 100%;
212
+  width: 60rpx;
213
+  height: 60rpx;
214
+  line-height: 60rpx
215
+}

+ 47 - 0
components/dist/icon/index.js

@@ -0,0 +1,47 @@
1
+Component({
2
+    externalClasses: ['wux-class'],
3
+    properties: {
4
+        type: {
5
+            type: String,
6
+            value: '',
7
+        },
8
+        size: {
9
+            type: [String, Number],
10
+            value: 32,
11
+            observer: 'updated',
12
+        },
13
+        color: {
14
+            type: String,
15
+            value: '',
16
+        },
17
+        hidden: {
18
+            type: Boolean,
19
+            value: false,
20
+        },
21
+    },
22
+    data: {
23
+        fontSize: '',
24
+    },
25
+    methods: {
26
+        updated(size = this.data.size) {
27
+            let fontSize = size
28
+
29
+            if (typeof size === 'number') {
30
+                fontSize = `${size}px`
31
+            } else if (typeof size === 'string') {
32
+                if (!isNaN(Number(size))) {
33
+                    fontSize = `${size}px`
34
+                }
35
+            }
36
+
37
+            if (this.data.fontSize !== fontSize) {
38
+                this.setData({
39
+                    fontSize,
40
+                })
41
+            }
42
+        },
43
+    },
44
+    attached() {
45
+        this.updated()
46
+    },
47
+})

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

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

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

@@ -0,0 +1 @@
1
+<view class="wux-class ion {{ type ? 'ion-' + type : '' }}" style="font-size: {{ fontSize }}; {{ color ? 'color: ' + color : '' }}" hidden="{{ hidden }}"></view>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2820 - 0
components/dist/icon/index.wxss


+ 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
+}

+ 202 - 0
components/dist/rater/index.js

@@ -0,0 +1,202 @@
1
+import baseComponent from '../helpers/baseComponent'
2
+import classNames from '../helpers/classNames'
3
+import eventsMixin from '../helpers/eventsMixin'
4
+
5
+baseComponent({
6
+    behaviors: [eventsMixin()],
7
+    relations: {
8
+        '../field/index': {
9
+            type: 'ancestor',
10
+        },
11
+    },
12
+    properties: {
13
+        prefixCls: {
14
+            type: String,
15
+            value: 'wux-rater',
16
+        },
17
+        max: {
18
+            type: Number,
19
+            value: 5,
20
+            observer() {
21
+                this.setValue(this.data.inputValue)
22
+            },
23
+        },
24
+        icon: {
25
+            type: String,
26
+            value: '',
27
+        },
28
+        star: {
29
+            type: String,
30
+            value: '★',
31
+        },
32
+        defaultValue: {
33
+            type: Number,
34
+            value: 0,
35
+        },
36
+        value: {
37
+            type: Number,
38
+            value: 0,
39
+            observer(newVal) {
40
+                if (this.data.controlled) {
41
+                    this.setValue(newVal)
42
+                }
43
+            },
44
+        },
45
+        activeColor: {
46
+            type: String,
47
+            value: '#ffc900',
48
+        },
49
+        margin: {
50
+            type: Number,
51
+            value: 2,
52
+        },
53
+        fontSize: {
54
+            type: Number,
55
+            value: 25,
56
+        },
57
+        disabled: {
58
+            type: Boolean,
59
+            value: false,
60
+        },
61
+        allowHalf: {
62
+            type: Boolean,
63
+            value: false,
64
+        },
65
+        allowClear: {
66
+            type: Boolean,
67
+            value: false,
68
+        },
69
+        allowTouchMove: {
70
+            type: Boolean,
71
+            value: false,
72
+        },
73
+        controlled: {
74
+            type: Boolean,
75
+            value: false,
76
+        },
77
+    },
78
+    data: {
79
+        inputValue: 0,
80
+    },
81
+    computed: {
82
+        classes: ['prefixCls, disabled', function(prefixCls, disabled) {
83
+            const wrap = classNames(prefixCls, {
84
+                [`${prefixCls}--disabled`]: disabled,
85
+            })
86
+            const star = `${prefixCls}__star`
87
+            const box = `${prefixCls}__box`
88
+            const inner = `${prefixCls}__inner`
89
+            const outer = `${prefixCls}__outer`
90
+            const icon = `${prefixCls}__icon`
91
+
92
+            return {
93
+                wrap,
94
+                star,
95
+                box,
96
+                inner,
97
+                outer,
98
+                icon,
99
+            }
100
+        }],
101
+    },
102
+    observers: {
103
+        ['inputValue, max, activeColor'](inputValue, max, activeColor) {
104
+            const stars = [...new Array(max)].map((_, i) => i)
105
+            const colors = stars.reduce((a, _, i) => ([...a, i <= inputValue - 1 ? activeColor : '#ccc']), [])
106
+            const _val = inputValue.toString().split('.')
107
+            const sliceValue = _val.length === 1 ? [_val[0], 0] : _val
108
+
109
+            this.setData({
110
+                stars,
111
+                colors,
112
+                cutIndex: sliceValue[0] * 1,
113
+                cutPercent: sliceValue[1] * 10,
114
+            })
115
+        },
116
+    },
117
+    methods: {
118
+        updated(inputValue) {
119
+            if (this.hasFieldDecorator) return
120
+            if (this.data.inputValue !== inputValue) {
121
+                this.setData({ inputValue })
122
+            }
123
+        },
124
+        setValue(value) {
125
+            const { max } = this.data
126
+            const inputValue = value <= 0 ? 0 : value > max ? max : value
127
+
128
+            this.updated(inputValue)
129
+        },
130
+        updateHalfStarValue(index, x, cb) {
131
+            const { prefixCls } = this.data
132
+            const query = wx.createSelectorQuery().in(this)
133
+            query.selectAll(`.${prefixCls}__star`).boundingClientRect((rects) => {
134
+                if (rects.filter((n) => !n).length) return
135
+                const { left, width } = rects[index]
136
+                const has = (x - left) < width / 2
137
+                const value = has ? index + .5 : index + 1
138
+                cb.call(this, value, index)
139
+            }).exec()
140
+        },
141
+        onTap(e) {
142
+            const { index } = e.currentTarget.dataset
143
+            const { inputValue, disabled, allowHalf, allowClear } = this.data
144
+
145
+            // 判断是否禁用
146
+            if (!disabled) {
147
+                // 判断是否支持选中半星
148
+                if (!allowHalf) {
149
+                    const value = index + 1
150
+                    const isReset = allowClear && value === inputValue
151
+
152
+                    this.onChange(isReset ? 0 : value, index)
153
+                } else {
154
+                    this.updateHalfStarValue(index, e.detail.x, (value, index) => {
155
+                        const isReset = allowClear && value === inputValue
156
+
157
+                        this.onChange(isReset ? 0 : value, index)
158
+                    })
159
+                }
160
+            }
161
+        },
162
+        onChange(value, index) {
163
+            if (!this.data.controlled) {
164
+                this.setValue(value)
165
+            }
166
+
167
+            this.triggerEvent('change', { value, index })
168
+        },
169
+        onTouchMove(e) {
170
+            const { disabled, allowHalf, allowTouchMove } = this.data
171
+            if (!disabled && allowTouchMove) {
172
+                const x = e.changedTouches[0].pageX
173
+                const { prefixCls } = this.data
174
+                const query = wx.createSelectorQuery().in(this)
175
+                query.selectAll(`.${prefixCls}__star`).boundingClientRect((rects) => {
176
+                    if (rects.filter((n) => !n).length) return
177
+                    const { left, width } = rects[0]
178
+                    const maxWidth = rects.map((n) => n.width).reduce((a, b) => a + b)
179
+                    const diff = x - left
180
+                    let value = Math.ceil(diff / width)
181
+
182
+                    // 判断是否在组件宽度范围内
183
+                    if (diff > 0 && diff < maxWidth) {
184
+                        const index = value - 1
185
+                        if (allowHalf) {
186
+                            const star = rects[index]
187
+                            const has = (x - star.left) < star.width / 2
188
+                            value = has ? value - .5 : value
189
+                        }
190
+                        this.onChange(value, index)
191
+                    }
192
+                }).exec()
193
+            }
194
+        },
195
+    },
196
+    attached() {
197
+        const { defaultValue, value, controlled } = this.data
198
+        const inputValue = controlled ? value : defaultValue
199
+
200
+        this.setValue(inputValue)
201
+    },
202
+})

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

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

+ 16 - 0
components/dist/rater/index.wxml

@@ -0,0 +1,16 @@
1
+<view class="wux-class {{ classes.wrap }}" bindtouchmove="onTouchMove">
2
+    <block wx:for="{{ stars }}" wx:key="index">
3
+        <view class="{{ classes.star }}" bindtap="onTap" data-index="{{ index }}">
4
+            <view class="{{ classes.box }} {{ inputValue > index ? prefixCls + '__box--active' : '' }}" style="color: {{ colors && colors[index] ? colors[index]: '#ccc' }}; margin-right: {{ margin + 'px' }}; font-size: {{ fontSize + 'px' }}; width: {{ fontSize + 'px' }}; height: {{ fontSize + 'px' }}; line-height: {{ fontSize + 'px' }}">
5
+                <view class="{{ classes.inner }}">
6
+                    <wux-icon wx:if="{{ icon }}" wux-class="{{ classes.icon }}" type="{{ icon }}" size="{{ fontSize }}" color="{{ colors && colors[index] ? colors[index]: '#ccc' }}" />
7
+                    <block wx:else>{{ star }}</block>
8
+                    <view class="{{ classes.outer }}" style="color: {{ activeColor }}; width: {{ cutPercent + '%' }}" wx:if="{{ cutPercent > 0 && cutIndex === index }}">
9
+                        <wux-icon wx:if="{{ icon }}" wux-class="{{ classes.icon }}" type="{{ icon }}" size="{{ fontSize }}" color="{{ activeColor }}" />
10
+                        <block wx:else>{{ star }}</block>
11
+                    </view>
12
+                </view>
13
+            </view>
14
+        </view>
15
+    </block>
16
+</view>

+ 52 - 0
components/dist/rater/index.wxss

@@ -0,0 +1,52 @@
1
+.wux-rater {
2
+  display: inline-block;
3
+  margin: 0;
4
+  padding: 0;
5
+  line-height: normal;
6
+  vertical-align: middle;
7
+  font-weight: 400;
8
+  font-style: normal;
9
+  text-align: left
10
+}
11
+.wux-rater::after,
12
+.wux-rater::before {
13
+  display: table;
14
+  content: " "
15
+}
16
+.wux-rater::after {
17
+  clear: both
18
+}
19
+.wux-rater__star {
20
+  position: relative;
21
+  display: inline-block
22
+}
23
+.wux-rater__star:last-child .wux-rater__box {
24
+  padding-right: 4rpx!important;
25
+  margin-right: 0!important
26
+}
27
+.wux-rater__box {
28
+  position: relative;
29
+  display: inline-block;
30
+  text-align: center;
31
+  cursor: pointer;
32
+  color: #ccc;
33
+  transition: color .3s ease
34
+}
35
+.wux-rater__box--disabled {
36
+  color: #ccc!important;
37
+  cursor: not-allowed
38
+}
39
+.wux-rater__icon {
40
+  transition: color .3s ease
41
+}
42
+.wux-rater__inner {
43
+  position: relative;
44
+  display: inline-block
45
+}
46
+.wux-rater__outer {
47
+  position: absolute;
48
+  left: 0;
49
+  top: 0;
50
+  display: inline-block;
51
+  overflow: hidden
52
+}

+ 33 - 0
components/local/calendar/func/config.js

@@ -0,0 +1,33 @@
1
+import WxData from './wxData'
2
+
3
+class Config extends WxData {
4
+  constructor(component) {
5
+    super(component)
6
+    this.Component = component
7
+  }
8
+  getCalendarConfig() {
9
+    if (!this.Component || !this.Component.config) return {}
10
+    return this.Component.config
11
+  }
12
+  setCalendarConfig(config) {
13
+    return new Promise((resolve, reject) => {
14
+      if (!this.Component || !this.Component.config) {
15
+        reject('异常:未找到组件配置信息')
16
+        return
17
+      }
18
+      let conf = { ...this.Component.config, ...config }
19
+
20
+      this.Component.config = conf
21
+      this.setData(
22
+        {
23
+          calendarConfig: conf
24
+        },
25
+        () => {
26
+          resolve(conf)
27
+        }
28
+      )
29
+    })
30
+  }
31
+}
32
+
33
+export default component => new Config(component)

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1036 - 0
components/local/calendar/func/convertSolarLunar.js


+ 543 - 0
components/local/calendar/func/day.js

@@ -0,0 +1,543 @@
1
+import WxData from './wxData'
2
+import CalendarConfig from './config'
3
+import convertSolarLunar from './convertSolarLunar'
4
+import {
5
+  Logger,
6
+  GetDate,
7
+  getDateTimeStamp,
8
+  uniqueArrayByDate,
9
+  delRepeatedEnableDay,
10
+  convertEnableAreaToTimestamp,
11
+  converEnableDaysToTimestamp
12
+} from './utils'
13
+
14
+const logger = new Logger()
15
+const getDate = new GetDate()
16
+const toString = Object.prototype.toString
17
+
18
+class Day extends WxData {
19
+  constructor(component) {
20
+    super(component)
21
+    this.Component = component
22
+  }
23
+  getCalendarConfig() {
24
+    return this.Component.config
25
+  }
26
+  /**
27
+   *
28
+   * @param {number} year
29
+   * @param {number} month
30
+   */
31
+  buildDate(year, month) {
32
+    const today = getDate.todayDate()
33
+    const thisMonthDays = getDate.thisMonthDays(year, month)
34
+    const dates = []
35
+    for (let i = 1; i <= thisMonthDays; i++) {
36
+      const isToday =
37
+        +today.year === +year && +today.month === +month && i === +today.date
38
+      const config = this.getCalendarConfig()
39
+      const date = {
40
+        year,
41
+        month,
42
+        day: i,
43
+        choosed: false,
44
+        week: getDate.dayOfWeek(year, month, i),
45
+        isToday: isToday && config.highlightToday,
46
+        lunar: convertSolarLunar.solar2lunar(+year, +month, +i)
47
+      }
48
+      dates.push(date)
49
+    }
50
+    return dates
51
+  }
52
+  /**
53
+   * 指定可选日期范围
54
+   * @param {array} area 日期访问数组
55
+   */
56
+  enableArea(dateArea = []) {
57
+    if (dateArea.length === 2) {
58
+      const isRight = this.__judgeParam(dateArea)
59
+      if (isRight) {
60
+        let { days = [], selectedDay = [] } = this.getData('calendar')
61
+        const { startTimestamp, endTimestamp } = convertEnableAreaToTimestamp(
62
+          dateArea
63
+        )
64
+        const dataAfterHandle = this.__handleEnableArea(
65
+          {
66
+            dateArea,
67
+            days,
68
+            startTimestamp,
69
+            endTimestamp
70
+          },
71
+          selectedDay
72
+        )
73
+        this.setData({
74
+          'calendar.enableArea': dateArea,
75
+          'calendar.days': dataAfterHandle.dates,
76
+          'calendar.selectedDay': dataAfterHandle.selectedDay,
77
+          'calendar.enableAreaTimestamp': [startTimestamp, endTimestamp]
78
+        })
79
+      }
80
+    } else {
81
+      logger.warn(
82
+        'enableArea()参数需为时间范围数组,形如:["2018-8-4" , "2018-8-24"]'
83
+      )
84
+    }
85
+  }
86
+  /**
87
+   * 指定特定日期可选
88
+   * @param {array} days 指定日期数组
89
+   */
90
+  enableDays(dates = []) {
91
+    const { enableArea = [] } = this.getData('calendar')
92
+    let expectEnableDaysTimestamp = []
93
+    if (enableArea.length) {
94
+      expectEnableDaysTimestamp = delRepeatedEnableDay(dates, enableArea)
95
+    } else {
96
+      expectEnableDaysTimestamp = converEnableDaysToTimestamp(dates)
97
+    }
98
+    let { days = [], selectedDay = [] } = this.getData('calendar')
99
+    const dataAfterHandle = this.__handleEnableDays(
100
+      {
101
+        days,
102
+        expectEnableDaysTimestamp
103
+      },
104
+      selectedDay
105
+    )
106
+    this.setData({
107
+      'calendar.days': dataAfterHandle.dates,
108
+      'calendar.selectedDay': dataAfterHandle.selectedDay,
109
+      'calendar.enableDays': dates,
110
+      'calendar.enableDaysTimestamp': expectEnableDaysTimestamp
111
+    })
112
+  }
113
+  /**
114
+   * 设置多个日期选中
115
+   * @param {array} selected 需选中日期
116
+   */
117
+  setSelectedDays(selected) {
118
+    const config = CalendarConfig(this.Component).getCalendarConfig()
119
+    if (!config.multi) {
120
+      return logger.warn('单选模式下不能设置多日期选中,请配置 multi')
121
+    }
122
+    let { days } = this.getData('calendar')
123
+    let newSelectedDay = []
124
+    if (!selected) {
125
+      days.map(item => {
126
+        item.choosed = true
127
+        item.showTodoLabel = false
128
+      })
129
+      newSelectedDay = days
130
+    } else if (selected && selected.length) {
131
+      const { dates, selectedDates } = this.__handleSelectedDays(
132
+        days,
133
+        newSelectedDay,
134
+        selected
135
+      )
136
+      days = dates
137
+      newSelectedDay = selectedDates
138
+    }
139
+    CalendarConfig(this.Component).setCalendarConfig('multi', true)
140
+    this.setData({
141
+      'calendar.days': days,
142
+      'calendar.selectedDay': newSelectedDay
143
+    })
144
+  }
145
+  /**
146
+   * 禁用指定日期
147
+   * @param {array} dates  禁用
148
+   */
149
+  disableDays(dates) {
150
+    const { disableDays = [], days } = this.getData('calendar')
151
+    if (Object.prototype.toString.call(dates) !== '[object Array]') {
152
+      return logger.warn('disableDays 参数为数组')
153
+    }
154
+    let _disableDays = []
155
+    if (dates.length) {
156
+      _disableDays = uniqueArrayByDate(dates.concat(disableDays))
157
+      const disableDaysCol = _disableDays.map(d => getDate.toTimeStr(d))
158
+      days.forEach(item => {
159
+        const cur = getDate.toTimeStr(item)
160
+        if (disableDaysCol.includes(cur)) item.disable = true
161
+      })
162
+    } else {
163
+      days.forEach(item => {
164
+        item.disable = false
165
+      })
166
+    }
167
+    this.setData({
168
+      'calendar.days': days,
169
+      'calendar.disableDays': _disableDays
170
+    })
171
+  }
172
+  /**
173
+   * 设置连续日期选择区域
174
+   * @param {array} dateArea 区域开始结束日期数组
175
+   */
176
+  chooseArea(dateArea = []) {
177
+    return new Promise((resolve, reject) => {
178
+      if (dateArea.length === 1) {
179
+        dateArea = dateArea.concat(dateArea)
180
+      }
181
+      if (dateArea.length === 2) {
182
+        const isRight = this.__judgeParam(dateArea)
183
+        if (isRight) {
184
+          const config = CalendarConfig(this.Component).getCalendarConfig()
185
+          const { startTimestamp, endTimestamp } = convertEnableAreaToTimestamp(
186
+            dateArea
187
+          )
188
+          this.setData(
189
+            {
190
+              calendarConfig: {
191
+                ...config,
192
+                chooseAreaMode: true,
193
+                mulit: true
194
+              },
195
+              'calendar.chooseAreaTimestamp': [startTimestamp, endTimestamp]
196
+            },
197
+            () => {
198
+              this.__chooseContinuousDates(startTimestamp, endTimestamp)
199
+                .then(resolve)
200
+                .catch(reject)
201
+            }
202
+          )
203
+        }
204
+      }
205
+    })
206
+  }
207
+  __pusheNextMonthDateArea(item, startTimestamp, endTimestamp, selectedDates) {
208
+    const days = this.buildDate(item.year, item.month)
209
+    let daysLen = days.length
210
+    for (let i = 0; i < daysLen; i++) {
211
+      const item = days[i]
212
+      const timeStamp = getDateTimeStamp(item)
213
+      if (timeStamp <= endTimestamp && timeStamp >= startTimestamp) {
214
+        selectedDates.push({
215
+          ...item,
216
+          choosed: true
217
+        })
218
+      }
219
+      if (i === daysLen - 1 && timeStamp < endTimestamp) {
220
+        this.__pusheNextMonthDateArea(
221
+          getDate.nextMonth(item),
222
+          startTimestamp,
223
+          endTimestamp,
224
+          selectedDates
225
+        )
226
+      }
227
+    }
228
+  }
229
+  __pushPrevMonthDateArea(item, startTimestamp, endTimestamp, selectedDates) {
230
+    const days = getDate.sortDates(
231
+      this.buildDate(item.year, item.month),
232
+      'desc'
233
+    )
234
+    let daysLen = days.length
235
+    let firstDate = getDateTimeStamp(days[0])
236
+    for (let i = 0; i < daysLen; i++) {
237
+      const item = days[i]
238
+      const timeStamp = getDateTimeStamp(item)
239
+      if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
240
+        selectedDates.push({
241
+          ...item,
242
+          choosed: true
243
+        })
244
+      }
245
+      if (i === daysLen - 1 && firstDate > startTimestamp) {
246
+        this.__pushPrevMonthDateArea(
247
+          getDate.prevMonth(item),
248
+          startTimestamp,
249
+          endTimestamp,
250
+          selectedDates
251
+        )
252
+      }
253
+    }
254
+  }
255
+  /**
256
+   * 当设置日期区域非当前时保存其他月份的日期至已选日期数组
257
+   * @param {object} info
258
+   */
259
+  __calcDateWhenNotInOneMonth(info) {
260
+    const {
261
+      firstDate,
262
+      lastDate,
263
+      startTimestamp,
264
+      endTimestamp,
265
+      filterSelectedDate
266
+    } = info
267
+    if (getDateTimeStamp(firstDate) > startTimestamp) {
268
+      this.__pushPrevMonthDateArea(
269
+        getDate.prevMonth(firstDate),
270
+        startTimestamp,
271
+        endTimestamp,
272
+        filterSelectedDate
273
+      )
274
+    }
275
+    if (getDateTimeStamp(lastDate) < endTimestamp) {
276
+      this.__pusheNextMonthDateArea(
277
+        getDate.nextMonth(lastDate),
278
+        startTimestamp,
279
+        endTimestamp,
280
+        filterSelectedDate
281
+      )
282
+    }
283
+    const newSelectedDates = [...getDate.sortDates(filterSelectedDate)]
284
+    return newSelectedDates
285
+  }
286
+  /**
287
+   * 设置连续日期段
288
+   * @param {number} startTimestamp 连续日期段开始日期时间戳
289
+   * @param {number} endTimestamp 连续日期段结束日期时间戳
290
+   */
291
+  __chooseContinuousDates(startTimestamp, endTimestamp) {
292
+    return new Promise((resolve, reject) => {
293
+      const { days, selectedDay = [] } = this.getData('calendar')
294
+      const selectedDateStr = []
295
+      let filterSelectedDate = []
296
+      selectedDay.forEach(item => {
297
+        const timeStamp = getDateTimeStamp(item)
298
+        if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
299
+          filterSelectedDate.push(item)
300
+          selectedDateStr.push(getDate.toTimeStr(item))
301
+        }
302
+      })
303
+      days.forEach(item => {
304
+        const timeStamp = getDateTimeStamp(item)
305
+        const dateInSelecedArray = selectedDateStr.includes(
306
+          getDate.toTimeStr(item)
307
+        )
308
+        if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
309
+          if (dateInSelecedArray) {
310
+            return
311
+          }
312
+          item.choosed = true
313
+          filterSelectedDate.push(item)
314
+        } else {
315
+          item.choosed = false
316
+          if (dateInSelecedArray) {
317
+            const idx = filterSelectedDate.findIndex(
318
+              selectedDate =>
319
+                getDate.toTimeStr(selectedDate) === getDate.toTimeStr(item)
320
+            )
321
+            if (idx > -1) {
322
+              filterSelectedDate.splice(idx, 1)
323
+            }
324
+          }
325
+        }
326
+      })
327
+      const firstDate = days[0]
328
+      const lastDate = days[days.length - 1]
329
+      const newSelectedDates = this.__calcDateWhenNotInOneMonth({
330
+        firstDate,
331
+        lastDate,
332
+        startTimestamp,
333
+        endTimestamp,
334
+        filterSelectedDate
335
+      })
336
+      try {
337
+        this.setData(
338
+          {
339
+            'calendar.days': [...days],
340
+            'calendar.selectedDay': newSelectedDates
341
+          },
342
+          () => {
343
+            resolve(newSelectedDates)
344
+          }
345
+        )
346
+      } catch (err) {
347
+        reject(err)
348
+      }
349
+    })
350
+  }
351
+  /**
352
+   * 设置指定日期样式
353
+   * @param {array} dates 待设置特殊样式的日期
354
+   */
355
+  setDateStyle(dates) {
356
+    if (toString.call(dates) !== '[object Array]') return
357
+    const { days, specialStyleDates } = this.getData('calendar')
358
+    if (toString.call(specialStyleDates) === '[object Array]') {
359
+      dates = uniqueArrayByDate([...specialStyleDates, ...dates])
360
+    }
361
+    const _specialStyleDates = dates.map(
362
+      item => `${item.year}_${item.month}_${item.day}`
363
+    )
364
+    const _days = days.map(item => {
365
+      const idx = _specialStyleDates.indexOf(
366
+        `${item.year}_${item.month}_${item.day}`
367
+      )
368
+      if (idx > -1) {
369
+        return {
370
+          ...item,
371
+          class: dates[idx].class
372
+        }
373
+      } else {
374
+        return { ...item }
375
+      }
376
+    })
377
+    this.setData({
378
+      'calendar.days': _days,
379
+      'calendar.specialStyleDates': dates
380
+    })
381
+  }
382
+  __judgeParam(dateArea) {
383
+    const {
384
+      start,
385
+      end,
386
+      startTimestamp,
387
+      endTimestamp
388
+    } = convertEnableAreaToTimestamp(dateArea)
389
+    if (!start || !end) return
390
+    const startMonthDays = getDate.thisMonthDays(start[0], start[1])
391
+    const endMonthDays = getDate.thisMonthDays(end[0], end[1])
392
+    if (start[2] > startMonthDays || start[2] < 1) {
393
+      logger.warn('enableArea() 开始日期错误,指定日期不在当前月份天数范围内')
394
+      return false
395
+    } else if (start[1] > 12 || start[1] < 1) {
396
+      logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
397
+      return false
398
+    } else if (end[2] > endMonthDays || end[2] < 1) {
399
+      logger.warn('enableArea() 截止日期错误,指定日期不在当前月份天数范围内')
400
+      return false
401
+    } else if (end[1] > 12 || end[1] < 1) {
402
+      logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
403
+      return false
404
+    } else if (startTimestamp > endTimestamp) {
405
+      logger.warn('enableArea()参数最小日期大于了最大日期')
406
+      return false
407
+    } else {
408
+      return true
409
+    }
410
+  }
411
+  __getDisableDateTimestamp() {
412
+    let disableDateTimestamp
413
+    const { date, type } = this.getCalendarConfig().disableMode || {}
414
+    if (date) {
415
+      const t = date.split('-')
416
+      if (t.length < 3) {
417
+        logger.warn('配置 disableMode.date 格式错误')
418
+        return {}
419
+      }
420
+      disableDateTimestamp = getDateTimeStamp({
421
+        year: +t[0],
422
+        month: +t[1],
423
+        day: +t[2]
424
+      })
425
+    }
426
+    return {
427
+      disableDateTimestamp,
428
+      disableType: type
429
+    }
430
+  }
431
+  __handleEnableArea(data = {}, selectedDay = []) {
432
+    const { area, days, startTimestamp, endTimestamp } = data
433
+    const enableDays = this.getData('calendar.enableDays') || []
434
+    let expectEnableDaysTimestamp = []
435
+    if (enableDays.length) {
436
+      expectEnableDaysTimestamp = delRepeatedEnableDay(enableDays, area)
437
+    }
438
+    const {
439
+      disableDateTimestamp,
440
+      disableType
441
+    } = this.__getDisableDateTimestamp()
442
+    const dates = [...days]
443
+    dates.forEach(item => {
444
+      const timestamp = +getDate
445
+        .newDate(item.year, item.month, item.day)
446
+        .getTime()
447
+      const ifOutofArea =
448
+        (+startTimestamp > timestamp || timestamp > +endTimestamp) &&
449
+        !expectEnableDaysTimestamp.includes(timestamp)
450
+      if (
451
+        ifOutofArea ||
452
+        (disableType === 'before' &&
453
+          disableDateTimestamp &&
454
+          timestamp < disableDateTimestamp) ||
455
+        (disableType === 'after' &&
456
+          disableDateTimestamp &&
457
+          timestamp > disableDateTimestamp)
458
+      ) {
459
+        item.disable = true
460
+        if (item.choosed) {
461
+          item.choosed = false
462
+          selectedDay = selectedDay.filter(
463
+            d => getDate.toTimeStr(item) !== getDate.toTimeStr(d)
464
+          )
465
+        }
466
+      } else if (item.disable) {
467
+        item.disable = false
468
+      }
469
+    })
470
+    return {
471
+      dates,
472
+      selectedDay
473
+    }
474
+  }
475
+  __handleEnableDays(data = {}, selectedDay = []) {
476
+    const { days, expectEnableDaysTimestamp } = data
477
+    const { enableAreaTimestamp = [] } = this.getData('calendar')
478
+    const dates = [...days]
479
+    dates.forEach(item => {
480
+      const timestamp = getDate
481
+        .newDate(item.year, item.month, item.day)
482
+        .getTime()
483
+      let setDisable = false
484
+      if (enableAreaTimestamp.length) {
485
+        if (
486
+          (+enableAreaTimestamp[0] > +timestamp ||
487
+            +timestamp > +enableAreaTimestamp[1]) &&
488
+          !expectEnableDaysTimestamp.includes(+timestamp)
489
+        ) {
490
+          setDisable = true
491
+        }
492
+      } else if (!expectEnableDaysTimestamp.includes(+timestamp)) {
493
+        setDisable = true
494
+      }
495
+      if (setDisable) {
496
+        item.disable = true
497
+        if (item.choosed) {
498
+          item.choosed = false
499
+          selectedDay = selectedDay.filter(
500
+            d => getDate.toTimeStr(item) !== getDate.toTimeStr(d)
501
+          )
502
+        }
503
+      } else {
504
+        item.disable = false
505
+      }
506
+    })
507
+    return {
508
+      dates,
509
+      selectedDay
510
+    }
511
+  }
512
+  __handleSelectedDays(days = [], newSelectedDay = [], selected) {
513
+    const { selectedDay, showLabelAlways } = this.getData('calendar')
514
+    if (selectedDay && selectedDay.length) {
515
+      newSelectedDay = uniqueArrayByDate(selectedDay.concat(selected))
516
+    } else {
517
+      newSelectedDay = selected
518
+    }
519
+    const { year: curYear, month: curMonth } = days[0]
520
+    const currentSelectedDays = []
521
+    newSelectedDay.forEach(item => {
522
+      if (+item.year === +curYear && +item.month === +curMonth) {
523
+        currentSelectedDays.push(getDate.toTimeStr(item))
524
+      }
525
+    })
526
+    ;[...days].map(item => {
527
+      if (currentSelectedDays.includes(getDate.toTimeStr(item))) {
528
+        item.choosed = true
529
+        if (showLabelAlways && item.showTodoLabel) {
530
+          item.showTodoLabel = true
531
+        } else {
532
+          item.showTodoLabel = false
533
+        }
534
+      }
535
+    })
536
+    return {
537
+      dates: days,
538
+      selectedDates: newSelectedDay
539
+    }
540
+  }
541
+}
542
+
543
+export default component => new Day(component)

+ 375 - 0
components/local/calendar/func/render.js

@@ -0,0 +1,375 @@
1
+import Day from './day'
2
+import Todo from './todo'
3
+import WxData from './wxData'
4
+import convertSolarLunar from './convertSolarLunar'
5
+import {
6
+  Logger,
7
+  GetDate,
8
+  delRepeatedEnableDay,
9
+  getDateTimeStamp,
10
+  converEnableDaysToTimestamp
11
+} from './utils'
12
+
13
+const getDate = new GetDate()
14
+const logger = new Logger()
15
+
16
+class Calendar extends WxData {
17
+  constructor(component) {
18
+    super(component)
19
+    this.Component = component
20
+  }
21
+  getCalendarConfig() {
22
+    return this.Component.config
23
+  }
24
+  /**
25
+   * 渲染日历
26
+   * @param {number} curYear 年份
27
+   * @param {number} curMonth  月份
28
+   * @param {number} curDate  日期
29
+   * @param {boolean} disableSelect 是否禁用选中
30
+   */
31
+  renderCalendar(curYear, curMonth, curDate, disableSelect) {
32
+    return new Promise(resolve => {
33
+      const config = this.getCalendarConfig()
34
+      this.calculateEmptyGrids(curYear, curMonth)
35
+      this.calculateDays(curYear, curMonth, curDate, disableSelect).then(() => {
36
+        const { todoLabels, specialStyleDates, enableDays, selectedDay } =
37
+          this.getData('calendar') || {}
38
+        if (
39
+          todoLabels &&
40
+          todoLabels.find(
41
+            item => +item.month === +curMonth && +item.year === +curYear
42
+          )
43
+        ) {
44
+          Todo(this.Component).setTodoLabels()
45
+        }
46
+        if (
47
+          specialStyleDates &&
48
+          specialStyleDates.length &&
49
+          specialStyleDates.find(
50
+            item => +item.month === +curMonth && +item.year === +curYear
51
+          )
52
+        ) {
53
+          Day(this.Component).setDateStyle(specialStyleDates)
54
+        }
55
+
56
+        if (
57
+          enableDays &&
58
+          enableDays.length &&
59
+          enableDays.find(item => {
60
+            let ymd = item.split('-')
61
+            return +ymd[1] === +curMonth && +ymd[0] === +curYear
62
+          })
63
+        ) {
64
+          Day(this.Component).enableDays(enableDays)
65
+        }
66
+
67
+        if (
68
+          selectedDay &&
69
+          selectedDay.length &&
70
+          selectedDay.find(
71
+            item => +item.month === +curMonth && +item.year === +curYear
72
+          ) &&
73
+          config.mulit
74
+        ) {
75
+          Day(this.Component).setSelectedDays(selectedDay)
76
+        }
77
+
78
+        if (!this.Component.firstRender) {
79
+          resolve({
80
+            firstRender: true
81
+          })
82
+        } else {
83
+          resolve({
84
+            firstRender: false
85
+          })
86
+        }
87
+      })
88
+    })
89
+  }
90
+  /**
91
+   * 计算当前月份前后两月应占的格子
92
+   * @param {number} year 年份
93
+   * @param {number} month 月份
94
+   */
95
+  calculateEmptyGrids(year, month) {
96
+    this.calculatePrevMonthGrids(year, month)
97
+    this.calculateNextMonthGrids(year, month)
98
+  }
99
+  /**
100
+   * 计算上月应占的格子
101
+   * @param {number} year 年份
102
+   * @param {number} month 月份
103
+   */
104
+  calculatePrevMonthGrids(year, month) {
105
+    let empytGrids = []
106
+    const prevMonthDays = getDate.thisMonthDays(year, month - 1)
107
+    let firstDayOfWeek = getDate.firstDayOfWeek(year, month)
108
+    const config = this.getCalendarConfig() || {}
109
+    if (config.firstDayOfWeek === 'Mon') {
110
+      if (firstDayOfWeek === 0) {
111
+        firstDayOfWeek = 6
112
+      } else {
113
+        firstDayOfWeek -= 1
114
+      }
115
+    }
116
+    if (firstDayOfWeek > 0) {
117
+      const len = prevMonthDays - firstDayOfWeek
118
+      const { onlyShowCurrentMonth } = config
119
+      const { showLunar } = this.getCalendarConfig()
120
+      for (let i = prevMonthDays; i > len; i--) {
121
+        if (onlyShowCurrentMonth) {
122
+          empytGrids.push('')
123
+        } else {
124
+          empytGrids.push({
125
+            day: i,
126
+            lunar: showLunar
127
+              ? convertSolarLunar.solar2lunar(year, month - 1, i)
128
+              : null
129
+          })
130
+        }
131
+      }
132
+      this.setData({
133
+        'calendar.empytGrids': empytGrids.reverse()
134
+      })
135
+    } else {
136
+      this.setData({
137
+        'calendar.empytGrids': null
138
+      })
139
+    }
140
+  }
141
+  /**
142
+   * 计算下一月日期是否需要多展示的日期
143
+   * 某些月份日期为5排,某些月份6排,统一为6排
144
+   * @param {number} year
145
+   * @param {number} month
146
+   * @param {object} config
147
+   */
148
+  calculateExtraEmptyDate(year, month, config) {
149
+    let extDate = 0
150
+    if (+month === 2) {
151
+      extDate += 7
152
+      let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
153
+      if (config.firstDayOfWeek === 'Mon') {
154
+        if (+firstDayofMonth === 1) extDate += 7
155
+      } else {
156
+        if (+firstDayofMonth === 0) extDate += 7
157
+      }
158
+    } else {
159
+      let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
160
+      if (config.firstDayOfWeek === 'Mon') {
161
+        if (firstDayofMonth !== 0 && firstDayofMonth < 6) {
162
+          extDate += 7
163
+        }
164
+      } else {
165
+        if (firstDayofMonth <= 5) {
166
+          extDate += 7
167
+        }
168
+      }
169
+    }
170
+    return extDate
171
+  }
172
+  /**
173
+   * 计算下月应占的格子
174
+   * @param {number} year 年份
175
+   * @param {number} month  月份
176
+   */
177
+  calculateNextMonthGrids(year, month) {
178
+    let lastEmptyGrids = []
179
+    const thisMonthDays = getDate.thisMonthDays(year, month)
180
+    let lastDayWeek = getDate.dayOfWeek(year, month, thisMonthDays)
181
+    const config = this.getCalendarConfig() || {}
182
+    if (config.firstDayOfWeek === 'Mon') {
183
+      if (lastDayWeek === 0) {
184
+        lastDayWeek = 6
185
+      } else {
186
+        lastDayWeek -= 1
187
+      }
188
+    }
189
+    let len = 7 - (lastDayWeek + 1)
190
+    const { onlyShowCurrentMonth, showLunar } = config
191
+    if (!onlyShowCurrentMonth) {
192
+      len = len + this.calculateExtraEmptyDate(year, month, config)
193
+    }
194
+    for (let i = 1; i <= len; i++) {
195
+      if (onlyShowCurrentMonth) {
196
+        lastEmptyGrids.push('')
197
+      } else {
198
+        lastEmptyGrids.push({
199
+          day: i,
200
+          lunar: showLunar
201
+            ? convertSolarLunar.solar2lunar(year, month + 1, i)
202
+            : null
203
+        })
204
+      }
205
+    }
206
+    this.setData({
207
+      'calendar.lastEmptyGrids': lastEmptyGrids
208
+    })
209
+  }
210
+  /**
211
+   * 日历初始化将默认值写入 selectDay
212
+   * @param {number} year
213
+   * @param {number} month
214
+   * @param {number} curDate
215
+   */
216
+  setSelectedDay(year, month, curDate) {
217
+    let selectedDay = []
218
+    const config = this.getCalendarConfig()
219
+    if (config.noDefault) {
220
+      selectedDay = []
221
+      config.noDefault = false
222
+    } else {
223
+      const data = this.getData('calendar') || {}
224
+      const { showLunar } = this.getCalendarConfig()
225
+      selectedDay = curDate
226
+        ? [
227
+            {
228
+              year,
229
+              month,
230
+              day: curDate,
231
+              choosed: true,
232
+              week: getDate.dayOfWeek(year, month, curDate),
233
+              lunar: showLunar
234
+                ? convertSolarLunar.solar2lunar(year, month, curDate)
235
+                : null
236
+            }
237
+          ]
238
+        : data.selectedDay
239
+    }
240
+    return selectedDay
241
+  }
242
+  __getDisableDateTimestamp() {
243
+    let disableDateTimestamp
244
+    const { date, type } = this.getCalendarConfig().disableMode || {}
245
+    if (date) {
246
+      const t = date.split('-')
247
+      if (t.length < 3) {
248
+        logger.warn('配置 disableMode.date 格式错误')
249
+        return {}
250
+      }
251
+      disableDateTimestamp = getDateTimeStamp({
252
+        year: +t[0],
253
+        month: +t[1],
254
+        day: +t[2]
255
+      })
256
+    }
257
+    return {
258
+      disableDateTimestamp,
259
+      disableType: type
260
+    }
261
+  }
262
+  resetDates() {
263
+    this.setData({
264
+      'calendar.days': []
265
+    })
266
+  }
267
+  /**
268
+   * 设置日历面板数据
269
+   * @param {number} year 年份
270
+   * @param {number} month  月份
271
+   * @param {number} curDate  日期
272
+   * @param {boolean} disableSelect 是否禁用选中
273
+   */
274
+  calculateDays(year, month, curDate, disableSelect) {
275
+    return new Promise(resolve => {
276
+      // 避免切换日期时样式残影
277
+      this.resetDates()
278
+      let days = []
279
+      const {
280
+        disableDays = [],
281
+        chooseAreaTimestamp = [],
282
+        selectedDay: selectedDates = []
283
+      } = this.getData('calendar')
284
+      days = Day(this.Component).buildDate(year, month)
285
+      let selectedDay = selectedDates
286
+      if (!disableSelect) {
287
+        selectedDay = this.setSelectedDay(year, month, curDate)
288
+      }
289
+      const selectedDayStr = selectedDay.map(d => getDate.toTimeStr(d))
290
+      const disableDaysStr = disableDays.map(d => getDate.toTimeStr(d))
291
+      const [areaStart, areaEnd] = chooseAreaTimestamp
292
+      days.forEach(item => {
293
+        const cur = getDate.toTimeStr(item)
294
+        const timestamp = getDateTimeStamp(item)
295
+        if (selectedDayStr.includes(cur) && !disableSelect) {
296
+          item.choosed = true
297
+          if (timestamp > areaEnd || timestamp < areaStart) {
298
+            const idx = selectedDay.findIndex(
299
+              selectedDate =>
300
+                getDate.toTimeStr(selectedDate) === getDate.toTimeStr(item)
301
+            )
302
+            selectedDay.splice(idx, 1)
303
+          }
304
+        } else if (
305
+          areaStart &&
306
+          areaEnd &&
307
+          timestamp >= areaStart &&
308
+          timestamp <= areaEnd &&
309
+          !disableSelect
310
+        ) {
311
+          item.choosed = true
312
+          selectedDay.push(item)
313
+        }
314
+        if (disableDaysStr.includes(cur)) item.disable = true
315
+
316
+        const {
317
+          disableDateTimestamp,
318
+          disableType
319
+        } = this.__getDisableDateTimestamp()
320
+        let disabelByConfig = false
321
+        if (disableDateTimestamp) {
322
+          if (
323
+            (disableType === 'before' && timestamp < disableDateTimestamp) ||
324
+            (disableType === 'after' && timestamp > disableDateTimestamp)
325
+          ) {
326
+            disabelByConfig = true
327
+          }
328
+        }
329
+        const isDisable = disabelByConfig || this.__isDisable(timestamp)
330
+        if (isDisable) {
331
+          item.disable = true
332
+          item.choosed = false
333
+        }
334
+      })
335
+      this.setData(
336
+        {
337
+          'calendar.days': days,
338
+          'calendar.selectedDay': [...selectedDay] || []
339
+        },
340
+        () => {
341
+          resolve()
342
+        }
343
+      )
344
+    })
345
+  }
346
+  __isDisable(timestamp) {
347
+    const {
348
+      enableArea = [],
349
+      enableDays = [],
350
+      enableAreaTimestamp = []
351
+    } = this.getData('calendar')
352
+    let setDisable = false
353
+    let expectEnableDaysTimestamp = converEnableDaysToTimestamp(enableDays)
354
+    if (enableArea.length) {
355
+      expectEnableDaysTimestamp = delRepeatedEnableDay(enableDays, enableArea)
356
+    }
357
+    if (enableAreaTimestamp.length) {
358
+      if (
359
+        (+enableAreaTimestamp[0] > +timestamp ||
360
+          +timestamp > +enableAreaTimestamp[1]) &&
361
+        !expectEnableDaysTimestamp.includes(+timestamp)
362
+      ) {
363
+        setDisable = true
364
+      }
365
+    } else if (
366
+      expectEnableDaysTimestamp.length &&
367
+      !expectEnableDaysTimestamp.includes(+timestamp)
368
+    ) {
369
+      setDisable = true
370
+    }
371
+    return setDisable
372
+  }
373
+}
374
+
375
+export default component => new Calendar(component)

+ 182 - 0
components/local/calendar/func/todo.js

@@ -0,0 +1,182 @@
1
+import WxData from './wxData'
2
+import { Logger, uniqueArrayByDate, GetDate } from './utils'
3
+
4
+const logger = new Logger()
5
+const getDate = new GetDate()
6
+
7
+class Todo extends WxData {
8
+  constructor(component) {
9
+    super(component)
10
+    this.Component = component
11
+  }
12
+  /**
13
+   * 设置待办事项标志
14
+   * @param {object} options 待办事项配置
15
+   */
16
+  setTodoLabels(options) {
17
+    if (options) this.Component.todoConfig = options
18
+    const calendar = this.getData('calendar')
19
+    if (!calendar || !calendar.days) {
20
+      return logger.warn('请等待日历初始化完成后再调用该方法')
21
+    }
22
+    const dates = [...calendar.days]
23
+    const { curYear, curMonth } = calendar
24
+    const {
25
+      circle,
26
+      dotColor = '',
27
+      pos = 'bottom',
28
+      showLabelAlways,
29
+      days: todoDays = []
30
+    } = options || this.Component.todoConfig || {}
31
+    const { todoLabels = [] } = calendar
32
+    const currentMonthTodoLabels = this.getTodoLabels({
33
+      year: curYear,
34
+      month: curMonth
35
+    })
36
+    let newTodoLabels = todoDays.filter(
37
+      item => +item.year === +curYear && +item.month === +curMonth
38
+    )
39
+    if (this.Component.weekMode) {
40
+      newTodoLabels = todoDays
41
+    }
42
+    const allTodos = currentMonthTodoLabels.concat(newTodoLabels)
43
+    for (let todo of allTodos) {
44
+      let target
45
+      if (this.Component.weekMode) {
46
+        target = dates.find(
47
+          date =>
48
+            +todo.year === +date.year &&
49
+            +todo.month === +date.month &&
50
+            +todo.day === +date.day
51
+        )
52
+      } else {
53
+        target = dates[todo.day - 1]
54
+      }
55
+      if (!target) continue
56
+      if (showLabelAlways) {
57
+        target.showTodoLabel = true
58
+      } else {
59
+        target.showTodoLabel = !target.choosed
60
+      }
61
+      if (target.showTodoLabel) {
62
+        target.todoText = todo.todoText
63
+      }
64
+      target.color = todo.color
65
+    }
66
+    const o = {
67
+      'calendar.days': dates,
68
+      'calendar.todoLabels': uniqueArrayByDate(todoLabels.concat(todoDays))
69
+    }
70
+    if (!circle) {
71
+      o['calendar.todoLabelPos'] = pos
72
+      o['calendar.todoLabelColor'] = dotColor
73
+    }
74
+    o['calendar.todoLabelCircle'] = circle || false
75
+    o['calendar.showLabelAlways'] = showLabelAlways || false
76
+    this.setData(o)
77
+  }
78
+  /**
79
+   *  删除指定日期的待办事项
80
+   * @param {array} todos 需要删除待办事项的日期
81
+   */
82
+  deleteTodoLabels(todos) {
83
+    if (!(todos instanceof Array) || !todos.length) return
84
+    const todoLabels = this.filterTodos(todos)
85
+    const { days: dates, curYear, curMonth } = this.getData('calendar')
86
+    const currentMonthTodoLabels = todoLabels.filter(
87
+      item => curYear === +item.year && curMonth === +item.month
88
+    )
89
+    dates.forEach(item => {
90
+      item.showTodoLabel = false
91
+    })
92
+    currentMonthTodoLabels.forEach(item => {
93
+      dates[item.day - 1].showTodoLabel = !dates[item.day - 1].choosed
94
+    })
95
+    this.setData({
96
+      'calendar.days': dates,
97
+      'calendar.todoLabels': todoLabels
98
+    })
99
+  }
100
+  /**
101
+   * 清空所有待办事项
102
+   */
103
+  clearTodoLabels() {
104
+    const { days = [] } = this.getData('calendar')
105
+    const dates = [].concat(days)
106
+    dates.forEach(item => {
107
+      item.showTodoLabel = false
108
+    })
109
+    this.setData({
110
+      'calendar.days': dates,
111
+      'calendar.todoLabels': []
112
+    })
113
+  }
114
+  /**
115
+   * 获取所有待办事项
116
+   * @param {object} target 指定年月
117
+   * @param {number} [target.year] 年
118
+   * @param {number} [target.month] 月
119
+   */
120
+  getTodoLabels(target) {
121
+    const { todoLabels = [] } = this.getData('calendar')
122
+    if (target) {
123
+      const { year, month } = target
124
+      const _todoLabels = todoLabels.filter(
125
+        item => +item.year === +year && +item.month === +month
126
+      )
127
+      return _todoLabels
128
+    }
129
+    return todoLabels
130
+  }
131
+  /**
132
+   * 过滤将删除的待办事项
133
+   * @param {array} todos 需要删除待办事项
134
+   */
135
+  filterTodos(todos) {
136
+    const todoLabels = this.getData('calendar.todoLabels') || []
137
+    const deleteTodo = todos.map(item => getDate.toTimeStr(item))
138
+    return todoLabels.filter(
139
+      item => !deleteTodo.includes(getDate.toTimeStr(item))
140
+    )
141
+  }
142
+  /**
143
+   * 单选时显示待办事项
144
+   * @param {array} todoDays
145
+   * @param {array} days
146
+   * @param {array} selectedDays
147
+   */
148
+  showTodoLabels(todoDays, days, selectedDays) {
149
+    todoDays.forEach(item => {
150
+      if (this.Component.weekMode) {
151
+        days.forEach((_item, idx) => {
152
+          if (+_item.day === +item.day) {
153
+            const day = days[idx]
154
+            day.hasTodo = true
155
+            day.todoText = item.todoText
156
+            if (
157
+              selectedDays &&
158
+              selectedDays.length &&
159
+              +selectedDays[0].day === +item.day
160
+            ) {
161
+              day.showTodoLabel = true
162
+            }
163
+          }
164
+        })
165
+      } else {
166
+        const day = days[item.day - 1]
167
+        if (!day) return
168
+        day.hasTodo = true
169
+        day.todoText = item.todoText
170
+        if (
171
+          selectedDays &&
172
+          selectedDays.length &&
173
+          +selectedDays[0].day === +item.day
174
+        ) {
175
+          days[selectedDays[0].day - 1].showTodoLabel = true
176
+        }
177
+      }
178
+    })
179
+  }
180
+}
181
+
182
+export default component => new Todo(component)

+ 367 - 0
components/local/calendar/func/utils.js

@@ -0,0 +1,367 @@
1
+import convertSolarLunar from './convertSolarLunar'
2
+
3
+let systemInfo
4
+export function getSystemInfo() {
5
+  if (systemInfo) return systemInfo
6
+  systemInfo = wx.getSystemInfoSync()
7
+  return systemInfo
8
+}
9
+
10
+export function isComponent(target) {
11
+  return (
12
+    target &&
13
+    target.__wxExparserNodeId__ !== void 0 &&
14
+    typeof target.setData === 'function'
15
+  )
16
+}
17
+
18
+export class Logger {
19
+  info(msg) {
20
+    console.log(
21
+      '%cInfo: %c' + msg,
22
+      'color:#FF0080;font-weight:bold',
23
+      'color: #FF509B'
24
+    )
25
+  }
26
+  warn(msg) {
27
+    console.log(
28
+      '%cWarn: %c' + msg,
29
+      'color:#FF6600;font-weight:bold',
30
+      'color: #FF9933'
31
+    )
32
+  }
33
+  tips(msg) {
34
+    console.log(
35
+      '%cTips: %c' + msg,
36
+      'color:#00B200;font-weight:bold',
37
+      'color: #00CC33'
38
+    )
39
+  }
40
+}
41
+
42
+export class Slide {
43
+  /**
44
+   * 上滑
45
+   * @param {object} e 事件对象
46
+   * @returns {boolean} 布尔值
47
+   */
48
+  isUp(gesture = {}, touche = {}) {
49
+    const { startX, startY } = gesture
50
+    const deltaX = touche.clientX - startX
51
+    const deltaY = touche.clientY - startY
52
+    if (deltaY < -60 && deltaX < 20 && deltaX > -20) {
53
+      this.slideLock = false
54
+      return true
55
+    } else {
56
+      return false
57
+    }
58
+  }
59
+  /**
60
+   * 下滑
61
+   * @param {object} e 事件对象
62
+   * @returns {boolean} 布尔值
63
+   */
64
+  isDown(gesture = {}, touche = {}) {
65
+    const { startX, startY } = gesture
66
+    const deltaX = touche.clientX - startX
67
+    const deltaY = touche.clientY - startY
68
+    if (deltaY > 60 && deltaX < 20 && deltaX > -20) {
69
+      return true
70
+    } else {
71
+      return false
72
+    }
73
+  }
74
+  /**
75
+   * 左滑
76
+   * @param {object} e 事件对象
77
+   * @returns {boolean} 布尔值
78
+   */
79
+  isLeft(gesture = {}, touche = {}) {
80
+    const { startX, startY } = gesture
81
+    const deltaX = touche.clientX - startX
82
+    const deltaY = touche.clientY - startY
83
+    if (deltaX < -60 && deltaY < 20 && deltaY > -20) {
84
+      return true
85
+    } else {
86
+      return false
87
+    }
88
+  }
89
+  /**
90
+   * 右滑
91
+   * @param {object} e 事件对象
92
+   * @returns {boolean} 布尔值
93
+   */
94
+  isRight(gesture = {}, touche = {}) {
95
+    const { startX, startY } = gesture
96
+    const deltaX = touche.clientX - startX
97
+    const deltaY = touche.clientY - startY
98
+
99
+    if (deltaX > 60 && deltaY < 20 && deltaY > -20) {
100
+      return true
101
+    } else {
102
+      return false
103
+    }
104
+  }
105
+}
106
+
107
+export class GetDate {
108
+  /**
109
+   * new Date 区分平台
110
+   * @param {number} year
111
+   * @param {number} month
112
+   * @param {number} day
113
+   */
114
+  newDate(year, month, day) {
115
+    let cur = `${+year}-${+month}-${+day}`
116
+    if (isIos()) {
117
+      cur = `${+year}/${+month}/${+day}`
118
+    }
119
+    return new Date(cur)
120
+  }
121
+  /**
122
+   * 计算指定月份共多少天
123
+   * @param {number} year 年份
124
+   * @param {number} month  月份
125
+   */
126
+  thisMonthDays(year, month) {
127
+    return new Date(Date.UTC(year, month, 0)).getUTCDate()
128
+  }
129
+  /**
130
+   * 计算指定月份第一天星期几
131
+   * @param {number} year 年份
132
+   * @param {number} month  月份
133
+   */
134
+  firstDayOfWeek(year, month) {
135
+    return new Date(Date.UTC(year, month - 1, 1)).getUTCDay()
136
+  }
137
+  /**
138
+   * 计算指定日期星期几
139
+   * @param {number} year 年份
140
+   * @param {number} month  月份
141
+   * @param {number} date 日期
142
+   */
143
+  dayOfWeek(year, month, date) {
144
+    return new Date(Date.UTC(year, month - 1, date)).getUTCDay()
145
+  }
146
+  todayDate() {
147
+    const _date = new Date()
148
+    const year = _date.getFullYear()
149
+    const month = _date.getMonth() + 1
150
+    const date = _date.getDate()
151
+    return {
152
+      year,
153
+      month,
154
+      date
155
+    }
156
+  }
157
+  todayTimestamp() {
158
+    const { year, month, date } = this.todayDate()
159
+    const timestamp = this.newDate(year, month, date).getTime()
160
+    return timestamp
161
+  }
162
+  toTimeStr(dateInfo) {
163
+    if (dateInfo.day) {
164
+      dateInfo.date = dateInfo.day
165
+    }
166
+    return `${+dateInfo.year}-${+dateInfo.month}-${+dateInfo.date}`
167
+  }
168
+  sortDates(dates, sortType) {
169
+    return dates.sort(function(a, b) {
170
+      const at = getDateTimeStamp(a)
171
+      const bt = getDateTimeStamp(b)
172
+      if (at < bt && sortType !== 'desc') {
173
+        return -1
174
+      } else {
175
+        return 1
176
+      }
177
+    })
178
+  }
179
+  prevMonth(dataInfo) {
180
+    const prevMonthInfo =
181
+      +dataInfo.month > 1
182
+        ? {
183
+            year: dataInfo.year,
184
+            month: dataInfo.month - 1
185
+          }
186
+        : {
187
+            year: dataInfo.year - 1,
188
+            month: 12
189
+          }
190
+    return prevMonthInfo
191
+  }
192
+  nextMonth(dataInfo) {
193
+    const nextMonthInfo =
194
+      +dataInfo.month < 12
195
+        ? {
196
+            year: dataInfo.year,
197
+            month: dataInfo.month + 1
198
+          }
199
+        : {
200
+            year: dataInfo.year + 1,
201
+            month: 1
202
+          }
203
+    return nextMonthInfo
204
+  }
205
+  convertLunar(dates = []) {
206
+    const datesWithLunar = dates.map(date => {
207
+      if (date) {
208
+        date.lunar = convertSolarLunar.solar2lunar(
209
+          +date.year,
210
+          +date.month,
211
+          +date.day
212
+        )
213
+      }
214
+      return date
215
+    })
216
+    return datesWithLunar
217
+  }
218
+}
219
+
220
+export function isIos() {
221
+  const sys = getSystemInfo()
222
+  return /iphone|ios/i.test(sys.platform)
223
+}
224
+
225
+/**
226
+ * 浅比较对象是否相等
227
+ * @param {Object} origin 对比源
228
+ * @param {Object} target 对比目标
229
+ * @return {Boolean} true 为相等,false 为不等
230
+ */
231
+export function shallowEqual(origin, target) {
232
+  if (origin === target) {
233
+    return true
234
+  } else if (
235
+    typeof origin === 'object' &&
236
+    origin != null &&
237
+    typeof target === 'object' &&
238
+    target != null
239
+  ) {
240
+    if (Object.keys(origin).length !== Object.keys(target).length) return false
241
+    for (var prop in origin) {
242
+      if (target.hasOwnProperty(prop)) {
243
+        if (!shallowEqual(origin[prop], target[prop])) return false
244
+      } else return false
245
+    }
246
+    return true
247
+  } else return false
248
+}
249
+
250
+/**
251
+ * 获取当前页面实例
252
+ */
253
+export function getCurrentPage() {
254
+  const pages = getCurrentPages()
255
+  const last = pages.length - 1
256
+  return pages[last]
257
+}
258
+
259
+export function getComponent(componentId) {
260
+  const logger = new Logger()
261
+  let page = getCurrentPage() || {}
262
+  if (page.selectComponent && typeof page.selectComponent === 'function') {
263
+    if (componentId) {
264
+      return page.selectComponent(componentId)
265
+    } else {
266
+      logger.warn('请传入组件ID')
267
+    }
268
+  } else {
269
+    logger.warn('该基础库暂不支持多个小程序日历组件')
270
+  }
271
+}
272
+
273
+/**
274
+ * 日期数组根据日期去重
275
+ * @param {array} array 数组
276
+ */
277
+export function uniqueArrayByDate(array = []) {
278
+  let uniqueObject = {}
279
+  let uniqueArray = []
280
+  array.forEach(item => {
281
+    uniqueObject[`${item.year}-${item.month}-${item.day}`] = item
282
+  })
283
+  for (let i in uniqueObject) {
284
+    uniqueArray.push(uniqueObject[i])
285
+  }
286
+  return uniqueArray
287
+}
288
+
289
+/**
290
+ * 指定可选日期及可选日期数组去重
291
+ * @param {array} enableDays 特定可选日期数组
292
+ * @param {array} enableArea 可选日期区域数组
293
+ */
294
+export function delRepeatedEnableDay(enableDays = [], enableArea = []) {
295
+  let _startTimestamp
296
+  let _endTimestamp
297
+  if (enableArea.length === 2) {
298
+    const { startTimestamp, endTimestamp } = convertEnableAreaToTimestamp(
299
+      enableArea
300
+    )
301
+    _startTimestamp = startTimestamp
302
+    _endTimestamp = endTimestamp
303
+  }
304
+  const enableDaysTimestamp = converEnableDaysToTimestamp(enableDays)
305
+  const tmp = enableDaysTimestamp.filter(
306
+    item => item < _startTimestamp || item > _endTimestamp
307
+  )
308
+  return tmp
309
+}
310
+
311
+/**
312
+ *  指定日期区域转时间戳
313
+ * @param {array} timearea 时间区域
314
+ */
315
+export function convertEnableAreaToTimestamp(timearea = []) {
316
+  const getDate = new GetDate()
317
+  const start = timearea[0].split('-')
318
+  const end = timearea[1].split('-')
319
+  const logger = new Logger()
320
+  if (start.length !== 3 || end.length !== 3) {
321
+    logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]')
322
+    return {}
323
+  }
324
+  const startTimestamp = getDate.newDate(start[0], start[1], start[2]).getTime()
325
+  const endTimestamp = getDate.newDate(end[0], end[1], end[2]).getTime()
326
+  return {
327
+    start,
328
+    end,
329
+    startTimestamp,
330
+    endTimestamp
331
+  }
332
+}
333
+
334
+/**
335
+ * 计算指定日期时间戳
336
+ * @param {object} dateInfo
337
+ */
338
+export function getDateTimeStamp(dateInfo) {
339
+  if (Object.prototype.toString.call(dateInfo) !== '[object Object]') return
340
+  const getDate = new GetDate()
341
+  return getDate.newDate(dateInfo.year, dateInfo.month, dateInfo.day).getTime()
342
+}
343
+
344
+/**
345
+ *  指定特定日期数组转时间戳
346
+ * @param {array} enableDays 指定时间数组
347
+ */
348
+export function converEnableDaysToTimestamp(enableDays = []) {
349
+  const logger = new Logger()
350
+  const getDate = new GetDate()
351
+  const enableDaysTimestamp = []
352
+  enableDays.forEach(item => {
353
+    if (typeof item !== 'string')
354
+      return logger.warn('enableDays()入参日期格式错误')
355
+    const tmp = item.split('-')
356
+    if (tmp.length !== 3) return logger.warn('enableDays()入参日期格式错误')
357
+    const timestamp = getDate.newDate(tmp[0], tmp[1], tmp[2]).getTime()
358
+    enableDaysTimestamp.push(timestamp)
359
+  })
360
+  return enableDaysTimestamp
361
+}
362
+
363
+// 同一页面多个日历组件按先后顺序渲染
364
+export const initialTasks = {
365
+  flag: 'finished', // process 处理中,finished 处理完成
366
+  tasks: []
367
+}

+ 601 - 0
components/local/calendar/func/week.js

@@ -0,0 +1,601 @@
1
+import Day from './day'
2
+import WxData from './wxData'
3
+import Render from './render'
4
+import CalendarConfig from './config'
5
+import convertSolarLunar from './convertSolarLunar'
6
+import { GetDate, Logger, getDateTimeStamp } from './utils'
7
+
8
+const getDate = new GetDate()
9
+const logger = new Logger()
10
+
11
+class WeekMode extends WxData {
12
+  constructor(component) {
13
+    super(component)
14
+    this.Component = component
15
+    this.getCalendarConfig = CalendarConfig(this.Component).getCalendarConfig
16
+  }
17
+  /**
18
+   * 周、月视图切换
19
+   * @param {string} view  视图 [week, month]
20
+   * @param {object} date  {year: 2017, month: 11, day: 1}
21
+   */
22
+  switchWeek(view, date) {
23
+    return new Promise((resolve, reject) => {
24
+      const config = CalendarConfig(this.Component).getCalendarConfig()
25
+      if (config.multi) return logger.warn('多选模式不能切换周月视图')
26
+      const { selectedDay = [], curYear, curMonth } = this.getData('calendar')
27
+      let currentDate = []
28
+      let disableSelected = false
29
+      if (!selectedDay.length) {
30
+        currentDate = getDate.todayDate()
31
+        currentDate.day = currentDate.date
32
+        disableSelected = true
33
+        // return this.__tipsWhenCanNotSwtich();
34
+      } else {
35
+        currentDate = selectedDay[0]
36
+      }
37
+      let selectedDate = date || currentDate
38
+      const { year, month } = selectedDate
39
+      const notInCurrentMonth = curYear !== year || curMonth !== month
40
+      if (view === 'week') {
41
+        if (this.Component.weekMode) return
42
+        if ((selectedDay.length && notInCurrentMonth) || !selectedDay.length) {
43
+          // return this.__tipsWhenCanNotSwtich();
44
+          disableSelected = true
45
+          selectedDate = {
46
+            year: curYear,
47
+            month: curMonth,
48
+            day: selectedDate.day
49
+          }
50
+        }
51
+        this.Component.weekMode = true
52
+        this.setData({
53
+          'calendarConfig.weekMode': true
54
+        })
55
+        this.jump(selectedDate, disableSelected)
56
+          .then(resolve)
57
+          .catch(reject)
58
+      } else {
59
+        this.Component.weekMode = false
60
+        this.setData({
61
+          'calendarConfig.weekMode': false
62
+        })
63
+        const disableSelected =
64
+          (selectedDay.length && notInCurrentMonth) || !selectedDay.length
65
+        Render(this.Component)
66
+          .renderCalendar(curYear, curMonth, selectedDate.day, disableSelected)
67
+          .then(resolve)
68
+          .catch(reject)
69
+      }
70
+    })
71
+  }
72
+  /**
73
+   * 更新当前年月
74
+   */
75
+  updateCurrYearAndMonth(type) {
76
+    let { days, curYear, curMonth } = this.getData('calendar')
77
+    const { month: firstMonth } = days[0]
78
+    const { month: lastMonth } = days[days.length - 1]
79
+    const lastDayOfThisMonth = getDate.thisMonthDays(curYear, curMonth)
80
+    const lastDayOfThisWeek = days[days.length - 1]
81
+    const firstDayOfThisWeek = days[0]
82
+    if (
83
+      (lastDayOfThisWeek.day + 7 > lastDayOfThisMonth ||
84
+        (curMonth === firstMonth && firstMonth !== lastMonth)) &&
85
+      type === 'next'
86
+    ) {
87
+      curMonth = curMonth + 1
88
+      if (curMonth > 12) {
89
+        curYear = curYear + 1
90
+        curMonth = 1
91
+      }
92
+    } else if (
93
+      (+firstDayOfThisWeek.day <= 7 ||
94
+        (curMonth === lastMonth && firstMonth !== lastMonth)) &&
95
+      type === 'prev'
96
+    ) {
97
+      curMonth = curMonth - 1
98
+      if (curMonth <= 0) {
99
+        curYear = curYear - 1
100
+        curMonth = 12
101
+      }
102
+    }
103
+    return {
104
+      Uyear: curYear,
105
+      Umonth: curMonth
106
+    }
107
+  }
108
+  /**
109
+   * 计算周视图下当前这一周和当月的最后一天
110
+   */
111
+  calculateLastDay() {
112
+    const { days = [], curYear, curMonth } = this.getData('calendar')
113
+    const lastDayInThisWeek = days[days.length - 1].day
114
+    const lastDayInThisMonth = getDate.thisMonthDays(curYear, curMonth)
115
+    return { lastDayInThisWeek, lastDayInThisMonth }
116
+  }
117
+  /**
118
+   * 计算周视图下当前这一周第一天
119
+   */
120
+  calculateFirstDay() {
121
+    const { days } = this.getData('calendar')
122
+    const firstDayInThisWeek = days[0].day
123
+    return { firstDayInThisWeek }
124
+  }
125
+  /**
126
+   * 当月第一周所有日期范围
127
+   * @param {number} year
128
+   * @param {number} month
129
+   * @param {boolean} firstDayOfWeekIsMon 每周是否配置为以周一开始
130
+   */
131
+  firstWeekInMonth(year, month, firstDayOfWeekIsMon) {
132
+    let firstDay = getDate.dayOfWeek(year, month, 1)
133
+    if (firstDayOfWeekIsMon && firstDay === 0) {
134
+      firstDay = 7
135
+    }
136
+    const [, end] = [0, 7 - firstDay]
137
+    let days = this.getData('calendar.days') || []
138
+    if (this.Component.weekMode) {
139
+      days = Day(this.Component).buildDate(year, month)
140
+    }
141
+    const daysCut = days.slice(0, firstDayOfWeekIsMon ? end + 1 : end)
142
+    return daysCut
143
+  }
144
+  /**
145
+   * 当月最后一周所有日期范围
146
+   * @param {number} year
147
+   * @param {number} month
148
+   * @param {boolean} firstDayOfWeekIsMon 每周是否配置为以周一开始
149
+   */
150
+  lastWeekInMonth(year, month, firstDayOfWeekIsMon) {
151
+    const lastDay = getDate.thisMonthDays(year, month)
152
+    const lastDayWeek = getDate.dayOfWeek(year, month, lastDay)
153
+    const [start, end] = [lastDay - lastDayWeek, lastDay]
154
+    let days = this.getData('calendar.days') || []
155
+    if (this.Component.weekMode) {
156
+      days = Day(this.Component).buildDate(year, month)
157
+    }
158
+    const daysCut = days.slice(firstDayOfWeekIsMon ? start : start - 1, end)
159
+    return daysCut
160
+  }
161
+  __getDisableDateTimestamp(config) {
162
+    const { date, type } = config.disableMode || {}
163
+    let disableDateTimestamp
164
+    if (date) {
165
+      const t = date.split('-')
166
+      if (t.length < 3) {
167
+        logger.warn('配置 disableMode.date 格式错误')
168
+        return {}
169
+      }
170
+      disableDateTimestamp = getDateTimeStamp({
171
+        year: +t[0],
172
+        month: +t[1],
173
+        day: +t[2]
174
+      })
175
+    }
176
+    return {
177
+      disableDateTimestamp,
178
+      disableType: type
179
+    }
180
+  }
181
+  /**
182
+   * 渲染日期之前初始化已选日期
183
+   * @param {array} dates 当前日期数组
184
+   */
185
+  initSelectedDay(dates) {
186
+    let datesCopy = [...dates]
187
+    const { selectedDay = [] } = this.getData('calendar')
188
+    const selectedDayStr = selectedDay.map(
189
+      item => `${+item.year}-${+item.month}-${+item.day}`
190
+    )
191
+    const config = this.getCalendarConfig()
192
+    const {
193
+      disableDateTimestamp,
194
+      disableType
195
+    } = this.__getDisableDateTimestamp(config)
196
+    datesCopy = datesCopy.map(item => {
197
+      if (!item) return {}
198
+      const dateTimestamp = getDateTimeStamp(item)
199
+      let date = { ...item }
200
+      if (
201
+        selectedDayStr.includes(`${+date.year}-${+date.month}-${+date.day}`)
202
+      ) {
203
+        date.choosed = true
204
+      } else {
205
+        date.choosed = false
206
+      }
207
+      if (
208
+        (disableType === 'after' && dateTimestamp > disableDateTimestamp) ||
209
+        (disableType === 'before' && dateTimestamp < disableDateTimestamp)
210
+      ) {
211
+        date.disable = true
212
+      }
213
+      date = this.__setTodoWhenJump(date, config)
214
+      if (config.showLunar) {
215
+        date = this.__setSolarLunar(date)
216
+      }
217
+      if (config.highlightToday) {
218
+        date = this.__highlightToday(date)
219
+      }
220
+      return date
221
+    })
222
+    return datesCopy
223
+  }
224
+  /**
225
+   * 周视图下设置可选日期范围
226
+   * @param {object} days 当前展示的日期
227
+   */
228
+  setEnableAreaOnWeekMode(dates = []) {
229
+    let { enableAreaTimestamp = [], enableDaysTimestamp = [] } = this.getData(
230
+      'calendar'
231
+    )
232
+    dates.forEach(item => {
233
+      const timestamp = getDate
234
+        .newDate(item.year, item.month, item.day)
235
+        .getTime()
236
+
237
+      let setDisable = false
238
+      if (enableAreaTimestamp.length) {
239
+        if (
240
+          (+enableAreaTimestamp[0] > +timestamp ||
241
+            +timestamp > +enableAreaTimestamp[1]) &&
242
+          !enableDaysTimestamp.includes(+timestamp)
243
+        ) {
244
+          setDisable = true
245
+        }
246
+      } else if (
247
+        enableDaysTimestamp.length &&
248
+        !enableDaysTimestamp.includes(+timestamp)
249
+      ) {
250
+        setDisable = true
251
+      }
252
+      if (setDisable) {
253
+        item.disable = true
254
+        item.choosed = false
255
+      }
256
+      const config = CalendarConfig(this.Component).getCalendarConfig()
257
+      const {
258
+        disableDateTimestamp,
259
+        disableType
260
+      } = this.__getDisableDateTimestamp(config)
261
+      if (
262
+        (disableType === 'before' && timestamp < disableDateTimestamp) ||
263
+        (disableType === 'after' && timestamp > disableDateTimestamp)
264
+      ) {
265
+        item.disable = true
266
+      }
267
+    })
268
+  }
269
+  updateYMWhenSwipeCalendarHasSelected(dates) {
270
+    const hasSelectedDate = dates.filter(date => date.choosed)
271
+    if (hasSelectedDate && hasSelectedDate.length) {
272
+      const { year, month } = hasSelectedDate[0]
273
+      return {
274
+        year,
275
+        month
276
+      }
277
+    }
278
+    return {}
279
+  }
280
+  /**
281
+   * 计算下一周的日期
282
+   */
283
+  calculateNextWeekDays() {
284
+    let { lastDayInThisWeek, lastDayInThisMonth } = this.calculateLastDay()
285
+    let { curYear, curMonth } = this.getData('calendar')
286
+    let days = []
287
+    if (lastDayInThisMonth - lastDayInThisWeek >= 7) {
288
+      const { Uyear, Umonth } = this.updateCurrYearAndMonth('next')
289
+      curYear = Uyear
290
+      curMonth = Umonth
291
+      for (let i = lastDayInThisWeek + 1; i <= lastDayInThisWeek + 7; i++) {
292
+        days.push({
293
+          year: curYear,
294
+          month: curMonth,
295
+          day: i,
296
+          week: getDate.dayOfWeek(curYear, curMonth, i)
297
+        })
298
+      }
299
+    } else {
300
+      for (let i = lastDayInThisWeek + 1; i <= lastDayInThisMonth; i++) {
301
+        days.push({
302
+          year: curYear,
303
+          month: curMonth,
304
+          day: i,
305
+          week: getDate.dayOfWeek(curYear, curMonth, i)
306
+        })
307
+      }
308
+      const { Uyear, Umonth } = this.updateCurrYearAndMonth('next')
309
+      curYear = Uyear
310
+      curMonth = Umonth
311
+      for (let i = 1; i <= 7 - (lastDayInThisMonth - lastDayInThisWeek); i++) {
312
+        days.push({
313
+          year: curYear,
314
+          month: curMonth,
315
+          day: i,
316
+          week: getDate.dayOfWeek(curYear, curMonth, i)
317
+        })
318
+      }
319
+    }
320
+    days = this.initSelectedDay(days)
321
+    const {
322
+      year: updateYear,
323
+      month: updateMonth
324
+    } = this.updateYMWhenSwipeCalendarHasSelected(days)
325
+    if (updateYear && updateMonth) {
326
+      curYear = updateYear
327
+      curMonth = updateMonth
328
+    }
329
+    this.setEnableAreaOnWeekMode(days)
330
+    this.setData(
331
+      {
332
+        'calendar.curYear': curYear,
333
+        'calendar.curMonth': curMonth,
334
+        'calendar.days': days
335
+      },
336
+      () => {
337
+        Day(this.Component).setDateStyle()
338
+      }
339
+    )
340
+  }
341
+  /**
342
+   * 计算上一周的日期
343
+   */
344
+  calculatePrevWeekDays() {
345
+    let { firstDayInThisWeek } = this.calculateFirstDay()
346
+    let { curYear, curMonth } = this.getData('calendar')
347
+    let days = []
348
+
349
+    if (firstDayInThisWeek - 7 > 0) {
350
+      const { Uyear, Umonth } = this.updateCurrYearAndMonth('prev')
351
+      curYear = Uyear
352
+      curMonth = Umonth
353
+      for (let i = firstDayInThisWeek - 7; i < firstDayInThisWeek; i++) {
354
+        days.push({
355
+          year: curYear,
356
+          month: curMonth,
357
+          day: i,
358
+          week: getDate.dayOfWeek(curYear, curMonth, i)
359
+        })
360
+      }
361
+    } else {
362
+      let temp = []
363
+      for (let i = 1; i < firstDayInThisWeek; i++) {
364
+        temp.push({
365
+          year: curYear,
366
+          month: curMonth,
367
+          day: i,
368
+          week: getDate.dayOfWeek(curYear, curMonth, i)
369
+        })
370
+      }
371
+      const { Uyear, Umonth } = this.updateCurrYearAndMonth('prev')
372
+      curYear = Uyear
373
+      curMonth = Umonth
374
+      const prevMonthDays = getDate.thisMonthDays(curYear, curMonth)
375
+      for (
376
+        let i = prevMonthDays - Math.abs(firstDayInThisWeek - 7);
377
+        i <= prevMonthDays;
378
+        i++
379
+      ) {
380
+        days.push({
381
+          year: curYear,
382
+          month: curMonth,
383
+          day: i,
384
+          week: getDate.dayOfWeek(curYear, curMonth, i)
385
+        })
386
+      }
387
+      days = days.concat(temp)
388
+    }
389
+    days = this.initSelectedDay(days)
390
+    const {
391
+      year: updateYear,
392
+      month: updateMonth
393
+    } = this.updateYMWhenSwipeCalendarHasSelected(days)
394
+    if (updateYear && updateMonth) {
395
+      curYear = updateYear
396
+      curMonth = updateMonth
397
+    }
398
+    this.setEnableAreaOnWeekMode(days)
399
+    this.setData(
400
+      {
401
+        'calendar.curYear': curYear,
402
+        'calendar.curMonth': curMonth,
403
+        'calendar.days': days
404
+      },
405
+      () => {
406
+        Day(this.Component).setDateStyle()
407
+      }
408
+    )
409
+  }
410
+  calculateDatesWhenJump(
411
+    { year, month, day },
412
+    { firstWeekDays, lastWeekDays },
413
+    firstDayOfWeekIsMon
414
+  ) {
415
+    const inFirstWeek = this.__dateIsInWeek({ year, month, day }, firstWeekDays)
416
+    const inLastWeek = this.__dateIsInWeek({ year, month, day }, lastWeekDays)
417
+    let dates = []
418
+    if (inFirstWeek) {
419
+      dates = this.__calculateDatesWhenInFirstWeek(
420
+        firstWeekDays,
421
+        firstDayOfWeekIsMon
422
+      )
423
+    } else if (inLastWeek) {
424
+      dates = this.__calculateDatesWhenInLastWeek(
425
+        lastWeekDays,
426
+        firstDayOfWeekIsMon
427
+      )
428
+    } else {
429
+      dates = this.__calculateDates({ year, month, day }, firstDayOfWeekIsMon)
430
+    }
431
+    return dates
432
+  }
433
+  jump({ year, month, day }, disableSelected) {
434
+    return new Promise(resolve => {
435
+      if (!day) return
436
+      const config = this.getCalendarConfig()
437
+      const firstDayOfWeekIsMon = config.firstDayOfWeek === 'Mon'
438
+      const firstWeekDays = this.firstWeekInMonth(
439
+        year,
440
+        month,
441
+        firstDayOfWeekIsMon
442
+      )
443
+      let lastWeekDays = this.lastWeekInMonth(year, month, firstDayOfWeekIsMon)
444
+      let dates = this.calculateDatesWhenJump(
445
+        { year, month, day },
446
+        {
447
+          firstWeekDays,
448
+          lastWeekDays
449
+        },
450
+        firstDayOfWeekIsMon
451
+      )
452
+      dates = dates.map(d => {
453
+        let date = { ...d }
454
+        if (
455
+          +date.year === +year &&
456
+          +date.month === +month &&
457
+          +date.day === +day &&
458
+          !disableSelected
459
+        ) {
460
+          date.choosed = true
461
+        }
462
+        date = this.__setTodoWhenJump(date, config)
463
+        if (config.showLunar) {
464
+          date = this.__setSolarLunar(date)
465
+        }
466
+        if (config.highlightToday) {
467
+          date = this.__highlightToday(date)
468
+        }
469
+        return date
470
+      })
471
+      this.setEnableAreaOnWeekMode(dates)
472
+      const tmpData = {
473
+        'calendar.days': dates,
474
+        'calendar.curYear': year,
475
+        'calendar.curMonth': month,
476
+        'calendar.empytGrids': [],
477
+        'calendar.lastEmptyGrids': []
478
+      }
479
+      if (!disableSelected) {
480
+        tmpData['calendar.selectedDay'] = dates.filter(item => item.choosed)
481
+      }
482
+      this.setData(tmpData, () => {
483
+        Day(this.Component).setDateStyle()
484
+        resolve({ year, month, date: day })
485
+      })
486
+    })
487
+  }
488
+  __setTodoWhenJump(dateInfo) {
489
+    const date = { ...dateInfo }
490
+    const { todoLabels = [], showLabelAlways } = this.getData('calendar')
491
+    const todosStr = todoLabels.map(d => `${+d.year}-${+d.month}-${+d.day}`)
492
+    const idx = todosStr.indexOf(`${+date.year}-${+date.month}-${+date.day}`)
493
+    if (idx !== -1) {
494
+      if (showLabelAlways) {
495
+        date.showTodoLabel = true
496
+      } else {
497
+        date.showTodoLabel = !date.choosed
498
+      }
499
+      const todo = todoLabels[idx] || {}
500
+      if (date.showTodoLabel && todo.todoText) date.todoText = todo.todoText
501
+      if (todo.color) date.color = todo.color
502
+    }
503
+    return date
504
+  }
505
+  __setSolarLunar(dateInfo) {
506
+    const date = { ...dateInfo }
507
+    date.lunar = convertSolarLunar.solar2lunar(
508
+      +date.year,
509
+      +date.month,
510
+      +date.day
511
+    )
512
+    return date
513
+  }
514
+  __highlightToday(dateInfo) {
515
+    const date = { ...dateInfo }
516
+    const today = getDate.todayDate()
517
+    const isToday =
518
+      +today.year === +date.year &&
519
+      +today.month === +date.month &&
520
+      +date.day === +today.date
521
+    date.isToday = isToday
522
+    return date
523
+  }
524
+  __calculateDatesWhenInFirstWeek(firstWeekDays) {
525
+    const dates = [...firstWeekDays]
526
+    if (dates.length < 7) {
527
+      let { year, month } = dates[0]
528
+      let len = 7 - dates.length
529
+      let lastDate
530
+      if (month > 1) {
531
+        month -= 1
532
+        lastDate = getDate.thisMonthDays(year, month)
533
+      } else {
534
+        month = 12
535
+        year -= 1
536
+        lastDate = getDate.thisMonthDays(year, month)
537
+      }
538
+      while (len) {
539
+        dates.unshift({
540
+          year,
541
+          month,
542
+          day: lastDate,
543
+          week: getDate.dayOfWeek(year, month, lastDate)
544
+        })
545
+        lastDate -= 1
546
+        len -= 1
547
+      }
548
+    }
549
+    return dates
550
+  }
551
+  __calculateDatesWhenInLastWeek(lastWeekDays) {
552
+    const dates = [...lastWeekDays]
553
+    if (dates.length < 7) {
554
+      let { year, month } = dates[0]
555
+      let len = 7 - dates.length
556
+      let firstDate = 1
557
+      if (month > 11) {
558
+        month = 1
559
+        year += 1
560
+      } else {
561
+        month += 1
562
+      }
563
+      while (len) {
564
+        dates.push({
565
+          year,
566
+          month,
567
+          day: firstDate,
568
+          week: getDate.dayOfWeek(year, month, firstDate)
569
+        })
570
+        firstDate += 1
571
+        len -= 1
572
+      }
573
+    }
574
+    return dates
575
+  }
576
+  __calculateDates({ year, month, day }, firstDayOfWeekIsMon) {
577
+    const week = getDate.dayOfWeek(year, month, day)
578
+    let range = [day - week, day + (6 - week)]
579
+    if (firstDayOfWeekIsMon) {
580
+      range = [day + 1 - week, day + (7 - week)]
581
+    }
582
+    const dates = Day(this.Component).buildDate(year, month)
583
+    const weekDates = dates.slice(range[0] - 1, range[1])
584
+    return weekDates
585
+  }
586
+  __dateIsInWeek(date, week) {
587
+    return week.find(
588
+      item =>
589
+        +item.year === +date.year &&
590
+        +item.month === +date.month &&
591
+        +item.day === +date.day
592
+    )
593
+  }
594
+  __tipsWhenCanNotSwtich() {
595
+    logger.info(
596
+      '当前月份未选中日期下切换为周视图,不能明确该展示哪一周的日期,故此情况不允许切换'
597
+    )
598
+  }
599
+}
600
+
601
+export default component => new WeekMode(component)

+ 26 - 0
components/local/calendar/func/wxData.js

@@ -0,0 +1,26 @@
1
+class WxData {
2
+  constructor(component) {
3
+    this.Component = component
4
+  }
5
+  getData(key) {
6
+    const data = this.Component.data
7
+    if (!key) return data
8
+    if (key.includes('.')) {
9
+      let keys = key.split('.')
10
+      const tmp = keys.reduce((prev, next) => {
11
+        return prev[next]
12
+      }, data)
13
+      return tmp
14
+    } else {
15
+      return this.Component.data[key]
16
+    }
17
+  }
18
+  setData(data, cb = () => {}) {
19
+    if (!data) return
20
+    if (typeof data === 'object') {
21
+      this.Component.setData(data, cb)
22
+    }
23
+  }
24
+}
25
+
26
+export default WxData

+ 254 - 0
components/local/calendar/index.js

@@ -0,0 +1,254 @@
1
+import Week from './func/week'
2
+import { Logger, Slide, GetDate, initialTasks } from './func/utils'
3
+import initCalendar, {
4
+  jump,
5
+  getCurrentYM,
6
+  whenChangeDate,
7
+  renderCalendar,
8
+  whenMulitSelect,
9
+  whenSingleSelect,
10
+  whenChooseArea,
11
+  getCalendarDates
12
+} from './main.js'
13
+
14
+const slide = new Slide()
15
+const logger = new Logger()
16
+const getDate = new GetDate()
17
+
18
+Component({
19
+  options: {
20
+    styleIsolation: 'apply-shared',
21
+    multipleSlots: true // 在组件定义时的选项中启用多slot支持
22
+  },
23
+  properties: {
24
+    calendarConfig: {
25
+      type: Object,
26
+      value: {}
27
+    }
28
+  },
29
+  data: {
30
+    handleMap: {
31
+      prev_year: 'chooseYear',
32
+      prev_month: 'chooseMonth',
33
+      next_month: 'chooseMonth',
34
+      next_year: 'chooseYear'
35
+    }
36
+  },
37
+  lifetimes: {
38
+    attached: function() {
39
+      this.initComp()
40
+    },
41
+    detached: function() {
42
+      initialTasks.flag = 'finished'
43
+      initialTasks.tasks.length = 0
44
+    }
45
+  },
46
+  methods: {
47
+    initComp() {
48
+      const calendarConfig = this.setDefaultDisableDate()
49
+      this.setConfig(calendarConfig)
50
+    },
51
+    setDefaultDisableDate() {
52
+      const calendarConfig = this.properties.calendarConfig || {}
53
+      if (calendarConfig.disableMode && !calendarConfig.disableMode.date) {
54
+        calendarConfig.disableMode.date = getDate.toTimeStr(getDate.todayDate())
55
+      }
56
+      return calendarConfig
57
+    },
58
+    setConfig(config) {
59
+      if (config.markToday && typeof config.markToday === 'string') {
60
+        config.highlightToday = true
61
+      }
62
+      config.theme = config.theme || 'default'
63
+      this.weekMode = config.weekMode
64
+      this.setData(
65
+        {
66
+          calendarConfig: config
67
+        },
68
+        () => {
69
+          initCalendar(this, config)
70
+        }
71
+      )
72
+    },
73
+    chooseDate(e) {
74
+      const { type } = e.currentTarget.dataset
75
+      if (!type) return
76
+      const methodName = this.data.handleMap[type]
77
+      this[methodName](type)
78
+    },
79
+    chooseYear(type) {
80
+      const { curYear, curMonth } = this.data.calendar
81
+      if (!curYear || !curMonth) return logger.warn('异常:未获取到当前年月')
82
+      if (this.weekMode) {
83
+        return console.warn('周视图下不支持点击切换年月')
84
+      }
85
+      let newYear = +curYear
86
+      let newMonth = +curMonth
87
+      if (type === 'prev_year') {
88
+        newYear -= 1
89
+      } else if (type === 'next_year') {
90
+        newYear += 1
91
+      }
92
+      this.render(curYear, curMonth, newYear, newMonth)
93
+    },
94
+    chooseMonth(type) {
95
+      const { curYear, curMonth } = this.data.calendar
96
+      if (!curYear || !curMonth) return logger.warn('异常:未获取到当前年月')
97
+      if (this.weekMode) return console.warn('周视图下不支持点击切换年月')
98
+      let newYear = +curYear
99
+      let newMonth = +curMonth
100
+      if (type === 'prev_month') {
101
+        newMonth = newMonth - 1
102
+        if (newMonth < 1) {
103
+          newYear -= 1
104
+          newMonth = 12
105
+        }
106
+      } else if (type === 'next_month') {
107
+        newMonth += 1
108
+        if (newMonth > 12) {
109
+          newYear += 1
110
+          newMonth = 1
111
+        }
112
+      }
113
+      this.render(curYear, curMonth, newYear, newMonth)
114
+    },
115
+    render(curYear, curMonth, newYear, newMonth) {
116
+      whenChangeDate.call(this, {
117
+        curYear,
118
+        curMonth,
119
+        newYear,
120
+        newMonth
121
+      })
122
+      this.setData({
123
+        'calendar.curYear': newYear,
124
+        'calendar.curMonth': newMonth
125
+      })
126
+      renderCalendar.call(this, newYear, newMonth)
127
+    },
128
+    /**
129
+     * 日期点击事件
130
+     * @param {!object} e 事件对象
131
+     */
132
+    tapDayItem(e) {
133
+      const { idx, date = {} } = e.currentTarget.dataset
134
+      const { day, disable } = date
135
+      if (disable || !day) return
136
+      const config = this.data.calendarConfig || this.config || {}
137
+      const { multi, chooseAreaMode } = config
138
+      if (multi) {
139
+        whenMulitSelect.call(this, idx)
140
+      } else if (chooseAreaMode) {
141
+        whenChooseArea.call(this, idx)
142
+      } else {
143
+        whenSingleSelect.call(this, idx)
144
+      }
145
+      this.setData({
146
+        'calendar.noDefault': false
147
+      })
148
+    },
149
+    doubleClickToToday() {
150
+      if (this.config.multi || this.weekMode) return
151
+      if (this.count === undefined) {
152
+        this.count = 1
153
+      } else {
154
+        this.count += 1
155
+      }
156
+      if (this.lastClick) {
157
+        const difference = new Date().getTime() - this.lastClick
158
+        if (difference < 500 && this.count >= 2) {
159
+          jump.call(this)
160
+        }
161
+        this.count = undefined
162
+        this.lastClick = undefined
163
+      } else {
164
+        this.lastClick = new Date().getTime()
165
+      }
166
+    },
167
+    /**
168
+     * 日历滑动开始
169
+     * @param {object} e
170
+     */
171
+    calendarTouchstart(e) {
172
+      const t = e.touches[0]
173
+      const startX = t.clientX
174
+      const startY = t.clientY
175
+      this.slideLock = true // 滑动事件加锁
176
+      this.setData({
177
+        'gesture.startX': startX,
178
+        'gesture.startY': startY
179
+      })
180
+    },
181
+    /**
182
+     * 日历滑动中
183
+     * @param {object} e
184
+     */
185
+    calendarTouchmove(e) {
186
+      const { gesture } = this.data
187
+      const { preventSwipe } = this.properties.calendarConfig
188
+      if (!this.slideLock || preventSwipe) return
189
+      if (slide.isLeft(gesture, e.touches[0])) {
190
+        this.handleSwipe('left')
191
+        this.slideLock = false
192
+      }
193
+      if (slide.isRight(gesture, e.touches[0])) {
194
+        this.handleSwipe('right')
195
+        this.slideLock = false
196
+      }
197
+    },
198
+    calendarTouchend(e) {
199
+      this.setData({
200
+        'calendar.leftSwipe': 0,
201
+        'calendar.rightSwipe': 0
202
+      })
203
+    },
204
+    handleSwipe(direction) {
205
+      let swipeKey = 'calendar.leftSwipe'
206
+      let swipeCalendarType = 'next_month'
207
+      let weekChangeType = 'next_week'
208
+      if (direction === 'right') {
209
+        swipeKey = 'calendar.rightSwipe'
210
+        swipeCalendarType = 'prev_month'
211
+        weekChangeType = 'prev_week'
212
+      }
213
+      this.setData({
214
+        [swipeKey]: 1
215
+      })
216
+      this.currentYM = getCurrentYM()
217
+      if (this.weekMode) {
218
+        this.slideLock = false
219
+        this.currentDates = getCalendarDates()
220
+        if (weekChangeType === 'prev_week') {
221
+          Week(this).calculatePrevWeekDays()
222
+        } else if (weekChangeType === 'next_week') {
223
+          Week(this).calculateNextWeekDays()
224
+        }
225
+        this.onSwipeCalendar(weekChangeType)
226
+        this.onWeekChange(weekChangeType)
227
+        return
228
+      }
229
+      this.chooseMonth(swipeCalendarType)
230
+      this.onSwipeCalendar(swipeCalendarType)
231
+    },
232
+    onSwipeCalendar(direction) {
233
+      this.triggerEvent('onSwipe', {
234
+        directionType: direction,
235
+        currentYM: this.currentYM
236
+      })
237
+    },
238
+    onWeekChange(direction) {
239
+      this.triggerEvent('whenChangeWeek', {
240
+        current: {
241
+          currentYM: this.currentYM,
242
+          dates: [...this.currentDates]
243
+        },
244
+        next: {
245
+          currentYM: getCurrentYM(),
246
+          dates: getCalendarDates()
247
+        },
248
+        directionType: direction
249
+      })
250
+      this.currentDates = null
251
+      this.currentYM = null
252
+    }
253
+  }
254
+})

+ 3 - 0
components/local/calendar/index.json

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 87 - 0
components/local/calendar/index.wxml


+ 214 - 0
components/local/calendar/index.wxss

@@ -0,0 +1,214 @@
1
+@import './theme/iconfont.wxss';
2
+@import './theme/theme-default.wxss';
3
+@import './theme/theme-elegant.wxss';
4
+
5
+.b {
6
+    display: flex;
7
+}
8
+
9
+.lr {
10
+    flex-direction: row;
11
+}
12
+
13
+.tb {
14
+    flex-direction: column;
15
+}
16
+
17
+.pc {
18
+    justify-content: center;
19
+}
20
+
21
+.ac {
22
+    align-items: center;
23
+}
24
+
25
+.cc {
26
+    align-items: center;
27
+    justify-content: center;
28
+}
29
+
30
+.wrap {
31
+    flex-wrap: wrap;
32
+}
33
+
34
+.flex {
35
+    flex-grow: 1;
36
+}
37
+
38
+.bg {
39
+    background-image: linear-gradient(to bottom, #faefe7, #ffcbd7);
40
+    overflow: hidden;
41
+}
42
+
43
+.white-color {
44
+    color: #fff;
45
+}
46
+
47
+.fs24 {
48
+    font-size: 24rpx;
49
+}
50
+
51
+.fs28 {
52
+    font-size: 28rpx;
53
+}
54
+
55
+.fs32 {
56
+    font-size: 32rpx;
57
+}
58
+
59
+.fs36 {
60
+    font-size: 36rpx;
61
+}
62
+
63
+.calendar {
64
+    width: 100%;
65
+    box-sizing: border-box;
66
+}
67
+
68
+/* 日历操作栏 */
69
+
70
+.handle {
71
+    height: 80rpx;
72
+}
73
+
74
+.prev-handle,
75
+.next-handle {
76
+    padding: 20rpx;
77
+}
78
+
79
+.date-in-handle {
80
+    height: 80rpx;
81
+}
82
+
83
+/* 星期栏 */
84
+
85
+.weeks {
86
+    height: 50rpx;
87
+    line-height: 50rpx;
88
+    opacity: 0.5;
89
+}
90
+
91
+.week {
92
+    text-align: center;
93
+}
94
+
95
+.grid,
96
+.week {
97
+    width: 14.286014285714286%;
98
+}
99
+
100
+.date-wrap {
101
+    width: 100%;
102
+    height: 80rpx;
103
+    position: relative;
104
+    left: 0;
105
+    top: 0;
106
+}
107
+
108
+.date {
109
+    position: relative;
110
+    left: 0;
111
+    top: 0;
112
+    width: 55rpx;
113
+    height: 55rpx;
114
+    text-align: center;
115
+    line-height: 55rpx;
116
+    font-size: 26rpx;
117
+    font-weight: 200;
118
+    border-radius: 50%;
119
+    transition: all 0.3s;
120
+    animation-name: choosed;
121
+    animation-duration: 0.5s;
122
+    animation-timing-function: linear;
123
+    animation-iteration-count: 1;
124
+}
125
+
126
+.date-area-mode {
127
+    width: 100%;
128
+    border-radius: 0;
129
+}
130
+
131
+.date-desc {
132
+    width: 150%;
133
+    height: 32rpx;
134
+    font-size: 20rpx;
135
+    line-height: 32rpx;
136
+    position: absolute;
137
+    left: 50%;
138
+    transform: translateX(-50%);
139
+    overflow: hidden;
140
+    word-break: break-all;
141
+    text-overflow: ellipsis;
142
+    white-space: nowrap;
143
+    -webkit-line-clamp: 1;
144
+    text-align: center;
145
+}
146
+
147
+@keyframes choosed {
148
+    from {
149
+        transform: scale(1);
150
+    }
151
+
152
+    50% {
153
+        transform: scale(0.9);
154
+    }
155
+
156
+    to {
157
+        transform: scale(1);
158
+    }
159
+}
160
+
161
+/* 日期圆圈标记 */
162
+.todo-circle {
163
+    border-width: 1rpx;
164
+    border-style: solid;
165
+    box-sizing: border-box;
166
+}
167
+
168
+/* 待办点标记相关样式 */
169
+.todo-dot {
170
+    width: 10rpx;
171
+    height: 10rpx;
172
+    border-radius: 50%;
173
+    position: absolute;
174
+    left: 50%;
175
+    transform: translateX(-50%);
176
+}
177
+
178
+.todo-dot-top {
179
+    top: 3rpx;
180
+}
181
+
182
+.todo-dot.todo-dot-top-always {
183
+    top: -8rpx;
184
+}
185
+
186
+.todo-dot.todo-dot-bottom {
187
+    bottom: 0;
188
+}
189
+
190
+.todo-dot.todo-dot-bottom-always {
191
+    bottom: -10rpx;
192
+}
193
+
194
+/* 日期描述文字(待办文字/农历)相关样式 */
195
+
196
+.date-desc.date-desc-top {
197
+    top: -6rpx;
198
+}
199
+
200
+.date-desc.date-desc-top-always {
201
+    top: -20rpx;
202
+}
203
+
204
+.date-desc.date-desc-bottom {
205
+    bottom: -14rpx;
206
+}
207
+
208
+.todo-circle .date-desc.date-desc-bottom {
209
+    bottom: -30rpx;
210
+}
211
+
212
+.date-desc.date-desc-bottom-always {
213
+    bottom: -28rpx;
214
+}

+ 877 - 0
components/local/calendar/main.js

@@ -0,0 +1,877 @@
1
+import Day from './func/day'
2
+import Week from './func/week'
3
+import Todo from './func/todo'
4
+import WxData from './func/wxData'
5
+import Calendar from './func/render'
6
+import CalendarConfig from './func/config'
7
+import convertSolarLunar from './func/convertSolarLunar'
8
+import {
9
+  Logger,
10
+  GetDate,
11
+  isComponent,
12
+  initialTasks,
13
+  getCurrentPage,
14
+  getComponent,
15
+  getDateTimeStamp
16
+} from './func/utils'
17
+
18
+let Component = {}
19
+let logger = new Logger()
20
+let getDate = new GetDate()
21
+let dataInstance = null
22
+
23
+/**
24
+ * 全局赋值正在操作的组件实例,方便读/写各自的 data
25
+ * @param {string} componentId 要操作的日历组件ID
26
+ */
27
+function bindCurrentComponent(componentId) {
28
+  if (componentId) {
29
+    Component = getComponent(componentId)
30
+  }
31
+  return Component
32
+}
33
+/**
34
+ * 获取日历内部数据
35
+ * @param {string} key 获取值的键名
36
+ * @param {string} componentId 要操作的日历组件ID
37
+ */
38
+function getData(key, componentId) {
39
+  bindCurrentComponent(componentId)
40
+  dataInstance = new WxData(Component)
41
+  return dataInstance.getData(key)
42
+}
43
+/**
44
+ * 设置日历内部数据
45
+ * @param {object}} data 待设置的数据
46
+ * @param {function} callback 设置成功回调函数
47
+ */
48
+function setData(data, callback = () => {}) {
49
+  const dataInstance = new WxData(Component)
50
+  return dataInstance.setData(data, callback)
51
+}
52
+
53
+const conf = {
54
+  /**
55
+   * 渲染日历
56
+   * @param {number} curYear
57
+   * @param {number} curMonth
58
+   * @param {number} curDate
59
+   */
60
+  renderCalendar(curYear, curMonth, curDate) {
61
+    if (isComponent(this)) Component = this
62
+    return new Promise((resolve, reject) => {
63
+      Calendar(Component)
64
+        .renderCalendar(curYear, curMonth, curDate)
65
+        .then((info = {}) => {
66
+          if (!info.firstRender) {
67
+            return resolve({
68
+              year: curYear,
69
+              month: curMonth,
70
+              date: curDate
71
+            })
72
+          }
73
+          mountEventsOnPage(getCurrentPage())
74
+          Component.triggerEvent('afterCalendarRender', Component)
75
+          Component.firstRender = true
76
+          initialTasks.flag = 'finished'
77
+          if (initialTasks.tasks.length) {
78
+            initialTasks.tasks.shift()()
79
+          }
80
+          resolve({
81
+            year: curYear,
82
+            month: curMonth,
83
+            date: curDate
84
+          })
85
+        })
86
+        .catch(err => {
87
+          reject(err)
88
+        })
89
+    })
90
+  },
91
+  /**
92
+   * 当改变月份时触发
93
+   * @param {object} param
94
+   */
95
+  whenChangeDate({ curYear, curMonth, newYear, newMonth }) {
96
+    Component.triggerEvent('whenChangeMonth', {
97
+      current: {
98
+        year: curYear,
99
+        month: curMonth
100
+      },
101
+      next: {
102
+        year: newYear,
103
+        month: newMonth
104
+      }
105
+    })
106
+  },
107
+  /**
108
+   * 多选
109
+   * @param {number} dateIdx 当前选中日期索引值
110
+   */
111
+  whenMulitSelect(dateIdx) {
112
+    if (isComponent(this)) Component = this
113
+    const { calendar = {} } = getData()
114
+    const { days, todoLabels } = calendar
115
+    const config = CalendarConfig(Component).getCalendarConfig()
116
+    let { selectedDay: selectedDays = [] } = calendar
117
+    const currentDay = days[dateIdx]
118
+    if (!currentDay) return
119
+    currentDay.choosed = !currentDay.choosed
120
+    if (!currentDay.choosed) {
121
+      currentDay.cancel = true // 该次点击是否为取消日期操作
122
+      const currentDayStr = getDate.toTimeStr(currentDay)
123
+      selectedDays = selectedDays.filter(
124
+        item => currentDayStr !== getDate.toTimeStr(item)
125
+      )
126
+      if (todoLabels) {
127
+        todoLabels.forEach(item => {
128
+          if (currentDayStr === getDate.toTimeStr(item)) {
129
+            currentDay.showTodoLabel = true
130
+          }
131
+        })
132
+      }
133
+    } else {
134
+      currentDay.cancel = false
135
+      const { showLabelAlways } = getData('calendar')
136
+      if (showLabelAlways && currentDay.showTodoLabel) {
137
+        currentDay.showTodoLabel = true
138
+      } else {
139
+        currentDay.showTodoLabel = false
140
+      }
141
+      if (!config.takeoverTap) {
142
+        selectedDays.push(currentDay)
143
+      }
144
+    }
145
+    if (config.takeoverTap) {
146
+      return Component.triggerEvent('onTapDay', currentDay)
147
+    }
148
+    setData({
149
+      'calendar.days': days,
150
+      'calendar.selectedDay': selectedDays
151
+    })
152
+    conf.afterTapDay(currentDay, selectedDays)
153
+  },
154
+  /**
155
+   * 单选
156
+   * @param {number} dateIdx 当前选中日期索引值
157
+   */
158
+  whenSingleSelect(dateIdx) {
159
+    if (isComponent(this)) Component = this
160
+    const { calendar = {} } = getData()
161
+    const { days, selectedDay: selectedDays = [], todoLabels } = calendar
162
+    let shouldMarkerTodoDay = []
163
+    const currentDay = days[dateIdx]
164
+    if (!currentDay) return
165
+    const preSelectedDate = [...selectedDays].pop() || {}
166
+    const { month: dMonth, year: dYear } = days[0] || {}
167
+    const config = CalendarConfig(Component).getCalendarConfig()
168
+    if (config.takeoverTap) {
169
+      return Component.triggerEvent('onTapDay', currentDay)
170
+    }
171
+    conf.afterTapDay(currentDay)
172
+    if (!config.inverse && preSelectedDate.day === currentDay.day) return
173
+    days.forEach((item, idx) => {
174
+      if (+item.day === +preSelectedDate.day) days[idx].choosed = false
175
+    })
176
+    if (todoLabels) {
177
+      // 筛选当月待办事项的日期
178
+      shouldMarkerTodoDay = todoLabels.filter(
179
+        item => +item.year === dYear && +item.month === dMonth
180
+      )
181
+    }
182
+    Todo(Component).showTodoLabels(shouldMarkerTodoDay, days, selectedDays)
183
+    const tmp = {
184
+      'calendar.days': days
185
+    }
186
+    if (preSelectedDate.day !== currentDay.day) {
187
+      preSelectedDate.choosed = false
188
+      currentDay.choosed = true
189
+      if (!calendar.showLabelAlways || !currentDay.showTodoLabel) {
190
+        currentDay.showTodoLabel = false
191
+      }
192
+      tmp['calendar.selectedDay'] = [currentDay]
193
+    } else if (config.inverse) {
194
+      if (currentDay.choosed) {
195
+        if (currentDay.showTodoLabel && calendar.showLabelAlways) {
196
+          currentDay.showTodoLabel = true
197
+        } else {
198
+          currentDay.showTodoLabel = false
199
+        }
200
+      }
201
+      tmp['calendar.selectedDay'] = []
202
+    }
203
+    if (config.weekMode) {
204
+      tmp['calendar.curYear'] = currentDay.year
205
+      tmp['calendar.curMonth'] = currentDay.month
206
+    }
207
+    setData(tmp)
208
+  },
209
+  gotoSetContinuousDates(start, end) {
210
+    return chooseDateArea([
211
+      `${getDate.toTimeStr(start)}`,
212
+      `${getDate.toTimeStr(end)}`
213
+    ])
214
+  },
215
+  timeRangeHelper(currentDate, selectedDay) {
216
+    const currentDateTimestamp = getDateTimeStamp(currentDate)
217
+    const startDate = selectedDay[0]
218
+    let endDate
219
+    let endDateTimestamp
220
+    let selectedLen = selectedDay.length
221
+    if (selectedLen > 1) {
222
+      endDate = selectedDay[selectedLen - 1]
223
+      endDateTimestamp = getDateTimeStamp(endDate)
224
+    }
225
+    const startTimestamp = getDateTimeStamp(startDate)
226
+    return {
227
+      endDate,
228
+      startDate,
229
+      currentDateTimestamp,
230
+      endDateTimestamp,
231
+      startTimestamp
232
+    }
233
+  },
234
+  /**
235
+   * 计算连续日期选择的开始及结束日期
236
+   * @param {object} currentDate 当前选择日期
237
+   * @param {array} selectedDay 已选择的的日期
238
+   */
239
+  calculateDateRange(currentDate, selectedDay) {
240
+    const {
241
+      endDate,
242
+      startDate,
243
+      currentDateTimestamp,
244
+      endDateTimestamp,
245
+      startTimestamp
246
+    } = this.timeRangeHelper(currentDate, selectedDay)
247
+    let range = []
248
+    let selectedLen = selectedDay.length
249
+    const isWantToChooseOneDate = selectedDay.filter(
250
+      item => getDate.toTimeStr(item) === getDate.toTimeStr(currentDate)
251
+    )
252
+    if (selectedLen === 2 && isWantToChooseOneDate.length) {
253
+      range = [currentDate, currentDate]
254
+      return range
255
+    }
256
+    if (
257
+      currentDateTimestamp >= startTimestamp &&
258
+      endDateTimestamp &&
259
+      currentDateTimestamp <= endDateTimestamp
260
+    ) {
261
+      const currentDateIdxInChoosedDateArea = selectedDay.findIndex(
262
+        item => getDate.toTimeStr(item) === getDate.toTimeStr(currentDate)
263
+      )
264
+      if (selectedLen / 2 > currentDateIdxInChoosedDateArea) {
265
+        range = [currentDate, endDate]
266
+      } else {
267
+        range = [startDate, currentDate]
268
+      }
269
+    } else if (currentDateTimestamp < startTimestamp) {
270
+      range = [currentDate, endDate]
271
+    } else if (currentDateTimestamp > startTimestamp) {
272
+      range = [startDate, currentDate]
273
+    }
274
+    return range
275
+  },
276
+  chooseAreaWhenExistArea(currentDate, selectedDay) {
277
+    return new Promise((resolve, reject) => {
278
+      const range = conf.calculateDateRange(
279
+        currentDate,
280
+        getDate.sortDates(selectedDay)
281
+      )
282
+      conf
283
+        .gotoSetContinuousDates(...range)
284
+        .then(data => {
285
+          resolve(data)
286
+          conf.afterTapDay(currentDate)
287
+        })
288
+        .catch(err => {
289
+          reject(err)
290
+          conf.afterTapDay(currentDate)
291
+        })
292
+    })
293
+  },
294
+  chooseAreaWhenHasOneDate(currentDate, selectedDay, lastChoosedDate) {
295
+    return new Promise((resolve, reject) => {
296
+      const startDate = lastChoosedDate || selectedDay[0]
297
+      let range = [startDate, currentDate]
298
+      const currentDateTimestamp = getDateTimeStamp(currentDate)
299
+      const lastChoosedDateTimestamp = getDateTimeStamp(startDate)
300
+      if (lastChoosedDateTimestamp > currentDateTimestamp) {
301
+        range = [currentDate, startDate]
302
+      }
303
+      conf
304
+        .gotoSetContinuousDates(...range)
305
+        .then(data => {
306
+          resolve(data)
307
+          conf.afterTapDay(currentDate)
308
+        })
309
+        .catch(err => {
310
+          reject(err)
311
+          conf.afterTapDay(currentDate)
312
+        })
313
+    })
314
+  },
315
+  /**
316
+   * 日期范围选择模式
317
+   * @param {number} dateIdx 当前选中日期索引值
318
+   */
319
+  whenChooseArea(dateIdx) {
320
+    return new Promise((resolve, reject) => {
321
+      if (isComponent(this)) Component = this
322
+      if (Component.weekMode) return
323
+      const { days = [], selectedDay, lastChoosedDate } = getData('calendar')
324
+      const currentDate = days[dateIdx]
325
+      if (currentDate.disable) return
326
+      const config = CalendarConfig(Component).getCalendarConfig()
327
+      if (config.takeoverTap) {
328
+        return Component.triggerEvent('onTapDay', currentDate)
329
+      }
330
+      if (selectedDay && selectedDay.length > 1) {
331
+        conf
332
+          .chooseAreaWhenExistArea(currentDate, selectedDay)
333
+          .then(dates => {
334
+            resolve(dates)
335
+          })
336
+          .catch(err => {
337
+            reject(err)
338
+          })
339
+      } else if (lastChoosedDate || (selectedDay && selectedDay.length === 1)) {
340
+        conf
341
+          .chooseAreaWhenHasOneDate(currentDate, selectedDay, lastChoosedDate)
342
+          .then(dates => {
343
+            resolve(dates)
344
+          })
345
+          .catch(err => {
346
+            reject(err)
347
+          })
348
+      } else {
349
+        days.forEach(date => {
350
+          if (+date.day === +currentDate.day) {
351
+            date.choosed = true
352
+          } else {
353
+            date.choosed = false
354
+          }
355
+        })
356
+
357
+        const dataInstance = new WxData(Component)
358
+        dataInstance.setData({
359
+          'calendar.days': [...days],
360
+          'calendar.lastChoosedDate': currentDate
361
+        })
362
+      }
363
+    })
364
+  },
365
+  /**
366
+   * 点击日期后触发事件
367
+   * @param {object} currentSelected 当前选择的日期
368
+   * @param {array} selectedDates  多选状态下选中的日期
369
+   */
370
+  afterTapDay(currentSelected, selectedDates) {
371
+    const config = CalendarConfig(Component).getCalendarConfig()
372
+    const { multi } = config
373
+    if (!multi) {
374
+      Component.triggerEvent('afterTapDay', currentSelected)
375
+    } else {
376
+      Component.triggerEvent('afterTapDay', {
377
+        currentSelected,
378
+        selectedDates
379
+      })
380
+    }
381
+  },
382
+  /**
383
+   * 跳转至今天
384
+   */
385
+  jumpToToday() {
386
+    return new Promise((resolve, reject) => {
387
+      const { year, month, date } = getDate.todayDate()
388
+      const timestamp = getDate.todayTimestamp()
389
+      const config = CalendarConfig(Component).getCalendarConfig()
390
+      setData({
391
+        'calendar.curYear': year,
392
+        'calendar.curMonth': month,
393
+        'calendar.selectedDay': [
394
+          {
395
+            year: year,
396
+            day: date,
397
+            month: month,
398
+            choosed: true,
399
+            lunar: config.showLunar
400
+              ? convertSolarLunar.solar2lunar(year, month, date)
401
+              : null
402
+          }
403
+        ],
404
+        'calendar.todayTimestamp': timestamp
405
+      })
406
+      conf
407
+        .renderCalendar(year, month, date)
408
+        .then(() => {
409
+          resolve({ year, month, date })
410
+        })
411
+        .catch(() => {
412
+          reject('jump failed')
413
+        })
414
+    })
415
+  }
416
+}
417
+
418
+export const whenChangeDate = conf.whenChangeDate
419
+export const renderCalendar = conf.renderCalendar
420
+export const whenSingleSelect = conf.whenSingleSelect
421
+export const whenChooseArea = conf.whenChooseArea
422
+export const whenMulitSelect = conf.whenMulitSelect
423
+export const calculatePrevWeekDays = conf.calculatePrevWeekDays
424
+export const calculateNextWeekDays = conf.calculateNextWeekDays
425
+
426
+/**
427
+ * 获取当前年月
428
+ * @param {string} componentId 要操作的日历组件ID
429
+ */
430
+export function getCurrentYM(componentId) {
431
+  bindCurrentComponent(componentId)
432
+  return {
433
+    year: getData('calendar.curYear'),
434
+    month: getData('calendar.curMonth')
435
+  }
436
+}
437
+
438
+/**
439
+ * 获取已选择的日期
440
+ * @param {object } options 日期配置选项 {lunar} 是否返回农历信息
441
+ * @param {string} componentId 要操作的日历组件ID
442
+ */
443
+export function getSelectedDay(options = {}, componentId) {
444
+  bindCurrentComponent(componentId)
445
+  const config = getCalendarConfig()
446
+  const dates = getData('calendar.selectedDay') || []
447
+  if (options.lunar && !config.showLunar) {
448
+    const datesWithLunar = getDate.convertLunar(dates)
449
+    return datesWithLunar
450
+  } else {
451
+    return dates
452
+  }
453
+}
454
+
455
+/**
456
+ * 取消选中日期
457
+ * @param {array} dates 需要取消的日期,不传则取消所有已选择的日期
458
+ * @param {string} componentId 要操作的日历组件ID
459
+ */
460
+export function cancelSelectedDates(dates, componentId) {
461
+  bindCurrentComponent(componentId)
462
+  const { days = [], selectedDay = [] } = getData('calendar') || {}
463
+  if (!dates || !dates.length) {
464
+    days.forEach(item => {
465
+      item.choosed = false
466
+    })
467
+    setData({
468
+      'calendar.days': days,
469
+      'calendar.selectedDay': []
470
+    })
471
+  } else {
472
+    const cancelDatesStr = dates.map(
473
+      date => `${+date.year}-${+date.month}-${+date.day}`
474
+    )
475
+    const filterSelectedDates = selectedDay.filter(
476
+      date =>
477
+        !cancelDatesStr.includes(`${+date.year}-${+date.month}-${+date.day}`)
478
+    )
479
+    days.forEach(date => {
480
+      if (
481
+        cancelDatesStr.includes(`${+date.year}-${+date.month}-${+date.day}`)
482
+      ) {
483
+        date.choosed = false
484
+      }
485
+    })
486
+    setData({
487
+      'calendar.days': days,
488
+      'calendar.selectedDay': filterSelectedDates
489
+    })
490
+  }
491
+}
492
+/**
493
+ * 周视图跳转
494
+ * @param {object} date info
495
+ * @param {boolean} disableSelected 跳转时是否需要选中,周视图切换调用该方法,如未选择日期时不选中日期
496
+ */
497
+function jumpWhenWeekMode({ year, month, day }, disableSelected) {
498
+  return new Promise((resolve, reject) => {
499
+    Week(Component)
500
+      .jump(
501
+        {
502
+          year: +year,
503
+          month: +month,
504
+          day: +day
505
+        },
506
+        disableSelected
507
+      )
508
+      .then(date => {
509
+        resolve(date)
510
+        Component.triggerEvent('afterCalendarRender', Component)
511
+      })
512
+      .catch(err => {
513
+        reject(err)
514
+        Component.triggerEvent('afterCalendarRender', Component)
515
+      })
516
+  })
517
+}
518
+
519
+/**
520
+ * 月视图跳转
521
+ * @param {object} date info
522
+ */
523
+function jumpWhenNormalMode({ year, month, day }) {
524
+  return new Promise((resolve, reject) => {
525
+    if (typeof +year !== 'number' || typeof +month !== 'number') {
526
+      return logger.warn('jump 函数年月日参数必须为数字')
527
+    }
528
+    const timestamp = getDate.todayTimestamp()
529
+    let tmp = {
530
+      'calendar.curYear': +year,
531
+      'calendar.curMonth': +month,
532
+      'calendar.todayTimestamp': timestamp
533
+    }
534
+    setData(tmp, () => {
535
+      conf
536
+        .renderCalendar(+year, +month, +day)
537
+        .then(date => {
538
+          resolve(date)
539
+        })
540
+        .catch(err => {
541
+          reject(err)
542
+        })
543
+    })
544
+  })
545
+}
546
+
547
+/**
548
+ * 跳转至指定日期
549
+ * @param {number} year
550
+ * @param {number} month
551
+ * @param {number} day
552
+ * @param {string} componentId 要操作的日历组件ID
553
+ */
554
+export function jump(year, month, day, componentId) {
555
+  return new Promise((resolve, reject) => {
556
+    bindCurrentComponent(componentId)
557
+    const { selectedDay = [] } = getData('calendar') || {}
558
+    const { weekMode } = getData('calendarConfig') || {}
559
+    const { year: y, month: m, day: d } = selectedDay[0] || {}
560
+    if (+y === +year && +m === +month && +d === +day) {
561
+      return
562
+    }
563
+    if (weekMode) {
564
+      let disableSelected = false
565
+      if (!year || !month || !day) {
566
+        const today = getDate.todayDate()
567
+        year = today.year
568
+        month = today.month
569
+        day = today.date
570
+        disableSelected = true
571
+      }
572
+      jumpWhenWeekMode({ year, month, day }, disableSelected)
573
+        .then(date => {
574
+          resolve(date)
575
+        })
576
+        .catch(err => {
577
+          reject(err)
578
+        })
579
+      mountEventsOnPage(getCurrentPage())
580
+      return
581
+    }
582
+    if (year && month) {
583
+      jumpWhenNormalMode({ year, month, day })
584
+        .then(date => {
585
+          resolve(date)
586
+        })
587
+        .catch(err => {
588
+          reject(err)
589
+        })
590
+    } else {
591
+      conf
592
+        .jumpToToday()
593
+        .then(date => {
594
+          resolve(date)
595
+        })
596
+        .catch(err => {
597
+          reject(err)
598
+        })
599
+    }
600
+  })
601
+}
602
+
603
+/**
604
+ * 设置待办事项日期标记
605
+ * @param {object} todos  待办事项配置
606
+ * @param {string} [todos.pos] 标记显示位置,默认值'bottom' ['bottom', 'top']
607
+ * @param {string} [todos.dotColor] 标记点颜色,backgroundColor 支持的值都行
608
+ * @param {object[]} [todos.days] 需要标记的所有日期,如:[{year: 2015, month: 5, day: 12}],其中年月日字段必填
609
+ * @param {string} componentId 要操作的日历组件ID
610
+ */
611
+export function setTodoLabels(todos, componentId) {
612
+  bindCurrentComponent(componentId)
613
+  Todo(Component).setTodoLabels(todos)
614
+}
615
+
616
+/**
617
+ * 删除指定日期待办事项
618
+ * @param {array} todos 需要删除的待办日期数组
619
+ * @param {string} componentId 要操作的日历组件ID
620
+ */
621
+export function deleteTodoLabels(todos, componentId) {
622
+  bindCurrentComponent(componentId)
623
+  Todo(Component).deleteTodoLabels(todos)
624
+}
625
+
626
+/**
627
+ * 清空所有待办事项
628
+ * @param {string} componentId 要操作的日历组件ID
629
+ */
630
+export function clearTodoLabels(componentId) {
631
+  bindCurrentComponent(componentId)
632
+  Todo(Component).clearTodoLabels()
633
+}
634
+
635
+/**
636
+ * 获取所有待办事项
637
+ * @param {object } options 日期配置选项 {lunar} 是否返回农历信息
638
+ * @param {string} componentId 要操作的日历组件ID
639
+ */
640
+export function getTodoLabels(options = {}, componentId) {
641
+  bindCurrentComponent(componentId)
642
+  const config = getCalendarConfig()
643
+  const todoDates = Todo(Component).getTodoLabels() || []
644
+  if (options.lunar && !config.showLunar) {
645
+    const todoDatesWithLunar = getDate.convertLunar(todoDates)
646
+    return todoDatesWithLunar
647
+  } else {
648
+    return todoDates
649
+  }
650
+}
651
+
652
+/**
653
+ * 禁用指定日期
654
+ * @param {array} days 日期
655
+ * @param {number} [days.year]
656
+ * @param {number} [days.month]
657
+ * @param {number} [days.day]
658
+ * @param {string} componentId 要操作的日历组件ID
659
+ */
660
+export function disableDay(days = [], componentId) {
661
+  bindCurrentComponent(componentId)
662
+  Day(Component).disableDays(days)
663
+}
664
+
665
+/**
666
+ * 指定可选日期范围
667
+ * @param {array} area 日期访问数组
668
+ * @param {string} componentId 要操作的日历组件ID
669
+ */
670
+export function enableArea(area = [], componentId) {
671
+  bindCurrentComponent(componentId)
672
+  Day(Component).enableArea(area)
673
+}
674
+
675
+/**
676
+ * 指定特定日期可选
677
+ * @param {array} days 指定日期数组
678
+ * @param {string} componentId 要操作的日历组件ID
679
+ */
680
+export function enableDays(days = [], componentId) {
681
+  bindCurrentComponent(componentId)
682
+  Day(Component).enableDays(days)
683
+}
684
+
685
+/**
686
+ * 设置选中日期(多选模式下)
687
+ * @param {array} selected 需选中日期
688
+ * @param {string} componentId 要操作的日历组件ID
689
+ */
690
+export function setSelectedDays(selected, componentId) {
691
+  bindCurrentComponent(componentId)
692
+  Day(Component).setSelectedDays(selected)
693
+}
694
+
695
+/**
696
+ * 获取当前日历配置
697
+ * @param {string} componentId 要操作的日历组件ID
698
+ */
699
+export function getCalendarConfig(componentId) {
700
+  bindCurrentComponent(componentId)
701
+  return CalendarConfig(Component).getCalendarConfig()
702
+}
703
+
704
+/**
705
+ * 设置日历配置
706
+ * @param {object} config
707
+ * @param {string} componentId 要操作的日历组件ID
708
+ */
709
+export function setCalendarConfig(config, componentId) {
710
+  bindCurrentComponent(componentId)
711
+  if (!config || Object.keys(config).length === 0) {
712
+    return logger.warn('setCalendarConfig 参数必须为非空对象')
713
+  }
714
+  const existConfig = getCalendarConfig()
715
+  return new Promise((resolve, reject) => {
716
+    CalendarConfig(Component)
717
+      .setCalendarConfig(config)
718
+      .then(conf => {
719
+        resolve(conf)
720
+        const { date, type } = existConfig.disableMode || {}
721
+        const { _date, _type } = config.disableMode || {}
722
+        if (type !== _type || date !== _date) {
723
+          const { year, month } = getCurrentYM()
724
+          jump(year, month)
725
+        }
726
+      })
727
+      .catch(err => {
728
+        reject(err)
729
+      })
730
+  })
731
+}
732
+
733
+/**
734
+ * 获取当前日历面板日期
735
+ * @param {object } options 日期配置选项 {lunar} 是否返回农历信息
736
+ * @param {string} componentId 要操作的日历组件ID
737
+ */
738
+export function getCalendarDates(options = {}, componentId) {
739
+  bindCurrentComponent(componentId)
740
+  const config = getCalendarConfig()
741
+  const dates = getData('calendar.days', componentId) || []
742
+  if (options.lunar && !config.showLunar) {
743
+    const datesWithLunar = getDate.convertLunar(dates)
744
+    return datesWithLunar
745
+  } else {
746
+    return dates
747
+  }
748
+}
749
+
750
+/**
751
+ * 选择连续日期范围
752
+ * @param {string} componentId 要操作的日历组件ID
753
+ */
754
+export function chooseDateArea(dateArea, componentId) {
755
+  bindCurrentComponent(componentId)
756
+  return Day(Component).chooseArea(dateArea)
757
+}
758
+
759
+/**
760
+ * 设置指定日期样式
761
+ * @param {array} dates 待设置特殊样式的日期
762
+ * @param {string} componentId 要操作的日历组件ID
763
+ */
764
+export function setDateStyle(dates, componentId) {
765
+  if (!dates) return
766
+  bindCurrentComponent(componentId)
767
+  Day(Component).setDateStyle(dates)
768
+}
769
+
770
+/**
771
+ * 切换周月视图
772
+ * 切换视图时可传入指定日期,如: {year: 2019, month: 1, day: 3}
773
+ * args[0] view 视图模式[week, month]
774
+ * args[1]|args[2]为day object或者 componentId
775
+ */
776
+export function switchView(...args) {
777
+  return new Promise((resolve, reject) => {
778
+    const view = args[0]
779
+    if (!args[1]) {
780
+      return Week(Component)
781
+        .switchWeek(view)
782
+        .then(resolve)
783
+        .catch(reject)
784
+    }
785
+    if (typeof args[1] === 'string') {
786
+      bindCurrentComponent(args[1], this)
787
+      Week(Component)
788
+        .switchWeek(view, args[2])
789
+        .then(resolve)
790
+        .catch(reject)
791
+    } else if (typeof args[1] === 'object') {
792
+      if (typeof args[2] === 'string') {
793
+        bindCurrentComponent(args[1], this)
794
+      }
795
+      Week(Component)
796
+        .switchWeek(view, args[1])
797
+        .then(resolve)
798
+        .catch(reject)
799
+    }
800
+  })
801
+}
802
+
803
+/**
804
+ * 绑定日历事件至当前页面实例
805
+ * @param {object} page 当前页面实例
806
+ */
807
+function mountEventsOnPage(page) {
808
+  page.calendar = {
809
+    jump,
810
+    switchView,
811
+    disableDay,
812
+    enableArea,
813
+    enableDays,
814
+    chooseDateArea,
815
+    getCurrentYM,
816
+    getSelectedDay,
817
+    cancelSelectedDates,
818
+    setDateStyle,
819
+    setTodoLabels,
820
+    getTodoLabels,
821
+    deleteTodoLabels,
822
+    clearTodoLabels,
823
+    setSelectedDays,
824
+    getCalendarConfig,
825
+    setCalendarConfig,
826
+    getCalendarDates
827
+  }
828
+}
829
+
830
+function setWeekHeader(firstDayOfWeek) {
831
+  let weeksCh = ['日', '一', '二', '三', '四', '五', '六']
832
+  if (firstDayOfWeek === 'Mon') {
833
+    weeksCh = ['一', '二', '三', '四', '五', '六', '日']
834
+  }
835
+  setData({
836
+    'calendar.weeksCh': weeksCh
837
+  })
838
+}
839
+
840
+function autoSelectDay(defaultDay) {
841
+  Component.firstRenderWeekMode = true
842
+  if (defaultDay && typeof defaultDay === 'string') {
843
+    const day = defaultDay.split('-')
844
+    if (day.length < 3) {
845
+      return logger.warn('配置 jumpTo 格式应为: 2018-4-2 或 2018-04-02')
846
+    }
847
+    jump(+day[0], +day[1], +day[2])
848
+  } else {
849
+    if (!defaultDay) {
850
+      Component.config.noDefault = true
851
+      setData({
852
+        'config.noDefault': true
853
+      })
854
+    }
855
+    jump()
856
+  }
857
+}
858
+
859
+function init(component, config) {
860
+  initialTasks.flag = 'process'
861
+  Component = component
862
+  Component.config = config
863
+  setWeekHeader(config.firstDayOfWeek)
864
+  autoSelectDay(config.defaultDay)
865
+  logger.tips(
866
+    '使用中若遇问题请反馈至 https://github.com/treadpit/wx_calendar/issues ✍️'
867
+  )
868
+}
869
+
870
+export default (component, config = {}) => {
871
+  if (initialTasks.flag === 'process') {
872
+    return initialTasks.tasks.push(function() {
873
+      init(component, config)
874
+    })
875
+  }
876
+  init(component, config)
877
+}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 29 - 0
components/local/calendar/theme/iconfont.wxss


+ 52 - 0
components/local/calendar/theme/theme-default.wxss

@@ -0,0 +1,52 @@
1
+
2
+/* 日历主要颜色相关样式 */
3
+
4
+.default_color,
5
+.default_weekend-color,
6
+.default_handle-color,
7
+.default_week-color {
8
+    color: #ff629a;
9
+}
10
+
11
+.default_today {
12
+    color: #fff;
13
+    background-color: #874fb4;
14
+}
15
+
16
+.default_choosed {
17
+    color: #fff;
18
+    background-color: #ff629a;
19
+}
20
+
21
+.default_date-disable {
22
+    color: #c7c7c7;
23
+}
24
+
25
+.default_prev-month-date,
26
+.default_next-month-date {
27
+    color: #e2e2e2;
28
+}
29
+
30
+.default_normal-date {
31
+    color: #88d2ac;
32
+}
33
+
34
+.default_todo-circle {
35
+    border-color: #88d2ac;
36
+}
37
+
38
+.default_todo-dot {
39
+    background-color: #e54d42;
40
+}
41
+
42
+.default_date-desc {
43
+    color: #c2c2c2;
44
+}
45
+
46
+.default_date-desc-lunar {
47
+    color: #e54d42;
48
+}
49
+
50
+.default_date-desc-disable {
51
+    color: #e2e2e2;
52
+}

+ 49 - 0
components/local/calendar/theme/theme-elegant.wxss

@@ -0,0 +1,49 @@
1
+.elegant_color,
2
+.elegant_weekend-color,
3
+.elegant_handle-color,
4
+.elegant_week-color {
5
+    color: #333;
6
+}
7
+
8
+.elegant_today {
9
+    color: #000;
10
+    background-color: #e1e7f5;
11
+}
12
+
13
+.elegant_choosed {
14
+    color: #000;
15
+    background-color: #e2e2e2;
16
+}
17
+
18
+.elegant_date-disable {
19
+    color: #c7c7c7;
20
+}
21
+
22
+.elegant_prev-month-date,
23
+.elegant_next-month-date {
24
+    color: #e2e2e2;
25
+}
26
+
27
+.elegant_normal-date {
28
+    color: #333;
29
+}
30
+
31
+.elegant_todo-circle {
32
+    border-color: #161035;
33
+}
34
+
35
+.elegant_todo-dot {
36
+    background-color: #161035;
37
+}
38
+
39
+.elegant_date-desc {
40
+    color: #c2c2c2;
41
+}
42
+
43
+.elegant_date-desc-lunar {
44
+    color: #161035;
45
+}
46
+
47
+.elegant_date-desc-disable {
48
+    color: #e2e2e2;
49
+}

+ 144 - 0
components/local/v2/core.js

@@ -0,0 +1,144 @@
1
+import { dateUtil, getCalendarConfig } from './utils/index'
2
+
3
+/**
4
+ * 计算当前月份前后两月应占的格子
5
+ * @param {number} year 年份
6
+ * @param {number} month 月份
7
+ */
8
+function calculateEmptyGrids(year, month, config) {
9
+  const prevMonthGrids = calculatePrevMonthGrids(year, month, config)
10
+  const nextMonthGrids = calculateNextMonthGrids(year, month, config)
11
+  return {
12
+    prevMonthGrids,
13
+    nextMonthGrids
14
+  }
15
+}
16
+
17
+/**
18
+ * 计算上月应占的格子
19
+ * @param {number} year 年份
20
+ * @param {number} month 月份
21
+ */
22
+function calculatePrevMonthGrids(year, month, config) {
23
+  let emptyGrids = []
24
+  const prevMonthDays = dateUtil.getDatesCountOfMonth(year, month - 1)
25
+  let firstDayOfWeek = dateUtil.firstDayOfWeek(year, month)
26
+  if (config.firstDayOfWeek === 'Mon') {
27
+    if (firstDayOfWeek === 0) {
28
+      firstDayOfWeek = 6
29
+    } else {
30
+      firstDayOfWeek -= 1
31
+    }
32
+  }
33
+  if (firstDayOfWeek > 0) {
34
+    const len = prevMonthDays - firstDayOfWeek
35
+    const { onlyShowCurrentMonth } = config
36
+    const YMInfo = dateUtil.getPrevMonthInfo({ year, month })
37
+    for (let i = prevMonthDays; i > len; i--) {
38
+      if (onlyShowCurrentMonth) {
39
+        emptyGrids.push('')
40
+      } else {
41
+        const week = dateUtil.getDayOfWeek(+year, +month, i)
42
+        emptyGrids.push({
43
+          ...YMInfo,
44
+          date: i,
45
+          week
46
+        })
47
+      }
48
+    }
49
+    emptyGrids.reverse()
50
+  }
51
+  return emptyGrids
52
+}
53
+/**
54
+ * 计算下一月日期是否需要多展示的日期
55
+ * 某些月份日期为5排,某些月份6排,统一为6排
56
+ * @param {number} year
57
+ * @param {number} month
58
+ * @param {object} config
59
+ */
60
+function calculateExtraEmptyDate(year, month, config) {
61
+  let extDate = 0
62
+  if (+month === 2) {
63
+    extDate += 7
64
+    let firstDayofMonth = dateUtil.getDayOfWeek(year, month, 1)
65
+    if (config.firstDayOfWeek === 'Mon') {
66
+      if (+firstDayofMonth === 1) extDate += 7
67
+    } else {
68
+      if (+firstDayofMonth === 0) extDate += 7
69
+    }
70
+  } else {
71
+    let firstDayofMonth = dateUtil.getDayOfWeek(year, month, 1)
72
+    if (config.firstDayOfWeek === 'Mon') {
73
+      if (firstDayofMonth !== 0 && firstDayofMonth < 6) {
74
+        extDate += 7
75
+      }
76
+    } else {
77
+      if (firstDayofMonth <= 5) {
78
+        extDate += 7
79
+      }
80
+    }
81
+  }
82
+  return extDate
83
+}
84
+/**
85
+ * 计算下月应占的格子
86
+ * @param {number} year 年份
87
+ * @param {number} month  月份
88
+ */
89
+function calculateNextMonthGrids(year, month, config) {
90
+  let emptyGrids = []
91
+  const datesCount = dateUtil.getDatesCountOfMonth(year, month)
92
+  let lastDayWeek = dateUtil.getDayOfWeek(year, month, datesCount)
93
+  if (config.firstDayOfWeek === 'Mon') {
94
+    if (lastDayWeek === 0) {
95
+      lastDayWeek = 6
96
+    } else {
97
+      lastDayWeek -= 1
98
+    }
99
+  }
100
+  let len = 7 - (lastDayWeek + 1)
101
+  const { onlyShowCurrentMonth } = config
102
+  if (!onlyShowCurrentMonth) {
103
+    len = len + calculateExtraEmptyDate(year, month, config)
104
+  }
105
+  const YMInfo = dateUtil.getNextMonthInfo({ year, month })
106
+  for (let i = 1; i <= len; i++) {
107
+    const week = dateUtil.getDayOfWeek(+year, +month, i)
108
+    if (onlyShowCurrentMonth) {
109
+      emptyGrids.push('')
110
+    } else {
111
+      emptyGrids.push({
112
+        id: i - 1,
113
+        ...YMInfo,
114
+        date: i,
115
+        week: week || 7
116
+      })
117
+    }
118
+  }
119
+  return emptyGrids
120
+}
121
+/**
122
+ * 设置日历面板数据
123
+ * @param {number} year 年份
124
+ * @param {number} month  月份
125
+ * @param {number} curDate  日期
126
+ */
127
+function calculateCurrentMonthDates(year, month) {
128
+  return dateUtil.calcDates(year, month)
129
+}
130
+
131
+export function calcJumpData({ dateInfo, config, component }) {
132
+  dateInfo = dateInfo || dateUtil.todayFMD()
133
+  const { year, month, date } = dateInfo
134
+  const calendarConfig = config || getCalendarConfig(component)
135
+  const emptyGrids = calculateEmptyGrids(year, month, calendarConfig)
136
+  const calendar = {
137
+    curYear: year,
138
+    curMonth: month,
139
+    curDate: date,
140
+    dates: calculateCurrentMonthDates(year, month),
141
+    ...emptyGrids
142
+  }
143
+  return calendar
144
+}

+ 12 - 0
components/local/v2/helper.js

@@ -0,0 +1,12 @@
1
+import { dateUtil } from './utils/index'
2
+
3
+export function calcTargetYMInfo() {
4
+  return {
5
+    right: dateUtil.getPrevMonthInfo,
6
+    left: dateUtil.getNextMonthInfo,
7
+    prev_month: dateUtil.getPrevMonthInfo,
8
+    next_month: dateUtil.getNextMonthInfo,
9
+    prev_year: dateUtil.getPrevYearInfo,
10
+    next_year: dateUtil.getNextYearInfo
11
+  }
12
+}

+ 257 - 0
components/local/v2/index.js

@@ -0,0 +1,257 @@
1
+import plugins from './plugins/index'
2
+import { calcJumpData } from './core'
3
+import { renderCalendar } from './render'
4
+import { calcTargetYMInfo } from './helper'
5
+import { dateUtil, calendarGesture, logger } from './utils/index'
6
+
7
+Component({
8
+  options: {
9
+    styleIsolation: 'apply-shared',
10
+    multipleSlots: true // 在组件定义时的选项中启用多slot支持
11
+  },
12
+  properties: {
13
+    config: {
14
+      type: Object,
15
+      value: {}
16
+    }
17
+  },
18
+  lifetimes: {
19
+    attached: function() {
20
+      this.initComp()
21
+    }
22
+  },
23
+  methods: {
24
+    initComp() {
25
+      const calendarConfig = this.setDefaultDisableDate()
26
+      this.setConfig(calendarConfig)
27
+    },
28
+    // 禁用某天日期配置默认为今天
29
+    setDefaultDisableDate() {
30
+      const calendarConfig = this.properties.config || {}
31
+      if (calendarConfig.disableMode && !calendarConfig.disableMode.date) {
32
+        calendarConfig.disableMode.date = dateUtil.toTimeStr(
33
+          dateUtil.todayFMD()
34
+        )
35
+      }
36
+      return calendarConfig
37
+    },
38
+    initCalendar(config) {
39
+      const { defaultDate } = config
40
+      let date = dateUtil.todayFMD()
41
+      if (defaultDate && typeof defaultDate === 'string') {
42
+        const dateInfo = defaultDate.split('-')
43
+        if (dateInfo.length < 3) {
44
+          return logger.warn('defaultDate配置格式应为: 2018-4-2 或 2018-04-02')
45
+        } else {
46
+          date = {
47
+            year: +dateInfo[0],
48
+            month: +dateInfo[1],
49
+            date: +dateInfo[2]
50
+          }
51
+        }
52
+      }
53
+      const waitRenderData = calcJumpData({
54
+        dateInfo: date,
55
+        config
56
+      })
57
+      const timestamp = dateUtil.todayTimestamp()
58
+      if (config.autoChoosedWhenJump) {
59
+        const target = waitRenderData.dates.filter(
60
+          item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(date)
61
+        )
62
+        if (target && target.length) {
63
+          if (!waitRenderData.selectedDates) {
64
+            waitRenderData.selectedDates = target
65
+          } else {
66
+            waitRenderData.selectedDates.push(target[0])
67
+          }
68
+        }
69
+      }
70
+      return {
71
+        ...waitRenderData,
72
+        todayTimestamp: timestamp,
73
+        weeksCh: dateUtil.getWeekHeader(config.firstDayOfWeek)
74
+      }
75
+    },
76
+    setConfig(config) {
77
+      if (config.markToday && typeof config.markToday === 'string') {
78
+        config.highlightToday = true
79
+      }
80
+      config.theme = config.theme || 'default'
81
+      this.setData(
82
+        {
83
+          config
84
+        },
85
+        () => {
86
+          for (let plugin of plugins.installed) {
87
+            const [, p] = plugin
88
+            if (typeof p.install === 'function') {
89
+              p.install(this)
90
+            }
91
+            if (typeof p.methods === 'function') {
92
+              const methods = p.methods(this)
93
+              for (let fnName in methods) {
94
+                if (fnName.startsWith('__')) continue
95
+                const fn = methods[fnName]
96
+                if (typeof fn === 'function') {
97
+                  if (!this.calendar) this.calendar = {}
98
+                  this.calendar[fnName] = fn
99
+                }
100
+              }
101
+            }
102
+          }
103
+          const initData = this.initCalendar(config)
104
+          renderCalendar.call(this, initData, config)
105
+        }
106
+      )
107
+    },
108
+    tapDate(e) {
109
+      const { info } = e.currentTarget.dataset
110
+      const { date, disable } = info || {}
111
+      if (disable || !date) return
112
+      const { calendar, config } = this.data
113
+      let calendarData = calendar
114
+      let calendarConfig = config
115
+      if (config.takeoverTap) {
116
+        return this.triggerEvent('takeoverTap', info)
117
+      }
118
+      for (let plugin of plugins.installed) {
119
+        const [, p] = plugin
120
+        if (typeof p.onTapDate === 'function') {
121
+          const {
122
+            calendarData: __calendarData,
123
+            calendarConfig: __calendarConfig
124
+          } = p.onTapDate(info, calendarData, calendarConfig)
125
+          calendarData = __calendarData
126
+          calendarConfig = __calendarConfig
127
+        }
128
+      }
129
+      renderCalendar.call(this, calendarData, calendarConfig).then(() => {
130
+        this.triggerEvent('afterTapDate', info)
131
+      })
132
+    },
133
+    /**
134
+     * 日历滑动开始
135
+     * @param {object} e
136
+     */
137
+    calendarTouchstart(e) {
138
+      const t = e.touches[0]
139
+      const startX = t.clientX
140
+      const startY = t.clientY
141
+      this.swipeLock = true
142
+      this.setData({
143
+        'gesture.startX': startX,
144
+        'gesture.startY': startY
145
+      })
146
+    },
147
+    /**
148
+     * 日历滑动中
149
+     * @param {object} e
150
+     */
151
+    calendarTouchmove(e) {
152
+      const { gesture } = this.data
153
+      const { preventSwipe } = this.properties.config
154
+      if (!this.swipeLock || preventSwipe) return
155
+      if (calendarGesture.isLeft(gesture, e.touches[0])) {
156
+        this.handleSwipe('left')
157
+        this.swipeLock = false
158
+      }
159
+      if (calendarGesture.isRight(gesture, e.touches[0])) {
160
+        this.handleSwipe('right')
161
+        this.swipeLock = false
162
+      }
163
+    },
164
+    calendarTouchend(e) {
165
+      this.setData({
166
+        'calendar.leftSwipe': 0,
167
+        'calendar.rightSwipe': 0
168
+      })
169
+    },
170
+    handleSwipe(direction) {
171
+      let swipeKey = 'calendar.leftSwipe'
172
+      if (direction === 'right') {
173
+        swipeKey = 'calendar.rightSwipe'
174
+      }
175
+      this.setData({
176
+        [swipeKey]: 1
177
+      })
178
+      const { calendar } = this.data
179
+      let calendarData = calendar
180
+      const { curYear, curMonth } = calendarData
181
+      const getMonthInfo = calcTargetYMInfo()[direction]
182
+      const target = getMonthInfo({
183
+        year: +curYear,
184
+        month: +curMonth
185
+      })
186
+      target.direction = direction
187
+      this.renderCalendar(target)
188
+    },
189
+    changeDate(e) {
190
+      const { type } = e.currentTarget.dataset
191
+      const { calendar: calendarData } = this.data
192
+      const { curYear, curMonth } = calendarData
193
+      const getMonthInfo = calcTargetYMInfo()[type]
194
+      const target = getMonthInfo({
195
+        year: +curYear,
196
+        month: +curMonth
197
+      })
198
+      target.direction = type
199
+      this.renderCalendar(target)
200
+    },
201
+    renderCalendar(target) {
202
+      let { calendar: calendarData, config } = this.data
203
+      const { curYear, curMonth } = calendarData || {}
204
+      for (let plugin of plugins.installed) {
205
+        const [, p] = plugin
206
+        if (typeof p.onSwitchCalendar === 'function') {
207
+          calendarData = p.onSwitchCalendar(target, calendarData, this)
208
+        }
209
+      }
210
+      return renderCalendar.call(this, calendarData, config).then(() => {
211
+        let triggerEventName = 'whenChangeMonth'
212
+        if (config.weekMode) {
213
+          triggerEventName = 'whenChangeWeek'
214
+        }
215
+        this.triggerEvent(triggerEventName, {
216
+          current: {
217
+            year: +curYear,
218
+            month: +curMonth
219
+          },
220
+          next: target
221
+        })
222
+        this.triggerEvent('onSwipe', {
223
+          current: {
224
+            year: +curYear,
225
+            month: +curMonth
226
+          },
227
+          next: target,
228
+          type: triggerEventName
229
+        })
230
+      })
231
+    },
232
+    doubleClickJumpToToday() {
233
+      const { multi, weekMode } = this.calendar.getCalendarConfig() || {}
234
+      if (multi || weekMode) return
235
+      if (this.count === undefined) {
236
+        this.count = 1
237
+      } else {
238
+        this.count += 1
239
+      }
240
+      if (this.lastClick) {
241
+        const difference = new Date().getTime() - this.lastClick
242
+        if (
243
+          difference < 500 &&
244
+          this.count >= 2 &&
245
+          typeof this.calendar.jump === 'function'
246
+        ) {
247
+          const today = dateUtil.todayFMD()
248
+          this.calendar.jump(today)
249
+        }
250
+        this.count = undefined
251
+        this.lastClick = undefined
252
+      } else {
253
+        this.lastClick = new Date().getTime()
254
+      }
255
+    }
256
+  }
257
+})

+ 3 - 0
components/local/v2/index.json

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 60 - 0
components/local/v2/index.wxml


+ 214 - 0
components/local/v2/index.wxss

@@ -0,0 +1,214 @@
1
+@import './theme/iconfont.wxss';
2
+@import './theme/theme-default.wxss';
3
+@import './theme/theme-elegant.wxss';
4
+
5
+.b {
6
+    display: flex;
7
+}
8
+
9
+.lr {
10
+    flex-direction: row;
11
+}
12
+
13
+.tb {
14
+    flex-direction: column;
15
+}
16
+
17
+.pc {
18
+    justify-content: center;
19
+}
20
+
21
+.ac {
22
+    align-items: center;
23
+}
24
+
25
+.cc {
26
+    align-items: center;
27
+    justify-content: center;
28
+}
29
+
30
+.wrap {
31
+    flex-wrap: wrap;
32
+}
33
+
34
+.flex {
35
+    flex-grow: 1;
36
+}
37
+
38
+.bg {
39
+    background-image: linear-gradient(to bottom, #faefe7, #ffcbd7);
40
+    overflow: hidden;
41
+}
42
+
43
+.white-color {
44
+    color: #fff;
45
+}
46
+
47
+.fs24 {
48
+    font-size: 24rpx;
49
+}
50
+
51
+.fs28 {
52
+    font-size: 28rpx;
53
+}
54
+
55
+.fs32 {
56
+    font-size: 32rpx;
57
+}
58
+
59
+.fs36 {
60
+    font-size: 36rpx;
61
+}
62
+
63
+.calendar {
64
+    width: 100%;
65
+    box-sizing: border-box;
66
+}
67
+
68
+/* 日历操作栏 */
69
+
70
+.handle {
71
+    height: 80rpx;
72
+}
73
+
74
+.prev-handle,
75
+.next-handle {
76
+    padding: 20rpx;
77
+}
78
+
79
+.date-in-handle {
80
+    height: 80rpx;
81
+}
82
+
83
+/* 星期栏 */
84
+
85
+.weeks {
86
+    height: 50rpx;
87
+    line-height: 50rpx;
88
+    opacity: 0.5;
89
+}
90
+
91
+.week {
92
+    text-align: center;
93
+}
94
+
95
+.grid,
96
+.week {
97
+    width: 14.286014285714286%;
98
+}
99
+
100
+.date-wrap {
101
+    width: 100%;
102
+    height: 80rpx;
103
+    position: relative;
104
+    left: 0;
105
+    top: 0;
106
+}
107
+
108
+.date {
109
+    position: relative;
110
+    left: 0;
111
+    top: 0;
112
+    width: 55rpx;
113
+    height: 55rpx;
114
+    text-align: center;
115
+    line-height: 55rpx;
116
+    font-size: 26rpx;
117
+    font-weight: 200;
118
+    border-radius: 50%;
119
+    transition: all 0.3s;
120
+    animation-name: choosed;
121
+    animation-duration: 0.5s;
122
+    animation-timing-function: linear;
123
+    animation-iteration-count: 1;
124
+}
125
+
126
+.date-area-mode {
127
+    width: 100%;
128
+    border-radius: 0;
129
+}
130
+
131
+.date-desc {
132
+    width: 150%;
133
+    height: 32rpx;
134
+    font-size: 20rpx;
135
+    line-height: 32rpx;
136
+    position: absolute;
137
+    left: 50%;
138
+    transform: translateX(-50%);
139
+    overflow: hidden;
140
+    word-break: break-all;
141
+    text-overflow: ellipsis;
142
+    white-space: nowrap;
143
+    -webkit-line-clamp: 1;
144
+    text-align: center;
145
+}
146
+
147
+@keyframes choosed {
148
+    from {
149
+        transform: scale(1);
150
+    }
151
+
152
+    50% {
153
+        transform: scale(0.9);
154
+    }
155
+
156
+    to {
157
+        transform: scale(1);
158
+    }
159
+}
160
+
161
+/* 日期圆圈标记 */
162
+.todo-circle {
163
+    border-width: 1rpx;
164
+    border-style: solid;
165
+    box-sizing: border-box;
166
+}
167
+
168
+/* 待办点标记相关样式 */
169
+.todo-dot {
170
+    width: 10rpx;
171
+    height: 10rpx;
172
+    border-radius: 50%;
173
+    position: absolute;
174
+    left: 50%;
175
+    transform: translateX(-50%);
176
+}
177
+
178
+.todo-dot-top {
179
+    top: 3rpx;
180
+}
181
+
182
+.todo-dot.todo-dot-top-always {
183
+    top: -8rpx;
184
+}
185
+
186
+.todo-dot.todo-dot-bottom {
187
+    bottom: 0;
188
+}
189
+
190
+.todo-dot.todo-dot-bottom-always {
191
+    bottom: -10rpx;
192
+}
193
+
194
+/* 日期描述文字(待办文字/农历)相关样式 */
195
+
196
+.date-desc.date-desc-top {
197
+    top: -6rpx;
198
+}
199
+
200
+.date-desc.date-desc-top-always {
201
+    top: -20rpx;
202
+}
203
+
204
+.date-desc.date-desc-bottom {
205
+    bottom: -14rpx;
206
+}
207
+
208
+.todo-circle .date-desc.date-desc-bottom {
209
+    bottom: -30rpx;
210
+}
211
+
212
+.date-desc.date-desc-bottom-always {
213
+    bottom: -28rpx;
214
+}

+ 212 - 0
components/local/v2/plugins/holidays/holidays-map.js

@@ -0,0 +1,212 @@
1
+/* *
2
+  @Author: drfu*
3
+  @Description: 数据来源于国务院办公厅关于2020年部分节假日安排的通知(国办发明电〔2019〕16号)_政府信息公开专栏,http://www.gov.cn/zhengce/content/2019-11/21/content_5454164.htm
4
+  @Date: 2020-10-12 14:29:45*
5
+ * @Last Modified by: drfu
6
+ * @Last Modified time: 2020-10-16 17:38:08
7
+*/
8
+
9
+// 节日列表
10
+export const festival = {
11
+  // 农历固定日期节日
12
+  lunar: {
13
+    1: {
14
+      1: {
15
+        type: 'festival',
16
+        name: '春节',
17
+        label: '春节'
18
+      },
19
+      8: {
20
+        type: 'festival',
21
+        name: '腊八节',
22
+        label: '腊八'
23
+      },
24
+      15: {
25
+        type: 'festival',
26
+        name: '元宵节',
27
+        label: '元宵'
28
+      }
29
+    },
30
+    7: {
31
+      7: {
32
+        type: 'festival',
33
+        name: '七夕节',
34
+        label: '七夕'
35
+      },
36
+      15: {
37
+        type: 'festival',
38
+        name: '中元节',
39
+        label: '中元节'
40
+      }
41
+    },
42
+    9: {
43
+      9: {
44
+        type: 'festival',
45
+        name: '重阳节',
46
+        label: '重阳节'
47
+      }
48
+    }
49
+  },
50
+  // 阳历固定日期节日
51
+  solar: {
52
+    2: {
53
+      14: {
54
+        type: 'festival',
55
+        name: '情人节',
56
+        label: '情人节'
57
+      }
58
+    },
59
+    3: {
60
+      12: {
61
+        type: 'festival',
62
+        name: '植树节',
63
+        label: '植树节'
64
+      }
65
+    },
66
+    4: {
67
+      1: {
68
+        type: 'festival',
69
+        name: '愚人节',
70
+        label: '愚人节'
71
+      },
72
+      5: {
73
+        type: 'festival',
74
+        name: '清明节',
75
+        label: '清明节'
76
+      }
77
+    },
78
+    5: {
79
+      1: {
80
+        type: 'festival',
81
+        name: '劳动节',
82
+        label: '劳动节'
83
+      }
84
+    },
85
+    6: {
86
+      1: {
87
+        type: 'festival',
88
+        name: '儿童节',
89
+        label: '儿童节'
90
+      }
91
+    },
92
+    7: {
93
+      1: {
94
+        type: 'festival',
95
+        name: '建党节',
96
+        label: '建党节'
97
+      }
98
+    },
99
+    8: {
100
+      1: {
101
+        type: 'festival',
102
+        name: '建军节',
103
+        label: '建军节'
104
+      }
105
+    },
106
+    9: {
107
+      10: {
108
+        type: 'festival',
109
+        name: '教师节',
110
+        label: '教师节'
111
+      }
112
+    },
113
+    10: {
114
+      1: {
115
+        type: 'festival',
116
+        name: '国庆节',
117
+        label: '国庆节'
118
+      }
119
+    },
120
+    12: {
121
+      25: {
122
+        type: 'festival',
123
+        name: '圣诞节',
124
+        label: '圣诞节'
125
+      }
126
+    }
127
+  }
128
+}
129
+
130
+export const holidays = {
131
+  2020: {
132
+    1: {
133
+      1: {
134
+        type: 'holiday',
135
+        name: '元旦',
136
+        label: '休'
137
+      },
138
+      19: {
139
+        type: 'work',
140
+        name: '调班',
141
+        label: '班'
142
+      },
143
+      '24-30': {
144
+        type: 'holiday',
145
+        name: '春节',
146
+        label: '休'
147
+      }
148
+    },
149
+    2: {
150
+      1: {
151
+        type: 'work',
152
+        name: '调班',
153
+        label: '班'
154
+      }
155
+    },
156
+    4: {
157
+      '4-6': {
158
+        type: 'holiday',
159
+        name: '清明节',
160
+        label: '休'
161
+      },
162
+      26: {
163
+        type: 'work',
164
+        name: '调班',
165
+        label: '班'
166
+      }
167
+    },
168
+    5: {
169
+      '1-5': {
170
+        type: 'holiday',
171
+        name: '劳动节',
172
+        label: '休'
173
+      },
174
+      9: {
175
+        type: 'work',
176
+        name: '调班',
177
+        label: '班'
178
+      }
179
+    },
180
+    6: {
181
+      '25-27': {
182
+        type: 'holiday',
183
+        name: '端午节',
184
+        label: '休'
185
+      },
186
+      28: {
187
+        type: 'work',
188
+        name: '调班',
189
+        label: '班'
190
+      }
191
+    },
192
+    9: {
193
+      27: {
194
+        type: 'work',
195
+        name: '调班',
196
+        label: '班'
197
+      }
198
+    },
199
+    10: {
200
+      '1-8': {
201
+        type: 'holiday',
202
+        name: '国庆节/中秋节',
203
+        label: '休'
204
+      },
205
+      10: {
206
+        type: 'work',
207
+        name: '调班',
208
+        label: '班'
209
+      }
210
+    }
211
+  }
212
+}

+ 201 - 0
components/local/v2/plugins/holidays/index.js

@@ -0,0 +1,201 @@
1
+/* *
2
+  @Author: drfu*
3
+  @Description: 显示法定节假日班/休情况
4
+  @Date: 2020-10-12 14:29:45*
5
+ * @Last Modified by: drfu
6
+ * @Last Modified time: 2020-10-16 17:34:13
7
+*/
8
+
9
+import { holidays, festival } from './holidays-map'
10
+import { dateUtil, getCalendarData, logger } from '../../utils/index'
11
+
12
+/**
13
+ * 当前是否在休假期内
14
+ * @param {object} { year, month }
15
+ * @param {object} { start, end, current }
16
+ * @returns
17
+ */
18
+function inHolidays({ year, month }, { start, end, current }) {
19
+  const getTimeStamp = dateUtil.getTimeStamp
20
+  const startTimestamp = getTimeStamp({
21
+    year,
22
+    month,
23
+    date: start
24
+  })
25
+  const endTimestamp = getTimeStamp({
26
+    year,
27
+    month,
28
+    date: end
29
+  })
30
+  const currentDateTimestamp = getTimeStamp({
31
+    year,
32
+    month,
33
+    date: current
34
+  })
35
+  if (
36
+    currentDateTimestamp >= startTimestamp &&
37
+    currentDateTimestamp <= endTimestamp
38
+  ) {
39
+    return true
40
+  }
41
+  return false
42
+}
43
+
44
+function addSpecialFestival(date, component) {
45
+  const { convertlLunar2Solar, convertSolarLunar } = component.calendar || {}
46
+  const lunarDateInfo = convertSolarLunar(date)
47
+  const { lYear, lMonth } = lunarDateInfo || {}
48
+  // 春节
49
+  const info = {
50
+    type: 'festival',
51
+    name: '除夕',
52
+    label: '除夕'
53
+  }
54
+  if (lMonth === 12) {
55
+    if (!festival.lunar['12']) festival.lunar['12'] = {}
56
+    if (convertlLunar2Solar(`${lYear}-12-30`) === -1) {
57
+      festival.lunar['12']['29'] = info
58
+    } else {
59
+      festival.lunar['12']['30'] = info
60
+    }
61
+  }
62
+}
63
+
64
+/**
65
+ * 是否匹配到节日
66
+ * @param {object} [dateInfo={}]
67
+ * @param {object} [component={}]
68
+ * @returns {object|boolean} 匹配到的节日数据或者false
69
+ */
70
+function hasFestivalDate(dateInfo = {}, component = {}) {
71
+  const { month, date } = dateInfo
72
+  let festivalDate = festival.solar[month] && festival.solar[month][date]
73
+  if (!festivalDate) {
74
+    const { convertSolarLunar } = component.calendar || {}
75
+    const lunarDateInfo = convertSolarLunar(dateInfo)
76
+    const { lMonth, lDay } = lunarDateInfo
77
+    festivalDate = festival.lunar[lMonth] && festival.lunar[lMonth][lDay]
78
+    if (!festivalDate) {
79
+      const festivalOfMonth = festival.lunar[lMonth] || {}
80
+      const festivalDateKey = Object.keys(festivalOfMonth).find(item =>
81
+        item.match(new RegExp(`\\b${lDay}\\b`))
82
+      )
83
+      if (!festivalDateKey) {
84
+        festivalDate = false
85
+      } else {
86
+        const festivalInfo = festival.lunar[lMonth][festivalDateKey]
87
+        if (!festivalInfo) {
88
+          festivalDate = false
89
+        } else {
90
+          const { condition } = festivalInfo
91
+          if (typeof condition === 'function') {
92
+            festivalDate = condition(lunarDateInfo)
93
+          } else {
94
+            festivalDate = false
95
+          }
96
+        }
97
+      }
98
+    }
99
+  }
100
+  return festivalDate
101
+}
102
+
103
+export default () => {
104
+  return {
105
+    name: 'holidays',
106
+    beforeRender(calendarData = {}, calendarConfig = {}, component) {
107
+      let { dates = [] } = calendarData
108
+      if (calendarConfig.showHolidays || calendarConfig.showFestival) {
109
+        dates = dates.map(d => {
110
+          let item = { ...d }
111
+          const { year, month, date } = item
112
+          const holidaysOfMonth =
113
+            (holidays[year] && holidays[year][month]) || {}
114
+          const holidayDate = holidaysOfMonth[date]
115
+          if (holidayDate) {
116
+            item = {
117
+              ...item,
118
+              ...holidayDate
119
+            }
120
+          } else {
121
+            const holidayKeys = Object.keys(holidaysOfMonth).filter(item =>
122
+              item.includes('-')
123
+            )
124
+            let target = ''
125
+            for (let v of holidayKeys) {
126
+              const [start, end] = v.split('-')
127
+              if (+d.date >= +start && +d.date <= +end) {
128
+                target = v
129
+                break
130
+              }
131
+            }
132
+            const [start, end] = target.split('-')
133
+            const isInHolidays = inHolidays(
134
+              {
135
+                year,
136
+                month
137
+              },
138
+              {
139
+                start,
140
+                end,
141
+                current: date
142
+              }
143
+            )
144
+            if (isInHolidays) {
145
+              item = {
146
+                ...item,
147
+                ...holidaysOfMonth[target]
148
+              }
149
+            } else if (calendarConfig.showFestival) {
150
+              const { convertSolarLunar, convertlLunar2Solar } =
151
+                component.calendar || {}
152
+              if (
153
+                typeof convertSolarLunar !== 'function' ||
154
+                typeof convertlLunar2Solar !== 'function'
155
+              ) {
156
+                return logger.warn(
157
+                  '农历节日显示需要引入农历插件(/component/v2/plugins/solarLunar)'
158
+                )
159
+              }
160
+              addSpecialFestival(item, component)
161
+              const festivalDate = hasFestivalDate(item, component)
162
+              if (festivalDate) {
163
+                item = {
164
+                  ...item,
165
+                  ...festivalDate
166
+                }
167
+              }
168
+            }
169
+          }
170
+          return item
171
+        })
172
+      }
173
+      return {
174
+        calendarData: {
175
+          ...calendarData,
176
+          dates: dates
177
+        },
178
+        calendarConfig
179
+      }
180
+    },
181
+    methods(component) {
182
+      return {
183
+        getHolidaysOfCurrentYear() {
184
+          const calendar = getCalendarData('calendar', component)
185
+          const { curYear } = calendar
186
+          return this.methods(component).getHolidaysOfYear(curYear)
187
+        },
188
+        getHolidaysOfYear(year) {
189
+          if (!year) return logger.warn('getHolidaysOfCurrentYear() 入参错误')
190
+          if (!holidays[year]) {
191
+            logger.warn('未匹配到当前年份节假日信息,请自行补充')
192
+            return {
193
+              err: 'not match'
194
+            }
195
+          }
196
+          return holidays[year]
197
+        }
198
+      }
199
+    }
200
+  }
201
+}

+ 18 - 0
components/local/v2/plugins/index.js

@@ -0,0 +1,18 @@
1
+import preset from './preset/index'
2
+
3
+export default {
4
+  installed: [...preset],
5
+  use(plugin) {
6
+    if (typeof plugin !== 'function') return
7
+    const info = plugin() || {}
8
+    const { name } = info
9
+    if (
10
+      name &&
11
+      name !== 'methods' &&
12
+      !this.installed.some(p => p[0] === name)
13
+    ) {
14
+      this.installed.unshift([name, info])
15
+    }
16
+    return this
17
+  }
18
+}

+ 277 - 0
components/local/v2/plugins/preset/base.js

@@ -0,0 +1,277 @@
1
+/**
2
+ * @Author: drfu*
3
+ * @Description: 基础功能
4
+ * @Date: 2020-10-08 21:22:09*
5
+ * @Last Modified by: drfu
6
+ * @Last Modified time: 2020-10-11 13:28:52
7
+ * */
8
+
9
+import { calcJumpData } from '../../core'
10
+import { renderCalendar } from '../../render'
11
+import {
12
+  dateUtil,
13
+  getCalendarData,
14
+  setCalendarData,
15
+  getCalendarConfig
16
+} from '../../utils/index'
17
+
18
+export default () => {
19
+  return {
20
+    name: 'base',
21
+    beforeRender(calendarData = {}, calendarConfig) {
22
+      const calendar = calendarData
23
+      const { selectedDates = [], dates } = calendar
24
+      let _dates = [...dates]
25
+      if (selectedDates.length) {
26
+        const selectedDatesStr = selectedDates.map(date =>
27
+          dateUtil.toTimeStr(date)
28
+        )
29
+        _dates.forEach(date => {
30
+          const dateStr = dateUtil.toTimeStr(date)
31
+          if (selectedDatesStr.includes(dateStr)) {
32
+            date.choosed = true
33
+          }
34
+        })
35
+      }
36
+      return {
37
+        calendarData: {
38
+          ...calendarData,
39
+          dates: _dates
40
+        },
41
+        calendarConfig
42
+      }
43
+    },
44
+    onTapDate(tapedDate, calendarData = {}, calendarConfig = {}) {
45
+      const calendar = {
46
+        ...calendarData
47
+      }
48
+      const dateIndex = dateUtil.findDateIndexInArray(
49
+        tapedDate,
50
+        calendarData.dates
51
+      )
52
+      const { multi, inverse } = calendarConfig
53
+      let dates = [...calendar.dates]
54
+      const { selectedDates = [] } = calendar
55
+      if (!multi) {
56
+        let preSelectedDate = {}
57
+        if (selectedDates.length) {
58
+          preSelectedDate = [...selectedDates].pop() || {}
59
+        }
60
+        const timeStr = dateUtil.toTimeStr
61
+        if (!inverse && timeStr(preSelectedDate) === timeStr(tapedDate)) {
62
+          return calendar
63
+        }
64
+        let _tapedDate = { ...tapedDate, choosed: !tapedDate.choosed }
65
+
66
+        dates[dateIndex] = _tapedDate
67
+        if (preSelectedDate.date) {
68
+          const idx = dateUtil.findDateIndexInArray(preSelectedDate, dates)
69
+          const date = dates[idx]
70
+          if (date) {
71
+            date.choosed = false
72
+          }
73
+        }
74
+        if (dates[dateIndex].choosed) {
75
+          calendar.selectedDates = [dates[dateIndex]]
76
+        } else {
77
+          calendar.selectedDates = []
78
+        }
79
+      } else {
80
+        dates[dateIndex] = {
81
+          ...dates[dateIndex],
82
+          choosed: !dates[dateIndex].choosed
83
+        }
84
+        if (!calendar.selectedDates) {
85
+          calendar.selectedDates = []
86
+        }
87
+        if (dates[dateIndex].choosed) {
88
+          calendar.selectedDates.push(dates[dateIndex])
89
+        } else {
90
+          calendar.selectedDates = calendar.selectedDates.filter(
91
+            date =>
92
+              dateUtil.toTimeStr(date) !== dateUtil.toTimeStr(dates[dateIndex])
93
+          )
94
+        }
95
+      }
96
+      return {
97
+        calendarData: {
98
+          ...calendar,
99
+          dates
100
+        },
101
+        calendarConfig
102
+      }
103
+    },
104
+    onSwitchCalendar(date, calendarData = {}, component) {
105
+      const calendarConfig = getCalendarConfig(component)
106
+      if (calendarConfig.weekMode) {
107
+        return calendarData
108
+      }
109
+      const updatedRenderData = calcJumpData({
110
+        dateInfo: date,
111
+        config: calendarConfig
112
+      })
113
+      return {
114
+        ...calendarData,
115
+        ...updatedRenderData
116
+      }
117
+    },
118
+    methods(component) {
119
+      return {
120
+        jump: dateInfo => {
121
+          if (Object.prototype.toString.call(dateInfo) !== '[object Object]')
122
+            return
123
+          const updatedRenderData = calcJumpData({
124
+            dateInfo,
125
+            component
126
+          })
127
+          const existCalendarData = getCalendarData('calendar', component)
128
+          const config = getCalendarConfig(component)
129
+          if (config.autoChoosedWhenJump) {
130
+            const target = updatedRenderData.dates[dateInfo.date - 1]
131
+            if (!updatedRenderData.selectedDates) {
132
+              updatedRenderData.selectedDates = [target]
133
+            } else {
134
+              updatedRenderData.selectedDates.push(target)
135
+            }
136
+          }
137
+          return renderCalendar.call(component, {
138
+            ...existCalendarData,
139
+            ...updatedRenderData
140
+          })
141
+        },
142
+        getCalendarConfig() {
143
+          return getCalendarConfig(component)
144
+        },
145
+        setCalendarConfig(config) {
146
+          return new Promise((resolve, reject) => {
147
+            if (!component || !component.data.config) {
148
+              reject('异常:未找到组件配置信息')
149
+              return
150
+            }
151
+            let conf = { ...component.config, ...config }
152
+            component.config = conf
153
+            setCalendarData({ config: conf }, component)
154
+              .then(resolve)
155
+              .catch(reject)
156
+          })
157
+        },
158
+        cancelSelectedDates(cancelDates = []) {
159
+          const existCalendarData = getCalendarData('calendar', component) || {}
160
+          const { dates = [], selectedDates = [] } = existCalendarData
161
+          let updatedRenderData = {}
162
+          const config = getCalendarConfig(component)
163
+          let chooseAreaData = {}
164
+          if (config.chooseAreaMode) {
165
+            chooseAreaData = {
166
+              chooseAreaTimestamp: [],
167
+              tempChooseAreaTimestamp: []
168
+            }
169
+          }
170
+          if (!cancelDates.length) {
171
+            dates.forEach(item => {
172
+              item.choosed = false
173
+            })
174
+            updatedRenderData = {
175
+              dates,
176
+              selectedDates: []
177
+            }
178
+          } else {
179
+            const cancelDatesStr = cancelDates.map(date =>
180
+              dateUtil.toTimeStr(date)
181
+            )
182
+            const filterSelectedDates = selectedDates.filter(
183
+              date => !cancelDatesStr.includes(dateUtil.toTimeStr(date))
184
+            )
185
+            dates.forEach(date => {
186
+              if (cancelDatesStr.includes(dateUtil.toTimeStr(date))) {
187
+                date.choosed = false
188
+              }
189
+            })
190
+            updatedRenderData = {
191
+              dates,
192
+              selectedDates: filterSelectedDates
193
+            }
194
+          }
195
+
196
+          return renderCalendar.call(component, {
197
+            ...existCalendarData,
198
+            ...updatedRenderData,
199
+            ...chooseAreaData
200
+          })
201
+        },
202
+        setSelectedDates: targetDates => {
203
+          const existCalendarData = getCalendarData('calendar', component)
204
+          let { dates, selectedDates = [] } = existCalendarData || {}
205
+          let __selectedDates = []
206
+          let __dates = dates
207
+          if (!targetDates) {
208
+            __dates = dates.map(item => {
209
+              const date = { ...item }
210
+              date.choosed = true
211
+              if (existCalendarData.showLabelAlways && date.showTodoLabel) {
212
+                date.showTodoLabel = true
213
+              } else {
214
+                date.showTodoLabel = false
215
+              }
216
+              return date
217
+            })
218
+            __selectedDates = dates
219
+          } else if (targetDates && targetDates.length) {
220
+            const allSelected = dateUtil.uniqueArrayByDate(
221
+              selectedDates.concat(targetDates)
222
+            )
223
+            const allSelectedDateStr = allSelected.map(d =>
224
+              dateUtil.toTimeStr(d)
225
+            )
226
+            __dates = dates.map(item => {
227
+              const date = { ...item }
228
+              if (allSelectedDateStr.includes(dateUtil.toTimeStr(date))) {
229
+                date.choosed = true
230
+                __selectedDates.push(date)
231
+              }
232
+              if (existCalendarData.showLabelAlways && date.showTodoLabel) {
233
+                date.showTodoLabel = true
234
+              } else {
235
+                date.showTodoLabel = false
236
+              }
237
+              return date
238
+            })
239
+          }
240
+          return renderCalendar.call(component, {
241
+            ...existCalendarData,
242
+            dates: __dates,
243
+            selectedDates: __selectedDates
244
+          })
245
+        },
246
+        setDateStyle: toSetDates => {
247
+          if (!Array.isArray(toSetDates)) return Promise.reject()
248
+          const existCalendarData = getCalendarData('calendar', component)
249
+          const { dates = [], specialStyleDates } = existCalendarData || {}
250
+          if (Array.isArray(specialStyleDates)) {
251
+            toSetDates = dateUtil.uniqueArrayByDate([
252
+              ...specialStyleDates,
253
+              ...toSetDates
254
+            ])
255
+          }
256
+          const toSetDatesStr = toSetDates.map(item => dateUtil.toTimeStr(item))
257
+          const _dates = dates.map(item => {
258
+            const idx = toSetDatesStr.indexOf(dateUtil.toTimeStr(item))
259
+            if (idx > -1) {
260
+              return {
261
+                ...item,
262
+                class: toSetDates[idx].class
263
+              }
264
+            } else {
265
+              return item
266
+            }
267
+          })
268
+          return renderCalendar.call(component, {
269
+            ...existCalendarData,
270
+            dates: _dates,
271
+            specialStyleDates: toSetDates
272
+          })
273
+        }
274
+      }
275
+    }
276
+  }
277
+}

+ 69 - 0
components/local/v2/plugins/preset/get-calendar-data.js

@@ -0,0 +1,69 @@
1
+/**
2
+ * @Author: drfu*
3
+ * @Description: 获取日历数据
4
+ * @Date: 2020-10-08 21:22:09*
5
+ * @Last Modified by: drfu
6
+ * @Last Modified time: 2020-10-11 13:42:37
7
+ * */
8
+
9
+import { getCalendarData, logger, getCalendarConfig } from '../../utils/index'
10
+
11
+function wrapDateWithLunar(dates = [], convertFn) {
12
+  const datesWithLunar = JSON.parse(JSON.stringify(dates)).map(date => ({
13
+    ...date,
14
+    lunar: convertFn(date)
15
+  }))
16
+  return datesWithLunar
17
+}
18
+
19
+export default () => {
20
+  return {
21
+    name: 'getData',
22
+    methods(component) {
23
+      return {
24
+        getCurrentYM: () => {
25
+          const { curYear, curMonth } = getCalendarData('calendar', component)
26
+          return {
27
+            year: curYear,
28
+            month: curMonth
29
+          }
30
+        },
31
+        getSelectedDates: (options = {}) => {
32
+          const dates =
33
+            getCalendarData('calendar.selectedDates', component) || []
34
+          const config = getCalendarConfig(component) || {}
35
+          if (options.lunar && !config.showLunar) {
36
+            const injectedFns = component.calendar || {}
37
+            if (typeof injectedFns.convertSolarLunar === 'function') {
38
+              return wrapDateWithLunar(dates, injectedFns.convertSolarLunar)
39
+            } else {
40
+              logger.warn('获取农历信息需引入农历插件')
41
+            }
42
+          } else {
43
+            return dates
44
+          }
45
+        },
46
+        getCalendarDates: (options = {}) => {
47
+          const config = getCalendarConfig(component) || {}
48
+          const dates = getCalendarData('calendar.dates', component)
49
+          if (options.lunar && !config.showLunar) {
50
+            const injectedFns = component.calendar || {}
51
+            if (typeof injectedFns.convertSolarLunar === 'function') {
52
+              return wrapDateWithLunar(dates, injectedFns.convertSolarLunar)
53
+            } else {
54
+              logger.warn('获取农历信息需引入农历插件')
55
+            }
56
+          } else {
57
+            return dates
58
+          }
59
+        },
60
+        getCalendarAllData: () => {
61
+          return {
62
+            data: getCalendarData('calendar', component) || {},
63
+            config: getCalendarConfig(component) || {}
64
+          }
65
+        }
66
+      }
67
+    }
68
+  }
69
+}

+ 9 - 0
components/local/v2/plugins/preset/index.js

@@ -0,0 +1,9 @@
1
+import base from './base'
2
+import getCalendarData from './get-calendar-data'
3
+
4
+const preset = [
5
+  ['base', base()],
6
+  ['get-calendar-data', getCalendarData()]
7
+]
8
+
9
+export default preset

+ 219 - 0
components/local/v2/plugins/selectable.js

@@ -0,0 +1,219 @@
1
+/**
2
+ * @Author: drfu*
3
+ * @Description: 禁用、启用日期选择
4
+ * @Date: 2020-10-08 21:22:09*
5
+ * @Last Modified by: drfu
6
+ * @Last Modified time: 2020-10-08 21:25:00
7
+ * */
8
+
9
+import { getCalendarData, dateUtil, logger } from '../utils/index'
10
+import { renderCalendar } from '../render'
11
+
12
+function convertEnableAreaToTimestamp(timearea = []) {
13
+  const start = timearea[0].split('-')
14
+  const end = timearea[1].split('-')
15
+  if (start.length !== 3 || end.length !== 3) {
16
+    logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]')
17
+    return {}
18
+  }
19
+  const startTimestamp = dateUtil
20
+    .newDate(start[0], start[1], start[2])
21
+    .getTime()
22
+  const endTimestamp = dateUtil.newDate(end[0], end[1], end[2]).getTime()
23
+  return {
24
+    start,
25
+    end,
26
+    startTimestamp,
27
+    endTimestamp
28
+  }
29
+}
30
+
31
+function isValiditeOfDateArea(dateArea) {
32
+  const {
33
+    start,
34
+    end,
35
+    startTimestamp,
36
+    endTimestamp
37
+  } = convertEnableAreaToTimestamp(dateArea)
38
+  if (!start || !end) return
39
+  const datesCountOfStart = dateUtil.getDatesCountOfMonth(start[0], start[1])
40
+  const datesCountOfEnd = dateUtil.getDatesCountOfMonth(end[0], end[1])
41
+  if (start[2] > datesCountOfStart || start[2] < 1) {
42
+    logger.warn('enableArea() 开始日期错误,指定日期不在指定月份天数范围内')
43
+    return false
44
+  } else if (start[1] > 12 || start[1] < 1) {
45
+    logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
46
+    return false
47
+  } else if (end[2] > datesCountOfEnd || end[2] < 1) {
48
+    logger.warn('enableArea() 截止日期错误,指定日期不在指定月份天数范围内')
49
+    return false
50
+  } else if (end[1] > 12 || end[1] < 1) {
51
+    logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
52
+    return false
53
+  } else if (startTimestamp > endTimestamp) {
54
+    logger.warn('enableArea()参数最小日期大于了最大日期')
55
+    return false
56
+  } else {
57
+    return true
58
+  }
59
+}
60
+
61
+function handleDisableMode(calendarConfig) {
62
+  const { disableMode } = calendarConfig
63
+  if (!disableMode) return {}
64
+  const disableBound =
65
+    dateUtil.getTimeStamp(disableMode.date) || dateUtil.todayTimestamp()
66
+  return {
67
+    disableBound,
68
+    disableType: disableMode.type
69
+  }
70
+}
71
+
72
+function disabledByConfig(dateInfo, currentDate, calendarConfig) {
73
+  const date = { ...dateInfo }
74
+  const { disableType, disableBound } = handleDisableMode(calendarConfig)
75
+  if (
76
+    (disableType === 'before' && disableBound && currentDate < disableBound) ||
77
+    (disableType === 'after' && disableBound && currentDate > disableBound)
78
+  ) {
79
+    date.disable = true
80
+  } else {
81
+    date.disable = false
82
+  }
83
+  return date
84
+}
85
+
86
+export default () => {
87
+  return {
88
+    name: 'enable',
89
+    beforeRender(calendarData = {}, calendarConfig = {}) {
90
+      const {
91
+        dates,
92
+        enableArea,
93
+        enableDates,
94
+        disableDates,
95
+        renderCausedBy
96
+      } = calendarData
97
+      const _dates = [...dates].map(date => {
98
+        let item = { ...date }
99
+        const timeStr = dateUtil.toTimeStr(date)
100
+        const timestamp = +dateUtil.getTimeStamp(item)
101
+        if (renderCausedBy === 'enableDates') {
102
+          if (enableDates && enableDates.length) {
103
+            if (enableDates.includes(timeStr)) {
104
+              item.disable = false
105
+            } else {
106
+              item.disable = true
107
+            }
108
+            return item
109
+          }
110
+        } else if (renderCausedBy === 'enableArea') {
111
+          if (enableArea && enableArea.length) {
112
+            const [startTimestamp, endTimestamp] = enableArea || []
113
+            const ifOutofArea =
114
+              +startTimestamp > timestamp || timestamp > +endTimestamp
115
+            item.disable = ifOutofArea
116
+            return item
117
+          }
118
+        } else if (renderCausedBy === 'disableDates') {
119
+          if (disableDates && disableDates.length) {
120
+            if (disableDates && disableDates.includes(timeStr)) {
121
+              item.disable = true
122
+            }
123
+            return item
124
+          }
125
+        }
126
+        return disabledByConfig(item, timestamp, calendarConfig)
127
+      })
128
+
129
+      return {
130
+        calendarData: {
131
+          ...calendarData,
132
+          dates: _dates
133
+        },
134
+        calendarConfig
135
+      }
136
+    },
137
+    methods(component) {
138
+      return {
139
+        enableArea: (dateArea = []) => {
140
+          if (dateArea.length === 2) {
141
+            const validate = isValiditeOfDateArea(dateArea)
142
+            if (validate) {
143
+              const existCalendarData = getCalendarData('calendar', component)
144
+              const {
145
+                startTimestamp,
146
+                endTimestamp
147
+              } = convertEnableAreaToTimestamp(dateArea)
148
+
149
+              return renderCalendar.call(component, {
150
+                ...existCalendarData,
151
+                renderCausedBy: 'enableArea',
152
+                enableArea: [startTimestamp, endTimestamp]
153
+              })
154
+            }
155
+          } else {
156
+            return Promise.inject(
157
+              'enableArea()参数需为时间范围数组,形如:["2018-8-4" , "2018-8-24"]'
158
+            )
159
+          }
160
+        },
161
+        enableDates: (toSet = []) => {
162
+          if (!toSet.length) return
163
+          const existCalendarData = getCalendarData('calendar', component)
164
+          const { enableDates = [] } = existCalendarData || {}
165
+          let toSetDates = toSet.map(item => {
166
+            if (typeof item === 'string') {
167
+              return dateUtil.transformDateRow2Dict(item)
168
+            }
169
+            return item
170
+          })
171
+          if (enableDates.length) {
172
+            toSetDates = dateUtil.uniqueArrayByDate([
173
+              ...toSetDates,
174
+              ...enableDates.map(d => dateUtil.transformDateRow2Dict(d))
175
+            ])
176
+          }
177
+          return renderCalendar.call(component, {
178
+            ...existCalendarData,
179
+            renderCausedBy: 'enableDates',
180
+            enableDates: toSetDates.map(date => {
181
+              if (typeof date !== 'string') {
182
+                return dateUtil.toTimeStr(date)
183
+              }
184
+              return date
185
+            })
186
+          })
187
+        },
188
+        disableDates: toSet => {
189
+          const existCalendarData = getCalendarData('calendar', component)
190
+          const { disableDates = [], dates = [] } = existCalendarData || {}
191
+          let toSetDates = toSet.map(item => {
192
+            let date = { ...item }
193
+            if (typeof date === 'string') {
194
+              return dateUtil.transformDateRow2Dict(item)
195
+            }
196
+            return item
197
+          })
198
+          if (disableDates && disableDates.length) {
199
+            toSetDates = dateUtil.uniqueArrayByDate([
200
+              ...toSetDates,
201
+              ...disableDates.map(d => dateUtil.transformDateRow2Dict(d))
202
+            ])
203
+          }
204
+          return renderCalendar.call(component, {
205
+            ...existCalendarData,
206
+            renderCausedBy: 'disableDates',
207
+            dates,
208
+            disableDates: toSetDates.map(date => {
209
+              if (typeof date !== 'string') {
210
+                return dateUtil.toTimeStr(date)
211
+              }
212
+              return date
213
+            })
214
+          })
215
+        }
216
+      }
217
+    }
218
+  }
219
+}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1036 - 0
components/local/v2/plugins/solarLunar/convertSolarLunar.js


+ 59 - 0
components/local/v2/plugins/solarLunar/index.js

@@ -0,0 +1,59 @@
1
+import { dateUtil } from '../../utils/index'
2
+import convertSolarLunar from './convertSolarLunar'
3
+
4
+function getDateRow2Dict(dateInfo) {
5
+  if (!dateInfo) return dateInfo
6
+  if (typeof dateInfo === 'string' && dateInfo.includes('-')) {
7
+    dateInfo = dateUtil.transformDateRow2Dict(dateInfo)
8
+  }
9
+  return dateInfo
10
+}
11
+
12
+export default () => {
13
+  return {
14
+    name: 'convertSolarLunar',
15
+    beforeRender(calendarData = {}, calendarConfig = {}) {
16
+      let { dates = [], selectedDates = [] } = calendarData
17
+      if (calendarConfig.showLunar) {
18
+        dates = dates.map(dataInfo => {
19
+          const { year, month, date } = dataInfo
20
+          return {
21
+            ...dataInfo,
22
+            lunar: convertSolarLunar.solar2lunar(year, month, date)
23
+          }
24
+        })
25
+        selectedDates = selectedDates.map(dataInfo => {
26
+          const { year, month, date } = dataInfo
27
+          return {
28
+            ...dataInfo,
29
+            lunar: convertSolarLunar.solar2lunar(year, month, date)
30
+          }
31
+        })
32
+      }
33
+      return {
34
+        calendarData: {
35
+          ...calendarData,
36
+          dates: dates,
37
+          selectedDates: selectedDates
38
+        },
39
+        calendarConfig
40
+      }
41
+    },
42
+    methods() {
43
+      return {
44
+        convertSolarLunar: dateInfo => {
45
+          dateInfo = getDateRow2Dict(dateInfo)
46
+          if (!dateInfo) return dateInfo
47
+          const { year, month, date } = dateInfo
48
+          return convertSolarLunar.solar2lunar(year, month, date)
49
+        },
50
+        convertlLunar2Solar: (dateInfo, isLeapMonth) => {
51
+          dateInfo = getDateRow2Dict(dateInfo)
52
+          if (!dateInfo) return dateInfo
53
+          const { year, month, date } = dateInfo
54
+          return convertSolarLunar.lunar2solar(year, month, date, isLeapMonth)
55
+        }
56
+      }
57
+    }
58
+  }
59
+}

+ 305 - 0
components/local/v2/plugins/time-range.js

@@ -0,0 +1,305 @@
1
+/**
2
+ * @Author: drfu*
3
+ * @Description: 时间区域选择
4
+ * @Date: 2020-10-08 21:22:09*
5
+ * @Last Modified by: drfu
6
+ * @Last Modified time: 2020-10-11 13:56:32
7
+ * */
8
+
9
+import { renderCalendar } from '../render'
10
+import {
11
+  logger,
12
+  dateUtil,
13
+  getCalendarConfig,
14
+  getCalendarData
15
+} from '../utils/index'
16
+
17
+function pusheNextMonthDateArea(
18
+  dateInfo = {},
19
+  startTimestamp,
20
+  endTimestamp,
21
+  selectedDates = []
22
+) {
23
+  let tempOfSelectedDate = [...selectedDates]
24
+  const dates = dateUtil.calcDates(dateInfo.year, dateInfo.month)
25
+  let datesLen = dates.length
26
+  for (let i = 0; i < datesLen; i++) {
27
+    const date = dates[i]
28
+    const timeStamp = dateUtil.getTimeStamp(date)
29
+    if (timeStamp <= endTimestamp && timeStamp >= startTimestamp) {
30
+      tempOfSelectedDate.push({
31
+        ...date,
32
+        choosed: true
33
+      })
34
+    }
35
+    if (i === datesLen - 1 && timeStamp < endTimestamp) {
36
+      pusheNextMonthDateArea(
37
+        dateUtil.getNextMonthInfo(date),
38
+        startTimestamp,
39
+        endTimestamp,
40
+        tempOfSelectedDate
41
+      )
42
+    }
43
+  }
44
+  return tempOfSelectedDate
45
+}
46
+function pushPrevMonthDateArea(
47
+  dateInfo = {},
48
+  startTimestamp,
49
+  endTimestamp,
50
+  selectedDates = []
51
+) {
52
+  let tempOfSelectedDate = [...selectedDates]
53
+  const dates = dateUtil.sortDatesByTime(
54
+    dateUtil.calcDates(dateInfo.year, dateInfo.month),
55
+    'desc'
56
+  )
57
+  let datesLen = dates.length
58
+  let firstDate = dateUtil.getTimeStamp(dates[0])
59
+  for (let i = 0; i < datesLen; i++) {
60
+    const date = dates[i]
61
+    const timeStamp = dateUtil.getTimeStamp(date)
62
+    if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
63
+      tempOfSelectedDate.push({
64
+        ...date,
65
+        choosed: true
66
+      })
67
+    }
68
+    if (i === datesLen - 1 && firstDate > startTimestamp) {
69
+      pushPrevMonthDateArea(
70
+        dateUtil.getPrevMonthInfo(date),
71
+        startTimestamp,
72
+        endTimestamp,
73
+        tempOfSelectedDate
74
+      )
75
+    }
76
+  }
77
+  return tempOfSelectedDate
78
+}
79
+/**
80
+ * 当设置日期区域非当前时保存其他月份的日期至已选日期数组
81
+ * @param {object} info
82
+ */
83
+function calcDateWhenNotInOneMonth(info) {
84
+  const { firstDate, lastDate, startTimestamp, endTimestamp } = info
85
+  let { selectedDate } = info
86
+  if (dateUtil.getTimeStamp(firstDate) > startTimestamp) {
87
+    selectedDate = pushPrevMonthDateArea(
88
+      dateUtil.getPrevMonthInfo(firstDate),
89
+      startTimestamp,
90
+      endTimestamp,
91
+      selectedDate
92
+    )
93
+  }
94
+  if (dateUtil.getTimeStamp(lastDate) < endTimestamp) {
95
+    selectedDate = pusheNextMonthDateArea(
96
+      dateUtil.getNextMonthInfo(lastDate),
97
+      startTimestamp,
98
+      endTimestamp,
99
+      selectedDate
100
+    )
101
+  }
102
+  return [...selectedDate]
103
+}
104
+
105
+/**
106
+ *  指定日期区域转时间戳
107
+ * @param {array} timearea 时间区域
108
+ */
109
+export function convertTimeRangeToTimestamp(timearea = []) {
110
+  const start = timearea[0].split('-')
111
+  const end = timearea[1].split('-')
112
+  if (start.length !== 3 || end.length !== 3) {
113
+    logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]')
114
+    return {}
115
+  }
116
+  const startTimestamp = dateUtil
117
+    .newDate(start[0], start[1], start[2])
118
+    .getTime()
119
+  const endTimestamp = dateUtil.newDate(end[0], end[1], end[2]).getTime()
120
+  return {
121
+    start,
122
+    end,
123
+    startTimestamp,
124
+    endTimestamp
125
+  }
126
+}
127
+
128
+/**
129
+ * 校验时间区域是否合法
130
+ * @param {array} dateArea 时间区域
131
+ */
132
+function validateTimeRange(dateArea) {
133
+  const {
134
+    start,
135
+    end,
136
+    startTimestamp,
137
+    endTimestamp
138
+  } = convertTimeRangeToTimestamp(dateArea)
139
+  if (!start || !end) return
140
+  const startMonthDays = dateUtil.getDatesCountOfMonth(start[0], start[1])
141
+  const endMonthDays = dateUtil.getDatesCountOfMonth(end[0], end[1])
142
+  if (start[2] > startMonthDays || start[2] < 1) {
143
+    logger.warn('enableArea() 开始日期错误,指定日期不在当前月份天数范围内')
144
+    return false
145
+  } else if (start[1] > 12 || start[1] < 1) {
146
+    logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
147
+    return false
148
+  } else if (end[2] > endMonthDays || end[2] < 1) {
149
+    logger.warn('enableArea() 截止日期错误,指定日期不在当前月份天数范围内')
150
+    return false
151
+  } else if (end[1] > 12 || end[1] < 1) {
152
+    logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
153
+    return false
154
+  } else if (startTimestamp > endTimestamp) {
155
+    logger.warn('enableArea()参数最小日期大于了最大日期')
156
+    return false
157
+  } else {
158
+    return true
159
+  }
160
+}
161
+
162
+export default () => {
163
+  return {
164
+    name: 'timeRange',
165
+    beforeRender(calendarData = {}, calendarConfig = {}) {
166
+      const {
167
+        chooseAreaTimestamp = [],
168
+        dates = [],
169
+        selectedDates = []
170
+      } = calendarData
171
+      let __dates = dates
172
+      let __selectedDates = selectedDates
173
+      const [startDateTimestamp, endDateTimestamp] = chooseAreaTimestamp
174
+      if (chooseAreaTimestamp.length === 2) {
175
+        __selectedDates = []
176
+        __dates = dates.map(d => {
177
+          const date = { ...d }
178
+          const dateTimeStamp = dateUtil.getTimeStamp(date)
179
+          if (
180
+            dateTimeStamp >= startDateTimestamp &&
181
+            endDateTimestamp >= dateTimeStamp
182
+          ) {
183
+            date.choosed = true
184
+            __selectedDates.push(date)
185
+          } else {
186
+            date.choosed = false
187
+            __selectedDates = __selectedDates.filter(
188
+              item => dateUtil.getTimeStamp(item) !== dateTimeStamp
189
+            )
190
+          }
191
+          return date
192
+        })
193
+        const monthOfStartDate = new Date(startDateTimestamp).getMonth()
194
+        const monthOfEndDate = new Date(endDateTimestamp).getMonth()
195
+        if (monthOfStartDate !== monthOfEndDate) {
196
+          __selectedDates = calcDateWhenNotInOneMonth({
197
+            firstDate: __dates[0],
198
+            lastDate: __dates[__dates.length - 1],
199
+            startTimestamp: startDateTimestamp,
200
+            endTimestamp: endDateTimestamp,
201
+            selectedDate: __selectedDates
202
+          })
203
+        }
204
+      }
205
+      return {
206
+        calendarData: {
207
+          ...calendarData,
208
+          dates: __dates,
209
+          selectedDates: dateUtil.sortDatesByTime(
210
+            dateUtil.uniqueArrayByDate(__selectedDates)
211
+          )
212
+        },
213
+        calendarConfig
214
+      }
215
+    },
216
+    onTapDate(tapedDate, calendarData = {}, calendarConfig = {}) {
217
+      if (!calendarConfig.chooseAreaMode) {
218
+        return {
219
+          calendarData,
220
+          calendarConfig
221
+        }
222
+      }
223
+      let {
224
+        tempChooseAreaTimestamp = [],
225
+        chooseAreaTimestamp: existChooseAreaTimestamp = [],
226
+        selectedDates = [],
227
+        dates = []
228
+      } = calendarData
229
+      const timestamp = dateUtil.getTimeStamp(tapedDate)
230
+      let __dates = [...dates]
231
+      let __selectedDates = [...selectedDates]
232
+      if (
233
+        tempChooseAreaTimestamp.length === 2 ||
234
+        existChooseAreaTimestamp.length === 2
235
+      ) {
236
+        tempChooseAreaTimestamp = [tapedDate]
237
+        __selectedDates = []
238
+        __dates.forEach(d => (d.choosed = false))
239
+      } else if (tempChooseAreaTimestamp.length === 1) {
240
+        const preChoosedDate = tempChooseAreaTimestamp[0]
241
+        const preTimestamp = dateUtil.getTimeStamp(preChoosedDate)
242
+        if (preTimestamp <= timestamp) {
243
+          tempChooseAreaTimestamp.push(tapedDate)
244
+        } else if (preTimestamp > timestamp) {
245
+          tempChooseAreaTimestamp.unshift(tapedDate)
246
+        }
247
+      } else {
248
+        tempChooseAreaTimestamp = [tapedDate]
249
+      }
250
+      let chooseAreaTimestamp = []
251
+      if (tempChooseAreaTimestamp.length === 2) {
252
+        const [startDate, endDate] = tempChooseAreaTimestamp
253
+        const startDateTimestamp = dateUtil.getTimeStamp(startDate)
254
+        const endDateTimestamp = dateUtil.getTimeStamp(endDate)
255
+        chooseAreaTimestamp = [startDateTimestamp, endDateTimestamp]
256
+      }
257
+      return {
258
+        calendarData: {
259
+          ...calendarData,
260
+          chooseAreaTimestamp,
261
+          tempChooseAreaTimestamp,
262
+          dates: __dates,
263
+          selectedDates: __selectedDates
264
+        },
265
+        calendarConfig: {
266
+          ...calendarConfig,
267
+          multi: true
268
+        }
269
+      }
270
+    },
271
+    methods(component) {
272
+      return {
273
+        /**
274
+         * 设置连续日期选择区域
275
+         * @param {array} dateArea 区域开始结束日期数组
276
+         */
277
+        chooseDateArea: (dateArea = []) => {
278
+          if (dateArea.length === 1) {
279
+            dateArea = dateArea.concat(dateArea)
280
+          }
281
+          if (dateArea.length !== 2) return
282
+          const isRight = validateTimeRange(dateArea)
283
+          if (!isRight) return
284
+          const config = getCalendarConfig(component) || {}
285
+          const { startTimestamp, endTimestamp } = convertTimeRangeToTimestamp(
286
+            dateArea
287
+          )
288
+          const existCalendarData = getCalendarData('calendar', component)
289
+          return renderCalendar.call(
290
+            component,
291
+            {
292
+              ...existCalendarData,
293
+              chooseAreaTimestamp: [startTimestamp, endTimestamp]
294
+            },
295
+            {
296
+              ...config,
297
+              multi: true,
298
+              chooseAreaMode: true
299
+            }
300
+          )
301
+        }
302
+      }
303
+    }
304
+  }
305
+}

+ 135 - 0
components/local/v2/plugins/todo.js

@@ -0,0 +1,135 @@
1
+/**
2
+ * @Author: drfu*
3
+ * @Description: 代办事项
4
+ * @Date: 2020-10-08 21:22:09*
5
+ * @Last Modified by: drfu
6
+ * @Last Modified time: 2020-10-11 14:23:02
7
+ * */
8
+
9
+import { getCalendarData, dateUtil } from '../utils/index'
10
+import { renderCalendar } from '../render'
11
+
12
+function updateDatePropertyOfTodoLabel(todos, dates, showLabelAlways) {
13
+  const datesInfo = [...dates]
14
+  for (let todo of todos) {
15
+    let targetIdx = datesInfo.findIndex(
16
+      item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(todo)
17
+    )
18
+    let target = datesInfo[targetIdx]
19
+    if (!target) continue
20
+    if (showLabelAlways) {
21
+      target.showTodoLabel = true
22
+    } else {
23
+      target.showTodoLabel = !target.choosed
24
+    }
25
+    if (target.showTodoLabel) {
26
+      target.todoText = todo.todoText
27
+    }
28
+    target.color = todo.color
29
+  }
30
+  return datesInfo
31
+}
32
+
33
+export default () => {
34
+  return {
35
+    name: 'todo',
36
+    beforeRender(calendarData = {}, calendarConfig = {}, component) {
37
+      const { todos = [], dates = [], showLabelAlways } = calendarData
38
+      const dateWithTodoInfo = updateDatePropertyOfTodoLabel(
39
+        todos,
40
+        dates,
41
+        showLabelAlways
42
+      )
43
+      return {
44
+        calendarData: {
45
+          ...calendarData,
46
+          dates: dateWithTodoInfo
47
+        },
48
+        calendarConfig
49
+      }
50
+    },
51
+    methods(component) {
52
+      return {
53
+        setTodos: (options = {}) => {
54
+          const calendar = getCalendarData('calendar', component)
55
+          if (!calendar || !calendar.dates) {
56
+            return Promise.reject('请等待日历初始化完成后再调用该方法')
57
+          }
58
+          const {
59
+            circle,
60
+            dotColor = '',
61
+            pos = 'bottom',
62
+            showLabelAlways,
63
+            dates: todoDates = []
64
+          } = options
65
+          const { todos = [] } = calendar
66
+          const tranformStr2NumOfTodo = todoDates.map(date =>
67
+            dateUtil.tranformStr2NumOfDate(date)
68
+          )
69
+          const calendarData = {
70
+            dates: calendar.dates,
71
+            todos: dateUtil.uniqueArrayByDate(
72
+              todos.concat(tranformStr2NumOfTodo)
73
+            )
74
+          }
75
+          if (!circle) {
76
+            calendarData.todoLabelPos = pos
77
+            calendarData.todoLabelColor = dotColor
78
+          }
79
+          calendarData.todoLabelCircle = circle || false
80
+          calendarData.showLabelAlways = showLabelAlways || false
81
+          const existCalendarData = getCalendarData('calendar', component)
82
+          return renderCalendar.call(component, {
83
+            ...existCalendarData,
84
+            ...calendarData
85
+          })
86
+        },
87
+        deleteTodos(todos = []) {
88
+          if (!(todos instanceof Array) || !todos.length)
89
+            return Promise.reject('deleteTodos()应为入参为非空数组')
90
+          const existCalendarData = getCalendarData('calendar', component)
91
+          const allTodos = existCalendarData.todos || []
92
+          const toDeleteTodos = todos.map(item => dateUtil.toTimeStr(item))
93
+          const remainTodos = allTodos.filter(
94
+            item => !toDeleteTodos.includes(dateUtil.toTimeStr(item))
95
+          )
96
+          const { dates, curYear, curMonth } = existCalendarData
97
+          const _dates = [...dates]
98
+          const currentMonthTodos = dateUtil.filterDatesByYM(
99
+            {
100
+              year: curYear,
101
+              month: curMonth
102
+            },
103
+            remainTodos
104
+          )
105
+          _dates.forEach(item => {
106
+            item.showTodoLabel = false
107
+          })
108
+          currentMonthTodos.forEach(item => {
109
+            _dates[item.date - 1].showTodoLabel = !_dates[item.date - 1].choosed
110
+          })
111
+          return renderCalendar.call(component, {
112
+            ...existCalendarData,
113
+            dates: _dates,
114
+            todos: remainTodos
115
+          })
116
+        },
117
+        clearTodos() {
118
+          const existCalendarData = getCalendarData('calendar', component)
119
+          const _dates = [...existCalendarData.dates]
120
+          _dates.forEach(item => {
121
+            item.showTodoLabel = false
122
+          })
123
+          return renderCalendar.call(component, {
124
+            ...existCalendarData,
125
+            dates: _dates,
126
+            todos: []
127
+          })
128
+        },
129
+        getTodos() {
130
+          return getCalendarData('calendar.todos', component) || []
131
+        }
132
+      }
133
+    }
134
+  }
135
+}

+ 432 - 0
components/local/v2/plugins/week.js

@@ -0,0 +1,432 @@
1
+/**
2
+ * @Author: drfu*
3
+ * @Description: 周视图
4
+ * @Date: 2020-10-08 21:22:09*
5
+ * @Last Modified by: drfu
6
+ * @Last Modified time: 2020-10-12 14:39:45
7
+ * */
8
+
9
+import { renderCalendar } from '../render'
10
+import {
11
+  getCalendarConfig,
12
+  getCalendarData,
13
+  logger,
14
+  dateUtil
15
+} from '../utils/index'
16
+import { calcJumpData } from '../core'
17
+
18
+/**
19
+ * 当月第一周所有日期
20
+ */
21
+function firstWeekInMonth(
22
+  target = {},
23
+  calendarDates = [],
24
+  calendarConfig = {}
25
+) {
26
+  const { firstDayOfWeek } = calendarConfig
27
+  const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon'
28
+  const { year, month } = target
29
+  let firstDay = dateUtil.getDayOfWeek(year, month, 1)
30
+  if (firstDayOfWeekIsMon && firstDay === 0) {
31
+    firstDay = 7
32
+  }
33
+  const [, end] = [0, 7 - firstDay]
34
+  return calendarDates.slice(0, firstDayOfWeekIsMon ? end + 1 : end)
35
+}
36
+
37
+/**
38
+ * 当月最后一周所有日期
39
+ */
40
+function lastWeekInMonth(target = {}, calendarDates = [], calendarConfig = {}) {
41
+  const { firstDayOfWeek } = calendarConfig
42
+  const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon'
43
+  const { year, month } = target
44
+  const lastDay = dateUtil.getDatesCountOfMonth(year, month)
45
+  let lastDayWeek = dateUtil.getDayOfWeek(year, month, lastDay)
46
+  if (firstDayOfWeekIsMon && lastDayWeek === 0) {
47
+    lastDayWeek = 7
48
+  }
49
+  const [start, end] = [lastDay - lastDayWeek, lastDay]
50
+  return calendarDates.slice(firstDayOfWeekIsMon ? start : start - 1, end)
51
+}
52
+
53
+/**
54
+ * 判断目标日期是否在某些指定日历内
55
+ */
56
+function dateIsInDatesRange(target, dates) {
57
+  if (!target || !dates || !dates.length) return false
58
+  const targetDateStr = dateUtil.toTimeStr(target)
59
+  let rst = false
60
+  for (let date of dates) {
61
+    const dateStr = dateUtil.toTimeStr(date)
62
+    if (dateStr === targetDateStr) {
63
+      rst = true
64
+      return rst
65
+    }
66
+    rst = false
67
+  }
68
+  return rst
69
+}
70
+
71
+function getDatesWhenTargetInFirstWeek(target, firstWeekDates) {
72
+  const { year, month } = target
73
+  const prevMonthInfo = dateUtil.getPrevMonthInfo({ year, month })
74
+  let lastMonthDatesCount = dateUtil.getDatesCountOfMonth(
75
+    prevMonthInfo.year,
76
+    prevMonthInfo.month
77
+  )
78
+  let dates = firstWeekDates
79
+  let firstWeekCount = firstWeekDates.length
80
+  for (let i = 0; i < 7 - firstWeekCount; i++) {
81
+    const week = dateUtil.getDayOfWeek(+year, +month, lastMonthDatesCount)
82
+    dates.unshift({
83
+      year: prevMonthInfo.year,
84
+      month: prevMonthInfo.month,
85
+      date: lastMonthDatesCount,
86
+      week
87
+    })
88
+    lastMonthDatesCount -= 1
89
+  }
90
+  return dates
91
+}
92
+
93
+function getDatesWhenTargetInLastWeek(target, lastWeekDates) {
94
+  const { year, month } = target
95
+  const prevMonthInfo = dateUtil.getNextMonthInfo({ year, month })
96
+  let dates = lastWeekDates
97
+  let lastWeekCount = lastWeekDates.length
98
+  for (let i = 0; i < 7 - lastWeekCount; i++) {
99
+    const week = dateUtil.getDayOfWeek(+year, +month, i + 1)
100
+    dates.push({
101
+      year: prevMonthInfo.year,
102
+      month: prevMonthInfo.month,
103
+      date: i + 1,
104
+      week
105
+    })
106
+  }
107
+  return dates
108
+}
109
+
110
+function getDates(target, calendarDates = [], calendarConfig = {}) {
111
+  const { year, month, date } = target
112
+  const targetDay = dateUtil.getDayOfWeek(year, month, date)
113
+  const { firstDayOfWeek } = calendarConfig
114
+  const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon'
115
+  if (firstDayOfWeekIsMon) {
116
+    const startIdx = date - (targetDay || 7)
117
+    return calendarDates.splice(startIdx, 7)
118
+  } else {
119
+    const startIdx = date - targetDay - 1
120
+    return calendarDates.splice(startIdx, 7)
121
+  }
122
+}
123
+
124
+function getTargetWeekDates(target, calendarConfig) {
125
+  if (!target) return
126
+  const { year, month } = target
127
+  const calendarDates = dateUtil.calcDates(year, month)
128
+  const firstWeekDates = firstWeekInMonth(target, calendarDates, calendarConfig)
129
+  const lastWeekDates = lastWeekInMonth(target, calendarDates, calendarConfig)
130
+  if (dateIsInDatesRange(target, firstWeekDates)) {
131
+    return getDatesWhenTargetInFirstWeek(target, firstWeekDates)
132
+  } else if (dateIsInDatesRange(target, lastWeekDates)) {
133
+    return getDatesWhenTargetInLastWeek(target, lastWeekDates)
134
+  } else {
135
+    return getDates(target, calendarDates, calendarConfig)
136
+  }
137
+}
138
+
139
+/**
140
+ * 计算周视图下当前这一周最后一天
141
+ */
142
+function calculateLastDateOfCurrentWeek(calendarData = {}) {
143
+  const { dates = [] } = calendarData
144
+  return dates[dates.length - 1]
145
+}
146
+/**
147
+ * 计算周视图下当前这一周第一天
148
+ */
149
+function calculateFirstDateOfCurrentWeek(calendarData = {}) {
150
+  const { dates } = calendarData
151
+  return dates[0]
152
+}
153
+
154
+/**
155
+ * 计算下一周的日期
156
+ */
157
+function calculateNextWeekDates(calendarData = {}) {
158
+  let { curYear, curMonth } = calendarData
159
+  let calendarDates = []
160
+  let lastDateInThisWeek = calculateLastDateOfCurrentWeek(calendarData)
161
+  const { year: LYear, month: LMonth } = lastDateInThisWeek
162
+  if (curYear !== LYear || curMonth !== LMonth) {
163
+    calendarDates = dateUtil.calcDates(LYear, LMonth)
164
+    curYear = LYear
165
+    curMonth = LMonth
166
+  } else {
167
+    calendarDates = dateUtil.calcDates(curYear, curMonth)
168
+  }
169
+  const lastDateInThisMonth = dateUtil.getDatesCountOfMonth(curYear, curMonth)
170
+  const count = lastDateInThisMonth - lastDateInThisWeek.date
171
+  const lastDateIdx = calendarDates.findIndex(
172
+    date => dateUtil.toTimeStr(date) === dateUtil.toTimeStr(lastDateInThisWeek)
173
+  )
174
+  const startIdx = lastDateIdx + 1
175
+  if (count >= 7) {
176
+    return {
177
+      dates: calendarDates.splice(startIdx, 7),
178
+      year: curYear,
179
+      month: curMonth
180
+    }
181
+  } else {
182
+    const nextMonth = dateUtil.getNextMonthInfo({
183
+      year: curYear,
184
+      month: curMonth
185
+    })
186
+    const { year, month } = nextMonth || {}
187
+    const calendarDatesOfNextMonth = dateUtil.calcDates(year, month)
188
+    const remainDatesOfThisMonth = calendarDates.splice(startIdx)
189
+    const patchDatesOfNextMonth = calendarDatesOfNextMonth.splice(
190
+      0,
191
+      7 - remainDatesOfThisMonth.length
192
+    )
193
+    return {
194
+      dates: [...remainDatesOfThisMonth, ...patchDatesOfNextMonth],
195
+      ...nextMonth
196
+    }
197
+  }
198
+}
199
+
200
+/**
201
+ * 计算上一周的日期
202
+ */
203
+function calculatePrevWeekDates(calendarData = {}) {
204
+  let { curYear, curMonth } = calendarData
205
+  let firstDateInThisWeek = calculateFirstDateOfCurrentWeek(calendarData)
206
+  let calendarDates = []
207
+  const { year: FYear, month: FMonth } = firstDateInThisWeek
208
+  if (curYear !== FYear || curMonth !== FMonth) {
209
+    calendarDates = dateUtil.calcDates(FYear, FMonth)
210
+    curYear = FYear
211
+    curMonth = FMonth
212
+  } else {
213
+    calendarDates = dateUtil.calcDates(curYear, curMonth)
214
+  }
215
+  const firstDateIdx = calendarDates.findIndex(
216
+    date => dateUtil.toTimeStr(date) === dateUtil.toTimeStr(firstDateInThisWeek)
217
+  )
218
+  if (firstDateIdx - 7 >= 0) {
219
+    const startIdx = firstDateIdx - 7
220
+    return {
221
+      dates: calendarDates.splice(startIdx, 7),
222
+      year: curYear,
223
+      month: curMonth
224
+    }
225
+  } else {
226
+    const prevMonth = dateUtil.getPrevMonthInfo({
227
+      year: curYear,
228
+      month: curMonth
229
+    })
230
+    const { year, month } = prevMonth || {}
231
+    const calendarDatesOfPrevMonth = dateUtil.calcDates(year, month)
232
+    const remainDatesOfThisMonth = calendarDates.splice(
233
+      0,
234
+      firstDateInThisWeek.date - 1
235
+    )
236
+    const patchDatesOfPrevMonth = calendarDatesOfPrevMonth.splice(
237
+      -(7 - remainDatesOfThisMonth.length)
238
+    )
239
+    return {
240
+      dates: [...patchDatesOfPrevMonth, ...remainDatesOfThisMonth],
241
+      ...prevMonth
242
+    }
243
+  }
244
+}
245
+
246
+export default () => {
247
+  return {
248
+    name: 'week',
249
+    beforeRender(calendarData = {}, calendarConfig = {}, component) {
250
+      const { initializedWeekMode, selectedDates } = calendarData
251
+      if (calendarConfig.weekMode && !initializedWeekMode) {
252
+        const { defaultDate } = calendarConfig
253
+        const target =
254
+          (selectedDates && selectedDates[0]) ||
255
+          (defaultDate && dateUtil.transformDateRow2Dict(defaultDate)) ||
256
+          dateUtil.todayFMD()
257
+        const waitRenderData = this.methods(
258
+          component
259
+        ).__calcDatesWhenSwitchView('week', target)
260
+        const { data, config } = waitRenderData || {}
261
+        const setSelectDates = this.methods(
262
+          component
263
+        ).__selectTargetDateWhenJump(target, data.dates, config)
264
+        return {
265
+          calendarData: {
266
+            ...data,
267
+            ...setSelectDates,
268
+            weeksCh: dateUtil.getWeekHeader(calendarConfig.firstDayOfWeek),
269
+            initializedWeekMode: true
270
+          },
271
+          calendarConfig
272
+        }
273
+      }
274
+      return {
275
+        calendarData,
276
+        calendarConfig
277
+      }
278
+    },
279
+    onSwitchCalendar(target = {}, calendarData = {}, component) {
280
+      const { direction } = target
281
+      const { curYear, curMonth } = calendarData
282
+      const calendarConfig = getCalendarConfig(component)
283
+      let waitRenderData = {}
284
+      if (calendarConfig.weekMode) {
285
+        if (direction === 'left') {
286
+          waitRenderData = calculateNextWeekDates(calendarData)
287
+        } else {
288
+          waitRenderData = calculatePrevWeekDates(calendarData)
289
+        }
290
+        const { dates, year, month } = waitRenderData
291
+        return {
292
+          ...calendarData,
293
+          dates,
294
+          curYear: year || curYear,
295
+          curMonth: month || curMonth
296
+        }
297
+      }
298
+      return calendarData
299
+    },
300
+    methods(component) {
301
+      return {
302
+        __selectTargetDateWhenJump: (target = {}, dates = [], config = {}) => {
303
+          let selectedDate = target
304
+          const weekDates = dates.map((date, idx) => {
305
+            const tmp = { ...date }
306
+            tmp.id = idx
307
+            const isTarget =
308
+              dateUtil.toTimeStr(target) === dateUtil.toTimeStr(tmp)
309
+            if (isTarget && !target.choosed && config.autoChoosedWhenJump) {
310
+              tmp.choosed = true
311
+              selectedDate = tmp
312
+            }
313
+            return tmp
314
+          })
315
+          return {
316
+            dates: weekDates,
317
+            selectedDates: [selectedDate]
318
+          }
319
+        },
320
+        __calcDatesForWeekMode(target, config = {}, calendarData = {}) {
321
+          const { year, month } = target || {}
322
+          const weekDates = getTargetWeekDates(target, config)
323
+          weekDates.forEach((date, idx) => (date.id = idx))
324
+          return {
325
+            data: {
326
+              ...calendarData,
327
+              prevMonthGrids: null,
328
+              nextMonthGrids: null,
329
+              dates: weekDates,
330
+              curYear: year,
331
+              curMonth: month
332
+            },
333
+            config: {
334
+              ...config,
335
+              weekMode: true
336
+            }
337
+          }
338
+        },
339
+        __calcDatesForMonthMode(target, config = {}, calendarData = {}) {
340
+          const { year, month } = target || {}
341
+          const waitRenderData = calcJumpData({
342
+            dateInfo: target,
343
+            config
344
+          })
345
+          return {
346
+            data: {
347
+              ...calendarData,
348
+              ...waitRenderData,
349
+              curYear: year,
350
+              curMonth: month
351
+            },
352
+            config: {
353
+              ...config,
354
+              weekMode: false
355
+            }
356
+          }
357
+        },
358
+        /**
359
+         * 周、月视图切换
360
+         * @param {string} view  视图 [week, month]
361
+         * @param {object} target
362
+         */
363
+        __calcDatesWhenSwitchView: (view, target) => {
364
+          const calendarConfig = getCalendarConfig(component)
365
+          if (calendarConfig.multi)
366
+            return logger.warn('多选模式不能切换周月视图')
367
+          const existCalendarData = getCalendarData('calendar', component) || {}
368
+          const {
369
+            selectedDates = [],
370
+            dates = [],
371
+            curYear,
372
+            curMonth
373
+          } = existCalendarData
374
+          const currentMonthSelected = selectedDates.filter(
375
+            item => curYear === +item.year || curMonth === +item.month
376
+          )
377
+          let jumpTarget = {}
378
+          if (target) {
379
+            jumpTarget = target
380
+          } else {
381
+            if (currentMonthSelected.length) {
382
+              jumpTarget = currentMonthSelected.pop()
383
+            } else {
384
+              jumpTarget = dates[0]
385
+            }
386
+          }
387
+          if (view === 'week') {
388
+            return this.methods(component).__calcDatesForWeekMode(
389
+              jumpTarget,
390
+              calendarConfig,
391
+              existCalendarData
392
+            )
393
+          } else {
394
+            return this.methods(component).__calcDatesForMonthMode(
395
+              jumpTarget,
396
+              calendarConfig,
397
+              existCalendarData
398
+            )
399
+          }
400
+        },
401
+        weekModeJump: dateInfo => {
402
+          const target = dateInfo || dateUtil.todayFMD()
403
+          const existCalendarData = getCalendarData('calendar', component) || {}
404
+          const waitRenderData = this.methods(
405
+            component
406
+          ).__calcDatesWhenSwitchView('week', target)
407
+          const { data, config } = waitRenderData || {}
408
+          const setSelectDates = this.methods(
409
+            component
410
+          ).__selectTargetDateWhenJump(target, data.dates, config)
411
+          return renderCalendar.call(
412
+            component,
413
+            {
414
+              ...existCalendarData,
415
+              ...data,
416
+              ...setSelectDates
417
+            },
418
+            config
419
+          )
420
+        },
421
+        switchView: (view, target) => {
422
+          const waitRenderData = this.methods(
423
+            component
424
+          ).__calcDatesWhenSwitchView(view, target)
425
+          const { data, config } = waitRenderData || {}
426
+          if (!data) return logger.warn('当前状态不能切换为周视图')
427
+          return renderCalendar.call(component, data, config)
428
+        }
429
+      }
430
+    }
431
+  }
432
+}

+ 51 - 0
components/local/v2/render.js

@@ -0,0 +1,51 @@
1
+import plugins from './plugins/index'
2
+import { getCalendarConfig } from './utils/index'
3
+
4
+/**
5
+ * 渲染日历
6
+ */
7
+export function renderCalendar(calendarData, calendarConfig) {
8
+  return new Promise(resolve => {
9
+    const Component = this
10
+    if (Component.firstRender === void 0) {
11
+      Component.firstRender = true
12
+    } else {
13
+      Component.firstRender = false
14
+    }
15
+    const exitData = Component.data.calendar || {}
16
+    for (let plugin of plugins.installed) {
17
+      const [, p] = plugin
18
+      if (typeof p.beforeRender === 'function') {
19
+        const {
20
+          calendarData: newData,
21
+          calendarConfig: newConfig
22
+        } = p.beforeRender(
23
+          { ...exitData, ...calendarData },
24
+          calendarConfig || getCalendarConfig(Component),
25
+          Component
26
+        )
27
+        calendarData = newData
28
+        calendarConfig = newConfig
29
+      }
30
+    }
31
+
32
+    Component.setData(
33
+      {
34
+        config: calendarConfig,
35
+        calendar: calendarData
36
+      },
37
+      () => {
38
+        const rst = {
39
+          calendar: calendarData,
40
+          config: calendarConfig,
41
+          firstRender: Component.firstRender
42
+        }
43
+        resolve(rst)
44
+        if (Component.firstRender) {
45
+          Component.triggerEvent('afterCalendarRender', rst)
46
+          Component.firstRender = false
47
+        }
48
+      }
49
+    )
50
+  })
51
+}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 29 - 0
components/local/v2/theme/iconfont.wxss


+ 61 - 0
components/local/v2/theme/theme-default.wxss

@@ -0,0 +1,61 @@
1
+
2
+/* 日历主要颜色相关样式 */
3
+
4
+.default_color,
5
+.default_weekend-color,
6
+.default_handle-color,
7
+.default_week-color {
8
+    color: #ff629a;
9
+}
10
+
11
+.default_today {
12
+    color: #fff;
13
+    background-color: #874fb4;
14
+}
15
+
16
+.default_choosed {
17
+    color: #fff;
18
+    background-color: #ff629a;
19
+}
20
+
21
+.default_date-disable {
22
+    color: #c7c7c7;
23
+}
24
+
25
+.default_choosed.default_date-disable {
26
+    color: #e2e2e2;
27
+    background-color: #c2afb6;
28
+}
29
+
30
+.default_prev-month-date,
31
+.default_next-month-date {
32
+    color: #e2e2e2;
33
+}
34
+
35
+.default_normal-date {
36
+    color: #88d2ac;
37
+}
38
+
39
+.default_todo-circle {
40
+    border-color: #88d2ac;
41
+}
42
+
43
+.default_todo-dot {
44
+    background-color: #e54d42;
45
+}
46
+
47
+.default_date-desc {
48
+    color: #c2c2c2;
49
+}
50
+
51
+.default_date-desc-lunar {
52
+    color: #e54d42;
53
+}
54
+
55
+.default_date-desc-disable {
56
+    color: #e2e2e2;
57
+}
58
+
59
+.default_festival {
60
+    color: #c2c2c2;
61
+}

+ 58 - 0
components/local/v2/theme/theme-elegant.wxss

@@ -0,0 +1,58 @@
1
+.elegant_color,
2
+.elegant_weekend-color,
3
+.elegant_handle-color,
4
+.elegant_week-color {
5
+    color: #333;
6
+}
7
+
8
+.elegant_today {
9
+    color: #000;
10
+    background-color: #e1e7f5;
11
+}
12
+
13
+.elegant_choosed {
14
+    color: #000;
15
+    background-color: #e2e2e2;
16
+}
17
+
18
+.elegant_date-disable {
19
+    color: #c7c7c7;
20
+}
21
+
22
+.elegant_choosed.elegant_date-disable {
23
+    color: #999;
24
+    background-color: #ebebeb;
25
+}
26
+
27
+.elegant_prev-month-date,
28
+.elegant_next-month-date {
29
+    color: #e2e2e2;
30
+}
31
+
32
+.elegant_normal-date {
33
+    color: #777;
34
+}
35
+
36
+.elegant_todo-circle {
37
+    border-color: #161035;
38
+}
39
+
40
+.elegant_todo-dot {
41
+    background-color: #161035;
42
+}
43
+
44
+.elegant_date-desc {
45
+    color: #c2c2c2;
46
+}
47
+
48
+.elegant_date-desc-lunar {
49
+    color: #161035;
50
+}
51
+
52
+.elegant_date-desc-disable {
53
+    color: #e2e2e2;
54
+}
55
+
56
+.elegant_festival {
57
+    color: #c2c2c2;
58
+}

+ 285 - 0
components/local/v2/utils/index.js

@@ -0,0 +1,285 @@
1
+import Logger from './logger'
2
+import WxData from './wxData'
3
+
4
+let systemInfo
5
+export function getSystemInfo() {
6
+  if (systemInfo) return systemInfo
7
+  systemInfo = wx.getSystemInfoSync()
8
+  return systemInfo
9
+}
10
+
11
+export function isIos() {
12
+  const sys = getSystemInfo()
13
+  return /iphone|ios/i.test(sys.platform)
14
+}
15
+
16
+class Gesture {
17
+  /**
18
+   * 左滑
19
+   * @param {object} e 事件对象
20
+   * @returns {boolean} 布尔值
21
+   */
22
+  isLeft(gesture = {}, touche = {}) {
23
+    const { startX, startY } = gesture
24
+    const deltaX = touche.clientX - startX
25
+    const deltaY = touche.clientY - startY
26
+    if (deltaX < -60 && deltaY < 20 && deltaY > -20) {
27
+      return true
28
+    } else {
29
+      return false
30
+    }
31
+  }
32
+  /**
33
+   * 右滑
34
+   * @param {object} e 事件对象
35
+   * @returns {boolean} 布尔值
36
+   */
37
+  isRight(gesture = {}, touche = {}) {
38
+    const { startX, startY } = gesture
39
+    const deltaX = touche.clientX - startX
40
+    const deltaY = touche.clientY - startY
41
+
42
+    if (deltaX > 60 && deltaY < 20 && deltaY > -20) {
43
+      return true
44
+    } else {
45
+      return false
46
+    }
47
+  }
48
+}
49
+
50
+class DateUtil {
51
+  newDate(year, month, date) {
52
+    let cur = `${+year}-${+month}-${+date}`
53
+    if (isIos()) {
54
+      cur = `${+year}/${+month}/${+date}`
55
+    }
56
+    return new Date(cur)
57
+  }
58
+  /**
59
+   * 计算指定日期时间戳
60
+   * @param {object} date
61
+   */
62
+  getTimeStamp(dateInfo) {
63
+    if (typeof dateInfo === 'string') {
64
+      dateInfo = this.transformDateRow2Dict(dateInfo)
65
+    }
66
+    if (Object.prototype.toString.call(dateInfo) !== '[object Object]') return
67
+    const dateUtil = new DateUtil()
68
+    return dateUtil
69
+      .newDate(dateInfo.year, dateInfo.month, dateInfo.date)
70
+      .getTime()
71
+  }
72
+  /**
73
+   * 计算指定月份共多少天
74
+   * @param {number} year 年份
75
+   * @param {number} month  月份
76
+   */
77
+  getDatesCountOfMonth(year, month) {
78
+    return new Date(Date.UTC(year, month, 0)).getUTCDate()
79
+  }
80
+  /**
81
+   * 计算指定月份第一天星期几
82
+   * @param {number} year 年份
83
+   * @param {number} month  月份
84
+   */
85
+  firstDayOfWeek(year, month) {
86
+    return new Date(Date.UTC(year, month - 1, 1)).getUTCDay()
87
+  }
88
+  /**
89
+   * 计算指定日期星期几
90
+   * @param {number} year 年份
91
+   * @param {number} month  月份
92
+   * @param {number} date 日期
93
+   */
94
+  getDayOfWeek(year, month, date) {
95
+    return new Date(Date.UTC(year, month - 1, date)).getUTCDay()
96
+  }
97
+  todayFMD() {
98
+    const _date = new Date()
99
+    const year = _date.getFullYear()
100
+    const month = _date.getMonth() + 1
101
+    const date = _date.getDate()
102
+    return {
103
+      year: +year,
104
+      month: +month,
105
+      date: +date
106
+    }
107
+  }
108
+  todayTimestamp() {
109
+    const { year, month, date } = this.todayFMD()
110
+    const timestamp = this.newDate(year, month, date).getTime()
111
+    return timestamp
112
+  }
113
+  toTimeStr(dateInfo = {}) {
114
+    return `${+dateInfo.year}-${+dateInfo.month}-${+dateInfo.date}`
115
+  }
116
+  transformDateRow2Dict(dateStr) {
117
+    if (typeof dateStr === 'string' && dateStr.includes('-')) {
118
+      const [year, month, date] = dateStr.split('-')
119
+      return this.tranformStr2NumOfDate({
120
+        year,
121
+        month,
122
+        date
123
+      })
124
+    }
125
+    return {}
126
+  }
127
+  tranformStr2NumOfDate(date = {}) {
128
+    const target = { ...date }
129
+    // 可能传入字符串
130
+    target.year = +target.year
131
+    target.month = +target.month
132
+    target.date = +target.date
133
+    return target
134
+  }
135
+  sortDatesByTime(dates = [], sortType) {
136
+    return dates.sort((a, b) => {
137
+      const at = this.getTimeStamp(a)
138
+      const bt = this.getTimeStamp(b)
139
+      if (at < bt && sortType !== 'desc') {
140
+        return -1
141
+      } else {
142
+        return 1
143
+      }
144
+    })
145
+  }
146
+  getPrevMonthInfo(date = {}) {
147
+    const prevMonthInfo =
148
+      Number(date.month) > 1
149
+        ? {
150
+            year: +date.year,
151
+            month: Number(date.month) - 1
152
+          }
153
+        : {
154
+            year: Number(date.year) - 1,
155
+            month: 12
156
+          }
157
+    return prevMonthInfo
158
+  }
159
+  getNextMonthInfo(date = {}) {
160
+    const nextMonthInfo =
161
+      Number(date.month) < 12
162
+        ? {
163
+            year: +date.year,
164
+            month: Number(date.month) + 1
165
+          }
166
+        : {
167
+            year: Number(date.year) + 1,
168
+            month: 1
169
+          }
170
+    return nextMonthInfo
171
+  }
172
+  getPrevYearInfo(date = {}) {
173
+    return {
174
+      year: Number(date.year) - 1,
175
+      month: +date.month
176
+    }
177
+  }
178
+  getNextYearInfo(date = {}) {
179
+    return {
180
+      year: Number(date.year) + 1,
181
+      month: +date.month
182
+    }
183
+  }
184
+  findDateIndexInArray(target, dates) {
185
+    return dates.findIndex(
186
+      item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(target)
187
+    )
188
+  }
189
+  calcDates(year, month) {
190
+    const datesCount = this.getDatesCountOfMonth(year, month)
191
+    const dates = []
192
+    const today = dateUtil.todayFMD()
193
+    for (let i = 1; i <= datesCount; i++) {
194
+      const week = dateUtil.getDayOfWeek(+year, +month, i)
195
+      const date = {
196
+        year: +year,
197
+        id: i - 1,
198
+        month: +month,
199
+        date: i,
200
+        week,
201
+        isToday:
202
+          +today.year === +year && +today.month === +month && i === +today.date
203
+      }
204
+      dates.push(date)
205
+    }
206
+    return dates
207
+  }
208
+  /**
209
+   * 日期数组根据日期去重
210
+   * @param {array} array 数组
211
+   */
212
+  uniqueArrayByDate(array = []) {
213
+    let uniqueObject = {}
214
+    let uniqueArray = []
215
+    array.forEach(item => {
216
+      uniqueObject[dateUtil.toTimeStr(item)] = item
217
+    })
218
+    for (let i in uniqueObject) {
219
+      uniqueArray.push(uniqueObject[i])
220
+    }
221
+    return uniqueArray
222
+  }
223
+  /**
224
+   * 筛选指定年月日期
225
+   * @param {object} target 指定年月
226
+   * @param {array} dates 待筛选日期
227
+   */
228
+  filterDatesByYM(target, dates) {
229
+    if (target) {
230
+      const { year, month } = target
231
+      const _dates = dates.filter(
232
+        item => +item.year === +year && +item.month === +month
233
+      )
234
+      return _dates
235
+    }
236
+    return dates
237
+  }
238
+  getWeekHeader(firstDayOfWeek) {
239
+    let weeksCh = ['日', '一', '二', '三', '四', '五', '六']
240
+    if (firstDayOfWeek === 'Mon') {
241
+      weeksCh = ['一', '二', '三', '四', '五', '六', '日']
242
+    }
243
+    return weeksCh
244
+  }
245
+}
246
+
247
+/**
248
+ * 获取当前页面实例
249
+ */
250
+export function getCurrentPage() {
251
+  const pages = getCurrentPages() || []
252
+  const last = pages.length - 1
253
+  return pages[last] || {}
254
+}
255
+
256
+export function getComponentById(componentId) {
257
+  const logger = new Logger()
258
+  let page = getCurrentPage() || {}
259
+  if (page.selectComponent && typeof page.selectComponent === 'function') {
260
+    if (componentId) {
261
+      return page.selectComponent(componentId)
262
+    } else {
263
+      logger.warn('请传入组件ID')
264
+    }
265
+  } else {
266
+    logger.warn('该基础库暂不支持多个小程序日历组件')
267
+  }
268
+}
269
+
270
+export const logger = new Logger()
271
+export const calendarGesture = new Gesture()
272
+export const dateUtil = new DateUtil()
273
+export const getCalendarData = (key, component) =>
274
+  new WxData(component).getData(key)
275
+export const setCalendarData = (data, component) =>
276
+  new WxData(component).setData(data)
277
+export const getCalendarConfig = component =>
278
+  getCalendarData('config', component)
279
+export const setCalendarConfig = (config, component) =>
280
+  setCalendarData(
281
+    {
282
+      config
283
+    },
284
+    component
285
+  )

+ 23 - 0
components/local/v2/utils/logger.js

@@ -0,0 +1,23 @@
1
+export default class Logger {
2
+  info(msg) {
3
+    console.log(
4
+      '%cInfo: %c' + msg,
5
+      'color:#FF0080;font-weight:bold',
6
+      'color: #FF509B'
7
+    )
8
+  }
9
+  warn(msg) {
10
+    console.log(
11
+      '%cWarn: %c' + msg,
12
+      'color:#FF6600;font-weight:bold',
13
+      'color: #FF9933'
14
+    )
15
+  }
16
+  tips(msg) {
17
+    console.log(
18
+      '%cTips: %c' + msg,
19
+      'color:#00B200;font-weight:bold',
20
+      'color: #00CC33'
21
+    )
22
+  }
23
+}

+ 30 - 0
components/local/v2/utils/wxData.js

@@ -0,0 +1,30 @@
1
+class WxData {
2
+  constructor(component) {
3
+    this.Component = component
4
+  }
5
+  getData(key) {
6
+    const data = this.Component.data
7
+    if (!key) return data
8
+    if (key.includes('.')) {
9
+      let keys = key.split('.')
10
+      const tmp = keys.reduce((prev, next) => {
11
+        return prev[next]
12
+      }, data)
13
+      return tmp
14
+    } else {
15
+      return this.Component.data[key]
16
+    }
17
+  }
18
+  setData(data) {
19
+    return new Promise((resolve, reject) => {
20
+      if (!data) return reject('no data to set')
21
+      if (typeof data === 'object') {
22
+        this.Component.setData(data, () => {
23
+          resolve(data)
24
+        })
25
+      }
26
+    })
27
+  }
28
+}
29
+
30
+export default WxData

BIN
images/.DS_Store


BIN
images/gzh.png


BIN
images/header.png


BIN
images/icon_01.png


BIN
images/icon_02.png


BIN
images/icon_05.png


BIN
images/icon_logo.png


BIN
images/right.png


BIN
images/wx.png


+ 105 - 0
pages/clientInfo/clientInfo.js

@@ -0,0 +1,105 @@
1
+// pages/clientInfo/clientInfo.js
2
+Page({
3
+
4
+  /**
5
+   * 页面的初始数据
6
+   */
7
+  data: {
8
+    urls: [
9
+      "https://img1.baidu.com/it/u=202543353,3627416815&fm=26&fmt=auto",
10
+      "https://img0.baidu.com/it/u=745609344,230882238&fm=26&fmt=auto",
11
+      "https://img0.baidu.com/it/u=286636366,3227707112&fm=26&fmt=auto",
12
+      "https://img1.baidu.com/it/u=2450865760,444795162&fm=26&fmt=auto",
13
+      "https://img0.baidu.com/it/u=4226275504,4103997964&fm=26&fmt=auto",
14
+      "https://img0.baidu.com/it/u=2247422843,411257408&fm=26&fmt=auto",
15
+      "https://img0.baidu.com/it/u=3098615520,360170704&fm=26&fmt=auto",
16
+      "https://img1.baidu.com/it/u=510862345,2249984174&fm=26&fmt=auto",
17
+      "https://img2.baidu.com/it/u=2222750380,2392750381&fm=26&fmt=auto",
18
+    ],
19
+    tags: ['美丽貌美','短短胳膊粗粗的腿','闭月羞花','人美声甜白富美']
20
+  },
21
+
22
+  /**
23
+   * 生命周期函数--监听页面加载
24
+   */
25
+  onLoad: function (options) {
26
+
27
+  },
28
+
29
+  /**
30
+   * 生命周期函数--监听页面初次渲染完成
31
+   */
32
+  onReady: function () {
33
+
34
+  },
35
+
36
+  /**
37
+   * 生命周期函数--监听页面显示
38
+   */
39
+  onShow: function () {
40
+
41
+  },
42
+
43
+  /**
44
+   * 生命周期函数--监听页面隐藏
45
+   */
46
+  onHide: function () {
47
+
48
+  },
49
+
50
+  /**
51
+   * 生命周期函数--监听页面卸载
52
+   */
53
+  onUnload: function () {
54
+
55
+  },
56
+
57
+  /**
58
+   * 页面相关事件处理函数--监听用户下拉动作
59
+   */
60
+  onPullDownRefresh: function () {
61
+
62
+  },
63
+
64
+  /**
65
+   * 页面上拉触底事件的处理函数
66
+   */
67
+  onReachBottom: function () {
68
+
69
+  },
70
+
71
+  /**
72
+   * 用户点击右上角分享
73
+   */
74
+  onShareAppMessage: function () {
75
+
76
+  },
77
+
78
+  afterCalendarRender(e) {
79
+    const calendar = this.selectComponent('#calendar').calendar
80
+    console.log('afterCalendarRender -> calendar', calendar)
81
+    const toSet = [
82
+      {
83
+        year: 2021,
84
+        month: 11,
85
+        date: 15
86
+      },
87
+      {
88
+        year: 2019,
89
+        month: 3,
90
+        date: 18
91
+      }
92
+    ]
93
+    calendar.setSelectedDates(toSet)
94
+  },
95
+  
96
+  /**
97
+   * 图片预览
98
+   */
99
+  previewImage(e) {
100
+    wx.previewImage({
101
+      current: e.currentTarget.dataset.index,
102
+      urls: this.data.urls
103
+    })
104
+  }
105
+})

+ 6 - 0
pages/clientInfo/clientInfo.json

@@ -0,0 +1,6 @@
1
+{
2
+  "navigationBarTitleText": "客户信息",
3
+  "usingComponents": {
4
+    "calendar": "../../components/local/v2/index"
5
+  }
6
+}

+ 68 - 0
pages/clientInfo/clientInfo.wxml

@@ -0,0 +1,68 @@
1
+<view class="content">
2
+  <view class="border-radius padding user-info">
3
+    <view class="top flex-justify-space-between">
4
+      <text style="font-family: PingFangSC-Medium, PingFang SC;font-weight: 500;">基本信息</text>
5
+      <navigator url="/pages/clientInfoEdit/clientInfoEdit" hover-class="none">
6
+        <view class="edit-btn flex-center">编辑信息</view>
7
+      </navigator>
8
+    </view>
9
+    <view class="user-avatar flex-align-center">
10
+      <image src=""></image>
11
+      <text style="font-size: 28rpx;">查理兹·塞隆…</text>
12
+      <text class="flex1"></text>
13
+      <text style="font-size: 28rpx;color: #999;">下午  3:45</text>
14
+    </view>
15
+    <view class="user-content flex-column">
16
+      <text>姓名:陈姐</text>
17
+      <text>性别:女</text>
18
+      <text>生日:1980-01-01</text>
19
+      <text>手机号码:13888888888</text>
20
+      <text>所在地区:广东省,深圳市,南山区</text>
21
+      <view class="tags-list flex-align-center">
22
+        <text>客户标签:</text>
23
+        <view class="item" wx:for="{{ tags }}" wx:key="index">
24
+          <text>{{ item }}</text>
25
+        </view>
26
+      </view>
27
+    </view>
28
+  </view>
29
+  <view class="border-radius padding buy-info">
30
+    <text style="font-family: PingFangSC-Medium, PingFang SC;font-weight: 500;">会员信息</text>
31
+    <view class="buy-content flex-column">
32
+      <text class="ellipsis {{ index == 0 ? 'active' : '' }}" wx:for="{{ 10 }}" wx:key="index">2020/01/01-2020/02/01:40分钟月卡会员</text>
33
+      <view class="buy-active flex-center">生效中</view>
34
+    </view>
35
+    <view class="btn-more flex-center" style="color: #666;">查看更多记录</view>
36
+  </view>
37
+  <view class="border-radius padding">
38
+    <calendar 
39
+      id="calendar"
40
+      bind:afterCalendarRender="afterCalendarRender"
41
+    />
42
+  </view>
43
+  <view class="border-radius padding list">
44
+    <view class="item-top flex-align-center">
45
+      <text class="flex1">反馈记录</text>
46
+      <view class="btn flex-center">订单</view>
47
+    </view>
48
+    <view class="item flex-column" wx:for="{{4}}" wx:key="index">
49
+      <view class="user-info flex-align-center">
50
+        <image></image>
51
+        <view class="info flex-column flex1">
52
+          <text style="font-size: 28rpx;margin-bottom: 6rpx;">查理兹·塞隆…</text>
53
+          <text style="font-size: 20rpx;color: #999;">下午  3:45</text>
54
+        </view>
55
+        <!-- <view class="more flex-align-center flex-justify-space-between" bindtap="onEditItem">
56
+          <text></text>
57
+          <text></text>
58
+          <text></text>
59
+        </view> -->
60
+        <!-- <view class="more">···</view> -->
61
+      </view>
62
+      <text style="font-size: 28rpx;color: #666;">当前皮肤毛孔出现堵塞,抓哟集中在T区,主要问题在于自身油脂分泌过多。</text>
63
+      <view class="box">
64
+        <image src="{{item}}" data-index="{{item}}" bindtap="previewImage" wx:for="{{urls}}" wx:key="index"></image>
65
+      </view>
66
+    </view>
67
+  </view>
68
+</view>

+ 160 - 0
pages/clientInfo/clientInfo.wxss

@@ -0,0 +1,160 @@
1
+.content {
2
+  padding:  10rpx 28rpx;
3
+}
4
+
5
+.border-radius {
6
+  background: #FFFFFF;
7
+  border-radius: 24rpx;
8
+  margin: 20rpx 0;
9
+}
10
+
11
+.padding {
12
+  padding: 28rpx;
13
+}
14
+
15
+.user-info .top .edit-btn {
16
+  width: 154rpx;
17
+  height: 52rpx;
18
+  border-radius: 26rpx;
19
+  border: 2rpx solid #333333;
20
+  font-size: 28rpx;
21
+}
22
+
23
+.user-info .user-avatar {
24
+  margin: 56rpx 0 30rpx;
25
+}
26
+
27
+.user-info .user-avatar image {
28
+  width: 64rpx;
29
+  height: 64rpx;
30
+  border-radius: 50%;
31
+  background-color: #ccc;
32
+  margin-right: 14rpx;
33
+}
34
+
35
+.user-info .user-content {
36
+  background: #FFF7FA;
37
+  border-radius: 16rpx;
38
+  padding: 24rpx 28rpx 4rpx;
39
+}
40
+
41
+.user-info .user-content text {
42
+  font-size: 28rpx;
43
+  color: #333333;
44
+  line-height: 48rpx;
45
+}
46
+
47
+.user-content .tags-list {
48
+  flex-wrap:wrap;
49
+  margin-top: 8rpx;
50
+}
51
+
52
+.user-content .tags-list .item {
53
+  padding: 6rpx 20rpx;
54
+  border-radius: 26rpx;
55
+  border: 2rpx solid #333333;
56
+  box-sizing: border-box;
57
+  margin-right: 6rpx;
58
+  margin-bottom: 20rpx;
59
+}
60
+
61
+.buy-info {
62
+  padding-bottom: 0;
63
+}
64
+
65
+.buy-info .buy-content {
66
+  background: #FFF7FA;
67
+  border-radius: 16rpx;
68
+  padding: 30rpx 26rpx;
69
+  margin-top: 30rpx;
70
+  position: relative;
71
+}
72
+
73
+.buy-info .buy-content text {
74
+  font-size: 28rpx;
75
+  color: #666;
76
+  line-height: 48rpx;
77
+}
78
+
79
+.buy-info .buy-content .buy-active {
80
+  color: #fff;
81
+  font-size: 16rpx;
82
+  width: 70rpx;
83
+  height: 40rpx;
84
+  background: #FF77B0;
85
+  overflow: hidden;
86
+  position: absolute;
87
+  border-top-left-radius: 200rpx;
88
+  border-bottom-left-radius: 200rpx;
89
+  right: 0;
90
+  top: 34rpx;
91
+  /* border-radius: 200rpx 0rpx rpx 200rpx; */
92
+}
93
+
94
+.buy-info .buy-content text.active {
95
+  font-family: PingFangSC-Medium, PingFang SC;
96
+  font-weight: 500;
97
+  color: #333333;
98
+}
99
+
100
+.btn-more {
101
+  height: 104rpx;
102
+  margin-top: 30rpx;
103
+  box-sizing: border-box;
104
+  border-top: 2rpx solid #F2F2F2;
105
+}
106
+
107
+
108
+
109
+.list .item {
110
+  padding-top: 30rpx;
111
+}
112
+
113
+.list .item-top .btn {
114
+  width: 154rpx;
115
+  height: 52rpx;
116
+  border-radius: 26rpx;
117
+  border: 2rpx solid #333333;
118
+  box-sizing: border-box;
119
+  font-size: 28rpx;
120
+}
121
+
122
+.list .user-info {
123
+  margin-bottom: 20rpx;
124
+}
125
+
126
+.list .user-info image {
127
+  width: 64rpx;
128
+  height: 64rpx;
129
+  border-radius: 50%;
130
+  background-color: red;
131
+  margin-right: 14rpx;
132
+}
133
+
134
+/* .list .more {
135
+  width: 48rpx;
136
+  height: 48rpx;
137
+  padding: 0 4rpx;
138
+  box-sizing: border-box;
139
+}
140
+
141
+.list .more text {
142
+  width: 8rpx;
143
+  height: 8rpx;
144
+  background: #333;
145
+  border-radius: 50%;
146
+} */
147
+
148
+.list .box {
149
+  display: grid;
150
+  grid-gap: 20rpx 22rpx;
151
+  grid-template-columns: repeat(3, 1fr);
152
+  padding: 30rpx 0;
153
+}
154
+
155
+.list .box image {
156
+  width: 198rpx;
157
+  height: 198rpx;
158
+  border-radius: 16rpx;
159
+  background-color: red;
160
+}

+ 157 - 0
pages/clientInfoEdit/clientInfoEdit.js

@@ -0,0 +1,157 @@
1
+// import {
2
+//   get, post,
3
+// } from '../../utils/http';
4
+Page({
5
+
6
+  /**
7
+   * 页面的初始数据
8
+   */
9
+  data: {
10
+    userInfo: {},
11
+    visible: false,
12
+    genders: ["男","女"],
13
+    genderIndex: 0,
14
+    date: '2016-09-01',
15
+    region: ['广东省', '广州市', '海珠区'],
16
+  },
17
+
18
+  /**
19
+   * 生命周期函数--监听页面加载
20
+   */
21
+  onLoad: function (options) {
22
+
23
+  },
24
+
25
+  /**
26
+   * 生命周期函数--监听页面初次渲染完成
27
+   */
28
+  onReady: function () {
29
+
30
+  },
31
+
32
+  /**
33
+   * 生命周期函数--监听页面显示
34
+   */
35
+  onShow: function () {
36
+    // this.getUserFiles()
37
+  },
38
+
39
+  /**
40
+   * 生命周期函数--监听页面隐藏
41
+   */
42
+  onHide: function () {
43
+    this.setData({
44
+      visible: false
45
+    })
46
+  },
47
+
48
+  /**
49
+   * 生命周期函数--监听页面卸载
50
+   */
51
+  onUnload: function () {
52
+    
53
+  },
54
+
55
+  /**
56
+   * 页面相关事件处理函数--监听用户下拉动作
57
+   */
58
+  onPullDownRefresh: function () {
59
+
60
+  },
61
+
62
+  /**
63
+   * 页面上拉触底事件的处理函数
64
+   */
65
+  onReachBottom: function () {
66
+
67
+  },
68
+
69
+  /**
70
+   * 用户点击右上角分享
71
+   */
72
+  onShareAppMessage: function () {
73
+
74
+  },
75
+
76
+  isShow() {},
77
+
78
+  /**
79
+   * 弹框状态
80
+   */
81
+  onPopupState(e, key, value) {
82
+    if (e) {
83
+      key = e.currentTarget.dataset.key
84
+      value = e.currentTarget.dataset.value
85
+    }
86
+    console.log(key)
87
+    console.log(value)
88
+    this.setData({
89
+      [key]: value
90
+    })
91
+  },
92
+
93
+  /**
94
+   * 选择性别
95
+  */
96
+  bindGenderChange(e) {
97
+    this.setData({
98
+      ['userInfo.sex']: Number(e.detail.value) + 1,
99
+    },() => {
100
+      this.setUserFiles()
101
+    })
102
+  },
103
+
104
+  /**
105
+   * 选择生日
106
+  */
107
+  bindDateChange: function(e) {
108
+    console.log(e)
109
+    this.setData({
110
+      ['userInfo.birthday']: e.detail.value,
111
+      // date: e.detail.value
112
+    },() => {
113
+      this.setUserFiles()
114
+    })
115
+  },
116
+
117
+  /**
118
+   * 选择所在地
119
+  */
120
+  bindRegionChange: function (e) {
121
+    this.setData({
122
+      region: e.detail.value,
123
+      ['userInfo.city']: e.detail.value.toString(),
124
+    },() => {
125
+      this.setUserFiles()
126
+    })
127
+  },
128
+
129
+  /**
130
+   * 获取用户档案
131
+   * api/user/files
132
+  */
133
+  getUserFiles() {
134
+    get('api/user/files',{},(res) => {
135
+      this.setData({
136
+        userInfo: res.data
137
+      })
138
+      console.log(res)
139
+    })
140
+  },
141
+
142
+  /**
143
+   * 更新用户档案
144
+   * api/user/save_files
145
+  */
146
+  setUserFiles() {
147
+    let { real_name,birthday,sex,city } = this.data.userInfo;
148
+    post('api/user/save_files',{
149
+      real_name,
150
+      birthday,
151
+      sex,
152
+      city
153
+    },() => {
154
+
155
+    })
156
+  }
157
+})

+ 7 - 0
pages/clientInfoEdit/clientInfoEdit.json

@@ -0,0 +1,7 @@
1
+{
2
+  "navigationBarTitleText": "编辑客户信息",
3
+  "usingComponents": {
4
+    "wux-landscape": "../../components/dist/landscape/index",
5
+    "wux-popup": "../../components/dist/popup/index"
6
+  }
7
+}

+ 91 - 0
pages/clientInfoEdit/clientInfoEdit.wxml

@@ -0,0 +1,91 @@
1
+<view class="list">
2
+  <view class="item flex-align-center">
3
+    <text class="flex1">头像</text>
4
+    <image class="avatar" src="{{userInfo.avatar_url}}"></image>
5
+  </view>
6
+  <navigator url="/pages/name/name" hover-class="none">
7
+    <view class="item flex-align-center">
8
+      <view class="flex-column flex1">
9
+        <text class="title">昵称</text>
10
+        <text class="flex1" wx:if="{{ userInfo.real_name }}">{{ userInfo.real_name  }}</text>
11
+        <text class="flex1" wx:else style="font-size: 28rpx;color: #ccc;">请填写昵称</text>
12
+      </view>
13
+      <image class="icon-right" src="/images/icon_02.png"></image>
14
+    </view>
15
+  </navigator>
16
+  <navigator url="/pages/name/name" hover-class="none">
17
+    <view class="item flex-align-center">
18
+      <view class="flex-column flex1">
19
+        <text class="title">姓名</text>
20
+        <text class="flex1" wx:if="{{ userInfo.real_name }}">{{ userInfo.real_name  }}</text>
21
+        <text class="flex1" wx:else style="font-size: 28rpx;color: #ccc;">请填写姓名</text>
22
+      </view>
23
+      <image class="icon-right" src="/images/icon_02.png"></image>
24
+    </view>
25
+  </navigator>
26
+  <picker bindchange="bindGenderChange" value="{{userInfo.sex == 0 ? 0 : userInfo.sex -1}}" range="{{genders}}">
27
+    <view class="item flex-align-center">
28
+      <view class="flex-column flex1">
29
+      <text class="title">性别</text>
30
+      <text class="flex1">{{ userInfo.sex == 0 ? '保密' : genders[userInfo.sex -1 ] }}</text>
31
+      <!-- <text class="flex1">{{genders[genderIndex]}}</text> -->
32
+    </view>
33
+    <image class="icon-right" src="/images/icon_02.png"></image>
34
+    </view>
35
+  </picker>
36
+  <picker mode="date" value="{{userInfo.birthday}}" start="1980-09-01" bindchange="bindDateChange">
37
+    <view class="item flex-align-center">
38
+      <view class="flex-column flex1">
39
+        <text class="title">生日</text>
40
+        <text class="flex1" wx:if="{{ userInfo.birthday }}">{{userInfo.birthday}}</text>
41
+        <text class="flex1" wx:else style="font-size: 28rpx;color: #ccc;">请选择生日</text>
42
+      </view>
43
+      <image class="icon-right" src="/images/icon_02.png"></image>
44
+    </view>
45
+  </picker>
46
+  <view class="item flex-align-center" bindtap="onPopupState" data-key="visible" data-value="{{true}}">
47
+    <view class="flex-column flex1">
48
+      <text class="title">手机号码</text>
49
+      <text class="flex1">{{ userInfo.mobile }}</text>
50
+    </view>
51
+    <image class="icon-right" src="/images/icon_02.png"></image>
52
+  </view>
53
+  <picker mode="region" bindchange="bindRegionChange">
54
+    <view class="item flex-align-center">
55
+      <view class="flex-column flex1">
56
+        <text class="title">所在地</text>
57
+        <text class="flex1" wx:if="{{ userInfo.city }}">{{ userInfo.city }}</text>
58
+        <text class="flex1" wx:else style="font-size: 28rpx;color: #ccc;">请选择常驻城市</text>
59
+        <!-- <text class="flex1">{{region[0]}},{{region[1]}},{{region[2]}}</text> -->
60
+      </view>
61
+      <image class="icon-right" src="/images/icon_02.png"></image>
62
+    </view>
63
+  </picker>
64
+  <navigator url="/pages/tagsEdit/tagsEdit" hover-class="none">
65
+    <view class="item flex-align-center" style="height: auto;padding: 40rpx 24rpx 30rpx;">
66
+      <view class="flex-column flex1">
67
+        <text class="title">客户标签</text>
68
+        <view class="tags-list flex">
69
+          <view class="tags-item" wx:for="{{ 10 }}" wx:key="index">美丽貌美</view>
70
+        </view>
71
+        <!-- <text class="flex1" wx:if="{{ userInfo.real_name }}">{{ userInfo.real_name  }}</text>
72
+        <text class="flex1" wx:else style="font-size: 28rpx;color: #ccc;">请填写昵称</text> -->
73
+      </view>
74
+      <image class="icon-right" src="/images/icon_02.png"></image>
75
+    </view>
76
+  </navigator>
77
+</view>
78
+
79
+<wux-landscape visible="{{ visible }}"  maskClosable="{{ true }}" closable="{{ false }}" bind:close="onPopupState" data-key="visible" data-value="{{false}}">
80
+  <view class="use-popup flex-column">
81
+    <text style="font-size: 36rpx;">更换手机</text>
82
+    <text style="color: #666666;margin: 28rpx 0 78rpx;">您确定要更换绑定的手机吗?</text>
83
+    <view class="use-btn">
84
+      <view class="btn cancel flex-center" bindtap="onPopupState" data-key="visible" data-value="{{false}}">取消</view>
85
+      <navigator url="/pages/phone/phone?phone={{userInfo.mobile}}" hover-class="none">
86
+        <view class="btn confirm flex-center">更换</view>
87
+      </navigator>
88
+    </view>
89
+    <!-- [温馨提示] 确认激活后,该会员卡将立即生效; 如暂不激活,可在“已购会员卡”中去选择激活。 -->
90
+  </view>
91
+</wux-landscape>

+ 84 - 0
pages/clientInfoEdit/clientInfoEdit.wxss

@@ -0,0 +1,84 @@
1
+page {
2
+  background: #fff;
3
+}
4
+
5
+.list .item {
6
+  height: 192rpx;
7
+  padding: 0 24rpx;
8
+  box-sizing: border-box;
9
+  border-bottom: 2rpx solid #EEEEEE;
10
+}
11
+
12
+.list .item text {
13
+  font-size: 36rpx;
14
+}
15
+
16
+.list .item .title {
17
+  color: #999;
18
+  font-size: 28rpx;
19
+  margin-bottom: 22rpx;
20
+}
21
+
22
+.list .item .avatar {
23
+  width: 116rpx;
24
+  height: 116rpx;
25
+  border-radius: 50%;
26
+  background: #EDEDED;
27
+  border: 2rpx solid #979797;
28
+}
29
+
30
+.tags-list {
31
+  flex-wrap: wrap;
32
+}
33
+
34
+.tags-list .tags-item {
35
+  padding: 6rpx 20rpx;
36
+  border-radius: 26rpx;
37
+  border: 2rpx solid #333333;
38
+  font-size: 28rpx;
39
+  margin-right: 6rpx;
40
+  margin-bottom: 20rpx;
41
+  box-sizing: border-box;
42
+  overflow: hidden;
43
+  color: #333333;
44
+}
45
+
46
+.icon-right {
47
+  width: 48rpx;
48
+  height: 48rpx;
49
+}
50
+
51
+
52
+
53
+.use-popup {
54
+  width: 558rpx;
55
+  height: 508rpx;
56
+  background: #FFFFFF;
57
+  border-radius: 40rpx;
58
+  justify-content: center;
59
+  /* justify-content: space-evenly; */
60
+}
61
+
62
+/* .use-popup .red {
63
+  font-size: 24rpx;
64
+  font-family: PingFangSC-Regular, PingFang SC;
65
+  font-weight: 400;
66
+  color: #E95564;
67
+} */
68
+
69
+.use-btn .btn {
70
+  /* width: 208rpx; */
71
+  height: 92rpx;
72
+  margin: 0 48rpx;
73
+  font-size: 36rpx;
74
+  color: #FF77B0;
75
+  border-radius: 52rpx;
76
+  border: 2rpx solid #FF77B0;
77
+}
78
+
79
+.use-btn .confirm {
80
+  margin-top: 28rpx;
81
+  /* margin-left: 46rpx; */
82
+  background-color: #FF77B0;
83
+  color: #fff;
84
+}

+ 66 - 0
pages/contact/contact.js

@@ -0,0 +1,66 @@
1
+// pages/contact/contact.js
2
+Page({
3
+
4
+  /**
5
+   * 页面的初始数据
6
+   */
7
+  data: {
8
+
9
+  },
10
+
11
+  /**
12
+   * 生命周期函数--监听页面加载
13
+   */
14
+  onLoad: function (options) {
15
+
16
+  },
17
+
18
+  /**
19
+   * 生命周期函数--监听页面初次渲染完成
20
+   */
21
+  onReady: function () {
22
+
23
+  },
24
+
25
+  /**
26
+   * 生命周期函数--监听页面显示
27
+   */
28
+  onShow: function () {
29
+
30
+  },
31
+
32
+  /**
33
+   * 生命周期函数--监听页面隐藏
34
+   */
35
+  onHide: function () {
36
+
37
+  },
38
+
39
+  /**
40
+   * 生命周期函数--监听页面卸载
41
+   */
42
+  onUnload: function () {
43
+
44
+  },
45
+
46
+  /**
47
+   * 页面相关事件处理函数--监听用户下拉动作
48
+   */
49
+  onPullDownRefresh: function () {
50
+
51
+  },
52
+
53
+  /**
54
+   * 页面上拉触底事件的处理函数
55
+   */
56
+  onReachBottom: function () {
57
+
58
+  },
59
+
60
+  /**
61
+   * 用户点击右上角分享
62
+   */
63
+  onShareAppMessage: function () {
64
+
65
+  }
66
+})

+ 4 - 0
pages/contact/contact.json

@@ -0,0 +1,4 @@
1
+{
2
+  "navigationBarTitleText": "联系我们",
3
+  "usingComponents": {}
4
+}

+ 21 - 0
pages/contact/contact.wxml

@@ -0,0 +1,21 @@
1
+<view class="content flex-column flex-align-center">
2
+  <!-- icon_logo.png -->
3
+  <image class="logo" src="/images/icon_logo.png"></image>
4
+  <text style="margin: 46rpx 0 14rpx;font-size: 36rpx;font-family: PingFangSC-Medium, PingFang SC;">玖哩玖哩</text>
5
+  <text style="font-size: 28rpx;color: #333333;">版本:1.0.0</text>
6
+  <view class="flex" style="margin-top: 118rpx;">
7
+    <view class="item flex-column flex-align-center" style="margin-right:52rpx">
8
+      <image src="/images/wx.png" show-menu-by-longpress></image>
9
+      <text>客服微信</text>
10
+    </view>
11
+    <view class="item flex-column flex-align-center">
12
+      <image src="/images/gzh.png" show-menu-by-longpress></image>
13
+      <text>公众号</text>
14
+    </view>
15
+  </view>
16
+</view>
17
+
18
+<view class="bottom flex-column flex-align-center">
19
+  <text>Copyright (C) 2021</text>
20
+  <text>玖哩玖哩 ijolijoli.com 版权所有</text>
21
+</view>

+ 33 - 0
pages/contact/contact.wxss

@@ -0,0 +1,33 @@
1
+page {
2
+  background: #F9F9F9;
3
+}
4
+
5
+.logo {
6
+  width: 272rpx;
7
+  height: 272rpx;
8
+  margin-top: 94rpx;
9
+}
10
+
11
+.item image {
12
+  width: 216rpx;
13
+  height: 216rpx;
14
+  background: #FFFFFF;
15
+  border: 2rpx solid #CCCCCC;
16
+  margin-bottom: 22rpx;
17
+}
18
+
19
+.item text {
20
+  font-family: PingFangSC-Medium, PingFang SC;
21
+  font-weight: 500;
22
+}
23
+
24
+.bottom {
25
+  position: fixed;
26
+  bottom: 56rpx;
27
+  width: 100%;
28
+}
29
+
30
+.bottom text {
31
+  font-size: 28rpx;
32
+  color: #333333;
33
+}

+ 155 - 0
pages/dossier/dossier.js

@@ -0,0 +1,155 @@
1
+// import {
2
+//   get, post,
3
+// } from '../../utils/http';
4
+Page({
5
+
6
+  /**
7
+   * 页面的初始数据
8
+   */
9
+  data: {
10
+    userInfo: {},
11
+    visible: false,
12
+    genders: ["男","女"],
13
+    genderIndex: 0,
14
+    date: '2016-09-01',
15
+    region: ['广东省', '广州市', '海珠区'],
16
+  },
17
+
18
+  /**
19
+   * 生命周期函数--监听页面加载
20
+   */
21
+  onLoad: function (options) {
22
+
23
+  },
24
+
25
+  /**
26
+   * 生命周期函数--监听页面初次渲染完成
27
+   */
28
+  onReady: function () {
29
+
30
+  },
31
+
32
+  /**
33
+   * 生命周期函数--监听页面显示
34
+   */
35
+  onShow: function () {
36
+    // this.getUserFiles()
37
+  },
38
+
39
+  /**
40
+   * 生命周期函数--监听页面隐藏
41
+   */
42
+  onHide: function () {
43
+    this.setData({
44
+      visible: false
45
+    })
46
+  },
47
+
48
+  /**
49
+   * 生命周期函数--监听页面卸载
50
+   */
51
+  onUnload: function () {
52
+    
53
+  },
54
+
55
+  /**
56
+   * 页面相关事件处理函数--监听用户下拉动作
57
+   */
58
+  onPullDownRefresh: function () {
59
+
60
+  },
61
+
62
+  /**
63
+   * 页面上拉触底事件的处理函数
64
+   */
65
+  onReachBottom: function () {
66
+
67
+  },
68
+
69
+  /**
70
+   * 用户点击右上角分享
71
+   */
72
+  onShareAppMessage: function () {
73
+
74
+  },
75
+
76
+  isShow() {},
77
+
78
+  /**
79
+   * 弹框状态
80
+   */
81
+  onPopupState(e, key, value) {
82
+    if (e) {
83
+      key = e.currentTarget.dataset.key
84
+      value = e.currentTarget.dataset.value
85
+    }
86
+    this.setData({
87
+      [key]: value
88
+    })
89
+  },
90
+
91
+  /**
92
+   * 选择性别
93
+  */
94
+  bindGenderChange(e) {
95
+    this.setData({
96
+      ['userInfo.sex']: Number(e.detail.value) + 1,
97
+    },() => {
98
+      this.setUserFiles()
99
+    })
100
+  },
101
+
102
+  /**
103
+   * 选择生日
104
+  */
105
+  bindDateChange: function(e) {
106
+    console.log(e)
107
+    this.setData({
108
+      ['userInfo.birthday']: e.detail.value,
109
+      // date: e.detail.value
110
+    },() => {
111
+      this.setUserFiles()
112
+    })
113
+  },
114
+
115
+  /**
116
+   * 选择所在地
117
+  */
118
+  bindRegionChange: function (e) {
119
+    this.setData({
120
+      region: e.detail.value,
121
+      ['userInfo.city']: e.detail.value.toString(),
122
+    },() => {
123
+      this.setUserFiles()
124
+    })
125
+  },
126
+
127
+  /**
128
+   * 获取用户档案
129
+   * api/user/files
130
+  */
131
+  getUserFiles() {
132
+    get('api/user/files',{},(res) => {
133
+      this.setData({
134
+        userInfo: res.data
135
+      })
136
+      console.log(res)
137
+    })
138
+  },
139
+
140
+  /**
141
+   * 更新用户档案
142
+   * api/user/save_files
143
+  */
144
+  setUserFiles() {
145
+    let { real_name,birthday,sex,city } = this.data.userInfo;
146
+    post('api/user/save_files',{
147
+      real_name,
148
+      birthday,
149
+      sex,
150
+      city
151
+    },() => {
152
+
153
+    })
154
+  }
155
+})

+ 7 - 0
pages/dossier/dossier.json

@@ -0,0 +1,7 @@
1
+{
2
+  "navigationBarTitleText": "个人信息",
3
+  "usingComponents": {
4
+    "wux-landscape": "../../components/dist/landscape/index",
5
+    "wux-popup": "../../components/dist/popup/index"
6
+  }
7
+}

+ 68 - 0
pages/dossier/dossier.wxml

@@ -0,0 +1,68 @@
1
+<view class="list">
2
+  <view class="item flex-align-center">
3
+    <text class="flex1">头像</text>
4
+    <image class="avatar" src="{{userInfo.avatar_url}}"></image>
5
+  </view>
6
+  <navigator url="/pages/name/name" hover-class="none">
7
+    <view class="item flex-align-center">
8
+      <view class="flex-column flex1">
9
+        <text class="title">姓名</text>
10
+        <text class="flex1" wx:if="{{ userInfo.real_name }}">{{ userInfo.real_name  }}</text>
11
+        <text class="flex1" wx:else style="font-size: 28rpx;color: #ccc;">请填写姓名</text>
12
+      </view>
13
+      <image class="icon-right" src="/images/my/right.png"></image>
14
+    </view>
15
+  </navigator>
16
+  <picker bindchange="bindGenderChange" value="{{userInfo.sex == 0 ? 0 : userInfo.sex -1}}" range="{{genders}}">
17
+    <view class="item flex-align-center">
18
+      <view class="flex-column flex1">
19
+      <text class="title">性别</text>
20
+      <text class="flex1">{{ userInfo.sex == 0 ? '保密' : genders[userInfo.sex -1 ] }}</text>
21
+      <!-- <text class="flex1">{{genders[genderIndex]}}</text> -->
22
+    </view>
23
+    <image class="icon-right" src="/images/my/right.png"></image>
24
+    </view>
25
+  </picker>
26
+  <picker mode="date" value="{{userInfo.birthday}}" start="1980-09-01" bindchange="bindDateChange">
27
+    <view class="item flex-align-center">
28
+      <view class="flex-column flex1">
29
+        <text class="title">生日</text>
30
+        <text class="flex1" wx:if="{{ userInfo.birthday }}">{{userInfo.birthday}}</text>
31
+        <text class="flex1" wx:else style="font-size: 28rpx;color: #ccc;">请选择生日</text>
32
+      </view>
33
+      <image class="icon-right" src="/images/my/right.png"></image>
34
+    </view>
35
+  </picker>
36
+  <view class="item flex-align-center" bindtap="onPopupState" data-key="visible" data-value="{{true}}">
37
+    <view class="flex-column flex1">
38
+      <text class="title">手机号码</text>
39
+      <text class="flex1">{{ userInfo.mobile }}</text>
40
+    </view>
41
+    <image class="icon-right" src="/images/my/right.png"></image>
42
+  </view>
43
+  <picker mode="region" bindchange="bindRegionChange">
44
+    <view class="item flex-align-center">
45
+      <view class="flex-column flex1">
46
+        <text class="title">所在地</text>
47
+        <text class="flex1" wx:if="{{ userInfo.city }}">{{ userInfo.city }}</text>
48
+        <text class="flex1" wx:else style="font-size: 28rpx;color: #ccc;">请选择常驻城市</text>
49
+        <!-- <text class="flex1">{{region[0]}},{{region[1]}},{{region[2]}}</text> -->
50
+      </view>
51
+      <image class="icon-right" src="/images/my/right.png"></image>
52
+    </view>
53
+  </picker>
54
+</view>
55
+
56
+<wux-landscape visible="{{ visible }}"  maskClosable="{{ true }}" closable="{{ false }}" bind:close="onPopupState" data-key="visible" data-value="{{false}}">
57
+  <view class="use-popup flex-column">
58
+    <text style="font-size: 36rpx;">更换手机</text>
59
+    <text style="color: #666666;margin: 28rpx 0 78rpx;">您确定要更换绑定的手机吗?</text>
60
+    <view class="use-btn">
61
+      <view class="btn cancel flex-center" bindtap="onPopupState" data-key="visible" data-value="{{false}}">取消</view>
62
+      <navigator url="/pages/phone/phone?phone={{userInfo.mobile}}" hover-class="none">
63
+        <view class="btn confirm flex-center">更换</view>
64
+      </navigator>
65
+    </view>
66
+    <!-- [温馨提示] 确认激活后,该会员卡将立即生效; 如暂不激活,可在“已购会员卡”中去选择激活。 -->
67
+  </view>
68
+</wux-landscape>

+ 68 - 0
pages/dossier/dossier.wxss

@@ -0,0 +1,68 @@
1
+page {
2
+  background: #fff;
3
+}
4
+
5
+.list .item {
6
+  height: 192rpx;
7
+  padding: 0 30rpx;
8
+  box-sizing: border-box;
9
+  border-bottom: 2rpx solid #EEEEEE;
10
+}
11
+
12
+.list .item text {
13
+  font-size: 36rpx;
14
+}
15
+
16
+.list .item .title {
17
+  color: #999;
18
+  font-size: 28rpx;
19
+  margin-bottom: 22rpx;
20
+}
21
+
22
+.list .item .avatar {
23
+  width: 116rpx;
24
+  height: 116rpx;
25
+  border-radius: 50%;
26
+  background: #EDEDED;
27
+  border: 2rpx solid #979797;
28
+}
29
+
30
+.icon-right {
31
+  width: 48rpx;
32
+  height: 48rpx;
33
+}
34
+
35
+
36
+
37
+.use-popup {
38
+  width: 558rpx;
39
+  height: 508rpx;
40
+  background: #FFFFFF;
41
+  border-radius: 40rpx;
42
+  justify-content: center;
43
+  /* justify-content: space-evenly; */
44
+}
45
+
46
+/* .use-popup .red {
47
+  font-size: 24rpx;
48
+  font-family: PingFangSC-Regular, PingFang SC;
49
+  font-weight: 400;
50
+  color: #E95564;
51
+} */
52
+
53
+.use-btn .btn {
54
+  /* width: 208rpx; */
55
+  height: 92rpx;
56
+  margin: 0 48rpx;
57
+  font-size: 36rpx;
58
+  color: #FF77B0;
59
+  border-radius: 52rpx;
60
+  border: 2rpx solid #FF77B0;
61
+}
62
+
63
+.use-btn .confirm {
64
+  margin-top: 28rpx;
65
+  /* margin-left: 46rpx; */
66
+  background-color: #FF77B0;
67
+  color: #fff;
68
+}

+ 191 - 31
pages/feedback/feedback.js

@@ -1,4 +1,7 @@
1
-// pages/feedback/feedback.js
1
+import {
2
+  get,
3
+  post
4
+} from '../../utils/http';
2 5
 Page({
3 6
 
4 7
   /**
@@ -8,43 +11,75 @@ Page({
8 11
     maxlength: 500,
9 12
     number: 0,
10 13
     value: "",
11
-    imgs: [
14
+    imgs: [],
15
+    // imgs: [
16
+    //   {
17
+    //     type: "image",
18
+    //     url: "https://img1.baidu.com/it/u=202543353,3627416815&fm=26&fmt=auto"
19
+    //   },
20
+    //   {
21
+    //     type: "image",
22
+    //     url: "https://img0.baidu.com/it/u=745609344,230882238&fm=26&fmt=auto"
23
+    //   },
24
+    //   {
25
+    //     type: "image",
26
+    //     url: "https://img0.baidu.com/it/u=286636366,3227707112&fm=26&fmt=auto"
27
+    //   },
28
+    //   {
29
+    //     type: "image",
30
+    //     url: "https://img1.baidu.com/it/u=2450865760,444795162&fm=26&fmt=auto"
31
+    //   },
32
+    //   {
33
+    //     type: "image",
34
+    //     url: "https://img0.baidu.com/it/u=4226275504,4103997964&fm=26&fmt=auto"
35
+    //   },
36
+    //   {
37
+    //     type: "image",
38
+    //     url: "https://img0.baidu.com/it/u=2247422843,411257408&fm=26&fmt=auto"
39
+    //   },
40
+    // ],
41
+    raterList: [
12 42
       {
13
-        type: "video",
14
-        url: "http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400"
43
+        text: '整体满意度',
44
+        num: 1
15 45
       },
16 46
       {
17
-        type: "image",
18
-        url: "https://img1.baidu.com/it/u=202543353,3627416815&fm=26&fmt=auto"
47
+        text: '效果认可度',
48
+        num: 2
19 49
       },
20 50
       {
21
-        type: "image",
22
-        url: "https://img0.baidu.com/it/u=745609344,230882238&fm=26&fmt=auto"
51
+        text: '价格接受度',
52
+        num: 3
23 53
       },
24 54
       {
25
-        type: "image",
26
-        url: "https://img0.baidu.com/it/u=286636366,3227707112&fm=26&fmt=auto"
55
+        text: '类别满意度',
56
+        num: 4
27 57
       },
28 58
       {
29
-        type: "image",
30
-        url: "https://img1.baidu.com/it/u=2450865760,444795162&fm=26&fmt=auto"
59
+        text: '安全认可度',
60
+        num: 5
31 61
       },
32
-      {
33
-        type: "image",
34
-        url: "https://img0.baidu.com/it/u=4226275504,4103997964&fm=26&fmt=auto"
35
-      },
36
-      {
37
-        type: "image",
38
-        url: "https://img0.baidu.com/it/u=2247422843,411257408&fm=26&fmt=auto"
39
-      },
40
-    ]
62
+    ],
63
+    raterStatus: {
64
+      1: '差',
65
+      2: '较差',
66
+      3: '一般',
67
+      4: '满意',
68
+      5: '非常好',
69
+    }
41 70
   },
42 71
 
43 72
   /**
44 73
    * 生命周期函数--监听页面加载
45 74
    */
46 75
   onLoad: function (options) {
47
-
76
+    if(options.orderId) {
77
+      this.setData({
78
+        orderId: options.orderId,
79
+        cover: options.cover,
80
+        name: options.name
81
+      })
82
+    }
48 83
   },
49 84
 
50 85
   /**
@@ -140,17 +175,142 @@ Page({
140 175
       sizeType: ['original', 'compressed'],
141 176
       sourceType: ['album', 'camera'],
142 177
       success (res) {
143
-        const tempFilePaths = res.tempFilePaths
144
-        tempFilePaths.forEach((item) => {
145
-          imgs.push({
146
-            type: 'image',
147
-            url: item
148
-          })
178
+        wx.showLoading({
179
+          title: '正在上传图片',
180
+          mask: true
181
+        });
182
+        const tempFiles = res.tempFiles
183
+        tempFiles.forEach((item) => {
184
+          that.upload(item.path).then((data) => {
185
+            imgs.push({
186
+              type: 'image',
187
+              url: data.data.url,
188
+            })
189
+            that.setData({ imgs })
190
+            wx.hideLoading()
191
+          });
149 192
         })
150
-        that.setData({
151
-          imgs
193
+        
194
+      }
195
+    })
196
+  },
197
+
198
+  /**
199
+   * 选择视频
200
+  */
201
+  chooseVideo(){
202
+    let that = this;
203
+    let imgs = this.data.imgs;
204
+    wx.chooseMedia({
205
+      count: 1,
206
+      mediaType: ['video'],
207
+      sourceType: ['album', 'camera'],
208
+      maxDuration: 30,
209
+      camera: 'back',
210
+      success(res) {
211
+        wx.showLoading({
212
+          title: '正在上传视频',
213
+          mask: true
214
+        });
215
+        const tempFiles = res.tempFiles
216
+        that.upload(tempFiles[0].tempFilePath).then((data) => {
217
+          imgs.unshift({
218
+            type: 'video',
219
+            thumb: tempFiles[0].thumbTempFilePath,
220
+            url: data.data.url
221
+          })
222
+          that.setData({ imgs })
223
+          wx.hideLoading()
224
+        });
225
+        return;
226
+        tempFiles.forEach((item) => {
227
+          imgs.unshift({
228
+            type: 'video',
229
+            thumb: item.thumbTempFilePath,
230
+            url: item.tempFilePath
231
+          })
152 232
         })
233
+        that.setData({ imgs })
153 234
       }
154 235
     })
155
-  }
236
+  },
237
+
238
+  /**
239
+   * 选择星星
240
+  */
241
+  onChange(e) {
242
+    let index = e.currentTarget.dataset.index
243
+    let value = e.detail.value
244
+    this.setData({
245
+      ['raterList[' + index + '].num']: value
246
+    })
247
+  },
248
+
249
+  /**
250
+   * 提交反馈
251
+  */
252
+  onSubmit() {
253
+    // api/feedback/add
254
+    let { orderId,value,raterList } = this.data;
255
+    console.log(this.data.imgs)
256
+    post('api/feedback/add',{
257
+      order_id: orderId,
258
+      content: value,
259
+      media_list: JSON.stringify(this.data.imgs),
260
+      score_whole: raterList[0].num,
261
+      score_effect: raterList[1].num,
262
+      score_price: raterList[2].num,
263
+      score_kind: raterList[3].num,
264
+      score_safe: raterList[4].num,
265
+    },(res) => {
266
+      console.log(res)
267
+    })
268
+  },
269
+
270
+  /**
271
+   * 上传视频/图片
272
+  */
273
+  upload(filePath) {
274
+    let that = this;
275
+    // 上传类型/业务类型:avatar头像,order订单反馈,check检查表反馈
276
+    return new Promise((resolve,reject) => {
277
+      let { imgs } = this.data;
278
+      wx.uploadFile({
279
+        url: 'https://store.test-api.ijolijoli.com/api/upload',
280
+        header: {
281
+          token: wx.getStorageSync('token') || '',
282
+        },
283
+        filePath,
284
+        name: 'file',
285
+        formData: {
286
+          'type': 'order'
287
+        },
288
+        success(res) {
289
+          console.log(res)
290
+          if(res.statusCode == 200 && res.data) {
291
+            let data = JSON.parse(res.data);
292
+            if(data.code == 200 && data.data) {
293
+              resolve(data)
294
+            } else {
295
+              wx.showToast({
296
+                title: '上传失败',
297
+                icon: 'none'
298
+              })
299
+            }
300
+          } else {
301
+            wx.showToast({
302
+              title: '上传失败',
303
+              icon: 'none'
304
+            })
305
+          }
306
+        },
307
+        fail(err) {
308
+          wx.showToast({
309
+            title: '上传失败',
310
+            icon: 'none'
311
+          })
312
+        }
313
+      })
314
+    })
315
+  },
156 316
 })

+ 3 - 1
pages/feedback/feedback.json

@@ -1,4 +1,6 @@
1 1
 {
2 2
   "navigationBarTitleText": "发布记录反馈",
3
-  "usingComponents": {}
3
+  "usingComponents": {
4
+    "wux-rater": "../../components/dist/rater/index"
5
+  }
4 6
 }

+ 23 - 5
pages/feedback/feedback.wxml

@@ -1,8 +1,8 @@
1 1
 <view class="content">
2 2
   <view class="detail border-radius">
3 3
     <view class="top flex-align-center">
4
-      <image src=""></image>
5
-      <text style="font-family: PingFangSC-Medium, PingFang SC;font-weight: 500;">水氧清洁水光提升综合管理</text>
4
+      <image src="{{ cover }}"></image>
5
+      <text class="flex1 ellipsis" style="font-family: PingFangSC-Medium, PingFang SC;font-weight: 500;">{{ name }}</text>
6 6
     </view>
7 7
     <view class="box-textarea">
8 8
       <textarea bindinput="bindTextAreaInput" value="{{value}}" maxlength="{{maxlength}}" placeholder="请输入记录反馈" />
@@ -19,7 +19,12 @@
19 19
             <block wx:if="{{ item.type == 'video' }}">
20 20
               <!-- <video src="{{ item.url }}"></video> -->
21 21
               <!-- <text>视频播放</text> -->
22
-              <view class="flex-center" style="font-size: 28rpx;width: 100%;height: 100%;background-color: #ccc;" data-index="{{index}}" bindtap="previewMedia">这个是视频</view>
22
+              <view class="flex-center" style="font-size: 28rpx;width: 100%;height: 100%;background-color: #ccc;" data-index="{{index}}" bindtap="previewMedia">
23
+                <!-- 这个是视频 -->
24
+                <image class="icon-01" src="/images/icon_01.png"></image>
25
+                <!-- <text style="position: absolute;">这个是视频</text> -->
26
+                <image class="img-content" data-index="{{index}}" src="{{ item.thumb }}" alt=""></image>
27
+              </view>
23 28
             </block>
24 29
             <block wx:if="{{ item.type == 'image' }}">
25 30
               <image class="img-content" data-index="{{index}}" src="{{ item.url }}" alt="" bindtap="previewMedia"></image>
@@ -28,15 +33,28 @@
28 33
           <image src="" class="delete-img" data-index="{{index}}" bindtap="onDeleteItem"></image>
29 34
         </view>
30 35
         
31
-        <view class="item upload flex-column flex-center" wx:if="{{ imgs.length>0 && imgs.length < 9 &&imgs[0].type != 'video' }}">
36
+        <view class="item upload flex-column flex-center" wx:if="{{ imgs.length < 9 &&imgs[0].type != 'video' }}" bindtap="chooseVideo">
32 37
           <image src=""></image>
33 38
           <text style="font-size: 28rpx;">添加视频</text>
34 39
         </view>
35
-        <view class="item upload flex-column flex-center" wx:if="{{ imgs.length > 0 && imgs.length < 9}}" bindtap="chooseImage">
40
+        <view class="item upload flex-column flex-center" wx:if="{{ imgs.length < 9}}" bindtap="chooseImage">
36 41
           <image src=""></image>
37 42
           <text style="font-size: 28rpx;">添加图片</text>
38 43
         </view>
39 44
       </view>
40 45
     </view>
41 46
   </view>
47
+  <view class="detail border-radius rater">
48
+    <view class="rater-text">
49
+      <text style="font-family: PingFangSC-Medium, PingFang SC;font-weight: 500;">客户评价</text>
50
+      <text style="font-size: 28rpx;color:#999;">(选填,且只有第一次填写生效)</text>
51
+    </view>
52
+
53
+    <view class="item flex-align-center" wx:for="{{ raterList }}" wx:key="index">
54
+      <text class="text-01">{{ item.text }}</text>
55
+      <wux-rater slot="footer" active-color="#FF77B0" margin="{{ 12 }}" font-size="{{ 18 }}" default-value="{{ item.num }}" data-index="{{ index }}" bind:change="onChange" />
56
+      <text class="text-02">{{ raterStatus[item.num] }}</text>
57
+    </view>
58
+  </view>
59
+  <view class="btn flex-center" bindtap="onSubmit">提交</view>
42 60
 </view>

+ 38 - 0
pages/feedback/feedback.wxss

@@ -98,7 +98,45 @@
98 98
   height: 100%;
99 99
 }
100 100
 
101
+.change-img .img-box .icon-01 {
102
+  width: 64rpx;
103
+  height: 64rpx;
104
+  position: absolute;
105
+}
106
+
101 107
 .change-img .img-box .img-content {
102 108
   width: 100%;
103 109
   height: 100%;
104 110
 }
111
+
112
+.rater .item {
113
+  margin-top: 34rpx;
114
+}
115
+
116
+.rater .text-01 {
117
+  font-size: 28rpx;
118
+  font-family: PingFangSC-Medium, PingFang SC;
119
+  font-weight: 500;
120
+  color: #666666;
121
+  line-height: 32rpx;
122
+  margin-right: 20rpx;
123
+}
124
+
125
+.rater .text-02 {
126
+  font-size: 28rpx;
127
+  font-family: PingFangSC-Medium, PingFang SC;
128
+  font-weight: 500;
129
+  color: #FF77B0;
130
+  line-height: 32rx;
131
+  margin-left: 36rpx;
132
+} 
133
+
134
+.btn {
135
+  margin: 30rpx 0;
136
+  height: 96rpx;
137
+  width: 100%;
138
+  background: #FF77B0;
139
+  border-radius: 48rpx;
140
+  font-size: 36rpx;
141
+  color: #fff;
142
+}

+ 83 - 6
pages/home/home.js

@@ -1,18 +1,39 @@
1
-// pages/home/home.js
1
+import {
2
+  get,
3
+  post
4
+} from '../../utils/http';
2 5
 Page({
3 6
 
4 7
   /**
5 8
    * 页面的初始数据
6 9
    */
7 10
   data: {
8
-    current: 0
11
+    current: 0,
12
+    currentDay: '2021-11-29',
13
+    startDay: '1998-04-20',
14
+    endDay: '2021-11-29',
15
+    homeData: {},
16
+    list: [],
17
+    total: 0,
18
+    page: 1
9 19
   },
10 20
 
11 21
   /**
12 22
    * 生命周期函数--监听页面加载
13 23
    */
14 24
   onLoad: function (options) {
15
-
25
+    let date = new Date()
26
+    let year = date.getFullYear()
27
+    let month = date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1
28
+    let day = date.getDate() < 10 ? '0'+date.getDate() : date.getDate()
29
+    console.log(year,month,day)
30
+    this.setData({
31
+      currentDay: `${year}-${month}-${day}`,
32
+      startDay: '1998-04-20',
33
+      endDay: `${year}-${month}-${day}`,
34
+    })
35
+    this.getHomeData();
36
+    this.getOrderList();
16 37
   },
17 38
 
18 39
   /**
@@ -54,7 +75,9 @@ Page({
54 75
    * 页面上拉触底事件的处理函数
55 76
    */
56 77
   onReachBottom: function () {
57
-
78
+    if(this.data.page * 10 < this.data.total) {
79
+      this.getOrderList(++this.data.page)
80
+    }
58 81
   },
59 82
 
60 83
   /**
@@ -73,9 +96,63 @@ Page({
73 96
     }
74 97
     this.setData({
75 98
       current,
76
-      // page: 1,
99
+      page: 1,
77 100
     },() => {
78
-      // this.getPlanList()
101
+      this.getOrderList(1)
102
+    })
103
+  },
104
+
105
+  /**
106
+   * 选择时间
107
+  */
108
+  bindDateChange: function(e) {
109
+    console.log('picker发送选择改变,携带值为', e.detail.value)
110
+    this.setData({
111
+      [e.currentTarget.dataset.date]: e.detail.value,
112
+    })
113
+  },
114
+
115
+  /**
116
+   * 订单搜索
117
+  */
118
+  onSearchOrder() {
119
+    this.getOrderList();
120
+  },
121
+
122
+  /**
123
+   * 获取首页统计数据
124
+  */
125
+  getHomeData() {
126
+    get('api/home',{},(res) => {
127
+      this.setData({
128
+        homeData: res.data
129
+      })
79 130
     })
80 131
   },
132
+
133
+  /**
134
+   * 首页订单列表
135
+  */
136
+  getOrderList(_page) {
137
+    let { list,current,page,startDay,endDay } = this.data;
138
+    get('api/order/list',{
139
+      start_day: startDay,
140
+      end_day: endDay,
141
+      type: current,
142
+      page: _page || page,
143
+      limit: 10
144
+    },(res) => {
145
+      if (_page == 1 || page == 1) {
146
+        list = []
147
+        this.data.page = 1
148
+      }
149
+      list.push(...res.data.list)
150
+      this.setData({
151
+        list,
152
+        total: res.data.total,
153
+        spinning: false
154
+      })
155
+      console.log(res)
156
+    })
157
+  }
81 158
 })

+ 24 - 17
pages/home/home.wxml

@@ -1,32 +1,37 @@
1 1
 <view class="content">
2 2
   <view class="top-text flex-column">
3
-    <text class="text-1" >今日预约订单数:18</text>
4
-    <text style="font-size: 28rpx;color: #999999;margin-top: 12rpx;">今日服务订单数:11</text>
3
+    <text class="text-1" >今日预约订单数:{{ homeData.order_num }}</text>
4
+    <text style="font-size: 28rpx;color: #999999;margin-top: 12rpx;">今日服务订单数:{{ homeData.served_num }}</text>
5 5
     <view class="flex-align-center" style="margin-top: 12rpx;">
6
-      <image src=""></image>
7
-      <text style="font-size: 24rpx;">南山华润置地XX楼808号(南山店)</text>
6
+      <image src="/images/icon_05.png"></image>
7
+      <text class="flex1 ellipsis" style="font-size: 24rpx;">{{ homeData.store_address }} {{ homeData.store_name }}</text>
8 8
     </view>
9 9
   </view>
10 10
   <view class="tabs-bgc">
11 11
     <view class="tabs-wrap flex-align-center">
12 12
       <view class="item flex-center {{current == 0 ? 'active' : ''}}" bindtap="onTabsChange" data-id="0">进行中</view>
13
-      <view class="item flex-center {{current == 1 ? 'active' : ''}}" bindtap="onTabsChange" data-id="1">待添加</view>
13
+      <view class="item flex-center {{current == 1 ? 'active' : ''}}" bindtap="onTabsChange" data-id="1">待进行</view>
14 14
       <view class="item flex-center {{current == 2 ? 'active' : ''}}" bindtap="onTabsChange" data-id="2">已结束</view>
15 15
     </view>
16 16
   </view>
17 17
   <view class="change-time border-radius flex-center">
18
-    <view class="time flex-align-center" style="margin-right: 50rpx;">
19
-      <text>2019-08-07</text>
20
-      <view class="triangle"></view>
21
-    </view>
22
-    <view class="time flex-align-center" style="margin-right: 64rpx;">
23
-      <text>2019-08-07</text>
24
-      <view class="triangle"></view>
25
-    </view>
26
-    <view class="search flex-center">搜索</view>
18
+    <picker mode="date" value="{{startDay}}" start="1998-04-20" end="{{endDay}}" bindchange="bindDateChange" data-date="startDay">
19
+      <view class="time flex-align-center" style="margin-right: 50rpx;">
20
+        <text>{{startDay}}</text>
21
+        <view class="triangle"></view>
22
+      </view>
23
+    </picker>
24
+    
25
+    <picker mode="date" value="{{endDay}}" start="{{startDay}}" end="{{currentDay}}" bindchange="bindDateChange" data-date="endDay">
26
+      <view class="time flex-align-center" style="margin-right: 64rpx;">
27
+        <text>{{endDay}}</text>
28
+        <view class="triangle"></view>
29
+      </view>
30
+    </picker>
31
+    <view class="search flex-center" bindtap="onSearchOrder">搜索</view>
27 32
   </view>
28 33
   <view class="tabs-content">
29
-    <navigator url="/pages/orderDetail/orderDetail" hover-class="none" wx:for="{{ 2 }}" wx:key="index">
34
+    <navigator url="/pages/orderDetail/orderDetail?orderId={{item.order_id}}" hover-class="none" wx:for="{{ list }}" wx:key="index">
30 35
       <view class="item border-radius">
31 36
         <view class="item-top flex-align-center">
32 37
           <text class="flex1">水氧清洁水光提升综合管理</text>
@@ -44,11 +49,13 @@
44 49
         </view>
45 50
         <view class="item-btn flex-align-center flex-justify-space-between">
46 51
           <view class="btn flex-center">订单详情</view>
47
-          <view class="btn flex-center">客户信息</view>
52
+          <navigator url="/pages/clientInfo/clientInfo" hover-class="none">
53
+            <view class="btn flex-center">客户信息</view>
54
+          </navigator>
48 55
           <view class="btn flex-center">记录反馈</view>
49 56
         </view>
50 57
       </view>
51 58
     </navigator>
52
-    
59
+    <view class="no-data" style="display: {{list.length == 0 ? 'block' : 'none'}};">暂无数据~</view>
53 60
   </view>
54 61
 </view>

+ 143 - 0
pages/login/login.js

@@ -0,0 +1,143 @@
1
+import {
2
+  post,
3
+} from '../../utils/http';
4
+import {
5
+  objToParam,
6
+} from '../../utils/util';
7
+Page({
8
+
9
+  /**
10
+   * 页面的初始数据
11
+   */
12
+  data: {
13
+    userInfo: null,
14
+    prePage: 0
15
+  },
16
+
17
+  /**
18
+   * 生命周期函数--监听页面加载
19
+   */
20
+  onLoad: function (options) {
21
+    console.log(options)
22
+    if(options.prePage) {
23
+      this.setData({
24
+        prePage: options.prePage
25
+      })
26
+    }
27
+    
28
+  },
29
+
30
+  /**
31
+   * 生命周期函数--监听页面初次渲染完成
32
+   */
33
+  onReady: function () {
34
+
35
+  },
36
+
37
+  /**
38
+   * 生命周期函数--监听页面显示
39
+   */
40
+  onShow: function () {
41
+
42
+  },
43
+
44
+  /**
45
+   * 生命周期函数--监听页面隐藏
46
+   */
47
+  onHide: function () {
48
+
49
+  },
50
+
51
+  /**
52
+   * 生命周期函数--监听页面卸载
53
+   */
54
+  onUnload: function () {
55
+
56
+  },
57
+
58
+  /**
59
+   * 页面相关事件处理函数--监听用户下拉动作
60
+   */
61
+  onPullDownRefresh: function () {
62
+
63
+  },
64
+
65
+  /**
66
+   * 页面上拉触底事件的处理函数
67
+   */
68
+  onReachBottom: function () {
69
+
70
+  },
71
+
72
+  /**
73
+   * 用户点击右上角分享
74
+   */
75
+  onShareAppMessage: function () {
76
+
77
+  },
78
+
79
+  /**
80
+   * 获取用户信息
81
+   */
82
+  getUserInfo(e) {
83
+    wx.getUserProfile({
84
+      desc: 'desc',
85
+      success: (res) => {
86
+        this.setData({
87
+          userInfo: JSON.parse(res.rawData)
88
+        })
89
+        console.log(JSON.parse(res.rawData))
90
+      }
91
+    })
92
+  },
93
+
94
+  /**
95
+   * 获取用户手机号
96
+   */
97
+  getPhoneNumber(e) {
98
+    let that = this;
99
+    wx.login({
100
+      success(res) {
101
+        post('api/login', {
102
+          js_code: res.code,
103
+          avatar_url: that.data.userInfo.avatarUrl,
104
+          nickname: that.data.userInfo.nickName,
105
+          iv: e.detail.iv,
106
+          encrypted_data: e.detail.encryptedData,
107
+          wx_data: JSON.stringify(that.data.userInfo)
108
+        }, (res) => {
109
+          console.log(res)
110
+          wx.setStorageSync('token', res.data.token);
111
+          wx.setStorageSync('userInfo', res.data)
112
+          wx.showToast({
113
+            title: res.msg,
114
+            icon: 'none'
115
+          })
116
+          setTimeout(() => {
117
+            if(that.data.prePage == 1) {
118
+              wx.navigateBack()
119
+            } else {
120
+              let sceneData = getApp().globalData.sceneData
121
+              wx.reLaunch({
122
+                url: `/${sceneData.path}?${objToParam(sceneData.query)}`,
123
+              })
124
+              // wx.switchTab({
125
+              //   url: '/pages/home/home',
126
+              // })
127
+            }
128
+          }, 1500);
129
+        })
130
+      }
131
+    })
132
+    
133
+  },
134
+
135
+  /**
136
+   * 跳转去用户协议
137
+   */
138
+  goToAgree(e) {
139
+    wx.navigateTo({
140
+      url: '/pages/agreement/agreement?agree=' + e.currentTarget.dataset.agree,
141
+    })
142
+  }
143
+})

+ 3 - 0
pages/login/login.json

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

+ 15 - 0
pages/login/login.wxml

@@ -0,0 +1,15 @@
1
+<view class="content flex-column">
2
+  <view class="box flex-column flex-center">
3
+    <image src="/images/icon_logo.png" wx:if="{{!userInfo}}"></image>
4
+    <image class="avatar" src="{{userInfo.avatarUrl}}" wx:else></image>
5
+    <button class="flex-center" bindtap="getUserInfo" wx:if="{{!userInfo}}">授权微信用户信息</button>
6
+    <button class="flex-center" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber" wx:else>授权绑定手机号</button>
7
+  </view>
8
+  <view class="flex1"></view>
9
+  <view class="bottom">
10
+    <text>登录代表同意I JOLI JOLI </text>
11
+    <text style="color: #E95564;" bindtap="goToAgree" data-agree="user_agreement">用户协议、</text>
12
+    <text style="color: #E95564;" bindtap="goToAgree" data-agree="user_private">隐私协议</text>
13
+  </view>
14
+</view>
15
+

+ 39 - 0
pages/login/login.wxss

@@ -0,0 +1,39 @@
1
+page {
2
+  background: #F9F9F9;
3
+}
4
+
5
+page,.content {
6
+  height: 100%;
7
+}
8
+
9
+.content button {
10
+  width: 654rpx;
11
+  height: 84rpx;
12
+  background-color: #12B311;
13
+  color: #fff;
14
+  font-size: 32rpx;
15
+  border-radius: 4rpx;
16
+}
17
+
18
+.content image {
19
+  width: 290rpx;
20
+  height: 290rpx;
21
+  margin: 176rpx 0 128rpx 0;
22
+}
23
+
24
+.content .avatar {
25
+  border-radius: 50%;
26
+  width: 254rpx;
27
+  height: 254rpx;
28
+}
29
+
30
+.bottom {
31
+  text-align: center;
32
+  margin-bottom: 26rpx;
33
+}
34
+
35
+.bottom text {
36
+  font-size: 24rpx;
37
+  color: #666;
38
+}
39
+

+ 87 - 5
pages/member/member.js

@@ -1,11 +1,21 @@
1
-// pages/member/member.js
1
+// import {
2
+//   get,
3
+//   post
4
+// } from '../../utils/http';
5
+// import {
6
+//   formatActivity,
7
+// } from '../../utils/time';
2 8
 Page({
3 9
 
4 10
   /**
5 11
    * 页面的初始数据
6 12
    */
7 13
   data: {
8
-
14
+    userInfo: {},
15
+    agree: false,
16
+    visible: false,
17
+    sum: 0,
18
+    activity: {}
9 19
   },
10 20
 
11 21
   /**
@@ -26,7 +36,9 @@ Page({
26 36
    * 生命周期函数--监听页面显示
27 37
    */
28 38
   onShow: function () {
29
-
39
+    // this.getUser()
40
+    // this.getUserActivity()
41
+    // this.setData({ agree: wx.getStorageSync('agree') || false })
30 42
   },
31 43
 
32 44
   /**
@@ -40,7 +52,9 @@ Page({
40 52
    * 生命周期函数--监听页面卸载
41 53
    */
42 54
   onUnload: function () {
43
-
55
+    if(this.timer_) {
56
+      clearInterval(this.timer_);
57
+    }
44 58
   },
45 59
 
46 60
   /**
@@ -62,5 +76,73 @@ Page({
62 76
    */
63 77
   onShareAppMessage: function () {
64 78
 
65
-  }
79
+  },
80
+
81
+  // 点击头像切换环境
82
+  onAvatar() {
83
+    this.data.sum++
84
+    if(this.data.sum >= 10) {
85
+      this.setData({
86
+        visible: true,
87
+        sum: 0
88
+      })
89
+    }
90
+  },
91
+
92
+  /**
93
+   * 获取用户信息
94
+   * api/user
95
+  */
96
+  getUser() {
97
+    get('api/user',{},(res) => {
98
+      if(res.data) {
99
+        this.setData({
100
+          userInfo: res.data
101
+        })
102
+        wx.setStorageSync('userInfo',res.data);
103
+      }
104
+      console.log(res)
105
+    })
106
+  },
107
+
108
+  /**
109
+   * 跳转vip页面
110
+  */
111
+  goToVip() {
112
+    console.log(this.data.userInfo)
113
+    let nextDatas = JSON.stringify(this.data.userInfo)
114
+    wx.navigateTo({
115
+      url: `/pages/vip/vip?userInfo=${encodeURIComponent(nextDatas)}`
116
+      // url: `/pages/vip/vip?userInfo=${JSON.stringify(this.data.userInfo)}`
117
+      // url: '/pages/vip/vip?userInfo='+ JSON.stringify(this.data.userInfo),
118
+    })
119
+  },
120
+
121
+  /**
122
+   * 获取首次开卡优惠
123
+   * api/user/activity
124
+  */
125
+  getUserActivity() {
126
+    if(this.timer) {
127
+      clearInterval(this.timer_);
128
+    }
129
+    get('api/user/activity',{},(res) => {
130
+      if(res.data) {
131
+        res.data.currentTime = formatActivity(res.data.expire_time_seconds)
132
+        this.setData({
133
+          activity: res.data
134
+        })
135
+        this.timer_ = setInterval(() => {
136
+          if(res.data.expire_time_seconds <= 0) {
137
+            clearInterval(this.timer_);
138
+          }
139
+          res.data.expire_time_seconds--
140
+          res.data.currentTime = formatActivity(res.data.expire_time_seconds)
141
+          this.setData({
142
+            activity: res.data
143
+          })
144
+        }, 1000);
145
+      }
146
+    })
147
+  },
66 148
 })

+ 111 - 2
pages/member/member.wxml

@@ -1,2 +1,111 @@
1
-<!--pages/member/member.wxml-->
2
-<text>pages/member/member.wxml</text>
1
+
2
+<view class="user-info">
3
+  <!-- <image class="bg" src="/images/background/my-background.png"></image> -->
4
+  <view class="user-info-box flex flex-align-center {{ userInfo.is_vip == 1 ? 'active' : '' }}">
5
+    <image class="avatar" src="{{userInfo.avatar_url || '/images/header.png'}}" bindtap="onAvatar"></image>
6
+    <view class="info-right flex-column">
7
+      <text class="nick-name">{{userInfo.nickname || '微信用户'}}</text>
8
+      <!-- <view class="intro flex-align-center">
9
+        <view class="gender flex-center" style="margin-right: 14rpx;" wx:if="{{ userInfo.sex == 1 || userInfo.sex == 2 }}">
10
+          <image src="/images/icon_gender_{{userInfo.sex}}.png"></image>
11
+        </view>
12
+        <view class="city flex-center" style="margin-right: 16rpx;">深圳</view>
13
+        <view class="vip-time flex-center" wx:if="{{userInfo.vip}}">
14
+          <image src="/images/icon_vip_04.png"></image>
15
+          <text>{{userInfo.vip.card_name}}({{userInfo.vip.store_time_alias}})</text>
16
+        </view>
17
+      </view>
18
+      <text style="color: #666;font-size: 24rpx;" wx:if="{{userInfo.is_vip == 1}}">会员有效期时间:{{userInfo.vip.valid_time}}</text>
19
+      <text style="color: #666;font-size: 28rpx;" wx:if="{{userInfo.is_vip == 0}}">您还没有开通会员哦~</text> -->
20
+    </view>
21
+  </view>
22
+</view>
23
+
24
+<!-- <view class="order item-radius" style="margin-top: -66rpx;">
25
+  <navigator url="/pages/order/order?current=0" hover-class="none">
26
+    <view class="item">
27
+      <image src="/images/dfk.png"></image>
28
+      <text>待付款</text>
29
+      <view class="item-num flex-center" wx:if="{{userInfo.order_num && userInfo.order_num.wait_pay_num > 0}}">{{userInfo.order_num.wait_pay_num > 99 ? '99' : userInfo.order_num.wait_pay_num }}</view>
30
+    </view>
31
+  </navigator>
32
+  <navigator url="/pages/order/order?current=1" hover-class="none">
33
+    <view class="item">
34
+      <image src="/images/yyy.png"></image>
35
+      <text>已预约</text>
36
+      <view class="item-num flex-center" wx:if="{{userInfo.order_num && userInfo.order_num.wait_use_num > 0}}">{{userInfo.order_num.wait_use_num > 99 ? '99' : userInfo.order_num.wait_use_num }}</view>
37
+    </view>
38
+  </navigator>
39
+  <navigator url="/pages/order/order?current=2" hover-class="none">
40
+    <view class="item">
41
+      <image src="/images/jxz.png"></image>
42
+      <text>进行中</text>
43
+      <view class="item-num flex-center" wx:if="{{userInfo.order_num && userInfo.order_num.using_num > 0}}">{{userInfo.order_num.using_num > 99 ? '99' : userInfo.order_num.using_num }}</view>
44
+    </view>
45
+  </navigator>
46
+  <navigator url="/pages/order/order?current=3" hover-class="none">
47
+    <view class="item">
48
+      <image src="/images/yjs.png"></image>
49
+      <text>已结算</text>
50
+    </view>
51
+  </navigator>
52
+  <navigator url="/pages/order/order?current=4" hover-class="none">
53
+    <view class="item">
54
+      <image src="/images/yqx.png"></image>
55
+      <text>已取消</text>
56
+    </view>
57
+  </navigator>
58
+</view> -->
59
+
60
+<view class="tools">
61
+  
62
+  <view class="item-radius" style="margin-top: 20rpx;">
63
+    <navigator url="/pages/recordList/recordList">
64
+      <view class="item line">
65
+        <image class="icon-left" src="/images/tools_07.png"></image>
66
+        <text class="flex1">门店检查记录</text>
67
+        <text class="active" style="color:#999999;">今日未检查</text>
68
+        <image class="icon-right" src="/images/right.png"></image>
69
+      </view>
70
+    </navigator>
71
+
72
+    <navigator url="/pages/beautyPlanList/beautyPlanList">
73
+      <view class="item line">
74
+        <image class="icon-left" src="/images/tools_08.png"></image>
75
+        <text class="flex1">门店客户记录</text>
76
+        <!-- <text class="active" wx:if="{{ userInfo.card_num>0 }}">{{userInfo.card_num}}张可用</text> -->
77
+        <image class="icon-right" src="/images/right.png"></image>
78
+      </view>
79
+    </navigator>
80
+    
81
+    <navigator url="/pages/dossier/dossier">
82
+      <view class="item line">
83
+        <image class="icon-left" src="/images/tools_02.png"></image>
84
+        <text class="flex1">个人信息</text>
85
+        <text class="active" style="color:#999999;">去修改</text>
86
+        <image class="icon-right" src="/images/right.png"></image>
87
+      </view>
88
+    </navigator>
89
+  </view>
90
+  
91
+  <view class="item-radius" style="margin-top: 20rpx;">
92
+    <navigator url="/pages/role/role" hover-class="none">
93
+      <view class="item line">
94
+        <image class="icon-left" src="/images/tools_05.png"></image>
95
+        <text class="flex1">切换角色</text>
96
+        <image class="icon-right" src="/images/right.png"></image>
97
+      </view>
98
+    </navigator>
99
+    <navigator url="/pages/contact/contact" hover-class="none">
100
+      <view class="item">
101
+        <image class="icon-left" src="/images/tools_06.png"></image>
102
+        <text class="flex1">联系我们</text>
103
+        <!-- <text class="active" style="color:#999999;">去修改</text> -->
104
+        <image class="icon-right" src="/images/right.png"></image>
105
+      </view>
106
+    </navigator>
107
+  </view>
108
+
109
+</view>
110
+
111
+<change-env visible="{{ visible }}"></change-env>

+ 204 - 1
pages/member/member.wxss

@@ -1 +1,204 @@
1
-/* pages/member/member.wxss */
1
+page {
2
+  background: #F9F9F9;
3
+  /* overflow: hidden; */
4
+  /* background: #fff; */
5
+}
6
+
7
+/* .user-info {
8
+  height: 290rpx;
9
+  position: relative;
10
+} */
11
+
12
+.user-info {
13
+  width: 100%;
14
+  height: 168rpx;
15
+  padding: 28rpx 30rpx;
16
+  box-sizing: border-box;
17
+  overflow: hidden;
18
+  position: relative;
19
+  /* position: relative;
20
+  z-index: -1;
21
+  overflow: hidden; */
22
+}
23
+
24
+.user-info::after {
25
+  content: '';
26
+  width: 100%;
27
+  height: 168rpx;
28
+  position: absolute;
29
+  left: 0;
30
+  top: 0;
31
+  z-index: -1;
32
+  border-radius: 0 0 24rpx 24rpx;
33
+  background: #FFCAE0;
34
+  overflow: hidden;
35
+}
36
+
37
+.user-info .bg {
38
+  width: 100%;
39
+  height: 100%;
40
+}
41
+
42
+/* .user-info-box {
43
+  position: absolute;
44
+  width: 100%;
45
+  height: 100%;
46
+  top: 0;
47
+} */
48
+
49
+.user-info-box .avatar {
50
+  width: 112rpx;
51
+  height: 112rpx;
52
+  /* margin-bottom: 20rpx; */
53
+  border-radius: 50%;
54
+  box-sizing: border-box;
55
+  border: 2rpx solid #FFFFFF;
56
+  /* background-color: #ccc; */
57
+}
58
+
59
+
60
+.user-info-box.active .avatar {
61
+  border: 2rpx solid #FF3803;
62
+}
63
+
64
+.user-info-box .info-right {
65
+  margin-left: 20rpx;
66
+}
67
+
68
+.user-info-box.active .nick-name {
69
+  font-family: PingFangSC-Medium, PingFang SC;
70
+  color: #FF3803;
71
+}
72
+
73
+.user-info-box .intro {
74
+  margin: 4rpx 0;
75
+}
76
+
77
+.user-info-box .gender {
78
+  width: 44rpx;
79
+  height: 28rpx;
80
+  background: #FFFFFF;
81
+  border-radius: 14rpx;
82
+}
83
+
84
+.user-info-box .gender image {
85
+  width: 28rpx;
86
+  height: 28rpx;
87
+}
88
+
89
+.user-info-box .city {
90
+  height: 28rpx;
91
+  padding: 0rpx 10rpx;
92
+  background: #FFFFFF;
93
+  border-radius: 14rpx;
94
+  font-size: 16rpx;
95
+  font-family: PingFangSC-Regular, PingFang SC;
96
+  font-weight: 400;
97
+  color: #666666;
98
+}
99
+
100
+.user-info-box .vip-time {
101
+  background: linear-gradient(298deg, #FFB16E 0%, #FF7F7F 100%);
102
+  border-radius: 20rpx;
103
+  height: 40rpx;
104
+  padding: 0 8rpx;
105
+}
106
+
107
+.user-info-box .vip-time image {
108
+  width: 48rpx;
109
+  height: 28rpx;
110
+}
111
+
112
+.user-info-box .vip-time text {
113
+  font-size: 28rpx;
114
+  font-family: PingFangSC-Medium, PingFang SC;
115
+  font-weight: 500;
116
+  color: #FFFFFF;
117
+}
118
+
119
+/* .user-info-box text {
120
+  color: #fff;
121
+} */
122
+
123
+.order {
124
+  height: 172rpx;
125
+  display: flex;
126
+  justify-content: space-around;
127
+  align-items: center;
128
+  background: #fff;
129
+}
130
+
131
+.order .item {
132
+  display: flex;
133
+  flex-direction: column;
134
+  position: relative;
135
+}
136
+
137
+.order .item image {
138
+  width: 72rpx;
139
+  height: 72rpx;
140
+  margin-bottom: 10rpx;
141
+}
142
+
143
+.order .item text {
144
+  font-size: 24rpx;
145
+  color: #666666;
146
+  font-family: PingFangSC-Regular;
147
+}
148
+
149
+.order .item .item-num {
150
+  position: absolute;
151
+  top: -14rpx;
152
+  right: -14rpx;
153
+  height: 28rpx;
154
+  width: 28rpx;
155
+  padding: 4rpx;
156
+  /* padding: 4rpx; */
157
+  background: #FF566B;
158
+  border-radius: 50%;
159
+  font-size: 22rpx;
160
+  font-family: PingFangSC-Regular, PingFang SC;
161
+  font-weight: 400;
162
+  color: #FFFFFF;
163
+}
164
+
165
+.tools .item {
166
+  display: flex;
167
+  align-items: center;
168
+  padding: 0 30rpx;
169
+  height: 112rpx;
170
+  background: #fff;
171
+}
172
+
173
+.tools .item.top {
174
+  margin-top: 20rpx;
175
+}
176
+
177
+.tools .item.line {
178
+  border-bottom: 1rpx solid #F9F9F9;
179
+}
180
+
181
+.tools .item .active {
182
+  font-size: 14px;
183
+  color: #FF87B9;
184
+}
185
+
186
+.tools .item .icon-left {
187
+  width: 48rpx;
188
+  height: 48rpx;
189
+  margin-right: 20rpx;
190
+  background-color: #ccc;
191
+}
192
+
193
+.tools .item .icon-right {
194
+  width: 24rpx;
195
+  height: 24rpx;
196
+  margin-left: 12rpx;
197
+}
198
+
199
+.item-radius {
200
+  background: #FFFFFF;
201
+  border-radius: 16rpx;
202
+  margin: 20rpx 30rpx;
203
+  overflow: hidden;
204
+}

+ 0 - 0
pages/name/name.js


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.