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,836 @@
1
+ # 部门人员选择器组件使用文档
2
+
3
+ ## 📋 目录
4
+
5
+ - [组件概述](#组件概述)
6
+ - [PopoverSelector 通用组件](#popoverselector-通用组件)
7
+ - [DepartmentSelector 部门选择器](#departmentselector-部门选择器)
8
+ - [PersonnelSelector 人员选择器](#personnelselector-人员选择器)
9
+ - [DepartmentPersonnelSelector 三栏选择器](#departmentpersonnelselector-三栏选择器)
10
+ - [快速开始](#快速开始)
11
+ - [反显使用指南](#反显使用指南) ⭐ 新增
12
+ - [常见问题](#常见问题)
13
+
14
+ ---
15
+
16
+ ## 组件概述
17
+
18
+ 本项目提供了一套完整的部门和人员选择器组件:
19
+
20
+ ### 📦 组件列表
21
+
22
+ | 组件名 | 描述 | 版本 |
23
+ |--------|------|------|
24
+ | `PopoverSelector` | 通用悬浮窗选择器(基础组件) | ⭐ 推荐 |
25
+ | `DepartmentSelector` | 部门选择器(树形结构) | V1 |
26
+ | `PersonnelSelector` | 人员选择器(列表形式) | V1 |
27
+ | `DepartmentPersonnelSelector` | 三栏选择器(部门+人员+已选) | V1 |
28
+ | `DepartmentSelectorV2` | 基于 PopoverSelector 的部门选择器 | V2 ⭐ 推荐 |
29
+
30
+ ### 🎯 功能特性
31
+
32
+ - ✅ **双模式支持**:输入框模式 / 按钮模式
33
+ - ✅ **单选/多选**:灵活的选择模式
34
+ - ✅ **Label 前缀**:可选的前置标签文字
35
+ - ✅ **Icon 切换**:hover 时清除/下拉图标智能切换
36
+ - ✅ **异步加载**:支持数据异步加载和 loading 状态
37
+ - ✅ **动画效果**:平滑的淡入淡出动画
38
+ - ✅ **响应式**:自动适配屏幕空间,智能定位
39
+ - ✅ **可清空**:支持一键清除选择
40
+ - ✅ **搜索功能**:快速搜索部门/人员
41
+
42
+ ---
43
+
44
+ ## PopoverSelector 通用组件
45
+
46
+ `PopoverSelector` 是一个高度解耦的通用悬浮窗选择器基础组件。
47
+
48
+ ### 🔧 Props
49
+
50
+ | 参数 | 类型 | 默认值 | 说明 |
51
+ |------|------|--------|------|
52
+ | `width` | `number\|string` | `400` | 悬浮窗宽度 |
53
+ | `height` | `number\|string` | `null` | 悬浮窗高度(不设置则自适应) |
54
+ | `maxHeight` | `number` | `400` | 悬浮窗最大高度 |
55
+ | `dropdownClass` | `string\|Array\|Object` | `''` | 悬浮窗自定义 class |
56
+ | `dropdownStyle` | `Object` | `{}` | 悬浮窗自定义 style |
57
+ | `disabled` | `boolean` | `false` | 是否禁用 |
58
+ | `placement` | `'auto'\|'top'\|'bottom'` | `'auto'` | 悬浮窗位置 |
59
+ | `offset` | `number` | `4` | 悬浮窗与触发器的间距 |
60
+ | `animated` | `boolean` | `true` | 是否显示动画 |
61
+ | `triggerEvent` | `'click'\|'hover'` | `'click'` | 触发方式 |
62
+ | `hoverDelay` | `number` | `200` | hover 触发延迟(毫秒) |
63
+
64
+ ### 📤 Events
65
+
66
+ | 事件名 | 参数 | 说明 |
67
+ |--------|------|------|
68
+ | `open` | - | 悬浮窗打开时触发 |
69
+ | `close` | - | 悬浮窗关闭时触发 |
70
+ | `update:visible` | `boolean` | 可见性变化时触发 |
71
+
72
+ ### 🎰 Slots
73
+
74
+ #### trigger(必需)
75
+
76
+ 触发器插槽,接收以下参数:
77
+
78
+ ```typescript
79
+ {
80
+ open: () => void, // 打开悬浮窗
81
+ close: () => void, // 关闭悬浮窗
82
+ toggle: () => void, // 切换悬浮窗
83
+ visible: boolean, // 当前可见性
84
+ events: Object, // 自动绑定的事件(hover 模式)
85
+ }
86
+ ```
87
+
88
+ #### default(必需)
89
+
90
+ 悬浮窗内容插槽,接收以下参数:
91
+
92
+ ```typescript
93
+ {
94
+ close: () => void, // 关闭悬浮窗
95
+ visible: boolean, // 当前可见性
96
+ }
97
+ ```
98
+
99
+ ### 📝 基本用法
100
+
101
+ #### 点击触发(默认)
102
+
103
+ ```jsx
104
+ <PopoverSelector width={400} maxHeight={500}>
105
+ {{
106
+ trigger: ({ open, visible }) => (
107
+ <el-button onClick={open}>
108
+ 选择部门 {visible && '▲' || '▼'}
109
+ </el-button>
110
+ ),
111
+ default: ({ close }) => (
112
+ <div>
113
+ <p>悬浮窗内容</p>
114
+ <button onClick={close}>关闭</button>
115
+ </div>
116
+ )
117
+ }}
118
+ </PopoverSelector>
119
+ ```
120
+
121
+ #### Hover 触发
122
+
123
+ ```jsx
124
+ <PopoverSelector
125
+ triggerEvent="hover"
126
+ hoverDelay={300}
127
+ width={400}
128
+ >
129
+ {{
130
+ trigger: ({ visible }) => (
131
+ <span>鼠标悬停查看详情</span>
132
+ ),
133
+ default: () => (
134
+ <div>详细信息...</div>
135
+ )
136
+ }}
137
+ </PopoverSelector>
138
+ ```
139
+
140
+ #### 自定义样式
141
+
142
+ ```jsx
143
+ <PopoverSelector
144
+ width={500}
145
+ maxHeight={600}
146
+ dropdownClass="custom-dropdown"
147
+ dropdownStyle={{ borderRadius: '8px' }}
148
+ >
149
+ {{
150
+ trigger: ({ open }) => <button onClick={open}>打开</button>,
151
+ default: () => <div>内容</div>
152
+ }}
153
+ </PopoverSelector>
154
+ ```
155
+
156
+ ---
157
+
158
+ ## DepartmentSelector 部门选择器
159
+
160
+ 部门选择器,提供树形结构的部门选择功能。
161
+
162
+ ### 🔧 Props
163
+
164
+ | 参数 | 类型 | 默认值 | 说明 |
165
+ |------|------|--------|------|
166
+ | `modelValue` | `number\|string\|Array` | `[]` | 选中的部门ID(单选时为number/string,多选时为Array) |
167
+ | `multiple` | `boolean` | `false` | 是否多选 |
168
+ | `placeholder` | `string` | `'请选择部门'` | 占位文本 |
169
+ | `displayType` | `'input'\|'button'` | `'input'` | 显示类型 |
170
+ | `label` | `string` | `''` | 前置标签文本 |
171
+ | `disabled` | `boolean` | `false` | 是否禁用 |
172
+ | `clearable` | `boolean` | `false` | 是否可清空 |
173
+ | `collapseTags` | `boolean` | `false` | 多选时是否折叠标签 |
174
+ | `collapseTagsTooltip` | `boolean` | `false` | 折叠时悬停显示所有标签 |
175
+ | `maxCollapseTags` | `number` | `0` | 最多显示的标签数量(0 不限制) |
176
+ | `remoteMethod` | `Function` | `null` | 远程搜索方法 |
177
+
178
+ ### 📤 Events
179
+
180
+ | 事件名 | 参数 | 说明 |
181
+ |--------|------|------|
182
+ | `update:modelValue` | `id` 或 `ids[]` | v-model 值变化时触发,返回 id |
183
+ | `change` | 单选:`department` 对象<br>多选:`departments[]` 数组 | 选择变化时触发,**返回完整对象** |
184
+
185
+ ### 📝 使用示例
186
+
187
+ #### 单选模式(输入框)
188
+
189
+ ```jsx
190
+ import DepartmentSelector from './components/DepartmentSelector.jsx'
191
+
192
+ const selectedDepartment = ref(null)
193
+
194
+ <DepartmentSelector
195
+ v-model={selectedDepartment.value}
196
+ placeholder="请选择部门"
197
+ clearable={true}
198
+ onChange={(dept) => {
199
+ console.log('选中的部门对象:', dept)
200
+ console.log('部门名称:', dept.label)
201
+ console.log('部门ID:', dept.id)
202
+ // 可以保存更多字段,不只是 id
203
+ }}
204
+ />
205
+ ```
206
+
207
+ #### 单选模式 - 反显示例
208
+
209
+ ```jsx
210
+ // 场景:从详情页返回或页面刷新时,需要显示已选的部门
211
+ const selectedDepartmentId = ref(911) // 已有初始值(从接口获取或 localStorage 读取)
212
+
213
+ <DepartmentSelector
214
+ v-model={selectedDepartmentId.value}
215
+ placeholder="请选择部门"
216
+ clearable={true}
217
+ onChange={(dept) => {
218
+ console.log('选中:', dept)
219
+ // 保存到后端或 localStorage
220
+ localStorage.setItem('selectedDepartmentId', dept.id)
221
+ localStorage.setItem('selectedDepartmentName', dept.label)
222
+ }}
223
+ />
224
+
225
+ // 组件会自动:
226
+ // 1. 检测到有初始值 911
227
+ // 2. 自动加载部门数据
228
+ // 3. 在输入框中显示 "前端开发组"
229
+ ```
230
+
231
+ #### 多选模式(按钮)
232
+
233
+ ```jsx
234
+ const selectedDepartments = ref([])
235
+
236
+ <DepartmentSelector
237
+ v-model={selectedDepartments.value}
238
+ displayType="button"
239
+ label="部门:"
240
+ multiple={true}
241
+ clearable={true}
242
+ collapseTags={true}
243
+ onChange={(depts) => {
244
+ console.log('选中的部门列表:', depts)
245
+ depts.forEach(dept => {
246
+ console.log(`部门:${dept.label}, ID: ${dept.id}`)
247
+ })
248
+ }}
249
+ />
250
+ ```
251
+
252
+ #### 多选模式 - 反显示例
253
+
254
+ ```jsx
255
+ // 场景:编辑页面,需要回显用户已选择的多个部门
256
+ const selectedDepartmentIds = ref([910, 911, 912]) // 从接口获取的已选 ids
257
+
258
+ <DepartmentSelector
259
+ v-model={selectedDepartmentIds.value}
260
+ displayType="input"
261
+ placeholder="请选择部门"
262
+ multiple={true}
263
+ clearable={true}
264
+ onChange={(depts) => {
265
+ console.log('当前选中的部门:', depts)
266
+ // 保存到后端
267
+ const ids = depts.map(d => d.id)
268
+ await saveDepartments(ids)
269
+ }}
270
+ />
271
+
272
+ // 组件会自动:
273
+ // 1. 检测到有初始值 [910, 911, 912]
274
+ // 2. 自动加载部门数据
275
+ // 3. 在输入框中显示三个标签:"后端开发组", "前端开发组", "测试组"
276
+ // 4. 或者在按钮中显示:"后端开发组 +2"
277
+ ```
278
+
279
+ #### 折叠标签 + 悬停提示
280
+
281
+ ```jsx
282
+ <DepartmentSelector
283
+ v-model={selectedDepartments.value}
284
+ multiple={true}
285
+ collapseTags={true}
286
+ collapseTagsTooltip={true}
287
+ />
288
+ ```
289
+
290
+ ---
291
+
292
+ ## PersonnelSelector 人员选择器
293
+
294
+ 人员选择器,提供列表形式的人员选择功能。
295
+
296
+ ### 🔧 Props
297
+
298
+ 基本与 `DepartmentSelector` 相同,额外增加:
299
+
300
+ | 参数 | 类型 | 默认值 | 说明 |
301
+ |------|------|--------|------|
302
+ | `departmentId` | `number\|string` | `null` | 按部门过滤人员 |
303
+
304
+ ### 📤 Events
305
+
306
+ | 事件名 | 参数 | 说明 |
307
+ |--------|------|------|
308
+ | `update:modelValue` | `id` 或 `ids[]` | v-model 值变化时触发,返回 id |
309
+ | `change` | 单选:`person` 对象<br>多选:`persons[]` 数组 | 选择变化时触发,**返回完整对象** |
310
+
311
+ ### 📝 使用示例
312
+
313
+ #### 单选模式
314
+
315
+ ```jsx
316
+ import PersonnelSelector from './components/PersonnelSelector.jsx'
317
+
318
+ const selectedPerson = ref(null)
319
+
320
+ <PersonnelSelector
321
+ v-model={selectedPerson.value}
322
+ placeholder="请选择人员"
323
+ clearable={true}
324
+ onChange={(person) => {
325
+ console.log('选中的人员对象:', person)
326
+ console.log('姓名:', person.name)
327
+ console.log('职位:', person.position)
328
+ console.log('电话:', person.phone)
329
+ console.log('邮箱:', person.email)
330
+ }}
331
+ />
332
+ ```
333
+
334
+ #### 单选模式 - 反显示例
335
+
336
+ ```jsx
337
+ // 场景:查看任务详情时,显示负责人信息
338
+ const assignedPersonId = ref(10245) // 从任务详情接口获取的负责人 id
339
+
340
+ <PersonnelSelector
341
+ v-model={assignedPersonId.value}
342
+ placeholder="请选择负责人"
343
+ clearable={true}
344
+ onChange={(person) => {
345
+ console.log('更换负责人:', person)
346
+ // 更新任务
347
+ await updateTaskAssignee({
348
+ taskId: taskId.value,
349
+ assigneeId: person.id,
350
+ assigneeName: person.name,
351
+ assigneePhone: person.phone
352
+ })
353
+ }}
354
+ />
355
+
356
+ // 组件会自动:
357
+ // 1. 检测到有初始值 10245
358
+ // 2. 自动加载人员数据
359
+ // 3. 在输入框中显示 "张三 - 高级工程师"
360
+ ```
361
+
362
+ #### 多选模式(按钮 + Label)
363
+
364
+ ```jsx
365
+ const selectedPersonnel = ref([])
366
+
367
+ <PersonnelSelector
368
+ v-model={selectedPersonnel.value}
369
+ displayType="button"
370
+ label="人员:"
371
+ multiple={true}
372
+ clearable={true}
373
+ onChange={(persons) => {
374
+ console.log('选中的人员列表:', persons)
375
+ persons.forEach(p => {
376
+ console.log(`${p.name} - ${p.position} - ${p.phone}`)
377
+ })
378
+ }}
379
+ />
380
+ ```
381
+
382
+ #### 多选模式 - 反显示例
383
+
384
+ ```jsx
385
+ // 场景:项目成员管理,编辑已有项目成员
386
+ const projectMemberIds = ref([10245, 10246, 10247]) // 从项目详情获取的成员 ids
387
+
388
+ <PersonnelSelector
389
+ v-model={projectMemberIds.value}
390
+ displayType="input"
391
+ placeholder="请选择项目成员"
392
+ multiple={true}
393
+ clearable={true}
394
+ onChange={(persons) => {
395
+ console.log('当前项目成员:', persons)
396
+ // 保存项目成员信息
397
+ await saveProjectMembers({
398
+ projectId: projectId.value,
399
+ members: persons.map(p => ({
400
+ id: p.id,
401
+ name: p.name,
402
+ position: p.position,
403
+ email: p.email
404
+ }))
405
+ })
406
+ }}
407
+ />
408
+
409
+ // 组件会自动:
410
+ // 1. 检测到有初始值 [10245, 10246, 10247]
411
+ // 2. 自动加载人员数据
412
+ // 3. 在输入框中显示三个标签:"张三", "李四", "王五"
413
+ // 4. 或者在按钮中显示:"张三 +2"
414
+ ```
415
+
416
+ #### 按部门过滤
417
+
418
+ ```jsx
419
+ <PersonnelSelector
420
+ v-model={selectedPersonnel.value}
421
+ departmentId={911} // 只显示该部门的人员
422
+ multiple={true}
423
+ />
424
+ ```
425
+
426
+ ---
427
+
428
+ ## DepartmentPersonnelSelector 三栏选择器
429
+
430
+ 三栏布局的部门人员选择器(部门树 + 人员列表 + 已选人员)。
431
+
432
+ ### 🔧 Props
433
+
434
+ 基本与前两个选择器相同,额外增加:
435
+
436
+ | 参数 | 类型 | 默认值 | 说明 |
437
+ |------|------|--------|------|
438
+ | `defaultSelectFirstDepartment` | `boolean` | `true` | 是否默认选择第一个部门 |
439
+
440
+ ### 📤 Events
441
+
442
+ | 事件名 | 参数 | 说明 |
443
+ |--------|------|------|
444
+ | `update:modelValue` | `id` 或 `ids[]` | v-model 值变化时触发,返回 id |
445
+ | `change` | 单选:`person` 对象<br>多选:`persons[]` 数组 | 选择变化时触发,**返回完整对象** |
446
+
447
+ ### 📝 使用示例
448
+
449
+ #### 单选模式
450
+
451
+ ```jsx
452
+ import DepartmentPersonnelSelector from './components/DepartmentPersonnelSelector.jsx'
453
+
454
+ const selectedPerson = ref(null)
455
+
456
+ <DepartmentPersonnelSelector
457
+ v-model={selectedPerson.value}
458
+ placeholder="请选择人员"
459
+ multiple={false}
460
+ clearable={true}
461
+ onChange={(person) => {
462
+ console.log('选中的人员:', person)
463
+ }}
464
+ />
465
+ ```
466
+
467
+ #### 单选模式 - 反显示例
468
+
469
+ ```jsx
470
+ // 场景:审批流程配置,设置审批人
471
+ const approverId = ref(10245) // 从配置中获取的审批人 id
472
+
473
+ <DepartmentPersonnelSelector
474
+ v-model={approverId.value}
475
+ placeholder="请选择审批人"
476
+ multiple={false}
477
+ clearable={true}
478
+ onChange={(person) => {
479
+ console.log('审批人变更为:', person)
480
+ // 保存审批配置
481
+ await saveApprovalConfig({
482
+ approverId: person.id,
483
+ approverName: person.name,
484
+ approverEmail: person.email
485
+ })
486
+ }}
487
+ />
488
+
489
+ // 组件会自动:
490
+ // 1. 检测到有初始值 10245
491
+ // 2. 加载部门数据和人员数据
492
+ // 3. 在输入框中显示 "张三"
493
+ // 4. 在三栏弹窗的右侧显示已选人员 "张三"
494
+ ```
495
+
496
+ #### 多选模式(按钮 + Label)
497
+
498
+ ```jsx
499
+ const selectedPersonnel = ref([])
500
+
501
+ <DepartmentPersonnelSelector
502
+ v-model={selectedPersonnel.value}
503
+ displayType="button"
504
+ label="已选人员:"
505
+ multiple={true}
506
+ clearable={true}
507
+ onChange={(persons) => {
508
+ console.log('已选人员:', persons)
509
+ }}
510
+ />
511
+ ```
512
+
513
+ #### 多选模式 - 反显示例
514
+
515
+ ```jsx
516
+ // 场景:会议邀请,编辑参会人员
517
+ const attendeeIds = ref([10245, 10246, 10247, 10248]) // 从会议详情获取
518
+
519
+ <DepartmentPersonnelSelector
520
+ v-model={attendeeIds.value}
521
+ displayType="input"
522
+ placeholder="请选择参会人员"
523
+ multiple={true}
524
+ clearable={true}
525
+ onChange={(persons) => {
526
+ console.log('参会人员更新:', persons)
527
+ // 保存会议信息
528
+ await updateMeeting({
529
+ meetingId: meetingId.value,
530
+ attendees: persons.map(p => ({
531
+ id: p.id,
532
+ name: p.name,
533
+ email: p.email,
534
+ department: p.departmentName
535
+ }))
536
+ })
537
+ }}
538
+ />
539
+
540
+ // 组件会自动:
541
+ // 1. 检测到有初始值 [10245, 10246, 10247, 10248]
542
+ // 2. 加载部门数据和所有人员数据
543
+ // 3. 在输入框中显示四个标签
544
+ // 4. 在三栏弹窗的右侧"已选人员"栏显示这4个人
545
+ // 5. 在中间人员列表中,这4个人会显示为已选中状态(带勾选框)
546
+ ```
547
+
548
+ #### 不默认选择部门
549
+
550
+ ```jsx
551
+ <DepartmentPersonnelSelector
552
+ v-model={selectedPersonnel.value}
553
+ defaultSelectFirstDepartment={false}
554
+ placeholder="请先选择部门"
555
+ multiple={true}
556
+ />
557
+ ```
558
+
559
+ ---
560
+
561
+ ## 快速开始
562
+
563
+ ### 1. 安装依赖
564
+
565
+ ```bash
566
+ npm install element-plus
567
+ ```
568
+
569
+ ### 2. 导入组件
570
+
571
+ ```jsx
572
+ import DepartmentSelector from './components/DepartmentSelector.jsx'
573
+ import PersonnelSelector from './components/PersonnelSelector.jsx'
574
+ import DepartmentPersonnelSelector from './components/DepartmentPersonnelSelector.jsx'
575
+
576
+ // 或使用 V2 版本
577
+ import DepartmentSelectorV2 from './components/DepartmentSelectorV2.jsx'
578
+ ```
579
+
580
+ ### 3. 使用组件
581
+
582
+ ```jsx
583
+ export default defineComponent({
584
+ setup() {
585
+ const selected = ref(null)
586
+
587
+ return () => (
588
+ <div>
589
+ <DepartmentSelector
590
+ v-model={selected.value}
591
+ placeholder="请选择部门"
592
+ clearable={true}
593
+ />
594
+ </div>
595
+ )
596
+ }
597
+ })
598
+ ```
599
+
600
+ ---
601
+
602
+ ## 反显使用指南
603
+
604
+ ### 什么是反显?
605
+
606
+ **反显**(回显)是指组件在初始化时就有值,需要自动显示对应的文本。常见于编辑页面、详情页面等场景。
607
+
608
+ ### 核心机制
609
+
610
+ 所有选择器组件都支持自动反显:
611
+ 1. ✅ 检测 `v-model` 是否有初始值
612
+ 2. ✅ 自动加载对应数据(部门/人员)
613
+ 3. ✅ 显示正确的文本/标签
614
+ 4. ✅ `onChange` 返回完整对象,方便保存更多信息
615
+
616
+ ### 快速示例
617
+
618
+ ```jsx
619
+ // 单选反显
620
+ const departmentId = ref(911) // 有初始值
621
+
622
+ <DepartmentSelector
623
+ v-model={departmentId.value} // 自动显示 "前端开发组"
624
+ onChange={(dept) => {
625
+ console.log('选中:', dept) // 获取完整对象
626
+ console.log('名称:', dept.label)
627
+ console.log('ID:', dept.id)
628
+ }}
629
+ />
630
+
631
+ // 多选反显
632
+ const memberIds = ref([10245, 10246, 10247]) // 有初始值
633
+
634
+ <PersonnelSelector
635
+ v-model={memberIds.value} // 自动显示三个标签
636
+ multiple={true}
637
+ onChange={(persons) => {
638
+ console.log('选中:', persons) // 获取完整对象数组
639
+ }}
640
+ />
641
+ ```
642
+
643
+ ### 完整文档
644
+
645
+ 查看 **[反显使用示例大全](./REFLEX_EXAMPLES.md)** 了解:
646
+
647
+ - 📝 编辑页面反显
648
+ - 📝 URL 参数反显
649
+ - 📝 localStorage 恢复
650
+ - 📝 详情页面反显
651
+ - 📝 批量操作反显
652
+ - 📝 搜索条件反显
653
+
654
+ ---
655
+
656
+ ## V2 版本推荐
657
+
658
+ ### DepartmentSelectorV2 使用示例
659
+
660
+ 基于 `PopoverSelector` 构建的新版本部门选择器。
661
+
662
+ ```jsx
663
+ import DepartmentSelectorV2 from './components/DepartmentSelectorV2.jsx'
664
+
665
+ // 单选
666
+ <DepartmentSelectorV2
667
+ v-model={selectedDepartment.value}
668
+ placeholder="请选择部门"
669
+ clearable={true}
670
+ />
671
+
672
+ // 多选(按钮模式)
673
+ <DepartmentSelectorV2
674
+ v-model={selectedDepartments.value}
675
+ displayType="button"
676
+ label="部门:"
677
+ multiple={true}
678
+ clearable={true}
679
+ />
680
+ ```
681
+
682
+ ### V2 版本优势
683
+
684
+ 1. **✅ 完全解耦**:触发器和悬浮窗内容通过插槽分离
685
+ 2. **✅ 高度可复用**:PopoverSelector 可用于任何悬浮窗场景
686
+ 3. **✅ 灵活配置**:丰富的参数支持各种定制
687
+ 4. **✅ 易于扩展**:可快速创建其他类型选择器
688
+ 5. **✅ 代码简洁**:业务逻辑更清晰
689
+
690
+ ---
691
+
692
+ ## 常见问题
693
+
694
+ ### Q: 输入框模式和按钮模式有什么区别?
695
+
696
+ **A**:
697
+ - **输入框模式** (`displayType="input"`):外观是输入框样式,多选时显示标签(tag)
698
+ - **按钮模式** (`displayType="button"`):使用 el-button 组件,多选时显示"第一个 +N"
699
+
700
+ ### Q: 如何自定义悬浮窗宽度?
701
+
702
+ **A**:
703
+ - **V1 版本**:悬浮窗宽度已固定为 400px(在 calculateDropdownPosition 函数中)
704
+ - **V2 版本**:通过 `width` prop 自定义:`<PopoverSelector width={500}>`
705
+
706
+ ### Q: 如何实现远程搜索?
707
+
708
+ **A**: 传入 `remoteMethod` prop:
709
+
710
+ ```jsx
711
+ const remoteSearch = async (query) => {
712
+ const result = await fetchDepartments(query)
713
+ return result
714
+ }
715
+
716
+ <DepartmentSelector
717
+ remoteMethod={remoteSearch}
718
+ />
719
+ ```
720
+
721
+ ### Q: 悬浮窗如何触发?
722
+
723
+ **A**:
724
+ - **V1 版本**:仅支持点击触发
725
+ - **V2 版本**:通过 `triggerEvent` prop 选择:
726
+ - `triggerEvent="click"`:点击触发(默认)
727
+ - `triggerEvent="hover"`:悬浮触发
728
+
729
+ ### Q: 如何禁用选择器?
730
+
731
+ **A**: 设置 `disabled={true}`:
732
+
733
+ ```jsx
734
+ <DepartmentSelector disabled={true} />
735
+ ```
736
+
737
+ ### Q: 数据加载时如何显示 loading?
738
+
739
+ **A**: 组件内置了 loading 状态,数据加载时会自动显示加载动画。
740
+
741
+ ### Q: 如何清空已选择的值?
742
+
743
+ **A**:
744
+ 1. 设置 `clearable={true}` 启用清除功能
745
+ 2. hover 时会显示清除图标
746
+ 3. 也可以通过代码清空:`selectedValue.value = null` 或 `[]`
747
+
748
+ ### Q: 多选模式下如何限制显示的标签数量?
749
+
750
+ **A**: 使用以下 props:
751
+
752
+ ```jsx
753
+ // 折叠为 "第一个 +N"
754
+ <DepartmentSelector
755
+ collapseTags={true}
756
+ />
757
+
758
+ // 最多显示 2 个标签
759
+ <DepartmentSelector
760
+ maxCollapseTags={2}
761
+ />
762
+
763
+ // 折叠 + 悬停显示全部
764
+ <DepartmentSelector
765
+ collapseTags={true}
766
+ collapseTagsTooltip={true}
767
+ />
768
+ ```
769
+
770
+ ---
771
+
772
+ ## 开发指南
773
+
774
+ ### 如何基于 PopoverSelector 创建新选择器?
775
+
776
+ ```jsx
777
+ import PopoverSelector from './PopoverSelector.jsx'
778
+
779
+ export default defineComponent({
780
+ props: {
781
+ modelValue: { /* ... */ },
782
+ // 其他业务 props
783
+ },
784
+
785
+ setup(props, { emit }) {
786
+ // 业务逻辑
787
+ const data = ref([])
788
+
789
+ return () => (
790
+ <PopoverSelector width={400}>
791
+ {{
792
+ trigger: ({ open }) => (
793
+ <el-button onClick={open}>
794
+ {props.modelValue || '请选择'}
795
+ </el-button>
796
+ ),
797
+ default: ({ close }) => (
798
+ <div>
799
+ {/* 你的选择器内容 */}
800
+ <YourContent
801
+ data={data.value}
802
+ onSelect={(value) => {
803
+ emit('update:modelValue', value)
804
+ close()
805
+ }}
806
+ />
807
+ </div>
808
+ )
809
+ }}
810
+ </PopoverSelector>
811
+ )
812
+ }
813
+ })
814
+ ```
815
+
816
+ ---
817
+
818
+ ## 更新日志
819
+
820
+ ### V2.0.0 (2024)
821
+ - ✨ 新增 PopoverSelector 通用组件
822
+ - ✨ 新增 DepartmentSelectorV2 示例
823
+ - ✨ 支持 hover 触发模式
824
+ - ✨ 支持自定义悬浮窗样式
825
+
826
+ ### V1.0.0 (2024)
827
+ - ✨ 初始版本
828
+ - ✨ DepartmentSelector、PersonnelSelector、DepartmentPersonnelSelector
829
+ - ✨ 支持单选/多选、输入框/按钮模式
830
+ - ✨ 支持 label、clearable、搜索等功能
831
+
832
+ ---
833
+
834
+ ## 许可证
835
+
836
+ MIT License