widget.qw 1.0.83 → 1.0.85

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 (108) hide show
  1. package/.env.development +10 -10
  2. package/.env.production +8 -8
  3. package/README.md +75 -75
  4. package/build/style.css +521 -471
  5. package/build/widget.qw.es.js +253 -84
  6. package/build/widget.qw.umd.js +252 -83
  7. package/index.html +17 -17
  8. package/package.json +47 -47
  9. package/src/App.vue +26 -26
  10. package/src/api/index.js +173 -165
  11. package/src/components/AuditBar.vue +396 -396
  12. package/src/components/BillCard.vue +164 -164
  13. package/src/components/CascaderPicker.vue +132 -132
  14. package/src/components/CascaderPop.vue +375 -371
  15. package/src/components/CheckGroup.vue +113 -113
  16. package/src/components/DatetimeDropdown.vue +62 -0
  17. package/src/components/{DatetimePicker/index.vue → DatetimePicker.vue} +124 -124
  18. package/src/components/DayDropdown.vue +56 -56
  19. package/src/components/FilePicker.vue +169 -169
  20. package/src/components/HeadBar.vue +130 -0
  21. package/src/components/Input.vue +80 -80
  22. package/src/components/MonthDropdown.vue +51 -51
  23. package/src/components/MultiPicker.vue +123 -123
  24. package/src/components/ObjsEditor.vue +334 -334
  25. package/src/components/SecretNotify.js +5 -5
  26. package/src/components/Sheet.vue +92 -92
  27. package/src/components/SingleApiPicker.vue +108 -108
  28. package/src/components/SinglePicker.vue +102 -102
  29. package/src/components/SingleUserSelector.vue +425 -425
  30. package/src/components/Switch.vue +64 -64
  31. package/src/components/TreePicker.vue +113 -113
  32. package/src/components/UserPicker.vue +106 -106
  33. package/src/components/UserProfile.vue +129 -129
  34. package/src/components/UsersPicker.vue +118 -118
  35. package/src/components/YearDropdown.vue +59 -59
  36. package/src/components/data_selector.vue +303 -303
  37. package/src/components/image_picker.vue +123 -123
  38. package/src/components/images_picker.vue +195 -195
  39. package/src/components/index.js +172 -162
  40. package/src/components/mult_list_selector.vue +155 -155
  41. package/src/components/subdepartment_selector.vue +481 -481
  42. package/src/components/user_selector.vue +639 -639
  43. package/src/components/widget/DataPop.vue +95 -95
  44. package/src/components/{DatetimePicker → widget}/DatetimePop.vue +342 -342
  45. package/src/components/widget/TreePop.vue +88 -88
  46. package/src/components/widget/UserPop.vue +347 -347
  47. package/src/main.js +117 -117
  48. package/src/router/index.ts +183 -173
  49. package/src/util/array_util.js +32 -32
  50. package/src/util/auth_util.js +72 -72
  51. package/src/util/bool_util.js +5 -5
  52. package/src/util/bus.js +1 -1
  53. package/src/util/cache_util.js +18 -18
  54. package/src/util/errer_code.js +6 -6
  55. package/src/util/eval_util.js +19 -19
  56. package/src/util/icon_util.js +36 -36
  57. package/src/util/image_util.js +27 -27
  58. package/src/util/index.js +57 -57
  59. package/src/util/num_util.js +70 -70
  60. package/src/util/obj_util.js +28 -28
  61. package/src/util/request.js +73 -73
  62. package/src/util/request_json.js +71 -71
  63. package/src/util/request_json_mute.js +65 -65
  64. package/src/util/request_upload.js +79 -79
  65. package/src/util/route_util.js +31 -31
  66. package/src/util/str_util.js +143 -143
  67. package/src/util/time_util.js +406 -406
  68. package/src/util/toast_util.js +24 -24
  69. package/src/util/tree_util.js +153 -153
  70. package/src/util/uuid_util.js +9 -9
  71. package/src/util/validate.js +182 -182
  72. package/src/util/vue_filter.js +223 -223
  73. package/src/views/auditbar/index.vue +78 -78
  74. package/src/views/billcard/index.vue +48 -48
  75. package/src/views/cascaderpicker/index.vue +0 -0
  76. package/src/views/cascaderpicker/userpicker.vue +183 -0
  77. package/src/views/cascaderpop/index.vue +90 -90
  78. package/src/views/checkgroup/index.vue +35 -35
  79. package/src/views/dataSelector/index.vue +48 -48
  80. package/src/views/datetimedropdown/index.vue +34 -0
  81. package/src/views/datetimepicker/index.vue +34 -34
  82. package/src/views/daydropdown/index.vue +31 -31
  83. package/src/views/filepicker/index.vue +32 -32
  84. package/src/views/headbar/index.vue +28 -0
  85. package/src/views/home/index.vue +0 -0
  86. package/src/views/imagepicker/index.vue +31 -31
  87. package/src/views/imagespicker/index.vue +37 -37
  88. package/src/views/input/index.vue +35 -35
  89. package/src/views/monthdropdown/index.vue +31 -31
  90. package/src/views/multListSelector/index.vue +61 -61
  91. package/src/views/multipicker/index.vue +36 -36
  92. package/src/views/objseditor/index.vue +277 -277
  93. package/src/views/productSelector/index.vue +35 -35
  94. package/src/views/projectpicker/index.vue +41 -41
  95. package/src/views/secretnotify/index.vue +27 -27
  96. package/src/views/sheet/index.vue +45 -45
  97. package/src/views/singlepicker/index.vue +35 -35
  98. package/src/views/subdepartmentSelector/index.vue +40 -40
  99. package/src/views/switch/index.vue +34 -34
  100. package/src/views/treepicker/index.vue +41 -41
  101. package/src/views/userSelector/index.vue +54 -54
  102. package/src/views/userSelectorNew/index.vue +45 -45
  103. package/src/views/userpicker/index.vue +43 -43
  104. package/src/views/userprofile/index.vue +30 -30
  105. package/src/views/userspicker/index.vue +43 -43
  106. package/src/views/yeardropdown/index.vue +32 -32
  107. package/src/vm/index.js +1 -1
  108. package/vite.config.ts +122 -122
@@ -1,425 +1,425 @@
1
- <template>
2
- <van-field v-if="!isGone" :is-link="isReadonly ? false : true" readonly :label="props.label"
3
- :placeholder="props.placeholder" @click="show = !isReadonly" :model-value="selectedUserInfo" :required="isRequired"
4
- :rules="props.rules" input-align="center" />
5
- <van-popup v-model:show="show" round position="bottom" class="user-popup">
6
- <div class="department-selector">
7
- <div class="picker-header">
8
- <div class="header-left">
9
- <van-button @click="goBack" icon="arrow-left" size="small" v-show="currentPath !== '请选择部门'" />
10
- </div>
11
-
12
- <!-- 移除header-center包装,直接使用current-path -->
13
- <div class="current-path">{{ currentPath }}</div>
14
-
15
- <div class="header-right">
16
- <van-button size="small" @click="handleClear" class="cancel-btn">清空</van-button>
17
-
18
- <van-button type="primary" size="small" @click="confirmSelection" class="confirm-btn">确定</van-button>
19
- </div>
20
- </div>
21
-
22
- <!-- 搜索框 -->
23
- <van-search v-model="searchValue" placeholder="输入工号查询" @clear="clearSearch" @search="handleSearch"
24
- @cancel="clearSearch" show-action>
25
- <template #action>
26
- <div @click="handleSearch">搜索</div>
27
- </template>
28
- </van-search>
29
-
30
- <div class="scroll-container">
31
- <!-- 遍历部门列表 -->
32
- <van-cell-group>
33
- <van-cell v-for="department in departments" :key="department.id" :title="department.name" is-link
34
- @click="toggleDepartment(department)" />
35
- </van-cell-group>
36
-
37
- <!-- 遍历人员列表 -->
38
- <van-radio-group v-model="tempSelectedUser">
39
- <van-cell-group>
40
- <van-cell v-for="user in userlist" :key="user.id" :title="user.name">
41
- <template #right-icon>
42
- <van-radio :name="user.userid" />
43
- </template>
44
- </van-cell>
45
- </van-cell-group>
46
- </van-radio-group>
47
- </div>
48
- </div>
49
- </van-popup>
50
- </template>
51
- <script setup>
52
- import { ref, onMounted, watch } from "vue";
53
- import util from "../util"
54
- import { useVModel } from '@vueuse/core'
55
-
56
- // 弹出层
57
- const show = ref(false);
58
-
59
- const props = defineProps({
60
- modelValue: {
61
- type: String,
62
- default: "",
63
- },
64
- label: {
65
- type: String,
66
- default: "选择人员",
67
- },
68
- placeholder: {
69
- type: String,
70
- default: "请选择人员",
71
- },
72
- required: {
73
- type: Boolean,
74
- default: false,
75
- },
76
- readonly: {
77
- type: Boolean,
78
- default: false,
79
- },
80
- rules: {
81
- type: Array,
82
- default: () => [],
83
- },
84
- // 默认查询所有组织机构
85
- currentDeptId: {
86
- type: String,
87
- default: "1", //组织机构id
88
- },
89
- // 二级部门取值范围字典id
90
- // 取dictionary库.dict的配置id
91
- deptRangeId: {
92
- type: String,
93
- default: ''
94
- },
95
- auth: {
96
- type: String,
97
- default: "",
98
- },
99
- });
100
- // 定义emit
101
- const emit = defineEmits(["update:modelValue", "select"]);
102
- const modelValue = useVModel(props, 'modelValue', emit)
103
- const { isRequired, isReadonly, isGone, isDisabled } = util.props2auth(props);
104
-
105
- // 部门列表
106
- let departments = ref([]);
107
- let departmentFulls = ref([])
108
- const searchValue = ref("");
109
- const currentPath = ref("请选择部门");
110
- // 定义用户
111
- const userlist = ref([]);
112
- // 定义选择单选按钮的值
113
- const tempSelectedUser = ref(null);
114
- // 定义展示表单展示区的值
115
- const selectedUserInfo = ref([]);
116
-
117
- // 获得部门列表
118
- const fetchAllDepartments = async () => {
119
- // 查询所有部门
120
- const response = await util.get_subdepartments({ departmentId: props.currentDeptId })
121
- let level2Depts = response.data.subDepartments
122
-
123
- // 取配置的二级部门取值范围
124
- let level2DeptIds = []
125
- if (props.deptRangeId) {
126
- let res = await util.dictionary_dict({ id: props.deptRangeId })
127
- if (res.data.parseValue && res.data.parseValue.length>0) {
128
- level2DeptIds = res.data.parseValue
129
- }
130
-
131
- // 当前用户所在二级部门列表
132
- const user = await getUserInfoById(props.modelValue)
133
- if (user && user.department.id) {
134
- level2DeptIds = [...level2DeptIds, user.department.id]
135
- }
136
- }
137
-
138
- // 只保留指定二级部门列表、当前用户的二级部门
139
- if (props.deptRangeId) {
140
- if (level2DeptIds && level2DeptIds.length > 0) {
141
- level2Depts = level2Depts.filter(item => {
142
- return level2DeptIds.includes(item.id)
143
- })
144
- }
145
- }
146
-
147
- //departments
148
- departments.value = getCurrentDepartments(level2Depts || [])
149
- departmentFulls.value = departments.value;
150
- };
151
-
152
- //嵌套处理获得树形结构
153
- const getCurrentDepartments = (data) => {
154
- let result = data.map((item) => {
155
- return {
156
- id: item.id,
157
- name: item.name,
158
- subDepartments: getCurrentDepartments(item.subDepartments || []),
159
- };
160
- });
161
- return result;
162
- };
163
- // 添加部门路径查找方法
164
- function findDeptPath(deptId, tree, path = []) {
165
- for (const dept of tree) {
166
- if (dept.id === deptId) {
167
- return [...path, dept];
168
- }
169
- if (dept.subDepartments?.length) {
170
- const found = findDeptPath(deptId, dept.subDepartments, [...path, dept]);
171
- if (found && found.length>0) return found;
172
- }
173
- }
174
- return [];
175
- }
176
-
177
- // 获得人员列表
178
- const fetchAllUsers = async (departmentId) => {
179
- const response = await util.get_subdepartments_users({ departmentId });
180
- return response.data.users || [];
181
- };
182
-
183
- // 通过id获得人员信息
184
- const getUserInfoById = async (userId) => {
185
- const response = await util.get_wxuser_by_id({ userId });
186
- return response.data;
187
- };
188
-
189
- // 清空搜索
190
- const clearSearch = () => {
191
- searchValue.value = ""; // 清空搜索框
192
- // 部门列表赋初始值
193
- departments.value = departmentFulls.value;
194
- };
195
-
196
- // 搜索人员
197
- const handleSearch = () => {
198
- // 在这里可以处理搜索逻辑
199
- if (searchValue.value) {
200
- departments.value = []; // 清空部门列表
201
- // 搜索用户
202
- getUserInfoById(searchValue.value).then((user) => {
203
- if (user) {
204
- userlist.value = [user];
205
- //更新currentPath显示用户部门
206
- const path = findDeptPath(user.mainDepartment, departmentFulls.value);
207
- console.log("path", path);
208
- currentPath.value = "";
209
- path.forEach((dept) => {
210
- currentPath.value += dept.name + "/";
211
- });
212
- } else {
213
- userlist.value = []; // 清空人员列表
214
- }
215
- });
216
- }
217
- };
218
-
219
- // 切换部门
220
- const toggleDepartment = (department) => {
221
- console.log("选择了部门:", department.subDepartments);
222
- departments.value = department.subDepartments;
223
- // 在这里可以处理部门选择逻辑
224
- console.log("选择的部门:", departments);
225
- // 更新当前路径
226
- if (currentPath.value === "请选择部门") {
227
- currentPath.value = "";
228
- }
229
- currentPath.value += department.name + "/";
230
- // 请求加载人员
231
- fetchAllUsers(department.id).then((users) => {
232
- // 在这里可以处理人员列表逻辑
233
- userlist.value = users;
234
- })
235
- };
236
-
237
- // 返回上一级部门
238
- const goBack = () => {
239
- searchValue.value = ""; // 清空搜索框
240
- if (currentPath.value === "请选择部门") return;
241
- currentPath.value = currentPath.value.replace(/\/+$/, "");
242
- currentPath.value = currentPath.value.substring(
243
- 0,
244
- currentPath.value.lastIndexOf("/")
245
- );
246
-
247
- if (currentPath.value !== "") {
248
- // 加载当前路径的部门列表
249
- const departmentlist = departmentFulls.value.find(
250
- (dept) => dept.name === currentPath.value.split("/")[0]
251
- );
252
- departments.value = departmentlist.subDepartments;
253
- // 加载当前路径的人员列表
254
- fetchAllUsers(departmentlist.id).then((users) => {
255
- userlist.value = users;
256
- });
257
- } else {
258
- currentPath.value = "请选择部门";
259
- departments.value = departmentFulls.value;
260
- }
261
- };
262
-
263
- // 确认按钮
264
- const confirmSelection = () => {
265
- modelValue.value = tempSelectedUser.value
266
- //对应的人员信息
267
- const selectedUser = userlist.value.find(
268
- (user) => user.userid === tempSelectedUser.value
269
- );
270
-
271
- selectedUserInfo.value = selectedUser.name;
272
-
273
- emit('select', selectedUser)
274
-
275
- show.value = false;
276
- };
277
- // 清空按钮
278
- const handleClear = () => {
279
- tempSelectedUser.value = null;
280
- selectedUserInfo.value = null;
281
- // 清空人员列表
282
- userlist.value = [];
283
- show.value = false;
284
- currentPath.value = "请选择部门";
285
- departments.value = departmentFulls.value;
286
- modelValue.value = tempSelectedUser.value
287
- };
288
-
289
- //首页加载
290
- onMounted(async () => {
291
- await fetchAllDepartments();
292
- await initDepartment();
293
- });
294
-
295
- // 人员初始化
296
- const initUser = async () => {
297
- console.log('initUser', props.modelValue, departmentFulls.value)
298
- if (!props.modelValue || !departmentFulls.value || departmentFulls.value.length < 1)
299
- return
300
-
301
- const user = await getUserInfoById(props.modelValue);
302
- if (user) {
303
- //更新currentPath显示用户部门
304
- const path = findDeptPath(user.mainDepartment, departmentFulls.value);
305
- currentPath.value = "";
306
- path.forEach((dept) => {
307
- currentPath.value += dept.name + "/";
308
- });
309
- // 清空部门列表
310
- departments.value = []; // 清空部门列表
311
- // 加载当前路径的人员列表
312
- fetchAllUsers(user.mainDepartment).then((users) => {
313
- userlist.value = users;
314
- // 选中当前用户
315
- tempSelectedUser.value = user.userid;
316
- selectedUserInfo.value = user.name;
317
- });
318
- } else {
319
- userlist.value = []; // 清空人员列表
320
- }
321
- };
322
- // 部门初始化
323
- const initDepartment = async () => {
324
- //更新currentPath显示用户部门
325
- const path = findDeptPath(props.currentDeptId, departmentFulls.value);
326
- currentPath.value = "";
327
- path.forEach((dept) => {
328
- currentPath.value += dept.name + "/";
329
- });
330
- // 更新部门列表
331
- departments.value = departmentFulls.value; // 重置部门列表
332
- };
333
-
334
- //监听searchValue变化
335
- watch(searchValue, (newValue, oldValue) => {
336
- if (newValue.length === 0) {
337
- // 重新加载部门列表
338
- departments.value = departmentFulls.value;
339
- // 清空人员列表
340
- userlist.value = [];
341
- }
342
- });
343
- watch(() => modelValue.value, (n, o) => {
344
- initUser()
345
- }, {
346
- immediate: true
347
- })
348
- watch(() => departmentFulls.value, (n, o) => {
349
- initUser()
350
- }, {
351
- immediate: true
352
- })
353
- </script>
354
-
355
- <style scoped>
356
- .user-popup {
357
- width: 100%;
358
- overflow: hidden;
359
- }
360
-
361
- .department-selector {
362
- padding: 10px;
363
- }
364
-
365
- .search-buttons {
366
- display: flex;
367
- gap: 10px;
368
- }
369
-
370
- .clear-btn {
371
- background-color: #f2f3f5;
372
- border: none;
373
- border-radius: 4px;
374
- padding: 4px 8px;
375
- font-size: 14px;
376
- }
377
-
378
- .confirm-btn {
379
- background-color: #1989fa;
380
- color: white;
381
- border: none;
382
- border-radius: 4px;
383
- padding: 4px 8px;
384
- font-size: 14px;
385
- }
386
-
387
- .no-results {
388
- text-align: center;
389
- padding: 20px;
390
- color: #969799;
391
- }
392
-
393
- .picker-header {
394
- padding: 10px;
395
- border-bottom: 1px solid #ebedf0;
396
- display: flex;
397
- justify-content: space-between;
398
- align-items: center;
399
- }
400
-
401
- .current-path {
402
- flex: 1;
403
- font-size: 14px;
404
- color: #323233;
405
- white-space: nowrap;
406
- overflow: hidden;
407
- text-overflow: ellipsis;
408
- }
409
-
410
- .header-right {
411
- display: flex;
412
- gap: 8px;
413
- margin-left: auto;
414
- }
415
-
416
- .cancel-btn {
417
- color: #969799;
418
- margin-right: 5px;
419
- }
420
-
421
- .scroll-container {
422
- height: calc(80vh - 150px);
423
- overflow-y: auto;
424
- }
425
- </style>
1
+ <template>
2
+ <van-field v-if="!isGone" :is-link="isReadonly ? false : true" readonly :label="props.label"
3
+ :placeholder="props.placeholder" @click="show = !isReadonly" :model-value="selectedUserInfo" :required="isRequired"
4
+ :rules="props.rules" input-align="center" />
5
+ <van-popup v-model:show="show" round position="bottom" class="user-popup">
6
+ <div class="department-selector">
7
+ <div class="picker-header">
8
+ <div class="header-left">
9
+ <van-button @click="goBack" icon="arrow-left" size="small" v-show="currentPath !== '请选择部门'" />
10
+ </div>
11
+
12
+ <!-- 移除header-center包装,直接使用current-path -->
13
+ <div class="current-path">{{ currentPath }}</div>
14
+
15
+ <div class="header-right">
16
+ <van-button size="small" @click="handleClear" class="cancel-btn">清空</van-button>
17
+
18
+ <van-button type="primary" size="small" @click="confirmSelection" class="confirm-btn">确定</van-button>
19
+ </div>
20
+ </div>
21
+
22
+ <!-- 搜索框 -->
23
+ <van-search v-model="searchValue" placeholder="输入工号查询" @clear="clearSearch" @search="handleSearch"
24
+ @cancel="clearSearch" show-action>
25
+ <template #action>
26
+ <div @click="handleSearch">搜索</div>
27
+ </template>
28
+ </van-search>
29
+
30
+ <div class="scroll-container">
31
+ <!-- 遍历部门列表 -->
32
+ <van-cell-group>
33
+ <van-cell v-for="department in departments" :key="department.id" :title="department.name" is-link
34
+ @click="toggleDepartment(department)" />
35
+ </van-cell-group>
36
+
37
+ <!-- 遍历人员列表 -->
38
+ <van-radio-group v-model="tempSelectedUser">
39
+ <van-cell-group>
40
+ <van-cell v-for="user in userlist" :key="user.id" :title="user.name">
41
+ <template #right-icon>
42
+ <van-radio :name="user.userid" />
43
+ </template>
44
+ </van-cell>
45
+ </van-cell-group>
46
+ </van-radio-group>
47
+ </div>
48
+ </div>
49
+ </van-popup>
50
+ </template>
51
+ <script setup>
52
+ import { ref, onMounted, watch } from "vue";
53
+ import util from "../util"
54
+ import { useVModel } from '@vueuse/core'
55
+
56
+ // 弹出层
57
+ const show = ref(false);
58
+
59
+ const props = defineProps({
60
+ modelValue: {
61
+ type: String,
62
+ default: "",
63
+ },
64
+ label: {
65
+ type: String,
66
+ default: "选择人员",
67
+ },
68
+ placeholder: {
69
+ type: String,
70
+ default: "请选择人员",
71
+ },
72
+ required: {
73
+ type: Boolean,
74
+ default: false,
75
+ },
76
+ readonly: {
77
+ type: Boolean,
78
+ default: false,
79
+ },
80
+ rules: {
81
+ type: Array,
82
+ default: () => [],
83
+ },
84
+ // 默认查询所有组织机构
85
+ currentDeptId: {
86
+ type: String,
87
+ default: "1", //组织机构id
88
+ },
89
+ // 二级部门取值范围字典id
90
+ // 取dictionary库.dict的配置id
91
+ deptRangeId: {
92
+ type: String,
93
+ default: ''
94
+ },
95
+ auth: {
96
+ type: String,
97
+ default: "",
98
+ },
99
+ });
100
+ // 定义emit
101
+ const emit = defineEmits(["update:modelValue", "select"]);
102
+ const modelValue = useVModel(props, 'modelValue', emit)
103
+ const { isRequired, isReadonly, isGone, isDisabled } = util.props2auth(props);
104
+
105
+ // 部门列表
106
+ let departments = ref([]);
107
+ let departmentFulls = ref([])
108
+ const searchValue = ref("");
109
+ const currentPath = ref("请选择部门");
110
+ // 定义用户
111
+ const userlist = ref([]);
112
+ // 定义选择单选按钮的值
113
+ const tempSelectedUser = ref(null);
114
+ // 定义展示表单展示区的值
115
+ const selectedUserInfo = ref([]);
116
+
117
+ // 获得部门列表
118
+ const fetchAllDepartments = async () => {
119
+ // 查询所有部门
120
+ const response = await util.get_subdepartments({ departmentId: props.currentDeptId })
121
+ let level2Depts = response.data.subDepartments
122
+
123
+ // 取配置的二级部门取值范围
124
+ let level2DeptIds = []
125
+ if (props.deptRangeId) {
126
+ let res = await util.dictionary_dict({ id: props.deptRangeId })
127
+ if (res.data.parseValue && res.data.parseValue.length>0) {
128
+ level2DeptIds = res.data.parseValue
129
+ }
130
+
131
+ // 当前用户所在二级部门列表
132
+ const user = await getUserInfoById(props.modelValue)
133
+ if (user && user.department.id) {
134
+ level2DeptIds = [...level2DeptIds, user.department.id]
135
+ }
136
+ }
137
+
138
+ // 只保留指定二级部门列表、当前用户的二级部门
139
+ if (props.deptRangeId) {
140
+ if (level2DeptIds && level2DeptIds.length > 0) {
141
+ level2Depts = level2Depts.filter(item => {
142
+ return level2DeptIds.includes(item.id)
143
+ })
144
+ }
145
+ }
146
+
147
+ //departments
148
+ departments.value = getCurrentDepartments(level2Depts || [])
149
+ departmentFulls.value = departments.value;
150
+ };
151
+
152
+ //嵌套处理获得树形结构
153
+ const getCurrentDepartments = (data) => {
154
+ let result = data.map((item) => {
155
+ return {
156
+ id: item.id,
157
+ name: item.name,
158
+ subDepartments: getCurrentDepartments(item.subDepartments || []),
159
+ };
160
+ });
161
+ return result;
162
+ };
163
+ // 添加部门路径查找方法
164
+ function findDeptPath(deptId, tree, path = []) {
165
+ for (const dept of tree) {
166
+ if (dept.id === deptId) {
167
+ return [...path, dept];
168
+ }
169
+ if (dept.subDepartments?.length) {
170
+ const found = findDeptPath(deptId, dept.subDepartments, [...path, dept]);
171
+ if (found && found.length>0) return found;
172
+ }
173
+ }
174
+ return [];
175
+ }
176
+
177
+ // 获得人员列表
178
+ const fetchAllUsers = async (departmentId) => {
179
+ const response = await util.get_subdepartments_users({ departmentId });
180
+ return response.data.users || [];
181
+ };
182
+
183
+ // 通过id获得人员信息
184
+ const getUserInfoById = async (userId) => {
185
+ const response = await util.get_wxuser_by_id({ userId });
186
+ return response.data;
187
+ };
188
+
189
+ // 清空搜索
190
+ const clearSearch = () => {
191
+ searchValue.value = ""; // 清空搜索框
192
+ // 部门列表赋初始值
193
+ departments.value = departmentFulls.value;
194
+ };
195
+
196
+ // 搜索人员
197
+ const handleSearch = () => {
198
+ // 在这里可以处理搜索逻辑
199
+ if (searchValue.value) {
200
+ departments.value = []; // 清空部门列表
201
+ // 搜索用户
202
+ getUserInfoById(searchValue.value).then((user) => {
203
+ if (user) {
204
+ userlist.value = [user];
205
+ //更新currentPath显示用户部门
206
+ const path = findDeptPath(user.mainDepartment, departmentFulls.value);
207
+ console.log("path", path);
208
+ currentPath.value = "";
209
+ path.forEach((dept) => {
210
+ currentPath.value += dept.name + "/";
211
+ });
212
+ } else {
213
+ userlist.value = []; // 清空人员列表
214
+ }
215
+ });
216
+ }
217
+ };
218
+
219
+ // 切换部门
220
+ const toggleDepartment = (department) => {
221
+ console.log("选择了部门:", department.subDepartments);
222
+ departments.value = department.subDepartments;
223
+ // 在这里可以处理部门选择逻辑
224
+ console.log("选择的部门:", departments);
225
+ // 更新当前路径
226
+ if (currentPath.value === "请选择部门") {
227
+ currentPath.value = "";
228
+ }
229
+ currentPath.value += department.name + "/";
230
+ // 请求加载人员
231
+ fetchAllUsers(department.id).then((users) => {
232
+ // 在这里可以处理人员列表逻辑
233
+ userlist.value = users;
234
+ })
235
+ };
236
+
237
+ // 返回上一级部门
238
+ const goBack = () => {
239
+ searchValue.value = ""; // 清空搜索框
240
+ if (currentPath.value === "请选择部门") return;
241
+ currentPath.value = currentPath.value.replace(/\/+$/, "");
242
+ currentPath.value = currentPath.value.substring(
243
+ 0,
244
+ currentPath.value.lastIndexOf("/")
245
+ );
246
+
247
+ if (currentPath.value !== "") {
248
+ // 加载当前路径的部门列表
249
+ const departmentlist = departmentFulls.value.find(
250
+ (dept) => dept.name === currentPath.value.split("/")[0]
251
+ );
252
+ departments.value = departmentlist.subDepartments;
253
+ // 加载当前路径的人员列表
254
+ fetchAllUsers(departmentlist.id).then((users) => {
255
+ userlist.value = users;
256
+ });
257
+ } else {
258
+ currentPath.value = "请选择部门";
259
+ departments.value = departmentFulls.value;
260
+ }
261
+ };
262
+
263
+ // 确认按钮
264
+ const confirmSelection = () => {
265
+ modelValue.value = tempSelectedUser.value
266
+ //对应的人员信息
267
+ const selectedUser = userlist.value.find(
268
+ (user) => user.userid === tempSelectedUser.value
269
+ );
270
+
271
+ selectedUserInfo.value = selectedUser.name;
272
+
273
+ emit('select', selectedUser)
274
+
275
+ show.value = false;
276
+ };
277
+ // 清空按钮
278
+ const handleClear = () => {
279
+ tempSelectedUser.value = null;
280
+ selectedUserInfo.value = null;
281
+ // 清空人员列表
282
+ userlist.value = [];
283
+ show.value = false;
284
+ currentPath.value = "请选择部门";
285
+ departments.value = departmentFulls.value;
286
+ modelValue.value = tempSelectedUser.value
287
+ };
288
+
289
+ //首页加载
290
+ onMounted(async () => {
291
+ await fetchAllDepartments();
292
+ await initDepartment();
293
+ });
294
+
295
+ // 人员初始化
296
+ const initUser = async () => {
297
+ console.log('initUser', props.modelValue, departmentFulls.value)
298
+ if (!props.modelValue || !departmentFulls.value || departmentFulls.value.length < 1)
299
+ return
300
+
301
+ const user = await getUserInfoById(props.modelValue);
302
+ if (user) {
303
+ //更新currentPath显示用户部门
304
+ const path = findDeptPath(user.mainDepartment, departmentFulls.value);
305
+ currentPath.value = "";
306
+ path.forEach((dept) => {
307
+ currentPath.value += dept.name + "/";
308
+ });
309
+ // 清空部门列表
310
+ departments.value = []; // 清空部门列表
311
+ // 加载当前路径的人员列表
312
+ fetchAllUsers(user.mainDepartment).then((users) => {
313
+ userlist.value = users;
314
+ // 选中当前用户
315
+ tempSelectedUser.value = user.userid;
316
+ selectedUserInfo.value = user.name;
317
+ });
318
+ } else {
319
+ userlist.value = []; // 清空人员列表
320
+ }
321
+ };
322
+ // 部门初始化
323
+ const initDepartment = async () => {
324
+ //更新currentPath显示用户部门
325
+ const path = findDeptPath(props.currentDeptId, departmentFulls.value);
326
+ currentPath.value = "";
327
+ path.forEach((dept) => {
328
+ currentPath.value += dept.name + "/";
329
+ });
330
+ // 更新部门列表
331
+ departments.value = departmentFulls.value; // 重置部门列表
332
+ };
333
+
334
+ //监听searchValue变化
335
+ watch(searchValue, (newValue, oldValue) => {
336
+ if (newValue.length === 0) {
337
+ // 重新加载部门列表
338
+ departments.value = departmentFulls.value;
339
+ // 清空人员列表
340
+ userlist.value = [];
341
+ }
342
+ });
343
+ watch(() => modelValue.value, (n, o) => {
344
+ initUser()
345
+ }, {
346
+ immediate: true
347
+ })
348
+ watch(() => departmentFulls.value, (n, o) => {
349
+ initUser()
350
+ }, {
351
+ immediate: true
352
+ })
353
+ </script>
354
+
355
+ <style scoped>
356
+ .user-popup {
357
+ width: 100%;
358
+ overflow: hidden;
359
+ }
360
+
361
+ .department-selector {
362
+ padding: 10px;
363
+ }
364
+
365
+ .search-buttons {
366
+ display: flex;
367
+ gap: 10px;
368
+ }
369
+
370
+ .clear-btn {
371
+ background-color: #f2f3f5;
372
+ border: none;
373
+ border-radius: 4px;
374
+ padding: 4px 8px;
375
+ font-size: 14px;
376
+ }
377
+
378
+ .confirm-btn {
379
+ background-color: #1989fa;
380
+ color: white;
381
+ border: none;
382
+ border-radius: 4px;
383
+ padding: 4px 8px;
384
+ font-size: 14px;
385
+ }
386
+
387
+ .no-results {
388
+ text-align: center;
389
+ padding: 20px;
390
+ color: #969799;
391
+ }
392
+
393
+ .picker-header {
394
+ padding: 10px;
395
+ border-bottom: 1px solid #ebedf0;
396
+ display: flex;
397
+ justify-content: space-between;
398
+ align-items: center;
399
+ }
400
+
401
+ .current-path {
402
+ flex: 1;
403
+ font-size: 14px;
404
+ color: #323233;
405
+ white-space: nowrap;
406
+ overflow: hidden;
407
+ text-overflow: ellipsis;
408
+ }
409
+
410
+ .header-right {
411
+ display: flex;
412
+ gap: 8px;
413
+ margin-left: auto;
414
+ }
415
+
416
+ .cancel-btn {
417
+ color: #969799;
418
+ margin-right: 5px;
419
+ }
420
+
421
+ .scroll-container {
422
+ height: calc(80vh - 150px);
423
+ overflow-y: auto;
424
+ }
425
+ </style>