npmapps 1.0.21 → 1.0.23

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 (105) hide show
  1. package/app/Wscats.vue-1.0.26.vsix +0 -0
  2. package/app/febean.vue-format-0.1.8.vsix +0 -0
  3. package/app/wujie-vue3-child/.claude/settings.local.json +8 -0
  4. package/app/wujie-vue3-child/.vscode/extensions.json +3 -0
  5. package/app/wujie-vue3-child/PROJECT_MEMORY.md +427 -0
  6. package/app/wujie-vue3-child/README.md +5 -0
  7. package/app/wujie-vue3-child/index.html +13 -0
  8. package/app/wujie-vue3-child/package-lock.json +5744 -0
  9. package/app/wujie-vue3-child/package.json +28 -0
  10. package/app/wujie-vue3-child/public/vite.svg +1 -0
  11. package/app/wujie-vue3-child/src/App.vue +130 -0
  12. package/app/wujie-vue3-child/src/assets/vue.svg +1 -0
  13. package/app/wujie-vue3-child/src/components/HelloWorld.vue +43 -0
  14. package/app/wujie-vue3-child/src/components/tags-view.vue +193 -0
  15. package/app/wujie-vue3-child/src/components/tags-view1.vue +131 -0
  16. package/app/wujie-vue3-child/src/hooks/useClickOutside.js +11 -0
  17. package/app/wujie-vue3-child/src/hooks/useTableDragSort.js +28 -0
  18. package/app/wujie-vue3-child/src/main.js +15 -0
  19. package/app/wujie-vue3-child/src/router/index.js +104 -0
  20. package/app/wujie-vue3-child/src/store/tagsViewStroe.js +34 -0
  21. package/app/wujie-vue3-child/src/style.css +4 -0
  22. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/README.md +836 -0
  23. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/REFLEX_EXAMPLES.md +728 -0
  24. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentPersonnelSelector.jsx +687 -0
  25. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentPersonnelSelector.module.scss +560 -0
  26. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelector.jsx +570 -0
  27. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelector.module.scss +330 -0
  28. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelectorV2.jsx +378 -0
  29. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelectorV2.module.scss +228 -0
  30. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/OptionsSelector.jsx +399 -0
  31. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/OptionsSelector.module.scss +252 -0
  32. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PersonnelSelector.jsx +585 -0
  33. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PersonnelSelector.module.scss +331 -0
  34. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PopoverSelector.jsx +392 -0
  35. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PopoverSelector.module.scss +39 -0
  36. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/README.md +248 -0
  37. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/SelectorTrigger.jsx +194 -0
  38. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/index.jsx +1459 -0
  39. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/mockData.js +301 -0
  40. package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/dialogueSegment/index.jsx +28 -4
  41. package/app/wujie-vue3-child/src/views/aiCoach/index.jsx +32 -0
  42. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ChartsPanel/index.jsx +121 -0
  43. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ChartsPanel/index.module.scss +76 -0
  44. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/DonutChart/index.jsx +104 -0
  45. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/PracticeTable/index.jsx +75 -0
  46. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/PracticeTable/index.module.scss +12 -0
  47. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankBarChart/index.jsx +62 -0
  48. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankBarChart/index.module.scss +43 -0
  49. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingGroup/index.jsx +29 -0
  50. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingGroup/index.module.scss +5 -0
  51. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingList/index.jsx +58 -0
  52. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingList/index.module.scss +85 -0
  53. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ScriptStatsPanel/index.jsx +92 -0
  54. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ScriptStatsPanel/index.module.scss +56 -0
  55. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/StatCardsRow/index.jsx +40 -0
  56. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/StatCardsRow/index.module.scss +53 -0
  57. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/echarts/EchartsDonut.jsx +106 -0
  58. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/echarts/EchartsRankBar.jsx +132 -0
  59. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/index.jsx +176 -0
  60. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/index.module.scss +96 -0
  61. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/CoachReport/index.jsx +162 -0
  62. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/CoachReport/index.module.scss +16 -0
  63. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ComprehensiveEvaluation/index.jsx +29 -0
  64. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ComprehensiveEvaluation/index.module.scss +25 -0
  65. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueBubble/index.jsx +106 -0
  66. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueBubble/index.module.scss +164 -0
  67. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueRecord/index.jsx +182 -0
  68. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueRecord/index.module.scss +203 -0
  69. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionDetail/index.jsx +145 -0
  70. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionDetail/index.module.scss +126 -0
  71. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionScores/index.jsx +67 -0
  72. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionScores/index.module.scss +105 -0
  73. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ReportHeader/index.jsx +81 -0
  74. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ReportHeader/index.module.scss +47 -0
  75. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/RoleInfo/index.jsx +64 -0
  76. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/RoleInfo/index.module.scss +85 -0
  77. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ScoreBadge/index.jsx +39 -0
  78. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ScoreBadge/index.module.scss +44 -0
  79. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/SubDimensionItem/index.jsx +83 -0
  80. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/SubDimensionItem/index.module.scss +101 -0
  81. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/index.jsx +50 -0
  82. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/index.module.scss +25 -0
  83. package/app/wujie-vue3-child/src/views/child-to-parent.vue +117 -0
  84. package/app/wujie-vue3-child/src/views/home.vue +53 -0
  85. package/app/wujie-vue3-child/src/views/jsx/btnSelect/btnSelect.vue +169 -0
  86. package/app/wujie-vue3-child/src/views/jsx/btnSelect/index.vue +69 -0
  87. package/app/wujie-vue3-child/src/views/jsx/com.vue +44 -0
  88. package/app/wujie-vue3-child/src/views/jsx/dialog.jsx +66 -0
  89. package/app/wujie-vue3-child/src/views/jsx/index.vue +72 -0
  90. package/app/wujie-vue3-child/src/views/jsx/props.vue +33 -0
  91. package/app/wujie-vue3-child/src/views/parent-to-child.vue +225 -0
  92. package/app/wujie-vue3-child/src/views/phone-code.vue +318 -0
  93. package/app/wujie-vue3-child/src/views/router-jump.vue +123 -0
  94. package/app/wujie-vue3-child/src/views/test.vue +192 -0
  95. package/app/wujie-vue3-child/vite.config.js +15 -0
  96. package/package.json +1 -1
  97. package/app/aiCoach/index.jsx +0 -20
  98. package/npmapps-1.0.20.tgz +0 -0
  99. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/collapseExpand/index.jsx +0 -0
  100. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/collapseExpand/index.module.scss +0 -0
  101. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/dialogueSegment/index.module.scss +0 -0
  102. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/index.jsx +0 -0
  103. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/index.module.scss +0 -0
  104. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/inputColumn/index.jsx +0 -0
  105. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/inputColumn/index.module.scss +0 -0
@@ -0,0 +1,330 @@
1
+ // 部门选择器样式
2
+ .departmentSelector {
3
+ position: relative;
4
+ width: 100%;
5
+ }
6
+
7
+ // 自定义输入框
8
+ .customInput {
9
+ display: flex;
10
+ align-items: center;
11
+ width: 100%;
12
+ min-height: 32px;
13
+ padding: 1px 11px;
14
+ background: #fff;
15
+ border: 1px solid #dcdfe6;
16
+ border-radius: 4px;
17
+ cursor: pointer;
18
+ transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
19
+ outline: none;
20
+ box-sizing: border-box;
21
+
22
+ &:hover {
23
+ border-color: #c0c4cc;
24
+ }
25
+
26
+ &.focused {
27
+ border-color: #409eff;
28
+ }
29
+
30
+ &.disabled {
31
+ background-color: #f5f7fa;
32
+ border-color: #e4e7ed;
33
+ color: #c0c4cc;
34
+ cursor: not-allowed;
35
+ }
36
+ }
37
+
38
+ // 按钮模式包装器
39
+ .buttonWrapper {
40
+ // width: 100%;
41
+ }
42
+
43
+ // 按钮内部容器
44
+ .buttonInner {
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: space-between;
48
+ width: 100%;
49
+ gap: 8px;
50
+ }
51
+
52
+ // 按钮内容区域
53
+ .buttonContent {
54
+ flex: 1;
55
+ display: flex;
56
+ align-items: center;
57
+ min-width: 0;
58
+ text-align: left;
59
+ overflow: hidden;
60
+ }
61
+
62
+ // 按钮文字包装器(用于显示文字+徽章)
63
+ .buttonTextWrapper {
64
+ display: flex;
65
+ align-items: center;
66
+ gap: 8px;
67
+ }
68
+
69
+ // 数量徽章(圆圈显示 +N)
70
+ .countBadge {
71
+ display: inline-flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ min-width: 20px;
75
+ height: 20px;
76
+ padding: 0 4px;
77
+ background-color: #ecf5ff;
78
+ color: #409eff;
79
+ font-size: 12px;
80
+ font-weight: 500;
81
+ border-radius: 10px;
82
+ white-space: nowrap;
83
+ flex-shrink: 0;
84
+ }
85
+
86
+ // 输入框内容区域
87
+ .inputContent {
88
+ flex: 1;
89
+ display: flex;
90
+ align-items: center;
91
+ min-width: 0;
92
+ min-height: 30px;
93
+ padding: 2px 0;
94
+ }
95
+
96
+ // 占位符
97
+ .placeholder {
98
+ // color: #c0c4cc;
99
+ font-size: 14px;
100
+ }
101
+
102
+ // 前置标签
103
+ .label {
104
+ color: #606266;
105
+ font-size: 14px;
106
+ margin-right: 4px;
107
+ flex-shrink: 0;
108
+ }
109
+
110
+ // 单选文本
111
+ .text {
112
+ color: #606266;
113
+ font-size: 14px;
114
+ overflow: hidden;
115
+ text-overflow: ellipsis;
116
+ white-space: nowrap;
117
+ }
118
+
119
+ // 标签容器
120
+ .tagsWrapper {
121
+ display: flex;
122
+ align-items: center;
123
+ flex-wrap: wrap;
124
+ gap: 4px;
125
+ }
126
+
127
+ // 标签
128
+ .tag {
129
+ margin: 0;
130
+ max-width: 100%;
131
+
132
+ :global(.el-tag__content) {
133
+ overflow: hidden;
134
+ text-overflow: ellipsis;
135
+ white-space: nowrap;
136
+ }
137
+ }
138
+
139
+ // 右侧图标区域
140
+ .suffixIcons {
141
+ display: flex;
142
+ align-items: center;
143
+ gap: 4px;
144
+ margin-left: 8px;
145
+ flex-shrink: 0;
146
+ }
147
+
148
+ // 清除图标
149
+ .clearIcon {
150
+ font-size: 14px;
151
+ color: #c0c4cc;
152
+ cursor: pointer;
153
+ transition: color 0.2s;
154
+
155
+ &:hover {
156
+ color: #909399;
157
+ }
158
+ }
159
+
160
+ // 下拉箭头图标
161
+ .arrowIcon {
162
+ font-size: 14px;
163
+ color: #c0c4cc;
164
+ transition: transform 0.3s;
165
+
166
+ &.reverse {
167
+ transform: rotate(180deg);
168
+ }
169
+ }
170
+
171
+ .disabled {
172
+ .clearIcon,
173
+ .arrowIcon {
174
+ cursor: not-allowed;
175
+ }
176
+ }
177
+
178
+ // 下拉框容器
179
+ .dropdown {
180
+ position: fixed;
181
+ background: #fff;
182
+ border: 1px solid #e4e7ed;
183
+ border-radius: 4px;
184
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
185
+ z-index: 2000;
186
+ display: flex;
187
+ flex-direction: column;
188
+ overflow: hidden;
189
+
190
+ // 添加动画效果
191
+ animation: dropdownSlideIn 0.2s ease-out;
192
+ transform-origin: top;
193
+ }
194
+
195
+ // 悬浮窗内容
196
+ .dropdownContent {
197
+ display: flex;
198
+ flex-direction: column;
199
+ height: 100%;
200
+ max-height: inherit; // 继承父容器的 maxHeight
201
+ overflow: hidden; // 防止内容溢出到悬浮窗外
202
+ }
203
+
204
+ // 下拉框淡入动画
205
+ @keyframes dropdownSlideIn {
206
+ from {
207
+ opacity: 0;
208
+ transform: scaleY(0.8);
209
+ }
210
+ to {
211
+ opacity: 1;
212
+ transform: scaleY(1);
213
+ }
214
+ }
215
+
216
+ // 搜索框区域
217
+ .searchBox {
218
+ padding: 8px;
219
+ border-bottom: 1px solid #e4e7ed;
220
+ background: #fff;
221
+ }
222
+
223
+ // 树形结构容器
224
+ .treeWrapper {
225
+ flex: 1;
226
+ overflow-y: auto;
227
+ padding: 8px 0;
228
+
229
+ // 自定义滚动条样式
230
+ &::-webkit-scrollbar {
231
+ width: 6px;
232
+ }
233
+
234
+ &::-webkit-scrollbar-track {
235
+ background: #f1f1f1;
236
+ border-radius: 3px;
237
+ }
238
+
239
+ &::-webkit-scrollbar-thumb {
240
+ background: #c1c1c1;
241
+ border-radius: 3px;
242
+
243
+ &:hover {
244
+ background: #a8a8a8;
245
+ }
246
+ }
247
+
248
+ // 树节点样式
249
+ :global {
250
+ .el-tree {
251
+ background: transparent;
252
+
253
+ .el-tree-node {
254
+ &:focus > .el-tree-node__content {
255
+ background-color: #f5f7fa;
256
+ }
257
+ }
258
+
259
+ .el-tree-node__content {
260
+ height: 32px;
261
+ padding-right: 12px;
262
+ transition: background-color 0.2s;
263
+
264
+ &:hover {
265
+ background-color: #f5f7fa;
266
+ }
267
+ }
268
+
269
+ .el-tree-node__label {
270
+ font-size: 14px;
271
+ color: #606266;
272
+ }
273
+
274
+ // 高亮当前选中节点(单选)
275
+ .el-tree-node.is-current > .el-tree-node__content {
276
+ background-color: #ecf5ff;
277
+ color: #409eff;
278
+
279
+ .el-tree-node__label {
280
+ color: #409eff;
281
+ font-weight: 500;
282
+ }
283
+ }
284
+
285
+ // 复选框样式
286
+ .el-checkbox {
287
+ margin-right: 8px;
288
+ }
289
+
290
+ // 展开/收起图标
291
+ .el-tree-node__expand-icon {
292
+ font-size: 12px;
293
+ color: #c0c4cc;
294
+
295
+ &.is-leaf {
296
+ color: transparent;
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }
302
+
303
+ // 空状态
304
+ .empty {
305
+ padding: 40px 16px;
306
+ text-align: center;
307
+ color: #909399;
308
+ font-size: 14px;
309
+ }
310
+
311
+ // 加载状态
312
+ .loading {
313
+ display: flex;
314
+ flex-direction: column;
315
+ align-items: center;
316
+ justify-content: center;
317
+ padding: 60px 16px;
318
+ color: #909399;
319
+ font-size: 14px;
320
+ gap: 12px;
321
+
322
+ :global(.el-icon) {
323
+ font-size: 24px;
324
+ color: #409eff;
325
+ }
326
+
327
+ span {
328
+ margin-top: 4px;
329
+ }
330
+ }
@@ -0,0 +1,378 @@
1
+ import { defineComponent, ref } from 'vue'
2
+ import { ArrowDown, CircleClose, Loading } from '@element-plus/icons-vue'
3
+ import PopoverSelector from './PopoverSelector.jsx'
4
+ import { fetchDepartments } from '../mockData.js'
5
+ import styles from './DepartmentSelectorV2.module.scss'
6
+
7
+ /**
8
+ * 基于 PopoverSelector 的部门选择器示例
9
+ * @component DepartmentSelectorV2
10
+ * @description 使用通用 PopoverSelector 组件构建的部门选择器
11
+ */
12
+ export default defineComponent({
13
+ name: 'DepartmentSelectorV2',
14
+
15
+ props: {
16
+ /**
17
+ * 选中的部门(单选时为对象,多选时为数组)
18
+ * @type {Object|Array<Object>}
19
+ * @example 单选:{ id: 911, label: '前端开发组' }
20
+ * @example 多选:[{ id: 910, label: '后端开发组' }, { id: 911, label: '前端开发组' }]
21
+ */
22
+ modelValue: {
23
+ type: [Object, Array],
24
+ default: null,
25
+ },
26
+ /**
27
+ * 是否多选
28
+ */
29
+ multiple: {
30
+ type: Boolean,
31
+ default: false,
32
+ },
33
+ /**
34
+ * 占位文本
35
+ */
36
+ placeholder: {
37
+ type: String,
38
+ default: '请选择部门',
39
+ },
40
+ /**
41
+ * 前置标签
42
+ */
43
+ label: {
44
+ type: String,
45
+ default: '',
46
+ },
47
+ /**
48
+ * 显示类型:input | button
49
+ */
50
+ displayType: {
51
+ type: String,
52
+ default: 'input',
53
+ validator: (value) => ['input', 'button'].includes(value),
54
+ },
55
+ /**
56
+ * 是否禁用
57
+ */
58
+ disabled: {
59
+ type: Boolean,
60
+ default: false,
61
+ },
62
+ /**
63
+ * 是否可清空
64
+ */
65
+ clearable: {
66
+ type: Boolean,
67
+ default: false,
68
+ },
69
+ },
70
+
71
+ emits: ['update:modelValue', 'change'],
72
+
73
+ setup(props, { emit }) {
74
+ // 部门树数据
75
+ const departments = ref([])
76
+ // 数据加载状态
77
+ const loading = ref(false)
78
+ // 是否 hover
79
+ const isHovering = ref(false)
80
+ // 树组件 ref
81
+ const treeRef = ref(null)
82
+ // 搜索关键词
83
+ const searchKeyword = ref('')
84
+
85
+ /**
86
+ * 获取选中的部门信息
87
+ */
88
+ const selectedDepartments = (() => {
89
+ if (!props.modelValue) return []
90
+ // v-model 直接绑定对象,单选转数组,多选直接使用
91
+ return Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue]
92
+ })
93
+
94
+ /**
95
+ * 显示文本
96
+ */
97
+ const displayText = (() => {
98
+ const selected = selectedDepartments()
99
+ if (selected.length === 0) return ''
100
+ return selected.map(d => d.label).join(', ')
101
+ })
102
+
103
+ /**
104
+ * 加载部门数据
105
+ */
106
+ const loadDepartments = async () => {
107
+ loading.value = true
108
+ try {
109
+ departments.value = await fetchDepartments(2000)
110
+ } catch (error) {
111
+ console.error('加载部门数据失败:', error)
112
+ } finally {
113
+ loading.value = false
114
+ }
115
+ }
116
+
117
+ /**
118
+ * 树节点点击
119
+ */
120
+ const handleNodeClick = (data, node, close) => {
121
+ if (!props.multiple) {
122
+ // 单选:选中后关闭,emit 完整对象
123
+ emit('update:modelValue', data)
124
+ emit('change', data)
125
+ close()
126
+ } else {
127
+ // 多选:点击展开/收起
128
+ if (node.childNodes && node.childNodes.length > 0) {
129
+ node.expanded = !node.expanded
130
+ }
131
+ }
132
+ }
133
+
134
+ /**
135
+ * 树节点勾选
136
+ */
137
+ const handleCheck = (data, checkedInfo) => {
138
+ if (props.multiple) {
139
+ const checkedKeys = checkedInfo.checkedKeys
140
+ // 找出所有选中的部门对象
141
+ const findDepartment = (depts, id) => {
142
+ for (const dept of depts) {
143
+ if (dept.id === id) return dept
144
+ if (dept.children) {
145
+ const found = findDepartment(dept.children, id)
146
+ if (found) return found
147
+ }
148
+ }
149
+ return null
150
+ }
151
+ const selectedDepts = checkedKeys.map(id => findDepartment(departments.value, id)).filter(Boolean)
152
+
153
+ emit('update:modelValue', selectedDepts) // emit 完整对象数组
154
+ emit('change', selectedDepts)
155
+ }
156
+ }
157
+
158
+ /**
159
+ * 清空选择
160
+ */
161
+ const handleClear = (e) => {
162
+ e.stopPropagation()
163
+ emit('update:modelValue', props.multiple ? [] : null)
164
+ emit('change', props.multiple ? [] : null)
165
+ }
166
+
167
+ /**
168
+ * 过滤节点
169
+ */
170
+ const filterNode = (value, data) => {
171
+ if (!value) return true
172
+ return data.label.toLowerCase().includes(value.toLowerCase())
173
+ }
174
+
175
+ /**
176
+ * 搜索变化
177
+ */
178
+ const handleSearchChange = (value) => {
179
+ if (treeRef.value) {
180
+ treeRef.value.filter(value)
181
+ }
182
+ }
183
+
184
+ /**
185
+ * 悬浮窗打开时
186
+ */
187
+ const handleOpen = async () => {
188
+ // 如果数据未加载,加载数据
189
+ if (departments.value.length === 0) {
190
+ await loadDepartments()
191
+ }
192
+
193
+ // 设置树的选中状态
194
+ setTimeout(() => {
195
+ if (treeRef.value) {
196
+ if (props.multiple) {
197
+ // 多选:从对象数组中提取 id
198
+ const ids = Array.isArray(props.modelValue)
199
+ ? props.modelValue.map(item => item.id)
200
+ : []
201
+ treeRef.value.setCheckedKeys(ids)
202
+ } else {
203
+ // 单选:从对象中提取 id
204
+ const id = props.modelValue?.id || null
205
+ treeRef.value.setCurrentKey(id)
206
+ }
207
+ }
208
+ }, 100)
209
+ }
210
+
211
+ return () => {
212
+ // 渲染显示内容
213
+ const renderContent = () => {
214
+ let content = null
215
+
216
+ if (props.multiple) {
217
+ const selected = selectedDepartments()
218
+ if (selected.length === 0) {
219
+ content = <span class={styles.placeholder}>{props.placeholder}</span>
220
+ } else if (props.displayType === 'button') {
221
+ // 按钮模式:显示文字 + 徽章
222
+ const first = selected[0]
223
+ const restCount = selected.length - 1
224
+ content = (
225
+ <div class={styles.textWrapper}>
226
+ <span class={styles.text}>{first.label}</span>
227
+ {restCount > 0 && (
228
+ <span class={styles.countBadge}>+{restCount}</span>
229
+ )}
230
+ </div>
231
+ )
232
+ } else {
233
+ // 输入框模式:显示文本
234
+ content = <span class={styles.text}>{displayText()}</span>
235
+ }
236
+ } else {
237
+ content = displayText() ? (
238
+ <span class={styles.text}>{displayText()}</span>
239
+ ) : (
240
+ <span class={styles.placeholder}>{props.placeholder}</span>
241
+ )
242
+ }
243
+
244
+ if (props.label) {
245
+ return (
246
+ <>
247
+ <span class={styles.label}>{props.label}</span>
248
+ {content}
249
+ </>
250
+ )
251
+ }
252
+
253
+ return content
254
+ }
255
+
256
+ // 渲染图标
257
+ const renderIcons = (visible) => {
258
+ const hasValue = props.multiple
259
+ ? selectedDepartments().length > 0
260
+ : !!displayText()
261
+
262
+ const showClearIcon = props.clearable && hasValue && isHovering.value
263
+
264
+ return (
265
+ <div class={styles.icons}>
266
+ {showClearIcon && (
267
+ <el-icon class={styles.clearIcon} onClick={handleClear}>
268
+ <CircleClose />
269
+ </el-icon>
270
+ )}
271
+ {!showClearIcon && (
272
+ <el-icon class={[styles.arrowIcon, visible && styles.reverse]}>
273
+ <ArrowDown />
274
+ </el-icon>
275
+ )}
276
+ </div>
277
+ )
278
+ }
279
+
280
+ return (
281
+ <PopoverSelector
282
+ width={400}
283
+ maxHeight={400}
284
+ disabled={props.disabled}
285
+ onOpen={handleOpen}
286
+ >
287
+ {{
288
+ // 触发器插槽
289
+ trigger: ({ open, visible }) => {
290
+ if (props.displayType === 'button') {
291
+ return (
292
+ <el-button
293
+ disabled={props.disabled}
294
+ onClick={open}
295
+ onMouseenter={() => (isHovering.value = true)}
296
+ onMouseleave={() => (isHovering.value = false)}
297
+ class={styles.buttonTrigger}
298
+ >
299
+ {{
300
+ default: () => (
301
+ <div class={styles.buttonInner}>
302
+ <div class={styles.content}>
303
+ {renderContent()}
304
+ </div>
305
+ {renderIcons(visible)}
306
+ </div>
307
+ ),
308
+ }}
309
+ </el-button>
310
+ )
311
+ }
312
+
313
+ return (
314
+ <div
315
+ class={[
316
+ styles.inputTrigger,
317
+ props.disabled && styles.disabled,
318
+ visible && styles.focused,
319
+ ]}
320
+ onClick={open}
321
+ onMouseenter={() => (isHovering.value = true)}
322
+ onMouseleave={() => (isHovering.value = false)}
323
+ >
324
+ <div class={styles.content}>{renderContent()}</div>
325
+ {renderIcons(visible)}
326
+ </div>
327
+ )
328
+ },
329
+
330
+ // 悬浮窗内容插槽
331
+ default: ({ close }) => (
332
+ <div class={styles.dropdownContent}>
333
+ {/* 搜索框 */}
334
+ <div class={styles.searchBox}>
335
+ <el-input
336
+ v-model={searchKeyword.value}
337
+ placeholder="搜索部门"
338
+ prefix-icon="Search"
339
+ clearable
340
+ size="small"
341
+ onInput={handleSearchChange}
342
+ />
343
+ </div>
344
+
345
+ {/* 树形结构 */}
346
+ <div class={styles.treeWrapper}>
347
+ {loading.value ? (
348
+ <div class={styles.loading}>
349
+ <el-icon class="is-loading">
350
+ <Loading />
351
+ </el-icon>
352
+ <span>加载中...</span>
353
+ </div>
354
+ ) : (
355
+ <el-tree
356
+ ref={treeRef}
357
+ data={departments.value}
358
+ node-key="id"
359
+ props={{ label: 'label', children: 'children' }}
360
+ show-checkbox={props.multiple}
361
+ check-strictly={true}
362
+ default-expand-all={false}
363
+ highlight-current={!props.multiple}
364
+ expand-on-click-node={false}
365
+ filter-node-method={filterNode}
366
+ onNodeClick={(data, node) => handleNodeClick(data, node, close)}
367
+ onCheck={handleCheck}
368
+ />
369
+ )}
370
+ </div>
371
+ </div>
372
+ ),
373
+ }}
374
+ </PopoverSelector>
375
+ )
376
+ }
377
+ },
378
+ })