wl-design-mobile 0.8.13 → 0.8.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +6 -6
  2. package/api/doc.js +25 -25
  3. package/api/x1.js +56 -56
  4. package/btn/components/lambo-info-btn.vue +123 -123
  5. package/btn/components/lambo-primary-btn.vue +117 -117
  6. package/btn/components/lambo-text-btn.vue +116 -116
  7. package/btn/lambo-btn-divide.vue +35 -35
  8. package/btn/lambo-btn-group.vue +46 -46
  9. package/btn/lambo-btn-split-line.vue +41 -41
  10. package/btn/lambo-btn.vue +68 -68
  11. package/btn/lambo-extend-btn.vue +121 -121
  12. package/btn/lambo-icon-btn.vue +82 -82
  13. package/card-item/card-row-icon.vue +71 -71
  14. package/card-item/filtrate-row.vue +73 -73
  15. package/card-item/lambo-card-footer.vue +40 -40
  16. package/card-item/lambo-card-head.vue +66 -66
  17. package/card-item/lambo-card-row.vue +59 -59
  18. package/collapse/lambo-collapse.vue +93 -93
  19. package/config/config.js +14 -14
  20. package/detail/lambo-detail-grid.vue +58 -58
  21. package/detail/lambo-detail-row.vue +59 -59
  22. package/doc/file/compoents/lambo-file-list.vue +42 -42
  23. package/doc/file/compoents/lambo-file-row.vue +77 -77
  24. package/doc/file/lambo-file-upload-collapse.vue +107 -107
  25. package/doc/file-type/file-type.js +286 -286
  26. package/doc/img/comonents/lambo-img-grid.vue +109 -109
  27. package/doc/img/lambo-img-upload-collapse.vue +107 -109
  28. package/doc/img/styles/css/index.css +3 -3
  29. package/edit/list/Lambo-edit-list.vue +87 -87
  30. package/edit/list/lambo-edit-card-group.vue +100 -100
  31. package/edit/list/styles/css/index.css +39 -39
  32. package/edit/select/lambo-edit-date-range.vue +147 -147
  33. package/edit/select/lambo-edit-date-time.vue +122 -122
  34. package/edit/select/lambo-edit-date.vue +120 -120
  35. package/edit/select/lambo-edit-input-textarea.vue +91 -91
  36. package/edit/select/lambo-edit-input.vue +91 -91
  37. package/edit/select/lambo-edit-multi-select.vue +168 -168
  38. package/edit/select/lambo-edit-search-single-select.vue +186 -186
  39. package/edit/select/lambo-edit-single-select.vue +162 -162
  40. package/edit/select/styles/css/index.css +133 -133
  41. package/flow/approval/flow-approval.vue +552 -552
  42. package/flow/comps/flow-attachment-list.vue +230 -230
  43. package/flow/comps/flow-attachment-modal.vue +194 -194
  44. package/flow/comps/flow-copy-users.vue +358 -358
  45. package/flow/comps/flow-node-card.vue +150 -150
  46. package/flow/comps/flow-node-header.vue +172 -172
  47. package/flow/comps/flow-util.js +156 -156
  48. package/flow/comps/icons/approved.svg +11 -11
  49. package/flow/comps/icons/canceled.svg +11 -11
  50. package/flow/comps/icons/rejected.svg +11 -11
  51. package/flow/comps/icons/underway.svg +11 -11
  52. package/flow/detail/flow-detail.vue +467 -467
  53. package/flow/index.js +51 -51
  54. package/flow/lambo-flow-detail.vue +912 -912
  55. package/flow/lambo-flow-edit.vue +15 -15
  56. package/flow/submit/flow-submit.vue +383 -383
  57. package/icon/lambo-required-red-star.vue +47 -47
  58. package/icon/lambo-vector-icon.vue +71 -71
  59. package/list/lambo-list.vue +412 -294
  60. package/nav/lambo-top-nav.vue +81 -81
  61. package/order/comps/icons/approved.svg +10 -10
  62. package/order/comps/icons/underway.svg +10 -10
  63. package/order/comps/order-attachment-list.vue +228 -228
  64. package/order/comps/order-attachment-modal.vue +194 -194
  65. package/order/comps/order-node-card.vue +126 -126
  66. package/order/comps/order-node-header.vue +133 -133
  67. package/order/comps/order-util.js +132 -132
  68. package/order/detail/order-detail.vue +432 -432
  69. package/order/handle/order-handle.vue +517 -517
  70. package/order/index.js +33 -33
  71. package/package.json +1 -1
  72. package/search/lambo-date-range-page.vue +196 -196
  73. package/search/lambo-date-range.vue +111 -111
  74. package/search/lambo-date.vue +105 -105
  75. package/search/lambo-input.vue +74 -74
  76. package/search/lambo-multi-select.vue +129 -129
  77. package/search/lambo-org-select-page.vue +109 -109
  78. package/search/lambo-search-box.vue +61 -61
  79. package/search/lambo-search-single-select.vue +152 -152
  80. package/search/lambo-single-select.vue +123 -123
  81. package/search/lambo-week-date-select.vue +521 -521
  82. package/search/styles/css/index.css +138 -138
  83. package/styles/base.less +77 -77
  84. package/styles/components/btn.less +7 -7
  85. package/styles/components/search.less +33 -33
  86. package/styles/components/tag.less +181 -181
  87. package/styles/index.less +4 -4
  88. package/table/lambo-ag-table.vue +228 -227
  89. package/tag/lambo-tag.vue +23 -23
  90. package/title/lambo-title1.vue +151 -151
  91. package/title/lambo-title2.vue +37 -37
  92. package/title/lambo-title3.vue +77 -77
  93. package/util/assist.js +43 -43
  94. package/util/axios/cacheconf.js +19 -19
  95. package/util/axios/index.js +11 -11
  96. package/util/axios/interceptors.js +101 -101
  97. package/util/bus.js +4 -4
  98. package/util/jssdk-sczy.js +104 -104
  99. package/util/jssdk.js +565 -565
  100. package/util/math.js +76 -76
  101. package/util/token.js +36 -36
@@ -1,522 +1,522 @@
1
- <template>
2
- <div class="week-date-picker" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd"
3
- @mousedown="handleMouseDown" @mousemove="handleMouseMove" @mouseup="handleMouseUp" @mouseleave="handleMouseLeave">
4
- <div class="week-days">
5
- <van-icon name="arrow-left" @click="goPrevWeek" />
6
- <div v-for="day in weekDays" :key="day.date.getTime()" class="day-item"
7
- :class="{
8
- 'today': day.isToday,
9
- 'active': day.isSelected,
10
- 'has-data': day.hasData
11
- }"
12
- @click="selectDay(day)">
13
- <div class="day-week">{{ day.weekDay }}</div>
14
- <div class="day-month">{{ day.year }}-{{ day.month }}</div>
15
- <div class="day-date">{{ day.date.getDate() }}</div>
16
- <!-- 数据点显示 -->
17
- <div v-if="day.hasData" class="data-dot"></div>
18
- </div>
19
- <van-icon name="arrow" @click="goNextWeek" />
20
- </div>
21
- </div>
22
- </template>
23
-
24
- <script>
25
- const WEEK_DAYS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
26
-
27
- export default {
28
- name: "LamboWeekDateSelect",
29
-
30
- data() {
31
- return {
32
- selectedDate_: new Date(), // 当前选中的日期(Date对象),用于判断选中状态
33
- weekStart: null, // 当前周的开始日期(周一)
34
- startX: 0, // 触摸/鼠标起始X坐标
35
- endX: 0, // 触摸/鼠标结束X坐标
36
- isDragging: false, // 是否正在拖动
37
- swipeThreshold: 50, // 滑动阈值(像素),超过这个值才触发切换周
38
- clickDisabled: false, // 是否禁用点击(滑动后禁用点击,避免触发日期选择)
39
- dataDates: new Set() // 有数据的日期集合(用于显示红点)
40
- }
41
- },
42
- props: {
43
- // 选中的日期(格式:yyyyMMDD),为空时使用今天
44
- selectedDate: {
45
- type: String,
46
- default: ""
47
- },
48
- // 有数据的日期列表
49
- dataDatesList: {
50
- type: Array,
51
- default: () => []
52
- }
53
- },
54
-
55
- created() {
56
- // 初始化数据日期
57
- this.initDataDates();
58
-
59
- if (this.selectedDate) {
60
- this.initWithWeekDate(this.selectedDate);
61
- } else {
62
- const today = new Date();
63
- const todayStr = this.formatDateToYYYYMMDD(today);
64
- this.initWithWeekDate(todayStr);
65
- }
66
- },
67
-
68
- watch: {
69
- // 监听数据日期列表变化
70
- dataDatesList: {
71
- handler(newList) {
72
- this.initDataDates(newList);
73
- // 重新计算weekDays以更新数据点显示
74
- this.$forceUpdate();
75
- },
76
- deep: true
77
- },
78
- // 监听选中日期变化
79
- selectedDate: {
80
- handler(newDate) {
81
- if (newDate) {
82
- this.initWithWeekDate(newDate);
83
- this.$forceUpdate();
84
- }
85
- }
86
- }
87
- },
88
-
89
- computed: {
90
- weekDays() {
91
- if (!this.weekStart) return [];
92
- return Array.from({ length: 7 }, (_, i) => {
93
- const date = new Date(this.weekStart);
94
- date.setDate(this.weekStart.getDate() + i);
95
- const today = new Date();
96
- const dateStr = this.formatDateToYYYYMMDD(date);
97
-
98
- return {
99
- date,
100
- year: date.getFullYear(),
101
- month: date.getMonth() + 1,
102
- weekDay: WEEK_DAYS[date.getDay()],
103
- isToday: this.isSameDay(date, today),
104
- isSelected: this.isSameDay(date, this.selectedDate_),
105
- hasData: this.dataDates.has(dateStr)
106
- };
107
- });
108
- }
109
- },
110
-
111
- methods: {
112
- /**
113
- * 获取当前周的开始和结束日期
114
- * @returns {Object} 包含 weekStart 和 weekEnd 的对象
115
- */
116
- getWeekRange() {
117
- const weekStartDate = this.formatDateToYYYYMMDD(this.weekStart);
118
- const weekEndDateObj = new Date(this.weekStart);
119
- weekEndDateObj.setDate(this.weekStart.getDate() + 6);
120
- const weekEndDate = this.formatDateToYYYYMMDD(weekEndDateObj);
121
-
122
- return {
123
- weekStart: weekStartDate,
124
- weekEnd: weekEndDate
125
- };
126
- },
127
-
128
- /**
129
- * 初始化数据日期集合
130
- * @param {Array} dateList - 日期字符串数组,格式:["20241205", "20241206"]
131
- */
132
- initDataDates(dateList = null) {
133
- const dates = dateList || this.dataDatesList;
134
- this.dataDates = new Set();
135
-
136
- dates.forEach(dateStr => {
137
- if (dateStr && typeof dateStr === 'string' && dateStr.length === 8) {
138
- this.dataDates.add(dateStr);
139
- }
140
- })
141
- },
142
-
143
- /**
144
- * 根据YYYYMMDD格式的字符串初始化组件
145
- * @param {string} dateFormat- 格式如:"20251205"
146
- */
147
- initWithWeekDate(dateFormat) {
148
- // 解析YYYYMMDD格式字符串为Date对象
149
- const date = this.parseYYYYMMDD(dateFormat);
150
-
151
- if (date) {
152
- this.setWeekStart(date);
153
- this.selectedDate_ = date;
154
- } else {
155
- const today = new Date();
156
- this.setWeekStart(today);
157
- this.selectedDate_ = today;
158
- }
159
- },
160
-
161
- /**
162
- * 解析YYYYMMDD格式字符串为Date对象
163
- * @param {string} yyyymmdd - 格式如:"20251205"
164
- * @returns {Date|null} Date对象或null
165
- */
166
- parseYYYYMMDD(dateFormat) {
167
- if (!dateFormat || typeof dateFormat !== 'string' || dateFormat.length !== 8) {
168
- return null;
169
- }
170
-
171
- try {
172
- const year = parseInt(dateFormat.substring(0, 4));
173
- const month = parseInt(dateFormat.substring(4, 6)) - 1;
174
- const day = parseInt(dateFormat.substring(6, 8));
175
-
176
- const date = new Date(year, month, day);
177
-
178
- // 验证日期是否有效
179
- if (isNaN(date.getTime()) ||
180
- date.getFullYear() !== year ||
181
- date.getMonth() !== month ||
182
- date.getDate() !== day) {
183
- return null;
184
- }
185
-
186
- return date;
187
- } catch (error) {
188
- console.error("日期解析错误:", error);
189
- return null;
190
- }
191
- },
192
-
193
- /**
194
- * 设置周起始日期(周一)
195
- * @param {Date} date - 任意日期
196
- */
197
- setWeekStart(date) {
198
- const d = new Date(date);
199
- const day = d.getDay();
200
- const diff = d.getDate() - (day === 0 ? 6 : day - 1);
201
- d.setDate(diff);
202
- d.setHours(0, 0, 0, 0);
203
- this.weekStart = d;
204
- },
205
-
206
- /**
207
- * 判断两个日期是否是同一天
208
- * @param {Date} d1 - 日期1
209
- * @param {Date} d2 - 日期2
210
- * @returns {boolean} 是否是同一天
211
- */
212
- isSameDay(d1, d2) {
213
- if (!d1 || !d2) return false;
214
- return d1.toDateString() === d2.toDateString();
215
- },
216
-
217
- /**
218
- * 格式化日期为 YYYYMMDD 字符串
219
- * @param {Date} date - 日期对象
220
- * @returns {string} 格式化后的日期字符串
221
- */
222
- formatDateToYYYYMMDD(date) {
223
- const year = date.getFullYear();
224
- const month = (date.getMonth() + 1).toString().padStart(2, '0');
225
- const day = date.getDate().toString().padStart(2, '0');
226
- return `${year}${month}${day}`;
227
- },
228
-
229
- /**
230
- * 选择日期
231
- * @param {Object} day - 日期对象(包含 date 属性)
232
- */
233
- selectDay(day) {
234
- if (this.clickDisabled) {
235
- this.clickDisabled = false;
236
- return;
237
- }
238
- this.selectedDate_ = new Date(day.date);
239
- const clickDate = this.formatDateToYYYYMMDD(this.selectedDate_);
240
- this.$emit('click', clickDate);
241
- },
242
-
243
- /**
244
- * 切换到上一周
245
- */
246
- goPrevWeek() {
247
- const newWeekStart = new Date(this.weekStart);
248
- newWeekStart.setDate(this.weekStart.getDate() - 7);
249
- this.setWeekStart(newWeekStart);
250
- this.emitWeekChange();
251
- },
252
-
253
- /**
254
- * 切换到下一周
255
- */
256
- goNextWeek() {
257
- const newWeekStart = new Date(this.weekStart);
258
- newWeekStart.setDate(this.weekStart.getDate() + 7);
259
- this.setWeekStart(newWeekStart);
260
- this.emitWeekChange();
261
- },
262
-
263
- /**
264
- * 触发周变化事件
265
- */
266
- emitWeekChange() {
267
- const weekRange = this.getWeekRange();
268
- this.$emit('on-week-change', weekRange);
269
- },
270
-
271
- /**
272
- * 触摸开始事件
273
- * @param {TouchEvent} e - 触摸事件
274
- */
275
- handleTouchStart(e) {
276
- this.startX = e.touches[0].clientX;
277
- this.endX = this.startX;
278
- this.isDragging = true;
279
- this.clickDisabled = false;
280
- },
281
-
282
- /**
283
- * 触摸移动事件
284
- * @param {TouchEvent} e - 触摸事件
285
- */
286
- handleTouchMove(e) {
287
- if (!this.isDragging) return;
288
- this.endX = e.touches[0].clientX;
289
- },
290
-
291
- /**
292
- * 触摸结束事件
293
- */
294
- handleTouchEnd() {
295
- if (!this.isDragging) return;
296
-
297
- const deltaX = this.endX - this.startX;
298
- this.handleSwipe(deltaX);
299
-
300
- this.isDragging = false;
301
- },
302
-
303
- /**
304
- * 鼠标按下事件
305
- * @param {MouseEvent} e - 鼠标事件
306
- */
307
- handleMouseDown(e) {
308
- this.startX = e.clientX;
309
- this.endX = this.startX;
310
- this.isDragging = true;
311
- this.clickDisabled = false;
312
- },
313
-
314
- /**
315
- * 鼠标移动事件
316
- * @param {MouseEvent} e - 鼠标事件
317
- */
318
- handleMouseMove(e) {
319
- if (!this.isDragging) return;
320
- this.endX = e.clientX;
321
- },
322
-
323
- /**
324
- * 鼠标松开事件
325
- */
326
- handleMouseUp() {
327
- if (!this.isDragging) return;
328
-
329
- const deltaX = this.endX - this.startX;
330
- this.handleSwipe(deltaX);
331
-
332
- this.isDragging = false;
333
- },
334
-
335
- /**
336
- * 鼠标离开事件
337
- */
338
- handleMouseLeave() {
339
- this.isDragging = false;
340
- },
341
-
342
- /**
343
- * 处理滑动操作
344
- * @param {number} deltaX - 滑动距离(正值向左滑,负值向右滑)
345
- */
346
- handleSwipe(deltaX) {
347
- if (Math.abs(deltaX) > this.swipeThreshold) {
348
- this.clickDisabled = true;
349
- if (deltaX > 0) {
350
- this.goPrevWeek();
351
- } else {
352
- this.goNextWeek();
353
- }
354
- }
355
- },
356
-
357
- /**
358
- * 手动添加/移除数据日期(供外部调用)
359
- * @param {string} dateStr - YYYYMMDD格式的日期字符串
360
- * @param {boolean} hasData - 是否有数据
361
- */
362
- setDateHasData(dateStr, hasData = true) {
363
- if (hasData) {
364
- this.dataDates.add(dateStr);
365
- } else {
366
- this.dataDates.delete(dateStr);
367
- }
368
- this.$forceUpdate();
369
- }
370
- }
371
- }
372
- </script>
373
-
374
- <style scoped>
375
- .week-date-picker {
376
- width: 100%;
377
- background: white;
378
- padding: 10px 0;
379
- box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
380
- user-select: none;
381
- border-radius: 8px;
382
- overflow: hidden;
383
- cursor: grab;
384
- }
385
-
386
- .week-date-picker:active {
387
- cursor: grabbing;
388
- }
389
-
390
- .week-days {
391
- display: flex;
392
- justify-content: space-around;
393
- align-items: center;
394
- padding: 0 8px;
395
- }
396
-
397
- .day-item {
398
- display: flex;
399
- flex-direction: column;
400
- align-items: center;
401
- justify-content: center;
402
- width: 44px;
403
- height: 64px;
404
- border-radius: 8px;
405
- cursor: pointer;
406
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
407
- position: relative;
408
- flex-shrink: 0;
409
- }
410
-
411
- /* 今天日期的浅蓝色背景 */
412
- .day-item.today {
413
- background-color: #e6efff;
414
- }
415
-
416
- .day-item:hover {
417
- background-color: #f5f5f5;
418
- transform: translateY(-2px);
419
- }
420
-
421
- .day-item.today:hover {
422
- background-color: #b0d2f9;
423
- }
424
-
425
- .day-item.active {
426
- background-color: #0070D2;
427
- box-shadow: 0 4px 12px rgba(22, 119, 255, 0.3);
428
- transform: translateY(-2px);
429
- }
430
-
431
- .day-item.active .day-week,
432
- .day-item.active .day-month,
433
- .day-item.active .day-date {
434
- color: white;
435
- }
436
-
437
- .day-week {
438
- font-size: 12px;
439
- color: #666;
440
- margin-bottom: 4px;
441
- }
442
-
443
- .day-month {
444
- font-size: 10px;
445
- color: #999;
446
- margin-bottom: 2px;
447
- }
448
-
449
- .day-date {
450
- font-size: 17px;
451
- font-weight: 500;
452
- color: #333;
453
- }
454
-
455
- /* 今天日期的文字颜色 */
456
- .day-item.today .day-week,
457
- .day-item.today .day-month,
458
- .day-item.today .day-date {
459
- color: #2c3e50;
460
- font-weight: 500;
461
- }
462
-
463
- /* 数据点样式 - 统一大小和颜色 */
464
- .data-dot {
465
- position: absolute;
466
- bottom: -4px;
467
- width: 5px;
468
- height: 5px;
469
- background-color: #129456;
470
- border-radius: 50%;
471
- animation: fadeIn 0.3s ease;
472
- }
473
-
474
- /* 选中状态下的数据点 - 统一为白色 */
475
- .day-item.active .data-dot {
476
- background-color: white;
477
- box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
478
- }
479
-
480
- /* 淡入动画 */
481
- @keyframes fadeIn {
482
- from {
483
- opacity: 0;
484
- transform: scale(0.5);
485
- }
486
- to {
487
- opacity: 1;
488
- transform: scale(1);
489
- }
490
- }
491
-
492
- /* 今天且有数据时的特殊样式 */
493
- .day-item.today.has-data {
494
- border: 1px solid #CBE2FB;
495
- }
496
-
497
- /* 移动端优化 */
498
- @media (max-width: 375px) {
499
- .day-item {
500
- width: 40px;
501
- height: 60px;
502
- }
503
-
504
- .day-date {
505
- font-size: 16px;
506
- }
507
-
508
- .day-week {
509
- font-size: 11px;
510
- }
511
-
512
- .day-month {
513
- font-size: 9px;
514
- }
515
-
516
- .data-dot {
517
- width: 4px;
518
- height: 4px;
519
- bottom: -3px;
520
- }
521
- }
1
+ <template>
2
+ <div class="week-date-picker" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd"
3
+ @mousedown="handleMouseDown" @mousemove="handleMouseMove" @mouseup="handleMouseUp" @mouseleave="handleMouseLeave">
4
+ <div class="week-days">
5
+ <van-icon name="arrow-left" @click="goPrevWeek" />
6
+ <div v-for="day in weekDays" :key="day.date.getTime()" class="day-item"
7
+ :class="{
8
+ 'today': day.isToday,
9
+ 'active': day.isSelected,
10
+ 'has-data': day.hasData
11
+ }"
12
+ @click="selectDay(day)">
13
+ <div class="day-week">{{ day.weekDay }}</div>
14
+ <div class="day-month">{{ day.year }}-{{ day.month }}</div>
15
+ <div class="day-date">{{ day.date.getDate() }}</div>
16
+ <!-- 数据点显示 -->
17
+ <div v-if="day.hasData" class="data-dot"></div>
18
+ </div>
19
+ <van-icon name="arrow" @click="goNextWeek" />
20
+ </div>
21
+ </div>
22
+ </template>
23
+
24
+ <script>
25
+ const WEEK_DAYS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
26
+
27
+ export default {
28
+ name: "LamboWeekDateSelect",
29
+
30
+ data() {
31
+ return {
32
+ selectedDate_: new Date(), // 当前选中的日期(Date对象),用于判断选中状态
33
+ weekStart: null, // 当前周的开始日期(周一)
34
+ startX: 0, // 触摸/鼠标起始X坐标
35
+ endX: 0, // 触摸/鼠标结束X坐标
36
+ isDragging: false, // 是否正在拖动
37
+ swipeThreshold: 50, // 滑动阈值(像素),超过这个值才触发切换周
38
+ clickDisabled: false, // 是否禁用点击(滑动后禁用点击,避免触发日期选择)
39
+ dataDates: new Set() // 有数据的日期集合(用于显示红点)
40
+ }
41
+ },
42
+ props: {
43
+ // 选中的日期(格式:yyyyMMDD),为空时使用今天
44
+ selectedDate: {
45
+ type: String,
46
+ default: ""
47
+ },
48
+ // 有数据的日期列表
49
+ dataDatesList: {
50
+ type: Array,
51
+ default: () => []
52
+ }
53
+ },
54
+
55
+ created() {
56
+ // 初始化数据日期
57
+ this.initDataDates();
58
+
59
+ if (this.selectedDate) {
60
+ this.initWithWeekDate(this.selectedDate);
61
+ } else {
62
+ const today = new Date();
63
+ const todayStr = this.formatDateToYYYYMMDD(today);
64
+ this.initWithWeekDate(todayStr);
65
+ }
66
+ },
67
+
68
+ watch: {
69
+ // 监听数据日期列表变化
70
+ dataDatesList: {
71
+ handler(newList) {
72
+ this.initDataDates(newList);
73
+ // 重新计算weekDays以更新数据点显示
74
+ this.$forceUpdate();
75
+ },
76
+ deep: true
77
+ },
78
+ // 监听选中日期变化
79
+ selectedDate: {
80
+ handler(newDate) {
81
+ if (newDate) {
82
+ this.initWithWeekDate(newDate);
83
+ this.$forceUpdate();
84
+ }
85
+ }
86
+ }
87
+ },
88
+
89
+ computed: {
90
+ weekDays() {
91
+ if (!this.weekStart) return [];
92
+ return Array.from({ length: 7 }, (_, i) => {
93
+ const date = new Date(this.weekStart);
94
+ date.setDate(this.weekStart.getDate() + i);
95
+ const today = new Date();
96
+ const dateStr = this.formatDateToYYYYMMDD(date);
97
+
98
+ return {
99
+ date,
100
+ year: date.getFullYear(),
101
+ month: date.getMonth() + 1,
102
+ weekDay: WEEK_DAYS[date.getDay()],
103
+ isToday: this.isSameDay(date, today),
104
+ isSelected: this.isSameDay(date, this.selectedDate_),
105
+ hasData: this.dataDates.has(dateStr)
106
+ };
107
+ });
108
+ }
109
+ },
110
+
111
+ methods: {
112
+ /**
113
+ * 获取当前周的开始和结束日期
114
+ * @returns {Object} 包含 weekStart 和 weekEnd 的对象
115
+ */
116
+ getWeekRange() {
117
+ const weekStartDate = this.formatDateToYYYYMMDD(this.weekStart);
118
+ const weekEndDateObj = new Date(this.weekStart);
119
+ weekEndDateObj.setDate(this.weekStart.getDate() + 6);
120
+ const weekEndDate = this.formatDateToYYYYMMDD(weekEndDateObj);
121
+
122
+ return {
123
+ weekStart: weekStartDate,
124
+ weekEnd: weekEndDate
125
+ };
126
+ },
127
+
128
+ /**
129
+ * 初始化数据日期集合
130
+ * @param {Array} dateList - 日期字符串数组,格式:["20241205", "20241206"]
131
+ */
132
+ initDataDates(dateList = null) {
133
+ const dates = dateList || this.dataDatesList;
134
+ this.dataDates = new Set();
135
+
136
+ dates.forEach(dateStr => {
137
+ if (dateStr && typeof dateStr === 'string' && dateStr.length === 8) {
138
+ this.dataDates.add(dateStr);
139
+ }
140
+ })
141
+ },
142
+
143
+ /**
144
+ * 根据YYYYMMDD格式的字符串初始化组件
145
+ * @param {string} dateFormat- 格式如:"20251205"
146
+ */
147
+ initWithWeekDate(dateFormat) {
148
+ // 解析YYYYMMDD格式字符串为Date对象
149
+ const date = this.parseYYYYMMDD(dateFormat);
150
+
151
+ if (date) {
152
+ this.setWeekStart(date);
153
+ this.selectedDate_ = date;
154
+ } else {
155
+ const today = new Date();
156
+ this.setWeekStart(today);
157
+ this.selectedDate_ = today;
158
+ }
159
+ },
160
+
161
+ /**
162
+ * 解析YYYYMMDD格式字符串为Date对象
163
+ * @param {string} yyyymmdd - 格式如:"20251205"
164
+ * @returns {Date|null} Date对象或null
165
+ */
166
+ parseYYYYMMDD(dateFormat) {
167
+ if (!dateFormat || typeof dateFormat !== 'string' || dateFormat.length !== 8) {
168
+ return null;
169
+ }
170
+
171
+ try {
172
+ const year = parseInt(dateFormat.substring(0, 4));
173
+ const month = parseInt(dateFormat.substring(4, 6)) - 1;
174
+ const day = parseInt(dateFormat.substring(6, 8));
175
+
176
+ const date = new Date(year, month, day);
177
+
178
+ // 验证日期是否有效
179
+ if (isNaN(date.getTime()) ||
180
+ date.getFullYear() !== year ||
181
+ date.getMonth() !== month ||
182
+ date.getDate() !== day) {
183
+ return null;
184
+ }
185
+
186
+ return date;
187
+ } catch (error) {
188
+ console.error("日期解析错误:", error);
189
+ return null;
190
+ }
191
+ },
192
+
193
+ /**
194
+ * 设置周起始日期(周一)
195
+ * @param {Date} date - 任意日期
196
+ */
197
+ setWeekStart(date) {
198
+ const d = new Date(date);
199
+ const day = d.getDay();
200
+ const diff = d.getDate() - (day === 0 ? 6 : day - 1);
201
+ d.setDate(diff);
202
+ d.setHours(0, 0, 0, 0);
203
+ this.weekStart = d;
204
+ },
205
+
206
+ /**
207
+ * 判断两个日期是否是同一天
208
+ * @param {Date} d1 - 日期1
209
+ * @param {Date} d2 - 日期2
210
+ * @returns {boolean} 是否是同一天
211
+ */
212
+ isSameDay(d1, d2) {
213
+ if (!d1 || !d2) return false;
214
+ return d1.toDateString() === d2.toDateString();
215
+ },
216
+
217
+ /**
218
+ * 格式化日期为 YYYYMMDD 字符串
219
+ * @param {Date} date - 日期对象
220
+ * @returns {string} 格式化后的日期字符串
221
+ */
222
+ formatDateToYYYYMMDD(date) {
223
+ const year = date.getFullYear();
224
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
225
+ const day = date.getDate().toString().padStart(2, '0');
226
+ return `${year}${month}${day}`;
227
+ },
228
+
229
+ /**
230
+ * 选择日期
231
+ * @param {Object} day - 日期对象(包含 date 属性)
232
+ */
233
+ selectDay(day) {
234
+ if (this.clickDisabled) {
235
+ this.clickDisabled = false;
236
+ return;
237
+ }
238
+ this.selectedDate_ = new Date(day.date);
239
+ const clickDate = this.formatDateToYYYYMMDD(this.selectedDate_);
240
+ this.$emit('click', clickDate);
241
+ },
242
+
243
+ /**
244
+ * 切换到上一周
245
+ */
246
+ goPrevWeek() {
247
+ const newWeekStart = new Date(this.weekStart);
248
+ newWeekStart.setDate(this.weekStart.getDate() - 7);
249
+ this.setWeekStart(newWeekStart);
250
+ this.emitWeekChange();
251
+ },
252
+
253
+ /**
254
+ * 切换到下一周
255
+ */
256
+ goNextWeek() {
257
+ const newWeekStart = new Date(this.weekStart);
258
+ newWeekStart.setDate(this.weekStart.getDate() + 7);
259
+ this.setWeekStart(newWeekStart);
260
+ this.emitWeekChange();
261
+ },
262
+
263
+ /**
264
+ * 触发周变化事件
265
+ */
266
+ emitWeekChange() {
267
+ const weekRange = this.getWeekRange();
268
+ this.$emit('on-week-change', weekRange);
269
+ },
270
+
271
+ /**
272
+ * 触摸开始事件
273
+ * @param {TouchEvent} e - 触摸事件
274
+ */
275
+ handleTouchStart(e) {
276
+ this.startX = e.touches[0].clientX;
277
+ this.endX = this.startX;
278
+ this.isDragging = true;
279
+ this.clickDisabled = false;
280
+ },
281
+
282
+ /**
283
+ * 触摸移动事件
284
+ * @param {TouchEvent} e - 触摸事件
285
+ */
286
+ handleTouchMove(e) {
287
+ if (!this.isDragging) return;
288
+ this.endX = e.touches[0].clientX;
289
+ },
290
+
291
+ /**
292
+ * 触摸结束事件
293
+ */
294
+ handleTouchEnd() {
295
+ if (!this.isDragging) return;
296
+
297
+ const deltaX = this.endX - this.startX;
298
+ this.handleSwipe(deltaX);
299
+
300
+ this.isDragging = false;
301
+ },
302
+
303
+ /**
304
+ * 鼠标按下事件
305
+ * @param {MouseEvent} e - 鼠标事件
306
+ */
307
+ handleMouseDown(e) {
308
+ this.startX = e.clientX;
309
+ this.endX = this.startX;
310
+ this.isDragging = true;
311
+ this.clickDisabled = false;
312
+ },
313
+
314
+ /**
315
+ * 鼠标移动事件
316
+ * @param {MouseEvent} e - 鼠标事件
317
+ */
318
+ handleMouseMove(e) {
319
+ if (!this.isDragging) return;
320
+ this.endX = e.clientX;
321
+ },
322
+
323
+ /**
324
+ * 鼠标松开事件
325
+ */
326
+ handleMouseUp() {
327
+ if (!this.isDragging) return;
328
+
329
+ const deltaX = this.endX - this.startX;
330
+ this.handleSwipe(deltaX);
331
+
332
+ this.isDragging = false;
333
+ },
334
+
335
+ /**
336
+ * 鼠标离开事件
337
+ */
338
+ handleMouseLeave() {
339
+ this.isDragging = false;
340
+ },
341
+
342
+ /**
343
+ * 处理滑动操作
344
+ * @param {number} deltaX - 滑动距离(正值向左滑,负值向右滑)
345
+ */
346
+ handleSwipe(deltaX) {
347
+ if (Math.abs(deltaX) > this.swipeThreshold) {
348
+ this.clickDisabled = true;
349
+ if (deltaX > 0) {
350
+ this.goPrevWeek();
351
+ } else {
352
+ this.goNextWeek();
353
+ }
354
+ }
355
+ },
356
+
357
+ /**
358
+ * 手动添加/移除数据日期(供外部调用)
359
+ * @param {string} dateStr - YYYYMMDD格式的日期字符串
360
+ * @param {boolean} hasData - 是否有数据
361
+ */
362
+ setDateHasData(dateStr, hasData = true) {
363
+ if (hasData) {
364
+ this.dataDates.add(dateStr);
365
+ } else {
366
+ this.dataDates.delete(dateStr);
367
+ }
368
+ this.$forceUpdate();
369
+ }
370
+ }
371
+ }
372
+ </script>
373
+
374
+ <style scoped>
375
+ .week-date-picker {
376
+ width: 100%;
377
+ background: white;
378
+ padding: 10px 0;
379
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
380
+ user-select: none;
381
+ border-radius: 8px;
382
+ overflow: hidden;
383
+ cursor: grab;
384
+ }
385
+
386
+ .week-date-picker:active {
387
+ cursor: grabbing;
388
+ }
389
+
390
+ .week-days {
391
+ display: flex;
392
+ justify-content: space-around;
393
+ align-items: center;
394
+ padding: 0 8px;
395
+ }
396
+
397
+ .day-item {
398
+ display: flex;
399
+ flex-direction: column;
400
+ align-items: center;
401
+ justify-content: center;
402
+ width: 44px;
403
+ height: 64px;
404
+ border-radius: 8px;
405
+ cursor: pointer;
406
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
407
+ position: relative;
408
+ flex-shrink: 0;
409
+ }
410
+
411
+ /* 今天日期的浅蓝色背景 */
412
+ .day-item.today {
413
+ background-color: #e6efff;
414
+ }
415
+
416
+ .day-item:hover {
417
+ background-color: #f5f5f5;
418
+ transform: translateY(-2px);
419
+ }
420
+
421
+ .day-item.today:hover {
422
+ background-color: #b0d2f9;
423
+ }
424
+
425
+ .day-item.active {
426
+ background-color: #0070D2;
427
+ box-shadow: 0 4px 12px rgba(22, 119, 255, 0.3);
428
+ transform: translateY(-2px);
429
+ }
430
+
431
+ .day-item.active .day-week,
432
+ .day-item.active .day-month,
433
+ .day-item.active .day-date {
434
+ color: white;
435
+ }
436
+
437
+ .day-week {
438
+ font-size: 12px;
439
+ color: #666;
440
+ margin-bottom: 4px;
441
+ }
442
+
443
+ .day-month {
444
+ font-size: 10px;
445
+ color: #999;
446
+ margin-bottom: 2px;
447
+ }
448
+
449
+ .day-date {
450
+ font-size: 17px;
451
+ font-weight: 500;
452
+ color: #333;
453
+ }
454
+
455
+ /* 今天日期的文字颜色 */
456
+ .day-item.today .day-week,
457
+ .day-item.today .day-month,
458
+ .day-item.today .day-date {
459
+ color: #2c3e50;
460
+ font-weight: 500;
461
+ }
462
+
463
+ /* 数据点样式 - 统一大小和颜色 */
464
+ .data-dot {
465
+ position: absolute;
466
+ bottom: -4px;
467
+ width: 5px;
468
+ height: 5px;
469
+ background-color: #129456;
470
+ border-radius: 50%;
471
+ animation: fadeIn 0.3s ease;
472
+ }
473
+
474
+ /* 选中状态下的数据点 - 统一为白色 */
475
+ .day-item.active .data-dot {
476
+ background-color: white;
477
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
478
+ }
479
+
480
+ /* 淡入动画 */
481
+ @keyframes fadeIn {
482
+ from {
483
+ opacity: 0;
484
+ transform: scale(0.5);
485
+ }
486
+ to {
487
+ opacity: 1;
488
+ transform: scale(1);
489
+ }
490
+ }
491
+
492
+ /* 今天且有数据时的特殊样式 */
493
+ .day-item.today.has-data {
494
+ border: 1px solid #CBE2FB;
495
+ }
496
+
497
+ /* 移动端优化 */
498
+ @media (max-width: 375px) {
499
+ .day-item {
500
+ width: 40px;
501
+ height: 60px;
502
+ }
503
+
504
+ .day-date {
505
+ font-size: 16px;
506
+ }
507
+
508
+ .day-week {
509
+ font-size: 11px;
510
+ }
511
+
512
+ .day-month {
513
+ font-size: 9px;
514
+ }
515
+
516
+ .data-dot {
517
+ width: 4px;
518
+ height: 4px;
519
+ bottom: -3px;
520
+ }
521
+ }
522
522
  </style>