npmapps 1.1.3 → 1.1.5

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 (114) hide show
  1. package/app/wujie-vue2-child/.eslintrc.js +19 -0
  2. package/app/wujie-vue2-child/README.md +24 -0
  3. package/app/wujie-vue2-child/babel.config.js +5 -0
  4. package/app/wujie-vue2-child/docs/superpowers/plans/2026-05-29-quill-editor.md +836 -0
  5. package/app/wujie-vue2-child/docs/superpowers/specs/2026-05-29-quill-editor-design.md +210 -0
  6. package/app/wujie-vue2-child/jsconfig.json +19 -0
  7. package/app/wujie-vue2-child/package-lock.json +21333 -0
  8. package/app/wujie-vue2-child/package.json +63 -0
  9. package/app/wujie-vue2-child/postcss.config.js +10 -0
  10. package/app/wujie-vue2-child/public/favicon.ico +0 -0
  11. package/app/wujie-vue2-child/public/index.html +17 -0
  12. package/app/wujie-vue2-child/public/rili.jpg +0 -0
  13. package/app/wujie-vue2-child/public//344/270/200/351/224/256/351/273/221/346/232/227.html +136 -0
  14. package/app/wujie-vue2-child/src/App.vue +110 -0
  15. package/app/wujie-vue2-child/src/assets/bpmn-camunda.jpg +0 -0
  16. package/app/wujie-vue2-child/src/assets/css/diagram.less +17 -0
  17. package/app/wujie-vue2-child/src/assets/icon/Icon.less +31 -0
  18. package/app/wujie-vue2-child/src/assets/icon/font/app-codes.css +26 -0
  19. package/app/wujie-vue2-child/src/assets/icon/font/app.eot +0 -0
  20. package/app/wujie-vue2-child/src/assets/icon/font/app.svg +60 -0
  21. package/app/wujie-vue2-child/src/assets/icon/font/app.ttf +0 -0
  22. package/app/wujie-vue2-child/src/assets/icon/font/app.woff +0 -0
  23. package/app/wujie-vue2-child/src/assets/icon/font/app.woff2 +0 -0
  24. package/app/wujie-vue2-child/src/assets/icon/font/config.json +248 -0
  25. package/app/wujie-vue2-child/src/assets/icon/font/source/raw/align-bottom-tool.svg +30 -0
  26. package/app/wujie-vue2-child/src/assets/icon/font/source/raw/align-horizontal-center-tool.svg +85 -0
  27. package/app/wujie-vue2-child/src/assets/icon/font/source/raw/align-left-tool.svg +84 -0
  28. package/app/wujie-vue2-child/src/assets/icon/font/source/raw/align-right-tool.svg +80 -0
  29. package/app/wujie-vue2-child/src/assets/icon/font/source/raw/align-top-tool.svg +84 -0
  30. package/app/wujie-vue2-child/src/assets/icon/font/source/raw/align-vertical-center-tool.svg +89 -0
  31. package/app/wujie-vue2-child/src/assets/icon/font/source/raw/distribute-horizontally-tool.svg +95 -0
  32. package/app/wujie-vue2-child/src/assets/icon/font/source/raw/distribute-vertically-tool.svg +99 -0
  33. package/app/wujie-vue2-child/src/assets/icon/font/source/raw/set-color-tool.svg +111 -0
  34. package/app/wujie-vue2-child/src/assets/icon/font/source/symbols/align-bottom-tool.svg +30 -0
  35. package/app/wujie-vue2-child/src/assets/icon/font/source/symbols/align-horizontal-center-tool.svg +30 -0
  36. package/app/wujie-vue2-child/src/assets/icon/font/source/symbols/align-left-tool.svg +30 -0
  37. package/app/wujie-vue2-child/src/assets/icon/font/source/symbols/align-right-tool.svg +30 -0
  38. package/app/wujie-vue2-child/src/assets/icon/font/source/symbols/align-top-tool.svg +30 -0
  39. package/app/wujie-vue2-child/src/assets/icon/font/source/symbols/align-vertical-center-tool.svg +30 -0
  40. package/app/wujie-vue2-child/src/assets/icon/font/source/symbols/distribute-horizontally-tool.svg +30 -0
  41. package/app/wujie-vue2-child/src/assets/icon/font/source/symbols/distribute-vertically-tool.svg +30 -0
  42. package/app/wujie-vue2-child/src/assets/icon/font/source/symbols/set-color-tool.svg +63 -0
  43. package/app/wujie-vue2-child/src/assets/logo.png +0 -0
  44. package/app/wujie-vue2-child/src/components/EllTable/README.md +70 -0
  45. package/app/wujie-vue2-child/src/components/EllTable/article.md +184 -0
  46. package/app/wujie-vue2-child/src/components/EllTable/index.js +213 -0
  47. package/app/wujie-vue2-child/src/components/EventCalendar/CalendarSkeleton.vue +147 -0
  48. package/app/wujie-vue2-child/src/components/EventCalendar/CodeBlock.vue +199 -0
  49. package/app/wujie-vue2-child/src/components/EventCalendar/MonthSelect.vue +376 -0
  50. package/app/wujie-vue2-child/src/components/EventCalendar/README.md +368 -0
  51. package/app/wujie-vue2-child/src/components/EventCalendar/example.vue +1551 -0
  52. package/app/wujie-vue2-child/src/components/EventCalendar/index.vue +1646 -0
  53. package/app/wujie-vue2-child/src/components/EventCalendar/mockData.js +349 -0
  54. package/app/wujie-vue2-child/src/components/EventCalendar/utils.js +201 -0
  55. package/app/wujie-vue2-child/src/components/FormulaEditor/FunctionSelector.vue +123 -0
  56. package/app/wujie-vue2-child/src/components/FormulaEditor/OperatorSelector.vue +184 -0
  57. package/app/wujie-vue2-child/src/components/FormulaEditor/ParameterSelector.vue +123 -0
  58. package/app/wujie-vue2-child/src/components/FormulaEditor/api.js +69 -0
  59. package/app/wujie-vue2-child/src/components/FormulaEditor/index.vue +435 -0
  60. package/app/wujie-vue2-child/src/components/HelloWorld.vue +58 -0
  61. package/app/wujie-vue2-child/src/components/PageHeader/index.vue +158 -0
  62. package/app/wujie-vue2-child/src/components/Splitter/README.md +144 -0
  63. package/app/wujie-vue2-child/src/components/Splitter/example.vue +84 -0
  64. package/app/wujie-vue2-child/src/components/Splitter/index.vue +203 -0
  65. package/app/wujie-vue2-child/src/components/diagram/ToolBar.vue +357 -0
  66. package/app/wujie-vue2-child/src/components/diagram/customTranslate/customTranslate.js +12 -0
  67. package/app/wujie-vue2-child/src/components/diagram/customTranslate/translationsGerman.js +241 -0
  68. package/app/wujie-vue2-child/src/components/diagram/index.vue +261 -0
  69. package/app/wujie-vue2-child/src/components/diagram/xmlData.js +29 -0
  70. package/app/wujie-vue2-child/src/directives/filldown.js +155 -0
  71. package/app/wujie-vue2-child/src/directives/filldownTable.js +291 -0
  72. package/app/wujie-vue2-child/src/main.js +40 -0
  73. package/app/wujie-vue2-child/src/router/index.js +72 -0
  74. package/app/wujie-vue2-child/src/store/index.js +23 -0
  75. package/app/wujie-vue2-child/src/utils/winBox.js +23 -0
  76. package/app/wujie-vue2-child/src/views/Extend/A.vue +12 -0
  77. package/app/wujie-vue2-child/src/views/Extend/B.vue +10 -0
  78. package/app/wujie-vue2-child/src/views/Extend/MagicalComponentsForELFormItem.vue +78 -0
  79. package/app/wujie-vue2-child/src/views/Extend/index.vue +59 -0
  80. package/app/wujie-vue2-child/src/views/Extend/tableMouseHorizontalWheel.vue +184 -0
  81. package/app/wujie-vue2-child/src/views/Home.vue +27 -0
  82. package/app/wujie-vue2-child/src/views/RouterJump.vue +145 -0
  83. package/app/wujie-vue2-child/src/views/calendar/index.vue +19 -0
  84. package/app/wujie-vue2-child/src/views/css.vue +57 -0
  85. package/app/wujie-vue2-child/src/views/cssComponents/EllipsisText.vue +73 -0
  86. package/app/wujie-vue2-child/src/views/cssComponents/HoverCard.vue +69 -0
  87. package/app/wujie-vue2-child/src/views/cssComponents/TableHover.vue +130 -0
  88. package/app/wujie-vue2-child/src/views/cssComponents/inputSlo.vue +42 -0
  89. package/app/wujie-vue2-child/src/views/cssComponents/tableFixed.vue +148 -0
  90. package/app/wujie-vue2-child/src/views/echarts/echart-dome.vue +73 -0
  91. package/app/wujie-vue2-child/src/views/echarts/index.vue +118 -0
  92. package/app/wujie-vue2-child/src/views/echarts/pei3d.vue +658 -0
  93. package/app/wujie-vue2-child/src/views/element/bpmn/index.vue +15 -0
  94. package/app/wujie-vue2-child/src/views/element/components/attendanceCycle/index.vue +131 -0
  95. package/app/wujie-vue2-child/src/views/element/components/attendanceGroup/index.vue +147 -0
  96. package/app/wujie-vue2-child/src/views/element/components/attendancePersonnel/index.vue +158 -0
  97. package/app/wujie-vue2-child/src/views/element/components/companyCalendar/index.vue +147 -0
  98. package/app/wujie-vue2-child/src/views/element/components/shift/index.vue +147 -0
  99. package/app/wujie-vue2-child/src/views/element/components/shiftRotationSystem/index.vue +147 -0
  100. package/app/wujie-vue2-child/src/views/element/elTableJsx/columnManagement.vue +340 -0
  101. package/app/wujie-vue2-child/src/views/element/elTableJsx/dialogInput.vue +71 -0
  102. package/app/wujie-vue2-child/src/views/element/elTableJsx/elTableJsx.vue +1826 -0
  103. package/app/wujie-vue2-child/src/views/element/elTableJsx/formTable.vue +595 -0
  104. package/app/wujie-vue2-child/src/views/element/elTableJsx/index.vue +29 -0
  105. package/app/wujie-vue2-child/src/views/element/elTableJsx/simpleTable.vue +189 -0
  106. package/app/wujie-vue2-child/src/views/element/elTableJsx.zip +0 -0
  107. package/app/wujie-vue2-child/src/views/element/index.vue +39 -0
  108. package/app/wujie-vue2-child/src/views/element/quillEditor/README.md +163 -0
  109. package/app/wujie-vue2-child/src/views/element/quillEditor/example.vue +307 -0
  110. package/app/wujie-vue2-child/src/views/element/quillEditor/index.vue +409 -0
  111. package/app/wujie-vue2-child/src/views/element/quillEditor/toolbar.js +122 -0
  112. package/app/wujie-vue2-child/vue.config.js +15 -0
  113. package/package.json +1 -1
  114. package/app/html-note(1).zip +0 -0
@@ -0,0 +1,349 @@
1
+ /**
2
+ * EventCalendar 模拟数据
3
+ * ------------------------------------------------------------------
4
+ * 用 setTimeout 模拟后端异步接口,便于演示「骨架屏 -> 真实数据」的过程。
5
+ * 真实接入时把 fetchXxx 换成自己的 request 即可,组件本身不关心数据来源。
6
+ *
7
+ * 注意:这里的字段名是「默认字段名」,若后端字段不同,
8
+ * 不需要改数据,只需给组件传 field-names(见 ALT_* 相关导出)。
9
+ */
10
+
11
+ /* -------------------------------------------------------------------------- */
12
+ /* 基础素材 */
13
+ /* -------------------------------------------------------------------------- */
14
+
15
+ /** 学校名池(取自设计图) */
16
+ const SCHOOLS = [
17
+ '香港中文大学(深圳)', '南方科技大学', '哈尔滨工业大学(深圳)',
18
+ '电子科技大学', '北京邮电大学', '厦门大学',
19
+ '四川大学', '中央财经大学', '东南大学',
20
+ '南京审计大学', '北京理工大学', '西南财经大学',
21
+ '中国人民大学', '南京大学', '武汉大学',
22
+ '华中科技大学', '复旦大学', '对外经济贸易大学', '北京师范大学',
23
+ '上海财经大学', '中南财经政法大学', '北京大学',
24
+ '华东师范大学', '清华大学', '西北工业大学', '南开大学',
25
+ '上海交通大学', '天津大学', '西安交通大学',
26
+ '浙江大学', '同济大学', '大连理工大学',
27
+ '中国科学技术大学', '吉林大学', '华南理工大学',
28
+ '中南大学', '山东大学', '重庆大学', '湖南大学', '兰州大学'
29
+ ]
30
+
31
+ /** 条目类型(角标文案 + 类型标识,类型用于组件内 typeColors 配色) */
32
+ const EVENT_TYPES = [
33
+ { badge: '宣讲', type: 'talk' },
34
+ { badge: '双选', type: 'fair' },
35
+ { badge: '直播', type: 'live' },
36
+ { badge: '笔试', type: 'exam' }
37
+ ]
38
+
39
+ /**
40
+ * 线性同余伪随机数生成器
41
+ * ------------------------------------------------------------------
42
+ * 不用 Math.random 是为了让每次刷新拿到的模拟数据都一致,
43
+ * 这样调试样式 / 截图对比时不会因为数据抖动而困扰。
44
+ */
45
+ function createRandom(seed) {
46
+ let s = seed >>> 0
47
+ return function random() {
48
+ s = (s * 1664525 + 1013904223) >>> 0
49
+ return s / 4294967296
50
+ }
51
+ }
52
+
53
+ function pad2(n) {
54
+ return n < 10 ? '0' + n : String(n)
55
+ }
56
+
57
+ /**
58
+ * 生成某个月的日期数据
59
+ * @param {String} monthKey '2026-09'
60
+ * @param {Object} options
61
+ * @param {Number} options.seed 随机种子
62
+ * @param {Number} options.maxItems 单日最多条目数
63
+ * @param {Number} options.density 有数据的日期占比 0~1
64
+ * @param {Boolean} options.skipWeekend 周末是否基本不排期(贴近真实业务)
65
+ */
66
+ function createMonthDays(monthKey, options = {}) {
67
+ const { seed = 20260907, maxItems = 6, density = 0.62, skipWeekend = true } = options
68
+ const random = createRandom(seed)
69
+
70
+ const [year, month] = monthKey.split('-').map(Number)
71
+ const daysInMonth = new Date(year, month, 0).getDate()
72
+
73
+ const days = []
74
+ let uid = 0
75
+
76
+ for (let day = 1; day <= daysInMonth; day++) {
77
+ const date = new Date(year, month - 1, day)
78
+ const isWeekend = date.getDay() === 0 || date.getDay() === 6
79
+
80
+ // 周末排期概率大幅降低
81
+ const hit = random() < (skipWeekend && isWeekend ? density * 0.12 : density)
82
+ if (!hit) continue
83
+
84
+ // 单日条目数 1 ~ maxItems,故意让少数几天超过 4 条以演示滚动条
85
+ const count = 1 + Math.floor(random() * maxItems)
86
+
87
+ const items = []
88
+ for (let i = 0; i < count; i++) {
89
+ const school = SCHOOLS[Math.floor(random() * SCHOOLS.length)]
90
+ const evt = EVENT_TYPES[Math.floor(random() * EVENT_TYPES.length)]
91
+ items.push({
92
+ id: `${monthKey}-${day}-${uid++}`,
93
+ label: school,
94
+ badge: evt.badge,
95
+ type: evt.type,
96
+ // 冗余字段:演示插槽里可以拿到完整原始对象
97
+ address: `${school}就业指导中心`,
98
+ startTime: `${pad2(9 + (i % 8))}:30`
99
+ })
100
+ }
101
+
102
+ days.push({
103
+ date: `${monthKey}-${pad2(day)}`,
104
+ items
105
+ })
106
+ }
107
+
108
+ return days
109
+ }
110
+
111
+ /** 统一的异步包装:delay 毫秒后 resolve,模拟网络请求 */
112
+ function delayResolve(payload, delay) {
113
+ return new Promise((resolve) => {
114
+ setTimeout(() => resolve(payload), delay)
115
+ })
116
+ }
117
+
118
+ /* -------------------------------------------------------------------------- */
119
+ /* 默认字段名接口 */
120
+ /* -------------------------------------------------------------------------- */
121
+
122
+ /**
123
+ * 拉取宣讲日历数据(默认字段名,组件开箱即用)
124
+ *
125
+ * @param {Object} options
126
+ * @param {Array} options.months 要生成的月份,默认 2026 年 9/10/11 月
127
+ * @param {Number} options.delay 模拟网络耗时(ms),默认 1500
128
+ * @returns {Promise<Array>} [{ month, monthLabel, days: [{ date, items: [] }] }]
129
+ */
130
+ export function fetchCalendarData(options = {}) {
131
+ const {
132
+ months = ['2026-09', '2026-10', '2026-11'],
133
+ delay = 1500,
134
+ maxItems = 6,
135
+ density = 0.62
136
+ } = options
137
+
138
+ const data = months.map((monthKey, index) => {
139
+ const [year, month] = monthKey.split('-').map(Number)
140
+ return {
141
+ month: monthKey,
142
+ monthLabel: `${year}年${month}月`,
143
+ days: createMonthDays(monthKey, {
144
+ seed: year * 100 + month + index * 7,
145
+ maxItems,
146
+ density
147
+ })
148
+ }
149
+ })
150
+
151
+ return delayResolve(data, delay)
152
+ }
153
+
154
+ /**
155
+ * 跨年数据:2025-12 / 2026-01 / 2026-02
156
+ * ------------------------------------------------------------------
157
+ * 专门用于验证需求里点名的边界情况:
158
+ * - 1 月要往前补 2025 年 12 月的日期;
159
+ * - 2 月只有 28 天(2026 非闰年),末尾要补 3 月;
160
+ * - 12 月要往后补 2026 年 1 月。
161
+ */
162
+ export function fetchCrossYearData(options = {}) {
163
+ const { delay = 1200 } = options
164
+ return fetchCalendarData({
165
+ months: ['2025-12', '2026-01', '2026-02'],
166
+ delay,
167
+ density: 0.55
168
+ })
169
+ }
170
+
171
+ /** 空数据(演示空状态) */
172
+ export function fetchEmptyData(options = {}) {
173
+ const { delay = 900, months = ['2026-09'] } = options
174
+ const data = months.map((monthKey) => ({
175
+ month: monthKey,
176
+ monthLabel: monthKey,
177
+ days: []
178
+ }))
179
+ return delayResolve(data, delay)
180
+ }
181
+
182
+ /* -------------------------------------------------------------------------- */
183
+ /* 自定义字段名接口(演示 fieldNames) */
184
+ /* -------------------------------------------------------------------------- */
185
+
186
+ /**
187
+ * 与后端字段不一致时的映射配置。
188
+ * 把这个对象传给组件的 field-names,组件就能读懂下面 fetchCalendarDataV2 的数据。
189
+ */
190
+ export const ALT_FIELD_NAMES = {
191
+ month: 'ym',
192
+ monthLabel: 'ymText',
193
+ days: 'dateList',
194
+ date: 'dayStr',
195
+ items: 'activityList',
196
+ itemKey: 'activityId',
197
+ itemLabel: 'schoolName',
198
+ itemBadge: 'activityTypeName',
199
+ itemType: 'activityType',
200
+ itemDisabled: 'expired'
201
+ }
202
+
203
+ /**
204
+ * 「后端字段完全不同」的模拟接口。
205
+ * 数据结构故意与默认约定不一致:month -> ym、days -> dateList、label -> schoolName ...
206
+ */
207
+ export function fetchCalendarDataV2(options = {}) {
208
+ const { months = ['2026-09', '2026-10'], delay = 1500 } = options
209
+
210
+ const data = months.map((monthKey, index) => {
211
+ const [year, month] = monthKey.split('-').map(Number)
212
+ const days = createMonthDays(monthKey, { seed: 999 + index * 31, maxItems: 5 })
213
+
214
+ return {
215
+ ym: monthKey,
216
+ ymText: `${month} 月场次`,
217
+ dateList: days.map((d) => ({
218
+ dayStr: d.date,
219
+ activityList: d.items.map((it) => ({
220
+ activityId: it.id,
221
+ schoolName: it.label,
222
+ activityTypeName: it.badge,
223
+ activityType: it.type,
224
+ expired: false,
225
+ rawAddress: it.address
226
+ }))
227
+ }))
228
+ }
229
+ })
230
+
231
+ return delayResolve(data, delay)
232
+ }
233
+
234
+ /* -------------------------------------------------------------------------- */
235
+ /* 固定演示数据 */
236
+ /* -------------------------------------------------------------------------- */
237
+
238
+ /**
239
+ * 一份写死的小数据,用于「基础用法」示例,
240
+ * 便于阅读文档时一眼看清数据结构。
241
+ */
242
+ export const SIMPLE_DATA = [
243
+ {
244
+ month: '2026-09',
245
+ monthLabel: '2026年9月',
246
+ days: [
247
+ {
248
+ date: '2026-09-09',
249
+ items: [
250
+ { id: 'a1', label: '香港中文大学(深圳)', badge: '宣讲', type: 'talk' },
251
+ { id: 'a2', label: '南方科技大学', badge: '宣讲', type: 'talk' },
252
+ { id: 'a3', label: '哈尔滨工业大学(深圳)', badge: '宣讲', type: 'talk' }
253
+ ]
254
+ },
255
+ {
256
+ date: '2026-09-10',
257
+ items: [
258
+ { id: 'b1', label: '电子科技大学', badge: '宣讲', type: 'talk' },
259
+ { id: 'b2', label: '北京邮电大学', badge: '宣讲', type: 'talk' },
260
+ { id: 'b3', label: '厦门大学', badge: '宣讲', type: 'talk' }
261
+ ]
262
+ },
263
+ {
264
+ date: '2026-09-11',
265
+ items: [
266
+ { id: 'c1', label: '四川大学', badge: '宣讲', type: 'talk' },
267
+ { id: 'c2', label: '中央财经大学', badge: '宣讲', type: 'talk' },
268
+ { id: 'c3', label: '东南大学', badge: '宣讲', type: 'talk' }
269
+ ]
270
+ },
271
+ {
272
+ date: '2026-09-12',
273
+ items: [
274
+ { id: 'd1', label: '南京审计大学', badge: '双选', type: 'fair' },
275
+ { id: 'd2', label: '北京理工大学', badge: '宣讲', type: 'talk' },
276
+ { id: 'd3', label: '西南财经大学', badge: '宣讲', type: 'talk' }
277
+ ]
278
+ },
279
+ {
280
+ date: '2026-09-14',
281
+ items: [
282
+ { id: 'e1', label: '中央财经大学', badge: '双选', type: 'fair' },
283
+ { id: 'e2', label: '中国人民大学', badge: '宣讲', type: 'talk' },
284
+ { id: 'e3', label: '南京大学', badge: '宣讲', type: 'talk' },
285
+ { id: 'e4', label: '武汉大学', badge: '宣讲', type: 'talk' },
286
+ { id: 'e5', label: '山东大学', badge: '直播', type: 'live' },
287
+ { id: 'e6', label: '重庆大学', badge: '笔试', type: 'exam' }
288
+ ]
289
+ },
290
+ {
291
+ date: '2026-09-16',
292
+ items: [
293
+ { id: 'f1', label: '华中科技大学', badge: '宣讲', type: 'talk' },
294
+ { id: 'f2', label: '复旦大学', badge: '宣讲', type: 'talk' },
295
+ { id: 'f3', label: '对外经济贸易大学', badge: '宣讲', type: 'talk' },
296
+ { id: 'f4', label: '北京师范大学', badge: '宣讲', type: 'talk' }
297
+ ]
298
+ },
299
+ {
300
+ date: '2026-09-17',
301
+ items: [
302
+ { id: 'g1', label: '上海财经大学', badge: '宣讲', type: 'talk' },
303
+ { id: 'g2', label: '中南财经政法大学', badge: '宣讲', type: 'talk' },
304
+ { id: 'g3', label: '北京大学', badge: '宣讲', type: 'talk' }
305
+ ]
306
+ },
307
+ {
308
+ date: '2026-09-18',
309
+ items: [
310
+ { id: 'h1', label: '华东师范大学', badge: '宣讲', type: 'talk' },
311
+ { id: 'h2', label: '清华大学', badge: '宣讲', type: 'talk' },
312
+ { id: 'h3', label: '西北工业大学', badge: '宣讲', type: 'talk' },
313
+ { id: 'h4', label: '南开大学', badge: '宣讲', type: 'talk' }
314
+ ]
315
+ },
316
+ {
317
+ date: '2026-09-19',
318
+ items: [
319
+ { id: 'i1', label: '上海交通大学', badge: '宣讲', type: 'talk' },
320
+ { id: 'i2', label: '天津大学', badge: '宣讲', type: 'talk' },
321
+ { id: 'i3', label: '西安交通大学', badge: '宣讲', type: 'talk' }
322
+ ]
323
+ },
324
+ {
325
+ date: '2026-09-22',
326
+ items: [
327
+ { id: 'j1', label: '浙江大学', badge: '宣讲', type: 'talk' },
328
+ { id: 'j2', label: '同济大学', badge: '宣讲', type: 'talk' },
329
+ { id: 'j3', label: '大连理工大学', badge: '宣讲', type: 'talk' }
330
+ ]
331
+ },
332
+ {
333
+ date: '2026-09-23',
334
+ items: [
335
+ { id: 'k1', label: '中国科学技术大学', badge: '宣讲', type: 'talk' },
336
+ { id: 'k2', label: '吉林大学', badge: '宣讲', type: 'talk' },
337
+ { id: 'k3', label: '华南理工大学', badge: '宣讲', type: 'talk' }
338
+ ]
339
+ },
340
+ {
341
+ date: '2026-09-24',
342
+ items: [
343
+ { id: 'l1', label: '中南大学', badge: '双选', type: 'fair' },
344
+ { id: 'l2', label: '哈尔滨工业大学', badge: '宣讲', type: 'talk' }
345
+ ]
346
+ }
347
+ ]
348
+ }
349
+ ]
@@ -0,0 +1,201 @@
1
+ /**
2
+ * EventCalendar 工具函数集
3
+ * ------------------------------------------------------------------
4
+ * 说明:本文件不依赖任何第三方库(含 dayjs / moment / lodash),
5
+ * 仅使用原生 Date,保证组件文件夹可以整体拷贝到任意项目使用。
6
+ */
7
+
8
+ /** 数字补零:7 -> '07' */
9
+ export function pad2(n) {
10
+ return n < 10 ? '0' + n : String(n)
11
+ }
12
+
13
+ /**
14
+ * 默认字段映射表
15
+ * ------------------------------------------------------------------
16
+ * 后端返回的字段名往往与组件内部约定不一致,
17
+ * 使用者可通过 `field-names` prop 覆盖其中任意一项(支持 'a.b.c' 点路径)。
18
+ */
19
+ export const DEFAULT_FIELD_NAMES = {
20
+ // —— 月份层(data 数组的每一项)——
21
+ month: 'month', // 月份标识,形如 '2026-09'
22
+ monthLabel: 'monthLabel', // 下拉框显示文案;缺省时自动生成「2026年9月」
23
+ days: 'days', // 该月的日期数组
24
+
25
+ // —— 日期层(days 数组的每一项)——
26
+ date: 'date', // 日期标识,形如 '2026-09-07'
27
+ items: 'items', // 当天的条目数组
28
+
29
+ // —— 条目层(items 数组的每一项)——
30
+ itemKey: 'id', // 条目唯一键(用于 v-for :key)
31
+ itemLabel: 'label', // 条目主文案,如「电子科技大学」
32
+ itemBadge: 'badge', // 条目右上角角标文案,如「宣讲」「双选」
33
+ itemType: 'type', // 条目类型,用于匹配 typeColors 配色
34
+ itemDisabled: 'disabled' // 条目是否禁用(禁用后不响应点击)
35
+ }
36
+
37
+ /**
38
+ * 按路径取值,支持 'a.b.c' 形式
39
+ * @param {Object} obj 源对象
40
+ * @param {String} path 字段路径
41
+ */
42
+ export function getByPath(obj, path) {
43
+ if (obj == null || !path) return undefined
44
+ if (path.indexOf('.') === -1) return obj[path]
45
+ return path.split('.').reduce((acc, key) => (acc == null ? acc : acc[key]), obj)
46
+ }
47
+
48
+ /** 是否为合法 Date 对象 */
49
+ function isValidDate(d) {
50
+ return d instanceof Date && !isNaN(d.getTime())
51
+ }
52
+
53
+ /**
54
+ * 把多种输入统一解析成 Date
55
+ * 支持:Date / '2026-09-07' / '2026/9/7' / 时间戳
56
+ */
57
+ export function toDate(value) {
58
+ if (isValidDate(value)) return value
59
+ if (value == null || value === '') return null
60
+
61
+ if (typeof value === 'number') {
62
+ const d = new Date(value)
63
+ return isValidDate(d) ? d : null
64
+ }
65
+
66
+ const str = String(value).trim()
67
+ // 手动解析,规避 Safari / iOS 对 '2026-09-07' 的时区差异(会被当成 UTC)
68
+ const m = str.match(/^(\d{4})[-/](\d{1,2})(?:[-/](\d{1,2}))?/)
69
+ if (m) {
70
+ const d = new Date(Number(m[1]), Number(m[2]) - 1, m[3] ? Number(m[3]) : 1)
71
+ return isValidDate(d) ? d : null
72
+ }
73
+
74
+ const fallback = new Date(str)
75
+ return isValidDate(fallback) ? fallback : null
76
+ }
77
+
78
+ /** Date -> 'YYYY-MM-DD' */
79
+ export function formatDateKey(date) {
80
+ const d = toDate(date)
81
+ if (!d) return ''
82
+ return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`
83
+ }
84
+
85
+ /** Date -> 'YYYY-MM' */
86
+ export function formatMonthKey(date) {
87
+ const d = toDate(date)
88
+ if (!d) return ''
89
+ return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}`
90
+ }
91
+
92
+ /**
93
+ * 解析月份标识
94
+ * @param {String|Date} monthKey '2026-09' / '2026/9' / Date
95
+ * @returns {{ year: Number, month: Number }} month 为 1~12
96
+ */
97
+ export function parseMonthKey(monthKey) {
98
+ const d = toDate(monthKey)
99
+ if (!d) {
100
+ const now = new Date()
101
+ return { year: now.getFullYear(), month: now.getMonth() + 1 }
102
+ }
103
+ return { year: d.getFullYear(), month: d.getMonth() + 1 }
104
+ }
105
+
106
+ /** 两个 Date 是否为同一天 */
107
+ export function isSameDay(a, b) {
108
+ const d1 = toDate(a)
109
+ const d2 = toDate(b)
110
+ if (!d1 || !d2) return false
111
+ return (
112
+ d1.getFullYear() === d2.getFullYear() &&
113
+ d1.getMonth() === d2.getMonth() &&
114
+ d1.getDate() === d2.getDate()
115
+ )
116
+ }
117
+
118
+ /**
119
+ * 月份偏移,自动跨年
120
+ * @param {String} monthKey '2026-01'
121
+ * @param {Number} offset -1 上个月 / 1 下个月
122
+ * @returns {String} '2025-12'
123
+ */
124
+ export function shiftMonth(monthKey, offset) {
125
+ const { year, month } = parseMonthKey(monthKey)
126
+ // new Date 的月份允许越界(-1 / 12),会自动进位到上一年 / 下一年
127
+ return formatMonthKey(new Date(year, month - 1 + offset, 1))
128
+ }
129
+
130
+ /**
131
+ * 构建一个月的日历格子矩阵(核心逻辑)
132
+ * ------------------------------------------------------------------
133
+ * 需求要点:
134
+ * 1. 每个月天数不固定(28/29/30/31),且 1 号是星期几也不固定;
135
+ * 2. 需要用「上个月末尾几天」补齐第一行,用「下个月开头几天」补齐最后一行;
136
+ * 3. 跨年(1 月往前补 12 月、12 月往后补次年 1 月)必须正确。
137
+ *
138
+ * 实现方式:先算出本月 1 号相对「一周起始日」的偏移量 leading,
139
+ * 然后以 `new Date(year, month - 1, 1 - leading + i)` 递推生成所有格子。
140
+ * 原生 Date 对天数越界(负数或超出当月天数)会自动进位/借位,
141
+ * 因此跨月、跨年都会被自动处理,无需特判 1 月 / 12 月。
142
+ *
143
+ * @param {String} monthKey '2026-02'
144
+ * @param {Number} firstDayOfWeek 一周起始日,0=周日(设计图)1=周一
145
+ * @param {Date} today 今天(便于测试时注入)
146
+ * @returns {Array} 42 或 35 个格子(始终是 7 的整数倍)
147
+ */
148
+ export function buildMonthMatrix(monthKey, firstDayOfWeek = 0, today = new Date()) {
149
+ const { year, month } = parseMonthKey(monthKey)
150
+
151
+ const firstDayOfMonth = new Date(year, month - 1, 1)
152
+ // new Date(y, m, 0) 表示「第 m 月的第 0 天」即上个月最后一天,用来取当月总天数
153
+ const daysInMonth = new Date(year, month, 0).getDate()
154
+
155
+ // 本月 1 号是星期几(0=周日),再换算成相对一周起始日的偏移
156
+ const leading = (firstDayOfMonth.getDay() - firstDayOfWeek + 7) % 7
157
+ // 向上取整到 7 的倍数,保证最后一行也被补满
158
+ const totalCells = Math.ceil((leading + daysInMonth) / 7) * 7
159
+
160
+ const cells = []
161
+ for (let i = 0; i < totalCells; i++) {
162
+ const date = new Date(year, month - 1, 1 - leading + i)
163
+ const cellMonth = date.getMonth() + 1
164
+ const isCurrentMonth = date.getFullYear() === year && cellMonth === month
165
+
166
+ cells.push({
167
+ date,
168
+ dateKey: formatDateKey(date),
169
+ monthKey: formatMonthKey(date),
170
+ year: date.getFullYear(),
171
+ month: cellMonth,
172
+ day: date.getDate(),
173
+ weekday: date.getDay(),
174
+ /** 是否属于当前渲染的月份(false 时置灰) */
175
+ isCurrentMonth,
176
+ /** 'prev' 上月补位 | 'current' 当月 | 'next' 下月补位 */
177
+ position: isCurrentMonth ? 'current' : i < leading ? 'prev' : 'next',
178
+ isToday: isSameDay(date, today),
179
+ isWeekend: date.getDay() === 0 || date.getDay() === 6,
180
+ /** 所在行索引,从 0 开始 */
181
+ rowIndex: Math.floor(i / 7),
182
+ colIndex: i % 7
183
+ })
184
+ }
185
+
186
+ return cells
187
+ }
188
+
189
+ /** 默认星期文案,下标即 Date.getDay() 的返回值 */
190
+ export const DEFAULT_WEEK_LABELS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
191
+
192
+ /** 默认日期文案格式化:{ month: 9, day: 7 } -> '9月7日' */
193
+ export function defaultDayFormatter(cell) {
194
+ return `${cell.month}月${cell.day}日`
195
+ }
196
+
197
+ /** 默认月份下拉文案:'2026-09' -> '2026年9月' */
198
+ export function defaultMonthFormatter(monthKey) {
199
+ const { year, month } = parseMonthKey(monthKey)
200
+ return `${year}年${month}月`
201
+ }
@@ -0,0 +1,123 @@
1
+ <template>
2
+ <div class="selector-container">
3
+ <div class="header">函数</div>
4
+ <div class="tree-wrapper">
5
+ <el-tree
6
+ :data="treeData"
7
+ :props="defaultProps"
8
+ @node-click="handleNodeClick"
9
+ default-expand-all
10
+ highlight-current
11
+ >
12
+ <span
13
+ class="custom-tree-node"
14
+ slot-scope="{ node, data }"
15
+ :draggable="!onlyLeafClickable || node.isLeaf"
16
+ @dragstart="handleDragStart(node, data, $event)"
17
+ >
18
+ {{ node.label }}
19
+ </span>
20
+ </el-tree>
21
+ </div>
22
+ </div>
23
+ </template>
24
+
25
+ <script>
26
+ import { getFunctions } from "./api";
27
+
28
+ export default {
29
+ name: "FunctionSelector",
30
+ props: {
31
+ onlyLeafClickable: {
32
+ type: Boolean,
33
+ default: true,
34
+ },
35
+ prefix: {
36
+ type: String,
37
+ default: "F_",
38
+ },
39
+ },
40
+ data() {
41
+ return {
42
+ treeData: [],
43
+ defaultProps: {
44
+ children: "children",
45
+ label: "label",
46
+ },
47
+ };
48
+ },
49
+ mounted() {
50
+ this.fetchData();
51
+ },
52
+ methods: {
53
+ async fetchData() {
54
+ this.treeData = await getFunctions();
55
+ },
56
+ handleNodeClick(data, node) {
57
+ if (this.onlyLeafClickable && !node.isLeaf) {
58
+ return;
59
+ }
60
+ this.$emit("select", { ...data, value: `${this.prefix}${data.name}()` });
61
+ },
62
+ handleDragStart(node, data, e) {
63
+ if (this.onlyLeafClickable && !node.isLeaf) {
64
+ e.preventDefault();
65
+ return;
66
+ }
67
+ const value = `${this.prefix}${data.name}()`;
68
+ e.dataTransfer.setData("text/plain", value);
69
+ e.dataTransfer.effectAllowed = "copy";
70
+
71
+ // 设置自定义拖拽镜像
72
+ const div = document.createElement('div');
73
+ div.innerText = value;
74
+ div.style.position = 'absolute';
75
+ div.style.top = '-9999px';
76
+ div.style.padding = '5px 10px';
77
+ div.style.background = '#fff';
78
+ div.style.border = '1px solid #409eff';
79
+ div.style.borderRadius = '4px';
80
+ div.style.fontSize = '14px';
81
+ div.style.color = '#303133';
82
+ div.style.boxShadow = '0 2px 12px 0 rgba(0,0,0,0.1)';
83
+ div.style.zIndex = '9999';
84
+ document.body.appendChild(div);
85
+
86
+ e.dataTransfer.setDragImage(div, 0, 0);
87
+
88
+ setTimeout(() => {
89
+ document.body.removeChild(div);
90
+ }, 0);
91
+ },
92
+ },
93
+ };
94
+ </script>
95
+
96
+ <style scoped>
97
+ .custom-tree-node {
98
+ flex: 1;
99
+ display: flex;
100
+ align-items: center;
101
+ font-size: 14px;
102
+ }
103
+ .selector-container {
104
+ width: 100%;
105
+ height: 100%;
106
+ display: flex;
107
+ flex-direction: column;
108
+ }
109
+
110
+ .header {
111
+ padding: 10px;
112
+ background-color: #f5f7fa;
113
+ border-bottom: 1px solid #ebeef5;
114
+ font-weight: bold;
115
+ text-align: center;
116
+ }
117
+
118
+ .tree-wrapper {
119
+ flex: 1;
120
+ overflow-y: auto;
121
+ padding: 10px;
122
+ }
123
+ </style>