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.
- package/app/Wscats.vue-1.0.26.vsix +0 -0
- package/app/febean.vue-format-0.1.8.vsix +0 -0
- package/app/wujie-vue3-child/.claude/settings.local.json +8 -0
- package/app/wujie-vue3-child/.vscode/extensions.json +3 -0
- package/app/wujie-vue3-child/PROJECT_MEMORY.md +427 -0
- package/app/wujie-vue3-child/README.md +5 -0
- package/app/wujie-vue3-child/index.html +13 -0
- package/app/wujie-vue3-child/package-lock.json +5744 -0
- package/app/wujie-vue3-child/package.json +28 -0
- package/app/wujie-vue3-child/public/vite.svg +1 -0
- package/app/wujie-vue3-child/src/App.vue +130 -0
- package/app/wujie-vue3-child/src/assets/vue.svg +1 -0
- package/app/wujie-vue3-child/src/components/HelloWorld.vue +43 -0
- package/app/wujie-vue3-child/src/components/tags-view.vue +193 -0
- package/app/wujie-vue3-child/src/components/tags-view1.vue +131 -0
- package/app/wujie-vue3-child/src/hooks/useClickOutside.js +11 -0
- package/app/wujie-vue3-child/src/hooks/useTableDragSort.js +28 -0
- package/app/wujie-vue3-child/src/main.js +15 -0
- package/app/wujie-vue3-child/src/router/index.js +104 -0
- package/app/wujie-vue3-child/src/store/tagsViewStroe.js +34 -0
- package/app/wujie-vue3-child/src/style.css +4 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/README.md +836 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/REFLEX_EXAMPLES.md +728 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentPersonnelSelector.jsx +687 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentPersonnelSelector.module.scss +560 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelector.jsx +570 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelector.module.scss +330 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelectorV2.jsx +378 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelectorV2.module.scss +228 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/OptionsSelector.jsx +399 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/OptionsSelector.module.scss +252 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PersonnelSelector.jsx +585 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PersonnelSelector.module.scss +331 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PopoverSelector.jsx +392 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PopoverSelector.module.scss +39 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/README.md +248 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/SelectorTrigger.jsx +194 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/index.jsx +1459 -0
- package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/mockData.js +301 -0
- package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/dialogueSegment/index.jsx +28 -4
- package/app/wujie-vue3-child/src/views/aiCoach/index.jsx +32 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ChartsPanel/index.jsx +121 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ChartsPanel/index.module.scss +76 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/DonutChart/index.jsx +104 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/PracticeTable/index.jsx +75 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/PracticeTable/index.module.scss +12 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankBarChart/index.jsx +62 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankBarChart/index.module.scss +43 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingGroup/index.jsx +29 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingGroup/index.module.scss +5 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingList/index.jsx +58 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingList/index.module.scss +85 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ScriptStatsPanel/index.jsx +92 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ScriptStatsPanel/index.module.scss +56 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/StatCardsRow/index.jsx +40 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/StatCardsRow/index.module.scss +53 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/echarts/EchartsDonut.jsx +106 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/echarts/EchartsRankBar.jsx +132 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/index.jsx +176 -0
- package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/index.module.scss +96 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/CoachReport/index.jsx +162 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/CoachReport/index.module.scss +16 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ComprehensiveEvaluation/index.jsx +29 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ComprehensiveEvaluation/index.module.scss +25 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueBubble/index.jsx +106 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueBubble/index.module.scss +164 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueRecord/index.jsx +182 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueRecord/index.module.scss +203 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionDetail/index.jsx +145 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionDetail/index.module.scss +126 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionScores/index.jsx +67 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionScores/index.module.scss +105 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ReportHeader/index.jsx +81 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ReportHeader/index.module.scss +47 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/RoleInfo/index.jsx +64 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/RoleInfo/index.module.scss +85 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ScoreBadge/index.jsx +39 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ScoreBadge/index.module.scss +44 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/SubDimensionItem/index.jsx +83 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/SubDimensionItem/index.module.scss +101 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/index.jsx +50 -0
- package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/index.module.scss +25 -0
- package/app/wujie-vue3-child/src/views/child-to-parent.vue +117 -0
- package/app/wujie-vue3-child/src/views/home.vue +53 -0
- package/app/wujie-vue3-child/src/views/jsx/btnSelect/btnSelect.vue +169 -0
- package/app/wujie-vue3-child/src/views/jsx/btnSelect/index.vue +69 -0
- package/app/wujie-vue3-child/src/views/jsx/com.vue +44 -0
- package/app/wujie-vue3-child/src/views/jsx/dialog.jsx +66 -0
- package/app/wujie-vue3-child/src/views/jsx/index.vue +72 -0
- package/app/wujie-vue3-child/src/views/jsx/props.vue +33 -0
- package/app/wujie-vue3-child/src/views/parent-to-child.vue +225 -0
- package/app/wujie-vue3-child/src/views/phone-code.vue +318 -0
- package/app/wujie-vue3-child/src/views/router-jump.vue +123 -0
- package/app/wujie-vue3-child/src/views/test.vue +192 -0
- package/app/wujie-vue3-child/vite.config.js +15 -0
- package/package.json +1 -1
- package/app/aiCoach/index.jsx +0 -20
- package/npmapps-1.0.20.tgz +0 -0
- /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/collapseExpand/index.jsx +0 -0
- /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/collapseExpand/index.module.scss +0 -0
- /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/dialogueSegment/index.module.scss +0 -0
- /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/index.jsx +0 -0
- /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/index.module.scss +0 -0
- /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/inputColumn/index.jsx +0 -0
- /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
|