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,570 @@
1
+ import { defineComponent, ref, computed, onBeforeUnmount } from 'vue'
2
+ import { Loading } from '@element-plus/icons-vue'
3
+ import { debounce } from 'lodash-es'
4
+ import PopoverSelector from './PopoverSelector.jsx'
5
+ import SelectorTrigger from './SelectorTrigger.jsx'
6
+ import { fetchDepartments } from '../mockData.js'
7
+ import styles from './DepartmentSelector.module.scss'
8
+
9
+ /**
10
+ * 部门选择器组件
11
+ * @component DepartmentSelector
12
+ * @description 基于 PopoverSelector 构建的部门选择器,支持单选和多选,提供树形结构展示
13
+ *
14
+ * @example
15
+ * // 单选模式(输入框)
16
+ * <DepartmentSelector
17
+ * v-model={selectedDepartment}
18
+ * placeholder="请选择部门"
19
+ * clearable={true}
20
+ * />
21
+ *
22
+ * // 多选模式(按钮)
23
+ * <DepartmentSelector
24
+ * v-model={selectedDepartments}
25
+ * displayType="button"
26
+ * label="部门:"
27
+ * multiple={true}
28
+ * clearable={true}
29
+ * />
30
+ */
31
+ export default defineComponent({
32
+ name: 'DepartmentSelector',
33
+
34
+ props: {
35
+ /**
36
+ * 选中的部门(单选时为对象,多选时为数组)
37
+ * @type {Object|Array<Object>}
38
+ * @example 单选:{ id: 911, label: '前端开发组' }
39
+ * @example 多选:[{ id: 910, label: '后端开发组' }, { id: 911, label: '前端开发组' }]
40
+ */
41
+ modelValue: {
42
+ type: [Object, Array],
43
+ default: null,
44
+ },
45
+ /**
46
+ * 是否多选
47
+ * @type {boolean}
48
+ */
49
+ multiple: {
50
+ type: Boolean,
51
+ default: false,
52
+ },
53
+ /**
54
+ * 输入框占位文本
55
+ * @type {string}
56
+ */
57
+ placeholder: {
58
+ type: String,
59
+ default: '请选择部门',
60
+ },
61
+ /**
62
+ * 显示类型:input 输入框模式 | button 按钮模式
63
+ * @type {string}
64
+ */
65
+ displayType: {
66
+ type: String,
67
+ default: 'input',
68
+ validator: (value) => ['input', 'button'].includes(value),
69
+ },
70
+ /**
71
+ * 前置标签文本,如:"部门:"
72
+ * @type {string}
73
+ */
74
+ label: {
75
+ type: String,
76
+ default: '',
77
+ },
78
+ /**
79
+ * 是否禁用
80
+ * @type {boolean}
81
+ */
82
+ disabled: {
83
+ type: Boolean,
84
+ default: false,
85
+ },
86
+ /**
87
+ * 是否可清空
88
+ * @type {boolean}
89
+ */
90
+ clearable: {
91
+ type: Boolean,
92
+ default: false,
93
+ },
94
+ /**
95
+ * 多选时是否将选中值按文字的形式展示
96
+ * @type {boolean}
97
+ */
98
+ collapseTags: {
99
+ type: Boolean,
100
+ default: false,
101
+ },
102
+ /**
103
+ * 当鼠标悬停于折叠标签的文本时,是否显示所有选中的标签
104
+ * @type {boolean}
105
+ */
106
+ collapseTagsTooltip: {
107
+ type: Boolean,
108
+ default: false,
109
+ },
110
+ /**
111
+ * 多选时用户最多可以选择的项目数,为 0 则不限制
112
+ * @type {number}
113
+ */
114
+ maxCollapseTags: {
115
+ type: Number,
116
+ default: 0,
117
+ },
118
+ /**
119
+ * 远程搜索方法
120
+ * @type {Function}
121
+ * @param {string} query - 搜索关键词
122
+ * @returns {Promise<Array>} 返回过滤后的部门树数据
123
+ */
124
+ remoteMethod: {
125
+ type: Function,
126
+ default: null,
127
+ },
128
+ },
129
+
130
+ emits: ['update:modelValue', 'change'],
131
+
132
+ setup(props, { emit }) {
133
+ // 部门树数据
134
+ const departments = ref([])
135
+
136
+ // 树组件 ref
137
+ const treeRef = ref(null)
138
+
139
+ // 搜索关键词
140
+ const searchKeyword = ref('')
141
+
142
+ // 远程搜索加载状态
143
+ const searchLoading = ref(false)
144
+
145
+ // 数据加载状态
146
+ const dataLoading = ref(false)
147
+
148
+ /**
149
+ * 获取选中的部门信息
150
+ * @returns {Array}
151
+ */
152
+ const selectedDepartments = computed(() => {
153
+ if (!props.modelValue) return []
154
+ // v-model 直接绑定对象,单选转数组,多选直接使用
155
+ return Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue]
156
+ })
157
+
158
+ /**
159
+ * 显示在输入框中的文本
160
+ * @returns {string}
161
+ */
162
+ const displayText = computed(() => {
163
+ if (selectedDepartments.value.length === 0) return ''
164
+ return selectedDepartments.value.map(d => d.label).join(', ')
165
+ })
166
+
167
+ /**
168
+ * 加载部门数据
169
+ */
170
+ const loadDepartments = async () => {
171
+ dataLoading.value = true
172
+ try {
173
+ departments.value = await fetchDepartments(2000)
174
+ } catch (error) {
175
+ console.error('加载部门数据失败:', error)
176
+ } finally {
177
+ dataLoading.value = false
178
+ }
179
+ }
180
+
181
+ /**
182
+ * 悬浮窗打开时的回调
183
+ */
184
+ const handleOpen = async () => {
185
+ // 如果数据未加载,加载数据
186
+ if (departments.value.length === 0) {
187
+ await loadDepartments()
188
+ }
189
+
190
+ // 设置树的选中状态
191
+ setTimeout(() => {
192
+ if (treeRef.value) {
193
+ if (props.multiple) {
194
+ // 多选:从对象数组中提取 id
195
+ const ids = Array.isArray(props.modelValue)
196
+ ? props.modelValue.map(item => item.id)
197
+ : []
198
+ treeRef.value.setCheckedKeys(ids)
199
+ } else {
200
+ // 单选:从对象中提取 id
201
+ const id = props.modelValue?.id || null
202
+ treeRef.value.setCurrentKey(id)
203
+ }
204
+ }
205
+ }, 100)
206
+ }
207
+
208
+ /**
209
+ * 树节点点击事件
210
+ * @param {Object} data - 节点数据
211
+ * @param {Object} node - 节点对象
212
+ * @param {Function} close - 关闭悬浮窗的函数
213
+ */
214
+ const handleNodeClick = (data, node, close) => {
215
+ if (!props.multiple) {
216
+ // 单选模式:点击直接选择并关闭,emit 完整对象
217
+ emit('update:modelValue', data)
218
+ emit('change', data)
219
+ close()
220
+ } else {
221
+ // 多选模式:点击展开/收起节点
222
+ if (node.childNodes && node.childNodes.length > 0) {
223
+ node.expanded = !node.expanded
224
+ }
225
+ }
226
+ }
227
+
228
+ /**
229
+ * 树节点勾选事件(多选)
230
+ * @param {Object} data - 节点数据
231
+ * @param {Object} checkedInfo - 勾选信息
232
+ */
233
+ const handleCheck = (data, checkedInfo) => {
234
+ if (props.multiple) {
235
+ const checkedKeys = checkedInfo.checkedKeys
236
+ // 找出所有选中的部门对象
237
+ const findDepartment = (depts, id) => {
238
+ for (const dept of depts) {
239
+ if (dept.id === id) return dept
240
+ if (dept.children) {
241
+ const found = findDepartment(dept.children, id)
242
+ if (found) return found
243
+ }
244
+ }
245
+ return null
246
+ }
247
+ const selectedDepts = checkedKeys.map(id => findDepartment(departments.value, id)).filter(Boolean)
248
+
249
+ emit('update:modelValue', selectedDepts) // emit 完整对象数组
250
+ emit('change', selectedDepts)
251
+ }
252
+ }
253
+
254
+ /**
255
+ * 清空选择
256
+ * @param {Event} e - 事件对象
257
+ */
258
+ const handleClear = (e) => {
259
+ e.stopPropagation()
260
+ emit('update:modelValue', props.multiple ? [] : null)
261
+ emit('change', props.multiple ? [] : null)
262
+ }
263
+
264
+ /**
265
+ * 移除单个标签
266
+ * @param {Event} e - 事件对象
267
+ * @param {number|string} id - 部门ID
268
+ */
269
+ const removeTag = (e, id) => {
270
+ e.stopPropagation()
271
+ if (props.multiple) {
272
+ const newValue = (Array.isArray(props.modelValue) ? props.modelValue : [])
273
+ .filter(item => item.id !== id)
274
+ emit('update:modelValue', newValue)
275
+ emit('change', newValue)
276
+ }
277
+ }
278
+
279
+ /**
280
+ * 过滤树节点(本地搜索)
281
+ * @param {string} value - 搜索值
282
+ * @param {Object} data - 节点数据
283
+ * @returns {boolean}
284
+ */
285
+ const filterNode = (value, data) => {
286
+ if (!value) return true
287
+ return data.label.toLowerCase().includes(value.toLowerCase())
288
+ }
289
+
290
+ /**
291
+ * 执行搜索(防抖)
292
+ * @param {string} value - 搜索关键词
293
+ */
294
+ const performSearch = async (value) => {
295
+ // 如果有远程搜索方法,调用远程搜索
296
+ if (props.remoteMethod && typeof props.remoteMethod === 'function') {
297
+ try {
298
+ searchLoading.value = true
299
+ const result = await props.remoteMethod(value)
300
+ // 更新部门数据
301
+ if (Array.isArray(result)) {
302
+ departments.value = result
303
+ }
304
+ } catch (error) {
305
+ console.error('远程搜索失败:', error)
306
+ } finally {
307
+ searchLoading.value = false
308
+ }
309
+ } else {
310
+ // 使用本地搜索
311
+ if (treeRef.value) {
312
+ treeRef.value.filter(value)
313
+ }
314
+ }
315
+ }
316
+
317
+ // 创建防抖搜索函数(300ms延迟)
318
+ const debouncedSearch = debounce(performSearch, 300)
319
+
320
+ /**
321
+ * 搜索关键词变化处理
322
+ * @param {string} value - 搜索关键词
323
+ */
324
+ const handleSearchChange = (value) => {
325
+ debouncedSearch(value)
326
+ }
327
+
328
+ // 组件卸载时
329
+ onBeforeUnmount(() => {
330
+ // 取消防抖函数
331
+ debouncedSearch.cancel()
332
+ })
333
+
334
+ return () => {
335
+ /**
336
+ * 渲染多选标签
337
+ */
338
+ const renderTags = () => {
339
+ if (!props.multiple || selectedDepartments.value.length === 0) return null
340
+
341
+ const depts = selectedDepartments.value
342
+
343
+ // collapse-tags 模式:只显示第一个 + 剩余数量
344
+ if (props.collapseTags) {
345
+ const firstDept = depts[0]
346
+ const restCount = depts.length - 1
347
+
348
+ const tagsContent = (
349
+ <div class={styles.tagsWrapper}>
350
+ <el-tag
351
+ closable
352
+ disable-transitions
353
+ onClose={(e) => removeTag(e, firstDept.id)}
354
+ size="small"
355
+ type="info"
356
+ class={styles.tag}
357
+ >
358
+ {firstDept.label}
359
+ </el-tag>
360
+ {restCount > 0 && (
361
+ <el-tag
362
+ disable-transitions
363
+ size="small"
364
+ type="info"
365
+ class={styles.tag}
366
+ >
367
+ +{restCount}
368
+ </el-tag>
369
+ )}
370
+ </div>
371
+ )
372
+
373
+ // collapse-tags-tooltip 模式:悬停显示所有标签
374
+ if (props.collapseTagsTooltip && restCount > 0) {
375
+ const tooltipContent = depts.slice(1).map(d => d.label).join('、')
376
+ return (
377
+ <el-tooltip
378
+ content={tooltipContent}
379
+ placement="top"
380
+ effect="light"
381
+ >
382
+ {tagsContent}
383
+ </el-tooltip>
384
+ )
385
+ }
386
+
387
+ return tagsContent
388
+ }
389
+
390
+ // max-collapse-tags 模式:限制显示的标签数量
391
+ if (props.maxCollapseTags > 0 && depts.length > props.maxCollapseTags) {
392
+ const visibleDepts = depts.slice(0, props.maxCollapseTags)
393
+ const restCount = depts.length - props.maxCollapseTags
394
+
395
+ return (
396
+ <div class={styles.tagsWrapper}>
397
+ {visibleDepts.map(dept => (
398
+ <el-tag
399
+ key={dept.id}
400
+ closable
401
+ disable-transitions
402
+ onClose={(e) => removeTag(e, dept.id)}
403
+ size="small"
404
+ type="info"
405
+ class={styles.tag}
406
+ >
407
+ {dept.label}
408
+ </el-tag>
409
+ ))}
410
+ <el-tag
411
+ disable-transitions
412
+ size="small"
413
+ type="info"
414
+ class={styles.tag}
415
+ >
416
+ +{restCount}
417
+ </el-tag>
418
+ </div>
419
+ )
420
+ }
421
+
422
+ // 默认模式:显示所有标签
423
+ return (
424
+ <div class={styles.tagsWrapper}>
425
+ {depts.map(dept => (
426
+ <el-tag
427
+ key={dept.id}
428
+ closable
429
+ disable-transitions
430
+ onClose={(e) => removeTag(e, dept.id)}
431
+ size="small"
432
+ type="info"
433
+ class={styles.tag}
434
+ >
435
+ {dept.label}
436
+ </el-tag>
437
+ ))}
438
+ </div>
439
+ )
440
+ }
441
+
442
+ /**
443
+ * 渲染显示内容(文本或标签)
444
+ */
445
+ const renderContent = () => {
446
+ let content = null
447
+
448
+ if (props.multiple) {
449
+ // 多选模式
450
+ if (selectedDepartments.value.length === 0) {
451
+ content = <span class={styles.placeholder}>{props.placeholder}</span>
452
+ } else if (props.displayType === 'button') {
453
+ // 按钮模式:显示文字 + 圆圈徽章
454
+ const firstDept = selectedDepartments.value[0]
455
+ const restCount = selectedDepartments.value.length - 1
456
+
457
+ content = (
458
+ <div class={styles.buttonTextWrapper}>
459
+ <span class={styles.text}>{firstDept.label}</span>
460
+ {restCount > 0 && (
461
+ <span class={styles.countBadge}>+{restCount}</span>
462
+ )}
463
+ </div>
464
+ )
465
+ } else {
466
+ // 输入框模式:显示标签
467
+ content = renderTags()
468
+ }
469
+ } else {
470
+ // 单选:显示文本
471
+ content = displayText.value ? (
472
+ <span class={styles.text}>{displayText.value}</span>
473
+ ) : (
474
+ <span class={styles.placeholder}>{props.placeholder}</span>
475
+ )
476
+ }
477
+
478
+ // 如果有 label,添加前缀
479
+ if (props.label) {
480
+ return (
481
+ <>
482
+ <span class={styles.label}>{props.label}</span>
483
+ {content}
484
+ </>
485
+ )
486
+ }
487
+
488
+ return content
489
+ }
490
+
491
+ return (
492
+ <PopoverSelector
493
+ width={400}
494
+ maxHeight={400}
495
+ disabled={props.disabled}
496
+ onOpen={handleOpen}
497
+ >
498
+ {{
499
+ // 触发器插槽
500
+ trigger: ({ open, visible }) => {
501
+ // 计算是否有选中值
502
+ const hasValue = props.multiple
503
+ ? selectedDepartments.value.length > 0
504
+ : !!displayText.value
505
+
506
+ return (
507
+ <SelectorTrigger
508
+ displayType={props.displayType}
509
+ visible={visible}
510
+ disabled={props.disabled}
511
+ clearable={props.clearable}
512
+ hasValue={hasValue}
513
+ onClick={open}
514
+ onClear={handleClear}
515
+ >
516
+ {renderContent()}
517
+ </SelectorTrigger>
518
+ )
519
+ },
520
+
521
+ // 悬浮窗内容插槽
522
+ default: ({ close }) => (
523
+ <div class={styles.dropdownContent}>
524
+ {/* 搜索框 */}
525
+ <div class={styles.searchBox}>
526
+ <el-input
527
+ v-model={searchKeyword.value}
528
+ placeholder="搜索部门"
529
+ prefix-icon="Search"
530
+ clearable
531
+ size="small"
532
+ loading={searchLoading.value}
533
+ onInput={handleSearchChange}
534
+ />
535
+ </div>
536
+
537
+ {/* 树形结构 */}
538
+ <div class={styles.treeWrapper}>
539
+ {(searchLoading.value || dataLoading.value) ? (
540
+ <div class={styles.loading}>
541
+ <el-icon class="is-loading">
542
+ <Loading />
543
+ </el-icon>
544
+ <span>{searchLoading.value ? '搜索中...' : '加载中...'}</span>
545
+ </div>
546
+ ) : (
547
+ <el-tree
548
+ ref={treeRef}
549
+ data={departments.value}
550
+ node-key="id"
551
+ props={{ label: 'label', children: 'children' }}
552
+ show-checkbox={props.multiple}
553
+ check-strictly={true}
554
+ default-expand-all={false}
555
+ highlight-current={!props.multiple}
556
+ expand-on-click-node={false}
557
+ filter-node-method={filterNode}
558
+ onNodeClick={(data, node) => handleNodeClick(data, node, close)}
559
+ onCheck={handleCheck}
560
+ />
561
+ )}
562
+ </div>
563
+ </div>
564
+ ),
565
+ }}
566
+ </PopoverSelector>
567
+ )
568
+ }
569
+ },
570
+ })