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,728 @@
1
+ # 反显使用示例大全
2
+
3
+ 本文档提供所有选择器组件的反显(回显)使用示例,涵盖各种实际业务场景。
4
+
5
+ ## 📋 目录
6
+
7
+ - [什么是反显](#什么是反显)
8
+ - [DepartmentSelector 反显](#departmentselector-反显)
9
+ - [PersonnelSelector 反显](#personnelselector-反显)
10
+ - [DepartmentPersonnelSelector 反显](#departmentpersonnelselector-反显)
11
+ - [常见场景](#常见场景)
12
+
13
+ ---
14
+
15
+ ## 什么是反显
16
+
17
+ **反显**(回显)是指组件在初始化时就已经有值,需要自动加载数据并显示对应的文本。
18
+
19
+ ### 常见场景:
20
+
21
+ 1. **编辑页面** - 修改已有数据时,需要显示原有的选择
22
+ 2. **详情页面** - 查看数据详情时,需要显示已保存的选择
23
+ 3. **页面刷新** - 从 localStorage 或 sessionStorage 恢复数据
24
+ 4. **路由传参** - 通过 URL 参数或 query 传递 id
25
+
26
+ ### 组件如何支持反显:
27
+
28
+ 所有选择器组件都会在 `onMounted` 时检测:
29
+ - 如果 `modelValue` 有初始值(不为 null/undefined/空数组)
30
+ - 自动加载对应的数据(部门/人员)
31
+ - 在输入框中显示对应的文本/标签
32
+
33
+ ---
34
+
35
+ ## DepartmentSelector 反显
36
+
37
+ ### 场景 1:编辑用户信息 - 单选部门
38
+
39
+ ```jsx
40
+ import { ref, onMounted } from 'vue'
41
+ import DepartmentSelector from './components/DepartmentSelector.jsx'
42
+
43
+ export default {
44
+ setup() {
45
+ const userId = ref(12345)
46
+ const userDepartmentId = ref(null)
47
+
48
+ // 从后端加载用户信息
49
+ onMounted(async () => {
50
+ const userInfo = await fetchUserInfo(userId.value)
51
+ // 设置用户的部门 id
52
+ userDepartmentId.value = userInfo.departmentId // 例如:911
53
+ })
54
+
55
+ const handleDepartmentChange = async (dept) => {
56
+ console.log('部门变更为:', dept)
57
+ // 更新用户部门
58
+ await updateUserDepartment(userId.value, {
59
+ departmentId: dept.id,
60
+ departmentName: dept.label
61
+ })
62
+ }
63
+
64
+ return () => (
65
+ <div>
66
+ <h3>编辑用户信息</h3>
67
+ <el-form>
68
+ <el-form-item label="所属部门">
69
+ <DepartmentSelector
70
+ v-model={userDepartmentId.value}
71
+ placeholder="请选择部门"
72
+ clearable={true}
73
+ onChange={handleDepartmentChange}
74
+ />
75
+ {/*
76
+ 当 userDepartmentId = 911 时:
77
+ 1. 组件检测到有初始值
78
+ 2. 自动加载部门数据
79
+ 3. 显示 "前端开发组"
80
+ */}
81
+ </el-form-item>
82
+ </el-form>
83
+ </div>
84
+ )
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### 场景 2:权限管理 - 多选部门
90
+
91
+ ```jsx
92
+ import { ref, onMounted } from 'vue'
93
+ import DepartmentSelector from './components/DepartmentSelector.jsx'
94
+
95
+ export default {
96
+ setup() {
97
+ const roleId = ref(5)
98
+ const roleDepartmentIds = ref([])
99
+
100
+ // 加载角色的可见部门列表
101
+ onMounted(async () => {
102
+ const roleInfo = await fetchRoleInfo(roleId.value)
103
+ // 设置角色可见的部门 ids
104
+ roleDepartmentIds.value = roleInfo.visibleDepartmentIds
105
+ // 例如:[910, 911, 912, 913]
106
+ })
107
+
108
+ const handleDepartmentsChange = async (depts) => {
109
+ console.log('可见部门更新:', depts)
110
+ // 保存角色权限
111
+ await updateRolePermissions(roleId.value, {
112
+ visibleDepartments: depts.map(d => ({
113
+ id: d.id,
114
+ name: d.label
115
+ }))
116
+ })
117
+ }
118
+
119
+ return () => (
120
+ <div>
121
+ <h3>角色权限配置</h3>
122
+ <el-form>
123
+ <el-form-item label="可见部门">
124
+ <DepartmentSelector
125
+ v-model={roleDepartmentIds.value}
126
+ displayType="input"
127
+ placeholder="请选择可见部门"
128
+ multiple={true}
129
+ clearable={true}
130
+ onChange={handleDepartmentsChange}
131
+ />
132
+ {/*
133
+ 当 roleDepartmentIds = [910, 911, 912, 913] 时:
134
+ 1. 组件检测到有初始值数组
135
+ 2. 自动加载部门数据
136
+ 3. 显示 4 个标签:后端开发组、前端开发组、测试组、产品部
137
+ */}
138
+ </el-form-item>
139
+ </el-form>
140
+ </div>
141
+ )
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### 场景 3:从 URL 参数反显
147
+
148
+ ```jsx
149
+ import { ref, onMounted } from 'vue'
150
+ import { useRoute } from 'vue-router'
151
+ import DepartmentSelector from './components/DepartmentSelector.jsx'
152
+
153
+ export default {
154
+ setup() {
155
+ const route = useRoute()
156
+ const departmentId = ref(null)
157
+
158
+ onMounted(() => {
159
+ // 从 URL query 中获取部门 id
160
+ // 例如:/page?deptId=911
161
+ if (route.query.deptId) {
162
+ departmentId.value = Number(route.query.deptId)
163
+ }
164
+ })
165
+
166
+ return () => (
167
+ <DepartmentSelector
168
+ v-model={departmentId.value}
169
+ placeholder="请选择部门"
170
+ />
171
+ // URL 有 deptId=911 时,自动显示对应部门
172
+ )
173
+ }
174
+ }
175
+ ```
176
+
177
+ ### 场景 4:从 localStorage 恢复
178
+
179
+ ```jsx
180
+ import { ref, onMounted } from 'vue'
181
+ import DepartmentSelector from './components/DepartmentSelector.jsx'
182
+
183
+ export default {
184
+ setup() {
185
+ const selectedDepartmentIds = ref([])
186
+
187
+ // 从 localStorage 恢复上次的选择
188
+ onMounted(() => {
189
+ const saved = localStorage.getItem('selectedDepartments')
190
+ if (saved) {
191
+ selectedDepartmentIds.value = JSON.parse(saved)
192
+ }
193
+ })
194
+
195
+ const handleChange = (depts) => {
196
+ // 保存到 localStorage
197
+ const ids = depts.map(d => d.id)
198
+ localStorage.setItem('selectedDepartments', JSON.stringify(ids))
199
+ }
200
+
201
+ return () => (
202
+ <DepartmentSelector
203
+ v-model={selectedDepartmentIds.value}
204
+ multiple={true}
205
+ onChange={handleChange}
206
+ />
207
+ )
208
+ }
209
+ }
210
+ ```
211
+
212
+ ---
213
+
214
+ ## PersonnelSelector 反显
215
+
216
+ ### 场景 1:任务详情 - 单选负责人
217
+
218
+ ```jsx
219
+ import { ref, onMounted } from 'vue'
220
+ import PersonnelSelector from './components/PersonnelSelector.jsx'
221
+
222
+ export default {
223
+ setup() {
224
+ const taskId = ref(888)
225
+ const assigneeId = ref(null)
226
+
227
+ // 加载任务详情
228
+ onMounted(async () => {
229
+ const task = await fetchTaskDetail(taskId.value)
230
+ assigneeId.value = task.assigneeId // 例如:10245
231
+ })
232
+
233
+ const handleAssigneeChange = async (person) => {
234
+ console.log('更换负责人:', person)
235
+ await updateTask(taskId.value, {
236
+ assigneeId: person.id,
237
+ assigneeName: person.name,
238
+ assigneeEmail: person.email,
239
+ assigneePhone: person.phone
240
+ })
241
+ }
242
+
243
+ return () => (
244
+ <div>
245
+ <h3>任务详情</h3>
246
+ <el-form>
247
+ <el-form-item label="负责人">
248
+ <PersonnelSelector
249
+ v-model={assigneeId.value}
250
+ placeholder="请选择负责人"
251
+ clearable={true}
252
+ onChange={handleAssigneeChange}
253
+ />
254
+ {/*
255
+ 当 assigneeId = 10245 时:
256
+ 1. 组件检测到有初始值
257
+ 2. 自动加载人员数据
258
+ 3. 显示 "张三 - 高级工程师"
259
+ */}
260
+ </el-form-item>
261
+ </el-form>
262
+ </div>
263
+ )
264
+ }
265
+ }
266
+ ```
267
+
268
+ ### 场景 2:项目成员管理 - 多选
269
+
270
+ ```jsx
271
+ import { ref, onMounted } from 'vue'
272
+ import PersonnelSelector from './components/PersonnelSelector.jsx'
273
+
274
+ export default {
275
+ setup() {
276
+ const projectId = ref('PRJ-2024-001')
277
+ const memberIds = ref([])
278
+
279
+ // 加载项目成员
280
+ onMounted(async () => {
281
+ const project = await fetchProjectInfo(projectId.value)
282
+ memberIds.value = project.memberIds
283
+ // 例如:[10245, 10246, 10247, 10248, 10249]
284
+ })
285
+
286
+ const handleMembersChange = async (persons) => {
287
+ console.log('项目成员更新:', persons)
288
+ await updateProjectMembers(projectId.value, {
289
+ members: persons.map(p => ({
290
+ id: p.id,
291
+ name: p.name,
292
+ position: p.position,
293
+ email: p.email
294
+ }))
295
+ })
296
+ }
297
+
298
+ return () => (
299
+ <div>
300
+ <h3>项目成员配置</h3>
301
+ <PersonnelSelector
302
+ v-model={memberIds.value}
303
+ displayType="input"
304
+ placeholder="请选择项目成员"
305
+ multiple={true}
306
+ clearable={true}
307
+ onChange={handleMembersChange}
308
+ />
309
+ {/*
310
+ 当 memberIds = [10245, 10246, 10247, 10248, 10249] 时:
311
+ 1. 组件检测到有初始值数组
312
+ 2. 自动加载人员数据
313
+ 3. 显示 5 个标签:张三、李四、王五、赵六、钱七
314
+ */}
315
+ </div>
316
+ )
317
+ }
318
+ }
319
+ ```
320
+
321
+ ### 场景 3:按钮模式反显
322
+
323
+ ```jsx
324
+ import { ref, onMounted } from 'vue'
325
+ import PersonnelSelector from './components/PersonnelSelector.jsx'
326
+
327
+ export default {
328
+ setup() {
329
+ const reviewerIds = ref([])
330
+
331
+ onMounted(async () => {
332
+ const config = await fetchReviewConfig()
333
+ reviewerIds.value = config.reviewerIds // [10245, 10246, 10247]
334
+ })
335
+
336
+ return () => (
337
+ <div style="display: flex; align-items: center; gap: 8px;">
338
+ <span>代码审核人:</span>
339
+ <PersonnelSelector
340
+ v-model={reviewerIds.value}
341
+ displayType="button"
342
+ placeholder="选择审核人"
343
+ multiple={true}
344
+ clearable={true}
345
+ />
346
+ {/*
347
+ 反显效果:按钮显示 "张三 +2"
348
+ */}
349
+ </div>
350
+ )
351
+ }
352
+ }
353
+ ```
354
+
355
+ ---
356
+
357
+ ## DepartmentPersonnelSelector 反显
358
+
359
+ ### 场景 1:会议管理 - 多选参会人
360
+
361
+ ```jsx
362
+ import { ref, onMounted } from 'vue'
363
+ import DepartmentPersonnelSelector from './components/DepartmentPersonnelSelector.jsx'
364
+
365
+ export default {
366
+ setup() {
367
+ const meetingId = ref('MTG-2024-0110')
368
+ const attendeeIds = ref([])
369
+
370
+ // 加载会议信息
371
+ onMounted(async () => {
372
+ const meeting = await fetchMeetingInfo(meetingId.value)
373
+ attendeeIds.value = meeting.attendeeIds
374
+ // 例如:[10245, 10246, 10247, 10248]
375
+ })
376
+
377
+ const handleAttendeesChange = async (persons) => {
378
+ console.log('参会人员更新:', persons)
379
+ await updateMeeting(meetingId.value, {
380
+ attendees: persons.map(p => ({
381
+ id: p.id,
382
+ name: p.name,
383
+ email: p.email,
384
+ department: p.departmentName
385
+ }))
386
+ })
387
+ }
388
+
389
+ return () => (
390
+ <div>
391
+ <h3>编辑会议</h3>
392
+ <el-form>
393
+ <el-form-item label="参会人员">
394
+ <DepartmentPersonnelSelector
395
+ v-model={attendeeIds.value}
396
+ displayType="input"
397
+ placeholder="请选择参会人员"
398
+ multiple={true}
399
+ clearable={true}
400
+ onChange={handleAttendeesChange}
401
+ />
402
+ {/*
403
+ 当 attendeeIds = [10245, 10246, 10247, 10248] 时:
404
+ 1. 组件检测到有初始值数组
405
+ 2. 自动加载部门数据和所有人员数据
406
+ 3. 输入框显示 4 个标签
407
+ 4. 打开弹窗时:
408
+ - 左侧显示部门树
409
+ - 中间显示当前部门的人员列表(这4个人会被勾选)
410
+ - 右侧"已选人员"栏显示这4个人
411
+ */}
412
+ </el-form-item>
413
+ </el-form>
414
+ </div>
415
+ )
416
+ }
417
+ }
418
+ ```
419
+
420
+ ### 场景 2:审批流程配置 - 单选审批人
421
+
422
+ ```jsx
423
+ import { ref, onMounted } from 'vue'
424
+ import DepartmentPersonnelSelector from './components/DepartmentPersonnelSelector.jsx'
425
+
426
+ export default {
427
+ setup() {
428
+ const workflowId = ref('WF-001')
429
+ const approverId = ref(null)
430
+
431
+ // 加载审批流程配置
432
+ onMounted(async () => {
433
+ const workflow = await fetchWorkflowConfig(workflowId.value)
434
+ approverId.value = workflow.approverId // 例如:10245
435
+ })
436
+
437
+ const handleApproverChange = async (person) => {
438
+ console.log('审批人变更:', person)
439
+ await updateWorkflowConfig(workflowId.value, {
440
+ approverId: person.id,
441
+ approverName: person.name,
442
+ approverEmail: person.email
443
+ })
444
+ }
445
+
446
+ return () => (
447
+ <div>
448
+ <h3>审批流程配置</h3>
449
+ <el-form>
450
+ <el-form-item label="审批人">
451
+ <DepartmentPersonnelSelector
452
+ v-model={approverId.value}
453
+ placeholder="请选择审批人"
454
+ multiple={false}
455
+ clearable={true}
456
+ onChange={handleApproverChange}
457
+ />
458
+ {/*
459
+ 当 approverId = 10245 时:
460
+ 1. 组件检测到有初始值
461
+ 2. 加载部门和人员数据
462
+ 3. 输入框显示 "张三"
463
+ 4. 打开弹窗时,右侧已选人员栏显示 "张三"
464
+ */}
465
+ </el-form-item>
466
+ </el-form>
467
+ </div>
468
+ )
469
+ }
470
+ }
471
+ ```
472
+
473
+ ### 场景 3:抄送人配置
474
+
475
+ ```jsx
476
+ import { ref, onMounted } from 'vue'
477
+ import DepartmentPersonnelSelector from './components/DepartmentPersonnelSelector.jsx'
478
+
479
+ export default {
480
+ setup() {
481
+ const ccPersonIds = ref([])
482
+
483
+ onMounted(async () => {
484
+ const saved = localStorage.getItem('defaultCCPersons')
485
+ if (saved) {
486
+ ccPersonIds.value = JSON.parse(saved)
487
+ }
488
+ })
489
+
490
+ const handleCCChange = (persons) => {
491
+ const ids = persons.map(p => p.id)
492
+ localStorage.setItem('defaultCCPersons', JSON.stringify(ids))
493
+ }
494
+
495
+ return () => (
496
+ <div style="display: flex; align-items: center; gap: 8px;">
497
+ <span>默认抄送:</span>
498
+ <DepartmentPersonnelSelector
499
+ v-model={ccPersonIds.value}
500
+ displayType="button"
501
+ placeholder="选择抄送人"
502
+ multiple={true}
503
+ onChange={handleCCChange}
504
+ />
505
+ </div>
506
+ )
507
+ }
508
+ }
509
+ ```
510
+
511
+ ---
512
+
513
+ ## 常见场景
514
+
515
+ ### 1. 表单编辑页面完整示例
516
+
517
+ ```jsx
518
+ import { ref, onMounted } from 'vue'
519
+ import { useRoute, useRouter } from 'vue-router'
520
+ import DepartmentSelector from './components/DepartmentSelector.jsx'
521
+ import PersonnelSelector from './components/PersonnelSelector.jsx'
522
+
523
+ export default {
524
+ setup() {
525
+ const route = useRoute()
526
+ const router = useRouter()
527
+
528
+ const formData = ref({
529
+ id: null,
530
+ name: '',
531
+ departmentId: null,
532
+ managerId: null,
533
+ memberIds: []
534
+ })
535
+
536
+ // 加载数据(编辑模式)
537
+ onMounted(async () => {
538
+ const id = route.params.id
539
+ if (id && id !== 'new') {
540
+ // 编辑模式:加载已有数据
541
+ const data = await fetchTeamInfo(id)
542
+ formData.value = {
543
+ id: data.id,
544
+ name: data.name,
545
+ departmentId: data.departmentId, // 反显部门
546
+ managerId: data.managerId, // 反显负责人
547
+ memberIds: data.memberIds // 反显成员列表
548
+ }
549
+ }
550
+ // 新建模式:所有字段为空,不会触发反显
551
+ })
552
+
553
+ const handleSave = async () => {
554
+ const isEdit = !!formData.value.id
555
+ if (isEdit) {
556
+ await updateTeam(formData.value)
557
+ } else {
558
+ await createTeam(formData.value)
559
+ }
560
+ router.push('/teams')
561
+ }
562
+
563
+ return () => (
564
+ <div>
565
+ <h2>{formData.value.id ? '编辑团队' : '新建团队'}</h2>
566
+ <el-form model={formData.value}>
567
+ <el-form-item label="团队名称">
568
+ <el-input v-model={formData.value.name} />
569
+ </el-form-item>
570
+
571
+ <el-form-item label="所属部门">
572
+ <DepartmentSelector
573
+ v-model={formData.value.departmentId}
574
+ placeholder="请选择部门"
575
+ clearable={true}
576
+ onChange={(dept) => {
577
+ console.log('部门变更:', dept)
578
+ }}
579
+ />
580
+ </el-form-item>
581
+
582
+ <el-form-item label="团队负责人">
583
+ <PersonnelSelector
584
+ v-model={formData.value.managerId}
585
+ placeholder="请选择负责人"
586
+ clearable={true}
587
+ onChange={(person) => {
588
+ console.log('负责人变更:', person)
589
+ }}
590
+ />
591
+ </el-form-item>
592
+
593
+ <el-form-item label="团队成员">
594
+ <PersonnelSelector
595
+ v-model={formData.value.memberIds}
596
+ placeholder="请选择成员"
597
+ multiple={true}
598
+ clearable={true}
599
+ onChange={(persons) => {
600
+ console.log('成员列表变更:', persons)
601
+ }}
602
+ />
603
+ </el-form-item>
604
+
605
+ <el-form-item>
606
+ <el-button type="primary" onClick={handleSave}>保存</el-button>
607
+ </el-form-item>
608
+ </el-form>
609
+ </div>
610
+ )
611
+ }
612
+ }
613
+ ```
614
+
615
+ ### 2. 搜索条件反显
616
+
617
+ ```jsx
618
+ import { ref, onMounted } from 'vue'
619
+ import { useRoute, useRouter } from 'vue-router'
620
+ import DepartmentSelector from './components/DepartmentSelector.jsx'
621
+
622
+ export default {
623
+ setup() {
624
+ const route = useRoute()
625
+ const router = useRouter()
626
+
627
+ const searchForm = ref({
628
+ departmentId: null,
629
+ keyword: ''
630
+ })
631
+
632
+ // 从 URL query 恢复搜索条件
633
+ onMounted(() => {
634
+ if (route.query.deptId) {
635
+ searchForm.value.departmentId = Number(route.query.deptId)
636
+ }
637
+ if (route.query.keyword) {
638
+ searchForm.value.keyword = route.query.keyword
639
+ }
640
+ })
641
+
642
+ const handleSearch = () => {
643
+ // 搜索并更新 URL
644
+ router.push({
645
+ query: {
646
+ deptId: searchForm.value.departmentId,
647
+ keyword: searchForm.value.keyword
648
+ }
649
+ })
650
+ }
651
+
652
+ return () => (
653
+ <div>
654
+ <el-form inline>
655
+ <el-form-item label="部门">
656
+ <DepartmentSelector
657
+ v-model={searchForm.value.departmentId}
658
+ placeholder="请选择部门"
659
+ clearable={true}
660
+ />
661
+ </el-form-item>
662
+ <el-form-item>
663
+ <el-button type="primary" onClick={handleSearch}>搜索</el-button>
664
+ </el-form-item>
665
+ </el-form>
666
+ </div>
667
+ )
668
+ }
669
+ }
670
+ ```
671
+
672
+ ### 3. 批量操作反显
673
+
674
+ ```jsx
675
+ import { ref, computed } from 'vue'
676
+ import PersonnelSelector from './components/PersonnelSelector.jsx'
677
+
678
+ export default {
679
+ setup() {
680
+ const tableData = ref([])
681
+ const selectedRows = ref([])
682
+ const selectedPersonIds = computed({
683
+ get: () => selectedRows.value.map(row => row.assigneeId),
684
+ set: (ids) => {
685
+ // 根据 ids 更新 selectedRows
686
+ }
687
+ })
688
+
689
+ const batchAssign = () => {
690
+ // 批量分配
691
+ }
692
+
693
+ return () => (
694
+ <div>
695
+ <div style="margin-bottom: 16px;">
696
+ <span>批量分配给:</span>
697
+ <PersonnelSelector
698
+ v-model={selectedPersonIds.value}
699
+ displayType="button"
700
+ placeholder="选择负责人"
701
+ />
702
+ <el-button onClick={batchAssign}>确认分配</el-button>
703
+ </div>
704
+
705
+ <el-table data={tableData.value} />
706
+ </div>
707
+ )
708
+ }
709
+ }
710
+ ```
711
+
712
+ ---
713
+
714
+ ## 总结
715
+
716
+ ### 反显的关键点:
717
+
718
+ 1. ✅ **v-model 有初始值** - 在 `onMounted` 或数据加载后设置
719
+ 2. ✅ **组件自动检测** - 检测到非空值会自动加载数据
720
+ 3. ✅ **onChange 返回对象** - 获取完整信息,方便保存
721
+ 4. ✅ **适用所有模式** - 单选、多选、输入框、按钮模式都支持
722
+
723
+ ### 最佳实践:
724
+
725
+ - 编辑页面:先加载详情数据,再设置 v-model
726
+ - URL 参数:从 route.query 或 route.params 读取 id
727
+ - 本地存储:从 localStorage/sessionStorage 恢复
728
+ - 保存数据:使用 onChange 的完整对象,不只保存 id