npmapps 1.2.0 → 1.3.1

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 (120) hide show
  1. package/README.md +41 -0
  2. package/files/part-002 +4 -0
  3. package/get.js +193 -0
  4. package/manifest.json +28 -0
  5. package/package.json +17 -10
  6. package/PUBLISH.md +0 -268
  7. package/app/wujie-wk/t.html +0 -90
  8. package/app/wujie-wk/wujie-vue2-child/.eslintrc.js +0 -19
  9. package/app/wujie-wk/wujie-vue2-child/README.md +0 -24
  10. package/app/wujie-wk/wujie-vue2-child/babel.config.js +0 -5
  11. package/app/wujie-wk/wujie-vue2-child/docs/superpowers/plans/2026-05-29-quill-editor.md +0 -836
  12. package/app/wujie-wk/wujie-vue2-child/docs/superpowers/specs/2026-05-29-quill-editor-design.md +0 -210
  13. package/app/wujie-wk/wujie-vue2-child/jsconfig.json +0 -19
  14. package/app/wujie-wk/wujie-vue2-child/package-lock.json +0 -21333
  15. package/app/wujie-wk/wujie-vue2-child/package.json +0 -63
  16. package/app/wujie-wk/wujie-vue2-child/postcss.config.js +0 -10
  17. package/app/wujie-wk/wujie-vue2-child/public/favicon.ico +0 -0
  18. package/app/wujie-wk/wujie-vue2-child/public/index.html +0 -17
  19. package/app/wujie-wk/wujie-vue2-child/public/rili.jpg +0 -0
  20. package/app/wujie-wk/wujie-vue2-child/public//344/270/200/351/224/256/351/273/221/346/232/227.html +0 -136
  21. package/app/wujie-wk/wujie-vue2-child/src/App.vue +0 -110
  22. package/app/wujie-wk/wujie-vue2-child/src/assets/bpmn-camunda.jpg +0 -0
  23. package/app/wujie-wk/wujie-vue2-child/src/assets/css/diagram.less +0 -17
  24. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/Icon.less +0 -31
  25. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/app-codes.css +0 -26
  26. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/app.eot +0 -0
  27. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/app.svg +0 -60
  28. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/app.ttf +0 -0
  29. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/app.woff +0 -0
  30. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/app.woff2 +0 -0
  31. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/config.json +0 -248
  32. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/raw/align-bottom-tool.svg +0 -30
  33. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/raw/align-horizontal-center-tool.svg +0 -85
  34. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/raw/align-left-tool.svg +0 -84
  35. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/raw/align-right-tool.svg +0 -80
  36. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/raw/align-top-tool.svg +0 -84
  37. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/raw/align-vertical-center-tool.svg +0 -89
  38. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/raw/distribute-horizontally-tool.svg +0 -95
  39. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/raw/distribute-vertically-tool.svg +0 -99
  40. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/raw/set-color-tool.svg +0 -111
  41. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/symbols/align-bottom-tool.svg +0 -30
  42. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/symbols/align-horizontal-center-tool.svg +0 -30
  43. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/symbols/align-left-tool.svg +0 -30
  44. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/symbols/align-right-tool.svg +0 -30
  45. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/symbols/align-top-tool.svg +0 -30
  46. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/symbols/align-vertical-center-tool.svg +0 -30
  47. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/symbols/distribute-horizontally-tool.svg +0 -30
  48. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/symbols/distribute-vertically-tool.svg +0 -30
  49. package/app/wujie-wk/wujie-vue2-child/src/assets/icon/font/source/symbols/set-color-tool.svg +0 -63
  50. package/app/wujie-wk/wujie-vue2-child/src/assets/logo.png +0 -0
  51. package/app/wujie-wk/wujie-vue2-child/src/components/EllTable/README.md +0 -70
  52. package/app/wujie-wk/wujie-vue2-child/src/components/EllTable/article.md +0 -184
  53. package/app/wujie-wk/wujie-vue2-child/src/components/EllTable/index.js +0 -213
  54. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/CalendarSkeleton.vue +0 -147
  55. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/CodeBlock.vue +0 -199
  56. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/MonthSelect.vue +0 -376
  57. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/README.md +0 -485
  58. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/example.vue +0 -1747
  59. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/index.vue +0 -2720
  60. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/mockData.js +0 -475
  61. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/recruitmentAdapter.js +0 -51
  62. package/app/wujie-wk/wujie-vue2-child/src/components/EventCalendar/utils.js +0 -202
  63. package/app/wujie-wk/wujie-vue2-child/src/components/FormulaEditor/FunctionSelector.vue +0 -123
  64. package/app/wujie-wk/wujie-vue2-child/src/components/FormulaEditor/OperatorSelector.vue +0 -184
  65. package/app/wujie-wk/wujie-vue2-child/src/components/FormulaEditor/ParameterSelector.vue +0 -123
  66. package/app/wujie-wk/wujie-vue2-child/src/components/FormulaEditor/api.js +0 -69
  67. package/app/wujie-wk/wujie-vue2-child/src/components/FormulaEditor/index.vue +0 -435
  68. package/app/wujie-wk/wujie-vue2-child/src/components/HelloWorld.vue +0 -58
  69. package/app/wujie-wk/wujie-vue2-child/src/components/PageHeader/index.vue +0 -158
  70. package/app/wujie-wk/wujie-vue2-child/src/components/Splitter/README.md +0 -144
  71. package/app/wujie-wk/wujie-vue2-child/src/components/Splitter/example.vue +0 -84
  72. package/app/wujie-wk/wujie-vue2-child/src/components/Splitter/index.vue +0 -203
  73. package/app/wujie-wk/wujie-vue2-child/src/components/diagram/ToolBar.vue +0 -357
  74. package/app/wujie-wk/wujie-vue2-child/src/components/diagram/customTranslate/customTranslate.js +0 -12
  75. package/app/wujie-wk/wujie-vue2-child/src/components/diagram/customTranslate/translationsGerman.js +0 -241
  76. package/app/wujie-wk/wujie-vue2-child/src/components/diagram/index.vue +0 -261
  77. package/app/wujie-wk/wujie-vue2-child/src/components/diagram/xmlData.js +0 -29
  78. package/app/wujie-wk/wujie-vue2-child/src/directives/filldown.js +0 -155
  79. package/app/wujie-wk/wujie-vue2-child/src/directives/filldownTable.js +0 -291
  80. package/app/wujie-wk/wujie-vue2-child/src/main.js +0 -40
  81. package/app/wujie-wk/wujie-vue2-child/src/router/index.js +0 -72
  82. package/app/wujie-wk/wujie-vue2-child/src/store/index.js +0 -23
  83. package/app/wujie-wk/wujie-vue2-child/src/utils/winBox.js +0 -23
  84. package/app/wujie-wk/wujie-vue2-child/src/views/Extend/A.vue +0 -12
  85. package/app/wujie-wk/wujie-vue2-child/src/views/Extend/B.vue +0 -10
  86. package/app/wujie-wk/wujie-vue2-child/src/views/Extend/MagicalComponentsForELFormItem.vue +0 -78
  87. package/app/wujie-wk/wujie-vue2-child/src/views/Extend/index.vue +0 -59
  88. package/app/wujie-wk/wujie-vue2-child/src/views/Extend/tableMouseHorizontalWheel.vue +0 -184
  89. package/app/wujie-wk/wujie-vue2-child/src/views/Home.vue +0 -27
  90. package/app/wujie-wk/wujie-vue2-child/src/views/RouterJump.vue +0 -145
  91. package/app/wujie-wk/wujie-vue2-child/src/views/calendar/index.vue +0 -19
  92. package/app/wujie-wk/wujie-vue2-child/src/views/css.vue +0 -57
  93. package/app/wujie-wk/wujie-vue2-child/src/views/cssComponents/EllipsisText.vue +0 -73
  94. package/app/wujie-wk/wujie-vue2-child/src/views/cssComponents/HoverCard.vue +0 -69
  95. package/app/wujie-wk/wujie-vue2-child/src/views/cssComponents/TableHover.vue +0 -130
  96. package/app/wujie-wk/wujie-vue2-child/src/views/cssComponents/inputSlo.vue +0 -42
  97. package/app/wujie-wk/wujie-vue2-child/src/views/cssComponents/tableFixed.vue +0 -148
  98. package/app/wujie-wk/wujie-vue2-child/src/views/echarts/echart-dome.vue +0 -73
  99. package/app/wujie-wk/wujie-vue2-child/src/views/echarts/index.vue +0 -118
  100. package/app/wujie-wk/wujie-vue2-child/src/views/echarts/pei3d.vue +0 -658
  101. package/app/wujie-wk/wujie-vue2-child/src/views/element/bpmn/index.vue +0 -15
  102. package/app/wujie-wk/wujie-vue2-child/src/views/element/components/attendanceCycle/index.vue +0 -131
  103. package/app/wujie-wk/wujie-vue2-child/src/views/element/components/attendanceGroup/index.vue +0 -147
  104. package/app/wujie-wk/wujie-vue2-child/src/views/element/components/attendancePersonnel/index.vue +0 -158
  105. package/app/wujie-wk/wujie-vue2-child/src/views/element/components/companyCalendar/index.vue +0 -147
  106. package/app/wujie-wk/wujie-vue2-child/src/views/element/components/shift/index.vue +0 -147
  107. package/app/wujie-wk/wujie-vue2-child/src/views/element/components/shiftRotationSystem/index.vue +0 -147
  108. package/app/wujie-wk/wujie-vue2-child/src/views/element/elTableJsx/columnManagement.vue +0 -340
  109. package/app/wujie-wk/wujie-vue2-child/src/views/element/elTableJsx/dialogInput.vue +0 -71
  110. package/app/wujie-wk/wujie-vue2-child/src/views/element/elTableJsx/elTableJsx.vue +0 -1826
  111. package/app/wujie-wk/wujie-vue2-child/src/views/element/elTableJsx/formTable.vue +0 -595
  112. package/app/wujie-wk/wujie-vue2-child/src/views/element/elTableJsx/index.vue +0 -29
  113. package/app/wujie-wk/wujie-vue2-child/src/views/element/elTableJsx/simpleTable.vue +0 -189
  114. package/app/wujie-wk/wujie-vue2-child/src/views/element/elTableJsx.zip +0 -0
  115. package/app/wujie-wk/wujie-vue2-child/src/views/element/index.vue +0 -39
  116. package/app/wujie-wk/wujie-vue2-child/src/views/element/quillEditor/README.md +0 -163
  117. package/app/wujie-wk/wujie-vue2-child/src/views/element/quillEditor/example.vue +0 -307
  118. package/app/wujie-wk/wujie-vue2-child/src/views/element/quillEditor/index.vue +0 -409
  119. package/app/wujie-wk/wujie-vue2-child/src/views/element/quillEditor/toolbar.js +0 -122
  120. package/app/wujie-wk/wujie-vue2-child/vue.config.js +0 -15
@@ -1,147 +0,0 @@
1
- <template>
2
- <div class="ec-skeleton" aria-busy="true" aria-live="polite">
3
- <div
4
- v-for="row in rows"
5
- :key="row"
6
- class="ec-skeleton__row"
7
- >
8
- <div
9
- v-for="col in 7"
10
- :key="col"
11
- class="ec-skeleton__cell"
12
- :style="{ minHeight: minRowHeight + 'px' }"
13
- >
14
- <!-- 日期占位 -->
15
- <span class="ec-skeleton__block ec-skeleton__date"></span>
16
- <!-- 条目占位:每格数量做一点变化,视觉上更接近真实数据 -->
17
- <span
18
- v-for="n in itemCountOf(row, col)"
19
- :key="n"
20
- class="ec-skeleton__block ec-skeleton__item"
21
- :style="{ height: itemHeight + 'px' }"
22
- ></span>
23
- </div>
24
- </div>
25
- </div>
26
- </template>
27
-
28
- <script>
29
- /**
30
- * CalendarSkeleton —— 日历骨架屏
31
- * ------------------------------------------------------------------
32
- * 数据返回前占位,避免布局跳动和白屏。
33
- * 条目数量用「行列下标的确定性函数」生成,不用随机数,
34
- * 保证每次渲染骨架形状一致(随机形状会让人误以为在闪烁)。
35
- */
36
- export default {
37
- name: 'CalendarSkeleton',
38
-
39
- props: {
40
- /** 骨架行数 */
41
- rows: {
42
- type: Number,
43
- default: 5
44
- },
45
- /** 单元格最小高度,与日历主体保持一致 */
46
- minRowHeight: {
47
- type: Number,
48
- default: 118
49
- },
50
- /** 条目占位高度 */
51
- itemHeight: {
52
- type: Number,
53
- default: 28
54
- }
55
- },
56
-
57
- methods: {
58
- /**
59
- * 依据行列位置推导条目数量(0~3)
60
- * 周日(col=1) / 周六(col=7) 留空,模拟真实业务里周末排期少的形态
61
- */
62
- itemCountOf(row, col) {
63
- if (col === 1 || col === 7) return 0
64
- return ((row + col) % 3) + 1
65
- }
66
- }
67
- }
68
- </script>
69
-
70
- <style scoped>
71
- .ec-skeleton {
72
- width: 100%;
73
- }
74
-
75
- .ec-skeleton__row {
76
- display: grid;
77
- grid-template-columns: repeat(7, minmax(0, 1fr));
78
- }
79
-
80
- .ec-skeleton__cell {
81
- display: flex;
82
- flex-direction: column;
83
- align-items: center;
84
- gap: 8px;
85
- padding: 12px 10px;
86
- box-sizing: border-box;
87
- border-right: 1px solid var(--ec-border-light, #f0f2f5);
88
- border-bottom: 1px solid var(--ec-border-light, #f0f2f5);
89
- }
90
-
91
- .ec-skeleton__cell:nth-child(7n) {
92
- border-right: none;
93
- }
94
-
95
- .ec-skeleton__row:last-child .ec-skeleton__cell {
96
- border-bottom: none;
97
- }
98
-
99
- /* 占位块:底色 + 从左到右扫过的高光 */
100
- .ec-skeleton__block {
101
- display: block;
102
- border-radius: 4px;
103
- background: #f0f2f5;
104
- background-image: linear-gradient(
105
- 90deg,
106
- rgba(255, 255, 255, 0) 0%,
107
- rgba(255, 255, 255, 0.85) 50%,
108
- rgba(255, 255, 255, 0) 100%
109
- );
110
- background-size: 200% 100%;
111
- background-repeat: no-repeat;
112
- animation: ec-skeleton-shimmer 1.4s ease-in-out infinite;
113
- }
114
-
115
- .ec-skeleton__date {
116
- width: 56px;
117
- height: 15px;
118
- }
119
-
120
- .ec-skeleton__item {
121
- width: 100%;
122
- }
123
-
124
- /* 让每一格的高光有轻微时间差,波浪感更自然 */
125
- .ec-skeleton__cell:nth-child(2n) .ec-skeleton__block {
126
- animation-delay: 0.12s;
127
- }
128
- .ec-skeleton__cell:nth-child(3n) .ec-skeleton__block {
129
- animation-delay: 0.24s;
130
- }
131
-
132
- @keyframes ec-skeleton-shimmer {
133
- 0% {
134
- background-position: 150% 0;
135
- }
136
- 100% {
137
- background-position: -50% 0;
138
- }
139
- }
140
-
141
- /* 尊重系统「减少动态效果」设置 */
142
- @media (prefers-reduced-motion: reduce) {
143
- .ec-skeleton__block {
144
- animation: none;
145
- }
146
- }
147
- </style>
@@ -1,199 +0,0 @@
1
- <template>
2
- <div class="code-block" :class="{ 'is-open': open }">
3
- <button
4
- v-if="!alwaysOpen"
5
- type="button"
6
- class="code-block__toggle"
7
- @click="open = !open"
8
- >
9
- <svg class="code-block__caret" viewBox="0 0 12 12" aria-hidden="true">
10
- <path d="M4.5 2.5 L8.5 6 L4.5 9.5" fill="none" stroke="currentColor"
11
- stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
12
- </svg>
13
- <span>{{ open ? '收起代码' : '查看代码' }}</span>
14
- </button>
15
-
16
- <div v-show="open" class="code-block__body">
17
- <button
18
- type="button"
19
- class="code-block__copy"
20
- :class="{ 'is-done': copied }"
21
- @click="copy"
22
- >{{ copied ? '已复制' : '复制' }}</button>
23
- <pre class="code-block__pre"><code>{{ code }}</code></pre>
24
- </div>
25
- </div>
26
- </template>
27
-
28
- <script>
29
- /**
30
- * CodeBlock —— 示例页里的可折叠代码块
31
- * ------------------------------------------------------------------
32
- * 代码内容以字符串形式传入(不参与 Vue 模板编译),
33
- * 因此片段里可以放心写 {{ }}、v-if 等语法。
34
- */
35
- export default {
36
- name: 'CodeBlock',
37
-
38
- props: {
39
- /** 代码字符串 */
40
- code: {
41
- type: String,
42
- default: ''
43
- },
44
- /** 始终展开,不显示折叠按钮 */
45
- alwaysOpen: {
46
- type: Boolean,
47
- default: false
48
- }
49
- },
50
-
51
- data() {
52
- return {
53
- open: this.alwaysOpen,
54
- copied: false
55
- }
56
- },
57
-
58
- beforeDestroy() {
59
- if (this._copyTimer) clearTimeout(this._copyTimer)
60
- },
61
-
62
- methods: {
63
- copy() {
64
- const done = () => {
65
- this.copied = true
66
- if (this._copyTimer) clearTimeout(this._copyTimer)
67
- this._copyTimer = setTimeout(() => {
68
- this.copied = false
69
- }, 1600)
70
- }
71
-
72
- // 优先用 Clipboard API;非 HTTPS / 旧浏览器回退到 execCommand
73
- if (navigator.clipboard && window.isSecureContext) {
74
- navigator.clipboard.writeText(this.code).then(done).catch(this.fallbackCopy)
75
- return
76
- }
77
- this.fallbackCopy()
78
- },
79
-
80
- fallbackCopy() {
81
- const ta = document.createElement('textarea')
82
- ta.value = this.code
83
- ta.style.position = 'fixed'
84
- ta.style.opacity = '0'
85
- document.body.appendChild(ta)
86
- ta.select()
87
- try {
88
- document.execCommand('copy')
89
- this.copied = true
90
- if (this._copyTimer) clearTimeout(this._copyTimer)
91
- this._copyTimer = setTimeout(() => {
92
- this.copied = false
93
- }, 1600)
94
- } finally {
95
- document.body.removeChild(ta)
96
- }
97
- }
98
- }
99
- }
100
- </script>
101
-
102
- <style scoped>
103
- .code-block {
104
- margin-top: 14px;
105
- }
106
-
107
- .code-block__toggle {
108
- display: inline-flex;
109
- align-items: center;
110
- gap: 6px;
111
- padding: 4px 10px 4px 6px;
112
- border: 1px dashed #dcdfe6;
113
- border-radius: 8px;
114
- background: transparent;
115
- color: #909399;
116
- font-size: 13px;
117
- cursor: pointer;
118
- transition: color 0.18s, border-color 0.18s;
119
- }
120
-
121
- .code-block__toggle:hover {
122
- color: #3d6cf0;
123
- border-color: #3d6cf0;
124
- }
125
-
126
- .code-block__caret {
127
- width: 12px;
128
- height: 12px;
129
- transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1);
130
- }
131
-
132
- .code-block.is-open .code-block__caret {
133
- transform: rotate(90deg);
134
- }
135
-
136
- .code-block__body {
137
- position: relative;
138
- margin-top: 10px;
139
- }
140
-
141
- .code-block__copy {
142
- position: absolute;
143
- top: 10px;
144
- right: 10px;
145
- z-index: 1;
146
- padding: 3px 10px;
147
- border: 1px solid rgba(255, 255, 255, 0.18);
148
- border-radius: 6px;
149
- background: rgba(255, 255, 255, 0.08);
150
- color: #b8c0cc;
151
- font-size: 12px;
152
- cursor: pointer;
153
- transition: all 0.18s;
154
- }
155
-
156
- .code-block__copy:hover {
157
- background: rgba(255, 255, 255, 0.16);
158
- color: #fff;
159
- }
160
-
161
- .code-block__copy.is-done {
162
- border-color: rgba(16, 163, 127, 0.5);
163
- color: #4ade80;
164
- }
165
-
166
- .code-block__pre {
167
- margin: 0;
168
- padding: 16px 18px;
169
- border-radius: 10px;
170
- background: #1f2430;
171
- color: #cdd6e4;
172
- font-family: Consolas, Monaco, 'Courier New', monospace;
173
- font-size: 12.5px;
174
- line-height: 1.7;
175
- overflow-x: auto;
176
- white-space: pre;
177
- tab-size: 2;
178
- }
179
-
180
- .code-block__pre code {
181
- padding: 0;
182
- background: transparent;
183
- color: inherit;
184
- font-size: inherit;
185
- }
186
-
187
- .code-block__pre::-webkit-scrollbar {
188
- height: 8px;
189
- }
190
-
191
- .code-block__pre::-webkit-scrollbar-thumb {
192
- background: #3a4254;
193
- border-radius: 4px;
194
- }
195
-
196
- .code-block__pre::-webkit-scrollbar-track {
197
- background: transparent;
198
- }
199
- </style>
@@ -1,376 +0,0 @@
1
- <template>
2
- <div
3
- class="ec-select"
4
- :class="{ 'is-open': visible, 'is-disabled': disabled }"
5
- ref="root"
6
- >
7
- <!-- 触发器 -->
8
- <div
9
- class="ec-select__trigger"
10
- role="combobox"
11
- :aria-expanded="String(visible)"
12
- :aria-disabled="String(disabled)"
13
- :tabindex="disabled ? -1 : 0"
14
- @click="toggle"
15
- @keydown="handleTriggerKeydown"
16
- >
17
- <span class="ec-select__label">{{ currentLabel }}</span>
18
- <svg class="ec-select__arrow" viewBox="0 0 12 12" aria-hidden="true">
19
- <path d="M2 4.2 L6 8.2 L10 4.2" fill="none" stroke="currentColor" stroke-width="1.6"
20
- stroke-linecap="round" stroke-linejoin="round" />
21
- </svg>
22
- </div>
23
-
24
- <!-- 下拉面板 -->
25
- <transition name="ec-select-fade">
26
- <div
27
- v-show="visible"
28
- class="ec-select__dropdown"
29
- :class="`is-${placement}`"
30
- role="listbox"
31
- >
32
- <ul class="ec-select__list" ref="list">
33
- <li
34
- v-for="(opt, index) in options"
35
- :key="opt.value"
36
- class="ec-select__option"
37
- :class="{
38
- 'is-selected': opt.value === value,
39
- 'is-active': index === activeIndex
40
- }"
41
- role="option"
42
- :aria-selected="String(opt.value === value)"
43
- @click="select(opt)"
44
- @mouseenter="activeIndex = index"
45
- >
46
- <span class="ec-select__option-text">{{ opt.label }}</span>
47
- <svg
48
- v-if="opt.value === value"
49
- class="ec-select__check"
50
- viewBox="0 0 12 12"
51
- aria-hidden="true"
52
- >
53
- <path d="M2 6.3 L4.6 8.9 L10 3.4" fill="none" stroke="currentColor"
54
- stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />
55
- </svg>
56
- </li>
57
- <li v-if="!options.length" class="ec-select__empty">暂无可选月份</li>
58
- </ul>
59
- </div>
60
- </transition>
61
- </div>
62
- </template>
63
-
64
- <script>
65
- /**
66
- * MonthSelect —— 自研轻量下拉选择器
67
- * ------------------------------------------------------------------
68
- * 需求要求日历组件不依赖任何第三方 UI 库,因此这里手写一个下拉框,
69
- * 只实现日历需要的能力:单选、点击外部关闭、键盘操作、选中态。
70
- *
71
- * 事件:
72
- * - change(value, option) 选中项变化
73
- * - visible-change(visible) 面板展开 / 收起(日历用它来暂停轮播)
74
- */
75
- export default {
76
- name: 'MonthSelect',
77
-
78
- props: {
79
- /** 当前值 */
80
- value: {
81
- type: [String, Number],
82
- default: ''
83
- },
84
- /** 选项 [{ label, value }] */
85
- options: {
86
- type: Array,
87
- default: () => []
88
- },
89
- /** 无选项时的占位文案 */
90
- placeholder: {
91
- type: String,
92
- default: '请选择'
93
- },
94
- disabled: {
95
- type: Boolean,
96
- default: false
97
- },
98
- /** 面板弹出方向 */
99
- placement: {
100
- type: String,
101
- default: 'bottom-end',
102
- validator: (v) => ['bottom-start', 'bottom-end'].includes(v)
103
- }
104
- },
105
-
106
- data() {
107
- return {
108
- visible: false,
109
- /** 键盘高亮项索引 */
110
- activeIndex: -1
111
- }
112
- },
113
-
114
- computed: {
115
- currentLabel() {
116
- const hit = this.options.find((o) => o.value === this.value)
117
- return hit ? hit.label : this.placeholder
118
- }
119
- },
120
-
121
- watch: {
122
- visible(val) {
123
- this.$emit('visible-change', val)
124
- if (val) {
125
- // 展开时把高亮定位到当前选中项
126
- this.activeIndex = this.options.findIndex((o) => o.value === this.value)
127
- this.$nextTick(this.scrollToActive)
128
- }
129
- }
130
- },
131
-
132
- mounted() {
133
- // 用捕获阶段监听,避免子元素 stopPropagation 后无法关闭
134
- document.addEventListener('click', this.handleDocumentClick, true)
135
- },
136
-
137
- beforeDestroy() {
138
- document.removeEventListener('click', this.handleDocumentClick, true)
139
- },
140
-
141
- methods: {
142
- toggle() {
143
- if (this.disabled) return
144
- this.visible = !this.visible
145
- },
146
-
147
- close() {
148
- this.visible = false
149
- },
150
-
151
- select(opt) {
152
- this.close()
153
- if (opt.value === this.value) return
154
- this.$emit('change', opt.value, opt)
155
- },
156
-
157
- /** 点击组件外部时收起面板 */
158
- handleDocumentClick(e) {
159
- const root = this.$refs.root
160
- if (!this.visible || !root) return
161
- if (!root.contains(e.target)) this.close()
162
- },
163
-
164
- /** 键盘无障碍:上下切换、回车选中、ESC 关闭 */
165
- handleTriggerKeydown(e) {
166
- if (this.disabled) return
167
- const { key } = e
168
-
169
- if (key === 'Escape' || key === 'Tab') {
170
- this.close()
171
- return
172
- }
173
-
174
- if (key === 'Enter' || key === ' ') {
175
- e.preventDefault()
176
- if (!this.visible) {
177
- this.visible = true
178
- } else if (this.activeIndex > -1 && this.options[this.activeIndex]) {
179
- this.select(this.options[this.activeIndex])
180
- }
181
- return
182
- }
183
-
184
- if (key === 'ArrowDown' || key === 'ArrowUp') {
185
- e.preventDefault()
186
- if (!this.visible) {
187
- this.visible = true
188
- return
189
- }
190
- const len = this.options.length
191
- if (!len) return
192
- const step = key === 'ArrowDown' ? 1 : -1
193
- this.activeIndex = (this.activeIndex + step + len) % len
194
- this.scrollToActive()
195
- }
196
- },
197
-
198
- /** 让键盘高亮项保持在可视区域内 */
199
- scrollToActive() {
200
- const list = this.$refs.list
201
- if (!list) return
202
- const el = list.children[this.activeIndex]
203
- if (el && el.scrollIntoView) {
204
- el.scrollIntoView({ block: 'nearest' })
205
- }
206
- }
207
- }
208
- }
209
- </script>
210
-
211
- <style scoped>
212
- .ec-select {
213
- position: relative;
214
- display: inline-block;
215
- font-size: 14px;
216
- user-select: none;
217
- }
218
-
219
- /* ------------------------------ 触发器 ------------------------------ */
220
- .ec-select__trigger {
221
- display: flex;
222
- align-items: center;
223
- gap: 8px;
224
- min-width: 108px;
225
- height: 34px;
226
- padding: 0 10px 0 12px;
227
- box-sizing: border-box;
228
- border: 1px solid var(--ec-border, #e4e7ed);
229
- border-radius: var(--ec-radius, 8px);
230
- background: var(--ec-select-bg, #fff);
231
- color: var(--ec-text, #303133);
232
- cursor: pointer;
233
- transition: border-color 0.2s, box-shadow 0.2s, background-color 0.2s;
234
- }
235
-
236
- .ec-select__trigger:hover {
237
- border-color: var(--ec-primary, #3d6cf0);
238
- }
239
-
240
- .ec-select__trigger:focus-visible {
241
- outline: none;
242
- border-color: var(--ec-primary, #3d6cf0);
243
- box-shadow: 0 0 0 3px var(--ec-primary-ghost, rgba(61, 108, 240, 0.14));
244
- }
245
-
246
- .ec-select.is-open .ec-select__trigger {
247
- border-color: var(--ec-primary, #3d6cf0);
248
- box-shadow: 0 0 0 3px var(--ec-primary-ghost, rgba(61, 108, 240, 0.14));
249
- }
250
-
251
- .ec-select.is-disabled .ec-select__trigger {
252
- cursor: not-allowed;
253
- background: #f5f7fa;
254
- color: #c0c4cc;
255
- border-color: #e4e7ed;
256
- }
257
-
258
- .ec-select__label {
259
- flex: 1;
260
- overflow: hidden;
261
- white-space: nowrap;
262
- text-overflow: ellipsis;
263
- line-height: 1;
264
- }
265
-
266
- .ec-select__arrow {
267
- flex: none;
268
- width: 12px;
269
- height: 12px;
270
- color: var(--ec-text-secondary, #909399);
271
- transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
272
- }
273
-
274
- .ec-select.is-open .ec-select__arrow {
275
- transform: rotate(180deg);
276
- color: var(--ec-primary, #3d6cf0);
277
- }
278
-
279
- /* ------------------------------ 下拉面板 ------------------------------ */
280
- .ec-select__dropdown {
281
- position: absolute;
282
- top: calc(100% + 6px);
283
- z-index: 20;
284
- min-width: 100%;
285
- padding: 5px;
286
- box-sizing: border-box;
287
- border: 1px solid var(--ec-border, #e4e7ed);
288
- border-radius: var(--ec-radius, 8px);
289
- background: #fff;
290
- box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
291
- }
292
-
293
- .ec-select__dropdown.is-bottom-end {
294
- right: 0;
295
- }
296
-
297
- .ec-select__dropdown.is-bottom-start {
298
- left: 0;
299
- }
300
-
301
- .ec-select__list {
302
- max-height: 236px;
303
- margin: 0;
304
- padding: 0;
305
- overflow-y: auto;
306
- list-style: none;
307
- }
308
-
309
- .ec-select__option {
310
- display: flex;
311
- align-items: center;
312
- justify-content: space-between;
313
- gap: 8px;
314
- height: 32px;
315
- padding: 0 10px;
316
- border-radius: 6px;
317
- color: var(--ec-text, #303133);
318
- white-space: nowrap;
319
- cursor: pointer;
320
- transition: background-color 0.16s, color 0.16s;
321
- }
322
-
323
- .ec-select__option.is-active {
324
- background: var(--ec-primary-ghost, rgba(61, 108, 240, 0.1));
325
- }
326
-
327
- .ec-select__option.is-selected {
328
- color: var(--ec-primary, #3d6cf0);
329
- font-weight: 600;
330
- }
331
-
332
- .ec-select__check {
333
- flex: none;
334
- width: 12px;
335
- height: 12px;
336
- }
337
-
338
- .ec-select__empty {
339
- padding: 10px;
340
- color: var(--ec-text-placeholder, #c0c4cc);
341
- text-align: center;
342
- }
343
-
344
- /* 下拉滚动条 */
345
- .ec-select__list::-webkit-scrollbar {
346
- width: 6px;
347
- }
348
- .ec-select__list::-webkit-scrollbar-thumb {
349
- background: #dcdfe6;
350
- border-radius: 3px;
351
- }
352
- .ec-select__list::-webkit-scrollbar-track {
353
- background: transparent;
354
- }
355
-
356
- /* ------------------------------ 动画 ------------------------------ */
357
- .ec-select-fade-enter-active,
358
- .ec-select-fade-leave-active {
359
- transition: opacity 0.18s ease, transform 0.18s cubic-bezier(0.4, 0, 0.2, 1);
360
- transform-origin: top center;
361
- }
362
-
363
- .ec-select-fade-enter,
364
- .ec-select-fade-leave-to {
365
- opacity: 0;
366
- transform: translateY(-6px) scaleY(0.94);
367
- }
368
-
369
- @media (prefers-reduced-motion: reduce) {
370
- .ec-select__arrow,
371
- .ec-select-fade-enter-active,
372
- .ec-select-fade-leave-active {
373
- transition: none;
374
- }
375
- }
376
- </style>