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