@jari-ace/element-plus-component 0.1.10 → 0.2.1

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 (156) hide show
  1. package/README.md +1 -18
  2. package/dist/components/form/JaForm.vue.d.ts +0 -3
  3. package/dist/components/form/JaForm.vue.d.ts.map +1 -1
  4. package/dist/components/form/JaForm.vue.js +68 -22
  5. package/dist/components/form/JaForm.vue.js.map +1 -1
  6. package/dist/components/formItem/JaFormItem.vue.d.ts +0 -4
  7. package/dist/components/formItem/JaFormItem.vue.d.ts.map +1 -1
  8. package/dist/components/formItem/JaFormItem.vue.js +8 -26
  9. package/dist/components/formItem/JaFormItem.vue.js.map +1 -1
  10. package/dist/components/userPicker/src/UserPicker.vue.d.ts +17 -0
  11. package/dist/components/userPicker/src/UserPicker.vue.d.ts.map +1 -1
  12. package/dist/components/userPicker/src/UserPicker.vue.js +15 -0
  13. package/dist/components/userPicker/src/UserPicker.vue.js.map +1 -1
  14. package/dist/components/userSelectDialog/src/userSelectDialog.vue.d.ts +17 -0
  15. package/dist/components/userSelectDialog/src/userSelectDialog.vue.d.ts.map +1 -1
  16. package/dist/components/userSelectDialog/src/userSelectDialog.vue.js +15 -0
  17. package/dist/components/userSelectDialog/src/userSelectDialog.vue.js.map +1 -1
  18. package/dist/hooks/useBackendValidations.js +0 -1
  19. package/dist/hooks/useBackendValidations.js.map +1 -1
  20. package/dist/hooks/useClassificationLevels.d.ts +21 -0
  21. package/dist/hooks/useClassificationLevels.d.ts.map +1 -0
  22. package/dist/hooks/useClassificationLevels.js +55 -0
  23. package/dist/hooks/useClassificationLevels.js.map +1 -0
  24. package/dist/hooks/useUserRefQuery.d.ts +6 -0
  25. package/dist/hooks/useUserRefQuery.d.ts.map +1 -1
  26. package/dist/hooks/useUserRefQuery.js +4 -0
  27. package/dist/hooks/useUserRefQuery.js.map +1 -1
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +2 -0
  31. package/dist/index.js.map +1 -1
  32. package/lib/index.css +1 -1
  33. package/lib/index.js +1808 -1774
  34. package/lib/index.umd.cjs +2 -2
  35. package/package.json +61 -61
  36. package/packages/components/autoComplete/JaAutoComplete.vue +47 -47
  37. package/packages/components/autoComplete/index.ts +5 -5
  38. package/packages/components/avatar/JaAvatar.vue +126 -126
  39. package/packages/components/avatar/avatarToken.ts +11 -11
  40. package/packages/components/avatar/defaultImg.ts +14 -14
  41. package/packages/components/avatar/index.ts +7 -7
  42. package/packages/components/button/JaButton.vue +51 -51
  43. package/packages/components/button/index.ts +4 -4
  44. package/packages/components/channelPicker/index.ts +7 -7
  45. package/packages/components/channelPicker/src/ChannelPicker.vue +43 -43
  46. package/packages/components/channelPicker/src/JaChannelPicker.vue +42 -42
  47. package/packages/components/checkbox/JaCheckbox.vue +73 -73
  48. package/packages/components/checkbox/index.ts +4 -4
  49. package/packages/components/checkboxGroup/JaCheckboxGroup.vue +45 -45
  50. package/packages/components/checkboxGroup/index.ts +4 -4
  51. package/packages/components/customGroupTree/index.ts +10 -10
  52. package/packages/components/customGroupTree/src/customGroupTree.vue +91 -91
  53. package/packages/components/datePicker/JaDatePicker.vue +52 -52
  54. package/packages/components/datePicker/index.ts +4 -4
  55. package/packages/components/departmentPicker/index.ts +4 -4
  56. package/packages/components/departmentPicker/src/DepartmentPicker.vue +107 -107
  57. package/packages/components/departmentPicker/src/consts.ts +2 -2
  58. package/packages/components/departmentTree/index.ts +10 -10
  59. package/packages/components/departmentTree/src/departmentTree.vue +135 -135
  60. package/packages/components/dropdownButton/JaDropdownButton.vue +59 -59
  61. package/packages/components/dropdownButton/index.ts +4 -4
  62. package/packages/components/enumList/EnumListInput.vue +107 -107
  63. package/packages/components/enumList/JaEnumList.vue +39 -39
  64. package/packages/components/enumList/index.ts +7 -7
  65. package/packages/components/enumPicker/index.ts +5 -5
  66. package/packages/components/enumPicker/src/EnumPicker.vue +103 -103
  67. package/packages/components/form/JaForm.vue +186 -146
  68. package/packages/components/form/index.ts +5 -5
  69. package/packages/components/form/types.ts +4 -4
  70. package/packages/components/formItem/JaFormItem.vue +68 -87
  71. package/packages/components/formItem/index.ts +4 -4
  72. package/packages/components/index.ts +34 -34
  73. package/packages/components/input/JaInput.vue +143 -143
  74. package/packages/components/input/index.ts +4 -4
  75. package/packages/components/inputI18n/I18nBundleEditor.vue +76 -76
  76. package/packages/components/inputI18n/InputI18n.vue +146 -146
  77. package/packages/components/inputI18n/JaInputI18n.vue +50 -50
  78. package/packages/components/inputI18n/index.ts +8 -8
  79. package/packages/components/inputNumber/JaInputNumber.vue +98 -98
  80. package/packages/components/inputNumber/index.ts +4 -4
  81. package/packages/components/mapItemList/JaMapItemList.vue +35 -35
  82. package/packages/components/mapItemList/MapItemListInput.vue +191 -191
  83. package/packages/components/mapItemList/index.ts +7 -7
  84. package/packages/components/numberList/JaNumberList.vue +36 -36
  85. package/packages/components/numberList/NumberListInput.vue +111 -111
  86. package/packages/components/numberList/index.ts +7 -7
  87. package/packages/components/properyPicker/JaPropertyPicker.vue +38 -38
  88. package/packages/components/properyPicker/PropertyPicker.vue +314 -314
  89. package/packages/components/properyPicker/index.ts +7 -7
  90. package/packages/components/radioGroup/JaRadioGroup.vue +50 -50
  91. package/packages/components/radioGroup/index.ts +4 -4
  92. package/packages/components/rolePicker/RoleEditor.vue +129 -129
  93. package/packages/components/rolePicker/RolePicker.vue +44 -44
  94. package/packages/components/rolePicker/RolePickerRaw.vue +56 -56
  95. package/packages/components/rolePicker/baseRolePicker.vue +87 -87
  96. package/packages/components/rolePicker/index.ts +10 -10
  97. package/packages/components/scrollbar/Scrollbar.vue +89 -89
  98. package/packages/components/scrollbar/index.ts +5 -5
  99. package/packages/components/scrollbar/utils.ts +17 -17
  100. package/packages/components/select/JaSelect.vue +48 -48
  101. package/packages/components/select/index.ts +4 -4
  102. package/packages/components/stringList/JaStringList.vue +36 -36
  103. package/packages/components/stringList/StringListInput.vue +96 -96
  104. package/packages/components/stringList/index.ts +7 -7
  105. package/packages/components/switch/JaSwitch.vue +50 -50
  106. package/packages/components/switch/index.ts +4 -4
  107. package/packages/components/timePicker/JaTimePicker.vue +52 -52
  108. package/packages/components/timePicker/index.ts +5 -5
  109. package/packages/components/tip/index.ts +4 -4
  110. package/packages/components/tip/src/AceTip.vue +43 -43
  111. package/packages/components/upload/index.ts +6 -6
  112. package/packages/components/upload/src/Upload.vue +25 -25
  113. package/packages/components/upload/src/type.ts +3 -3
  114. package/packages/components/userGroupPicker/index.ts +4 -4
  115. package/packages/components/userGroupPicker/src/UserGroupPicker.vue +94 -94
  116. package/packages/components/userGroupTree/index.ts +10 -10
  117. package/packages/components/userGroupTree/src/userGroupTree.vue +149 -149
  118. package/packages/components/userPicker/index.ts +10 -10
  119. package/packages/components/userPicker/src/CustomGroupManager.vue +189 -189
  120. package/packages/components/userPicker/src/JaUserList.vue +283 -283
  121. package/packages/components/userPicker/src/JaUserPicker.vue +37 -37
  122. package/packages/components/userPicker/src/UserPicker.vue +376 -368
  123. package/packages/components/userSelectDialog/index.ts +6 -6
  124. package/packages/components/userSelectDialog/src/userSelectDialog.vue +462 -455
  125. package/packages/components/userTag/UserInfoTag.vue +397 -397
  126. package/packages/components/userTag/index.ts +6 -6
  127. package/packages/components/userTag/sharedAxios.ts +8 -8
  128. package/packages/directives/auth/index.ts +41 -41
  129. package/packages/directives/autofocus/index.ts +29 -29
  130. package/packages/directives/index.ts +10 -10
  131. package/packages/directives/shortcut/index.ts +192 -192
  132. package/packages/hooks/useAppInstances.ts +34 -34
  133. package/packages/hooks/useBackendValidations.ts +81 -81
  134. package/packages/hooks/useBridage.ts +157 -157
  135. package/packages/hooks/useClassificationLevels.ts +62 -0
  136. package/packages/hooks/useDateTimeShortCuts.ts +65 -65
  137. package/packages/hooks/useRealms.ts +28 -28
  138. package/packages/hooks/useTreeData.ts +45 -45
  139. package/packages/hooks/useUserRefQuery.ts +232 -224
  140. package/packages/index.ts +24 -22
  141. package/packages/list.json +7 -7
  142. package/packages/types/custom.d.ts +13 -13
  143. package/packages/types/window.d.ts +16 -16
  144. package/packages/utils/install.ts +43 -43
  145. package/packages/utils/objectUtils.ts +31 -31
  146. package/theme-style/fonts/iconfont.json +5196 -5196
  147. package/theme-style/index.scss +10 -10
  148. package/theme-style/styles/element-plus-var.scss +1419 -1419
  149. package/theme-style/styles/iconfont.css +2979 -2979
  150. package/theme-style/styles/theme-var.scss +72 -72
  151. package/theme-style/styles/transition.scss +122 -122
  152. package/dist/utils/formUtils.d.ts +0 -7
  153. package/dist/utils/formUtils.d.ts.map +0 -1
  154. package/dist/utils/formUtils.js +0 -54
  155. package/dist/utils/formUtils.js.map +0 -1
  156. package/packages/utils/formUtils.ts +0 -57
@@ -1,455 +1,462 @@
1
- <script setup lang="tsx">
2
- import {
3
- ElDialog,
4
- ElIcon,
5
- ElTableV2,
6
- ElText,
7
- ElTabs,
8
- ElTabPane,
9
- ElScrollbar,
10
- ElTooltip,
11
- vLoading,
12
- ElButton, type Column
13
- } from "element-plus";
14
- import {JaDepartmentTree} from "../../departmentTree";
15
- import {
16
- h,
17
- onUnmounted,
18
- type PropType,
19
- provide,
20
- reactive,
21
- ref,
22
- watch
23
- } from "vue";
24
- import {
25
- type BooleanResultExpression,
26
- createAxiosWithoutCache, type CustomGroup, type Group,
27
- type ProjectedDepartment, QUser,
28
- useLoading,
29
- useLoginUser, type UserReference
30
- } from "@jari-ace/app-bolts";
31
- import {useUserRefQuery} from "../../../hooks/useUserRefQuery";
32
- import type {TreeNodeType} from "../../../hooks/useTreeData";
33
- import {JaUserInfoTag} from "../../userTag";
34
- import {SCROLL_BAR_WRAPPED_INJECT_KEY} from "../../scrollbar/utils";
35
- import {Check, Hide, QuestionFilled, View} from "@element-plus/icons-vue";
36
- import {JaButton} from "../../button";
37
- import throttle from "lodash-es/throttle";
38
- import {JaUserGroupTree} from "../../userGroupTree";
39
- import {JaCustomGroupTree} from "../../customGroupTree";
40
- import { useRealms } from "../../../hooks/useRealms";
41
- //#region Props和Const声明和初始化
42
- const props = defineProps({
43
- /**
44
- * 可选择用户的用户域ID
45
- */
46
- realmId: {type: String, required: false},
47
- /**
48
- * 是否多选模式
49
- */
50
- multiple: {type: Boolean, required: false, default: false},
51
- /**
52
- * 自定义筛选过滤条件,可以在此回调方法内使用qUser参数构造自定义的查询条件。例如 qUser => qUser.fullName.startWith('王')
53
- */
54
- customFilter: {
55
- type: Function as PropType<(qUser: InstanceType<typeof QUser>) => BooleanResultExpression>,
56
- required: false
57
- }
58
- })
59
- const selectedUsers = defineModel<UserReference[] | UserReference | null>({
60
- required: true
61
- });
62
- const localRealmId = ref<string>();
63
-
64
-
65
- async function loadRealmId() {
66
- if (props.realmId) {
67
- localRealmId.value = props.realmId;
68
- } else {
69
- const defaultRealm = await useRealms().getDefaultRealm();
70
- localRealmId.value = defaultRealm.id;
71
- }
72
- }
73
-
74
- loadRealmId()
75
-
76
-
77
- if (props.multiple) {
78
- if (!selectedUsers.value) {
79
- selectedUsers.value = [];
80
- }
81
- if (!Array.isArray(selectedUsers.value)) {
82
- throw new Error('多选模式的选择用户对话框(multiple设置为true)的v-model必须是数组类型')
83
- }
84
- } else {
85
- if (Array.isArray(selectedUsers.value)) {
86
- throw new Error('单选模式的选择用户对话框(multiple设置为false)的v-model只能为空或是单个UserReference对象')
87
- }
88
- }
89
- const visible = ref(false);
90
- const currentDept = ref<ProjectedDepartment & TreeNodeType | undefined>();
91
- const currentGroup = ref<Group & TreeNodeType | undefined>();
92
- const currentCustomGroup = ref<CustomGroup & TreeNodeType | undefined>()
93
- const axios = createAxiosWithoutCache()
94
- const loading = useLoading(axios);
95
- const showSelectedOnly = ref(false);
96
- const usersForBinding = ref<UserReference[]>([]);
97
- const map = reactive(new Map<string, UserReference>());
98
- const currentTab = ref<string>('dept');
99
-
100
- provide(SCROLL_BAR_WRAPPED_INJECT_KEY, undefined)
101
-
102
- const tempSel = ref(Array.isArray(selectedUsers.value) ? [...selectedUsers.value] : selectedUsers.value)
103
- const userQuery = useUserRefQuery(undefined,
104
- props.customFilter,
105
- result => {
106
- if (!showSelectedOnly.value) {
107
- usersForBinding.value = result ?? [];
108
- }
109
- },
110
- false,
111
- axios,
112
- true);
113
-
114
- const columns: Column<any>[] = [
115
- {
116
- key: "user",
117
- dataKey: "id",
118
- width: 290,
119
- title: "用户",
120
- cellRenderer: ({cellData: id, rowData: u, rowIndex}) => {
121
- const {hasAvatar, fullName} = u;
122
- const user = u;
123
- const index = rowIndex;
124
- return h("div", {
125
- class: "user-row", onMousedown: () => onMouseDown(user),
126
- onMouseenter: () => onMouseEnter(user),
127
- onMouseup: onMouseUp,
128
- onMouseover: (e: MouseEvent) => onMouseOver(e, index)
129
- }, [
130
- h(ElIcon, {color: "#0D6A9F"}, () => userSelected(user) ? h(Check) : undefined),
131
- h(JaUserInfoTag, {
132
- userId: id,
133
- theme: userTagTheme(user),
134
- placement: 'right-start',
135
- hasAvatar: false,
136
- fullName: fullName
137
- }),
138
- h("div", {style: "flex:auto"})
139
- ])
140
- }
141
- }
142
- ]
143
-
144
- const isDragSelecting = ref(false);
145
- const userTable = ref<InstanceType<typeof ElTableV2>>()
146
- const throttleDown = throttle(manualScrollDown, 100);
147
- const throttleSwitchSelect = throttle(switchUserSelect, 100)
148
- //#endregion
149
-
150
- //#region watch
151
- watch(currentDept, () => {
152
- if (currentTab.value == 'dept')
153
- userQuery.queryParams.value.currentDept = currentDept.value;
154
- })
155
-
156
- watch(currentGroup, () => {
157
- if (currentTab.value == 'group')
158
- userQuery.queryParams.value.currentGroup = currentGroup.value;
159
- })
160
-
161
- watch(currentCustomGroup, () => {
162
- if (currentTab.value == "customGroup") {
163
- userQuery.queryParams.value.currentCustomGroup = currentCustomGroup.value;
164
- }
165
- })
166
-
167
- watch(currentTab, () => {
168
- if (currentTab.value === "dept") {
169
- userQuery.queryParams.value.currentDept = currentDept.value;
170
- userQuery.queryParams.value.currentGroup = undefined;
171
- userQuery.queryParams.value.currentCustomGroup = undefined;
172
- } else if (currentTab.value === "group") {
173
- userQuery.queryParams.value.currentDept = undefined;
174
- userQuery.queryParams.value.currentCustomGroup = undefined;
175
- userQuery.queryParams.value.currentGroup = currentGroup.value;
176
- } else if (currentTab.value === "customGroup") {
177
- userQuery.queryParams.value.currentDept = undefined;
178
- userQuery.queryParams.value.currentCustomGroup = currentCustomGroup.value;
179
- userQuery.queryParams.value.currentGroup = undefined;
180
- }
181
- })
182
- //#endregion
183
-
184
- //#region 方法
185
- function userSelected(u: UserReference) {
186
- if (Array.isArray(tempSel.value)) {
187
- return map.has(u.id);
188
- } else {
189
- return tempSel.value && tempSel.value.id === u.id;
190
- }
191
- }
192
-
193
- function userTagTheme(u: UserReference) {
194
- if (userSelected(u)) {
195
- return "autumnOrange"
196
- } else {
197
- return "skyGray"
198
- }
199
- }
200
-
201
- function switchUserSelect(user: UserReference) {
202
- if (Array.isArray(tempSel.value)) {
203
- const index = tempSel.value.findIndex(x => x.id === user.id);
204
- if (index >= 0) {
205
- map.delete(user.id);
206
- tempSel.value.splice(index, 1);
207
- } else {
208
- map.set(user.id, user)
209
- tempSel.value.push(user);
210
- }
211
- } else {
212
- tempSel.value = user;
213
- }
214
- }
215
-
216
- //#endregion
217
-
218
- //#region 事件处理
219
- onUnmounted(() => {
220
- throttleDown.cancel()
221
- throttleSwitchSelect.cancel()
222
- })
223
-
224
- function selectAll() {
225
- if (Array.isArray(tempSel.value)) {
226
- const users = userQuery.queryResult.value.users.filter(u => !userSelected(u));
227
- users.forEach(u => map.set(u.id, u))
228
- tempSel.value.push(...users);
229
- }
230
- }
231
-
232
- function clearSelection() {
233
- if (Array.isArray(tempSel.value)) {
234
- map.clear()
235
- tempSel.value.splice(0)
236
- }
237
- }
238
-
239
- function inverseSelection() {
240
- if (Array.isArray(tempSel.value)) {
241
- const users = userQuery.queryResult.value.users.filter(u => !userSelected(u))
242
- map.clear();
243
- users.forEach(u => map.set(u.id, u))
244
- tempSel.value.splice(0);
245
- tempSel.value.push(...users);
246
- }
247
- }
248
-
249
- function onConfirm() {
250
- selectedUsers.value = tempSel.value;
251
- visible.value = false;
252
- }
253
-
254
- function switchSelectOnlyView() {
255
- showSelectedOnly.value = !showSelectedOnly.value;
256
- if (showSelectedOnly.value) {
257
- if (Array.isArray(tempSel.value)) {
258
- usersForBinding.value = [...tempSel.value];
259
- } else {
260
- usersForBinding.value = tempSel.value ? [tempSel.value] : []
261
- }
262
- } else {
263
- usersForBinding.value = userQuery.queryResult.value.users;
264
- }
265
- }
266
-
267
- function onOpen() {
268
- if (!userQuery.queryParams.value.realmId && localRealmId.value) {
269
- userQuery.queryParams.value.realmId = localRealmId.value;
270
- }
271
-
272
- tempSel.value = Array.isArray(selectedUsers.value) ? [...selectedUsers.value] : selectedUsers.value;
273
- if (Array.isArray(selectedUsers.value)) {
274
- map.clear()
275
- selectedUsers.value.forEach(u => map.set(u.id, u));
276
- }
277
- }
278
-
279
- function onMouseDown(u: UserReference) {
280
- switchUserSelect(u);
281
- isDragSelecting.value = true;
282
- }
283
-
284
- function onMouseUp() {
285
- isDragSelecting.value = false;
286
- }
287
-
288
- function onMouseEnter(u: UserReference) {
289
- if (isDragSelecting.value) {
290
- switchUserSelect(u);
291
- }
292
- }
293
-
294
- let currentIndex: number | undefined = undefined;
295
-
296
- function manualScrollDown() {
297
- if (currentIndex && currentIndex < usersForBinding.value.length) {
298
- (
299
- userTable.value as any
300
- ).scrollToRow(currentIndex + 1);
301
- }
302
- currentIndex = undefined;
303
- }
304
-
305
- function manualScrollUp() {
306
- if (currentIndex && currentIndex > 0) {
307
- (
308
- userTable.value as any
309
- ).scrollToRow(currentIndex - 1);
310
- }
311
- currentIndex = undefined;
312
- }
313
-
314
- function onMouseOver(event: MouseEvent, index: number) {
315
- isDragSelecting.value = event.buttons > 0;
316
- if (isDragSelecting.value) {
317
- currentIndex = index;
318
- throttleDown();
319
- }
320
- }
321
-
322
- //#endregion
323
-
324
- defineExpose({
325
- show() {
326
- visible.value = true;
327
- }
328
- })
329
- </script>
330
-
331
- <template>
332
- <el-dialog v-model="visible" destroy-on-close draggable width="640" title="选择人员"
333
- append-to-body
334
- @open="onOpen">
335
- <div class="ja-user-select-dialog">
336
- <el-tabs class="ja-user-select-dialog__left" v-model="currentTab">
337
- <el-tab-pane name="dept" label="按部门"
338
- class="ja-user-select-dialog__left-dept-pane">
339
- <el-scrollbar height="400">
340
- <ja-department-tree v-model="currentDept"
341
- :realm-id="localRealmId"
342
- ></ja-department-tree>
343
- </el-scrollbar>
344
- </el-tab-pane>
345
- <el-tab-pane name="group" label="按用户组">
346
- <el-scrollbar height="400">
347
- <ja-user-group-tree v-model="currentGroup"
348
- :realm-id="localRealmId"></ja-user-group-tree>
349
- </el-scrollbar>
350
- </el-tab-pane>
351
- <el-tab-pane name="customGroup" label="按自定义组">
352
- <el-scrollbar height="400">
353
- <ja-custom-group-tree v-model="currentCustomGroup"></ja-custom-group-tree>
354
- </el-scrollbar>
355
- </el-tab-pane>
356
- </el-tabs>
357
- <div class="ja-user-select-dialog__content">
358
- <el-table-v2
359
- ref="userTable"
360
- :row-height="40"
361
- :header-height="40"
362
- :columns="columns"
363
- :data="usersForBinding"
364
- :width="300"
365
- :height="450"
366
- v-loading="loading"
367
- fixed>
368
- <template #header>
369
- <div
370
- style="display: flex; flex-direction: row; align-content: baseline; width: 100%; gap:0;">
371
- <el-text style="flex:auto; font-weight: 600">
372
- 人员
373
- <el-tooltip
374
- content="切换筛选条件,不会清除已选人员。支持鼠标拖动多选。">
375
- <el-icon v-if="props.multiple">
376
- <QuestionFilled></QuestionFilled>
377
- </el-icon>
378
- </el-tooltip>
379
- </el-text>
380
- <el-text style="flex:none; margin-right: 12px" v-if="props.multiple">
381
- <el-tooltip
382
- :content="showSelectedOnly? '显示所有人员': '只显示选中人员'">
383
- <el-icon size="15"
384
- @click="switchSelectOnlyView">
385
- <Hide v-if="!showSelectedOnly"></Hide>
386
- <View v-else></View>
387
- </el-icon>
388
- </el-tooltip>
389
- </el-text>
390
- <ja-button plain style="flex: none;" v-if="props.multiple" link
391
- tooltip="全部选中" shortcut="Ctrl+A"
392
- @click="selectAll">全选
393
- </ja-button>
394
- <ja-button plain style="flex: none;" v-if="props.multiple" link
395
- tooltip="清除全部选中项" shortcut="Ctrl+Q"
396
- @click="clearSelection">全消
397
- </ja-button>
398
- <ja-button plain style="flex: none;" v-if="props.multiple" link
399
- tooltip="清除选中项,并选中之前未被选中项"
400
- shortcut="Ctrl+F"
401
- @click="inverseSelection">反选
402
- </ja-button>
403
- </div>
404
- </template>
405
- </el-table-v2>
406
- </div>
407
- </div>
408
- <template #footer>
409
- <ja-button type="primary" shortcut="Ctrl+Alt+Enter" @click="onConfirm" size="default">确定
410
- </ja-button>
411
- <el-button plain size="default" @click="visible=false">取消</el-button>
412
- </template>
413
- </el-dialog>
414
- </template>
415
-
416
- <style scoped lang="scss">
417
- .ja-user-select-dialog {
418
- display: flex;
419
- align-content: stretch;
420
- gap: 8px;
421
-
422
- &__left {
423
- flex: none;
424
- width: 300px;
425
- border-right: 1px solid var(--el-border-color);
426
- padding-right: 8px;
427
-
428
- &-dept-pane {
429
- padding: 0 16px 0 0;
430
- }
431
- }
432
-
433
- &__content {
434
- flex: auto;
435
-
436
- :deep(.user-row) {
437
- user-select: none;
438
- display: flex;
439
- height: 100%;
440
- width: 100%;
441
- flex-direction: row;
442
- gap: 4px;
443
- align-items: center;
444
-
445
- i {
446
- height: 100%;
447
- }
448
- }
449
- }
450
-
451
-
452
- }
453
-
454
-
455
- </style>
1
+ <script setup lang="tsx">
2
+ import {
3
+ ElDialog,
4
+ ElIcon,
5
+ ElTableV2,
6
+ ElText,
7
+ ElTabs,
8
+ ElTabPane,
9
+ ElScrollbar,
10
+ ElTooltip,
11
+ vLoading,
12
+ ElButton, type Column
13
+ } from "element-plus";
14
+ import {JaDepartmentTree} from "../../departmentTree";
15
+ import {
16
+ h,
17
+ onUnmounted,
18
+ type PropType,
19
+ provide,
20
+ reactive,
21
+ ref,
22
+ watch
23
+ } from "vue";
24
+ import {
25
+ type BooleanResultExpression,
26
+ createAxiosWithoutCache, type CustomGroup, type Group,
27
+ type ProjectedDepartment, QUser,
28
+ useLoading,
29
+ useLoginUser, type UserReference
30
+ } from "@jari-ace/app-bolts";
31
+ import {useUserRefQuery} from "../../../hooks/useUserRefQuery";
32
+ import type {TreeNodeType} from "../../../hooks/useTreeData";
33
+ import {JaUserInfoTag} from "../../userTag";
34
+ import {SCROLL_BAR_WRAPPED_INJECT_KEY} from "../../scrollbar/utils";
35
+ import {Check, Hide, QuestionFilled, View} from "@element-plus/icons-vue";
36
+ import {JaButton} from "../../button";
37
+ import throttle from "lodash-es/throttle";
38
+ import {JaUserGroupTree} from "../../userGroupTree";
39
+ import {JaCustomGroupTree} from "../../customGroupTree";
40
+ import {useRealms} from "../../../hooks/useRealms";
41
+ //#region Props和Const声明和初始化
42
+ const props = defineProps({
43
+ /**
44
+ * 可选择用户的用户域ID
45
+ */
46
+ realmId: {type: String, required: false},
47
+ /**
48
+ * 是否多选模式
49
+ */
50
+ multiple: {type: Boolean, required: false, default: false},
51
+ /**
52
+ * 密级过滤
53
+ */
54
+ classificationLevel: {type: Number, required: false, default: false},
55
+ /**
56
+ * 自定义筛选过滤条件,可以在此回调方法内使用qUser参数构造自定义的查询条件。例如 qUser => qUser.fullName.startWith('王')
57
+ */
58
+ customFilter: {
59
+ type: Function as PropType<(qUser: InstanceType<typeof QUser>) => BooleanResultExpression>,
60
+ required: false
61
+ }
62
+ })
63
+ const selectedUsers = defineModel<UserReference[] | UserReference | null>({
64
+ required: true
65
+ });
66
+ const localRealmId = ref<string>();
67
+
68
+
69
+ async function loadRealmId() {
70
+ if (props.realmId) {
71
+ localRealmId.value = props.realmId;
72
+ } else {
73
+ const defaultRealm = await useRealms().getDefaultRealm();
74
+ localRealmId.value = defaultRealm.id;
75
+ }
76
+ }
77
+
78
+ loadRealmId()
79
+
80
+ if (props.multiple) {
81
+ if (!selectedUsers.value) {
82
+ selectedUsers.value = [];
83
+ }
84
+ if (!Array.isArray(selectedUsers.value)) {
85
+ throw new Error('多选模式的选择用户对话框(multiple设置为true)的v-model必须是数组类型')
86
+ }
87
+ } else {
88
+ if (Array.isArray(selectedUsers.value)) {
89
+ throw new Error('单选模式的选择用户对话框(multiple设置为false)的v-model只能为空或是单个UserReference对象')
90
+ }
91
+ }
92
+ const visible = ref(false);
93
+ const currentDept = ref<ProjectedDepartment & TreeNodeType | undefined>();
94
+ const currentGroup = ref<Group & TreeNodeType | undefined>();
95
+ const currentCustomGroup = ref<CustomGroup & TreeNodeType | undefined>()
96
+ const axios = createAxiosWithoutCache()
97
+ const loading = useLoading(axios);
98
+ const showSelectedOnly = ref(false);
99
+ const usersForBinding = ref<UserReference[]>([]);
100
+ const map = reactive(new Map<string, UserReference>());
101
+ const currentTab = ref<string>('dept');
102
+
103
+ provide(SCROLL_BAR_WRAPPED_INJECT_KEY, undefined)
104
+
105
+ const tempSel = ref(Array.isArray(selectedUsers.value) ? [...selectedUsers.value] : selectedUsers.value)
106
+ const userQuery = useUserRefQuery(undefined,
107
+ props.customFilter,
108
+ result => {
109
+ if (!showSelectedOnly.value) {
110
+ usersForBinding.value = result ?? [];
111
+ }
112
+ },
113
+ false,
114
+ axios,
115
+ true);
116
+
117
+ const columns: Column<any>[] = [
118
+ {
119
+ key: "user",
120
+ dataKey: "id",
121
+ width: 290,
122
+ title: "用户",
123
+ cellRenderer: ({cellData: id, rowData: u, rowIndex}) => {
124
+ const {hasAvatar, fullName} = u;
125
+ const user = u;
126
+ const index = rowIndex;
127
+ return h("div", {
128
+ class: "user-row", onMousedown: () => onMouseDown(user),
129
+ onMouseenter: () => onMouseEnter(user),
130
+ onMouseup: onMouseUp,
131
+ onMouseover: (e: MouseEvent) => onMouseOver(e, index)
132
+ }, [
133
+ h(ElIcon, {color: "#0D6A9F"}, () => userSelected(user) ? h(Check) : undefined),
134
+ h(JaUserInfoTag, {
135
+ userId: id,
136
+ theme: userTagTheme(user),
137
+ placement: 'right-start',
138
+ hasAvatar: false,
139
+ fullName: fullName
140
+ }),
141
+ h("div", {style: "flex:auto"})
142
+ ])
143
+ }
144
+ }
145
+ ]
146
+
147
+ const isDragSelecting = ref(false);
148
+ const userTable = ref<InstanceType<typeof ElTableV2>>()
149
+ const throttleDown = throttle(manualScrollDown, 100);
150
+ const throttleSwitchSelect = throttle(switchUserSelect, 100)
151
+ //#endregion
152
+
153
+ //#region watch
154
+ watch(currentDept, () => {
155
+ if (currentTab.value == 'dept')
156
+ userQuery.queryParams.value.currentDept = currentDept.value;
157
+ })
158
+
159
+ watch(currentGroup, () => {
160
+ if (currentTab.value == 'group')
161
+ userQuery.queryParams.value.currentGroup = currentGroup.value;
162
+ })
163
+
164
+ watch(currentCustomGroup, () => {
165
+ if (currentTab.value == "customGroup") {
166
+ userQuery.queryParams.value.currentCustomGroup = currentCustomGroup.value;
167
+ }
168
+ })
169
+
170
+ watch(() => props.classificationLevel, () => {
171
+ userQuery.queryParams.value.classificationLevel = props.classificationLevel
172
+ })
173
+
174
+ watch(currentTab, () => {
175
+ if (currentTab.value === "dept") {
176
+ userQuery.queryParams.value.currentDept = currentDept.value;
177
+ userQuery.queryParams.value.currentGroup = undefined;
178
+ userQuery.queryParams.value.currentCustomGroup = undefined;
179
+ } else if (currentTab.value === "group") {
180
+ userQuery.queryParams.value.currentDept = undefined;
181
+ userQuery.queryParams.value.currentCustomGroup = undefined;
182
+ userQuery.queryParams.value.currentGroup = currentGroup.value;
183
+ } else if (currentTab.value === "customGroup") {
184
+ userQuery.queryParams.value.currentDept = undefined;
185
+ userQuery.queryParams.value.currentCustomGroup = currentCustomGroup.value;
186
+ userQuery.queryParams.value.currentGroup = undefined;
187
+ }
188
+ })
189
+ //#endregion
190
+
191
+ //#region 方法
192
+ function userSelected(u: UserReference) {
193
+ if (Array.isArray(tempSel.value)) {
194
+ return map.has(u.id);
195
+ } else {
196
+ return tempSel.value && tempSel.value.id === u.id;
197
+ }
198
+ }
199
+
200
+ function userTagTheme(u: UserReference) {
201
+ if (userSelected(u)) {
202
+ return "autumnOrange"
203
+ } else {
204
+ return "skyGray"
205
+ }
206
+ }
207
+
208
+ function switchUserSelect(user: UserReference) {
209
+ if (Array.isArray(tempSel.value)) {
210
+ const index = tempSel.value.findIndex(x => x.id === user.id);
211
+ if (index >= 0) {
212
+ map.delete(user.id);
213
+ tempSel.value.splice(index, 1);
214
+ } else {
215
+ map.set(user.id, user)
216
+ tempSel.value.push(user);
217
+ }
218
+ } else {
219
+ tempSel.value = user;
220
+ }
221
+ }
222
+
223
+ //#endregion
224
+
225
+ //#region 事件处理
226
+ onUnmounted(() => {
227
+ throttleDown.cancel()
228
+ throttleSwitchSelect.cancel()
229
+ })
230
+
231
+ function selectAll() {
232
+ if (Array.isArray(tempSel.value)) {
233
+ const users = userQuery.queryResult.value.users.filter(u => !userSelected(u));
234
+ users.forEach(u => map.set(u.id, u))
235
+ tempSel.value.push(...users);
236
+ }
237
+ }
238
+
239
+ function clearSelection() {
240
+ if (Array.isArray(tempSel.value)) {
241
+ map.clear()
242
+ tempSel.value.splice(0)
243
+ }
244
+ }
245
+
246
+ function inverseSelection() {
247
+ if (Array.isArray(tempSel.value)) {
248
+ const users = userQuery.queryResult.value.users.filter(u => !userSelected(u))
249
+ map.clear();
250
+ users.forEach(u => map.set(u.id, u))
251
+ tempSel.value.splice(0);
252
+ tempSel.value.push(...users);
253
+ }
254
+ }
255
+
256
+ function onConfirm() {
257
+ selectedUsers.value = tempSel.value;
258
+ visible.value = false;
259
+ }
260
+
261
+ function switchSelectOnlyView() {
262
+ showSelectedOnly.value = !showSelectedOnly.value;
263
+ if (showSelectedOnly.value) {
264
+ if (Array.isArray(tempSel.value)) {
265
+ usersForBinding.value = [...tempSel.value];
266
+ } else {
267
+ usersForBinding.value = tempSel.value ? [tempSel.value] : []
268
+ }
269
+ } else {
270
+ usersForBinding.value = userQuery.queryResult.value.users;
271
+ }
272
+ }
273
+
274
+ function onOpen() {
275
+ if (!userQuery.queryParams.value.realmId && localRealmId.value) {
276
+ userQuery.queryParams.value.realmId = localRealmId.value;
277
+ }
278
+
279
+ tempSel.value = Array.isArray(selectedUsers.value) ? [...selectedUsers.value] : selectedUsers.value;
280
+ if (Array.isArray(selectedUsers.value)) {
281
+ map.clear()
282
+ selectedUsers.value.forEach(u => map.set(u.id, u));
283
+ }
284
+ }
285
+
286
+ function onMouseDown(u: UserReference) {
287
+ switchUserSelect(u);
288
+ isDragSelecting.value = true;
289
+ }
290
+
291
+ function onMouseUp() {
292
+ isDragSelecting.value = false;
293
+ }
294
+
295
+ function onMouseEnter(u: UserReference) {
296
+ if (isDragSelecting.value) {
297
+ switchUserSelect(u);
298
+ }
299
+ }
300
+
301
+ let currentIndex: number | undefined = undefined;
302
+
303
+ function manualScrollDown() {
304
+ if (currentIndex && currentIndex < usersForBinding.value.length) {
305
+ (
306
+ userTable.value as any
307
+ ).scrollToRow(currentIndex + 1);
308
+ }
309
+ currentIndex = undefined;
310
+ }
311
+
312
+ function manualScrollUp() {
313
+ if (currentIndex && currentIndex > 0) {
314
+ (
315
+ userTable.value as any
316
+ ).scrollToRow(currentIndex - 1);
317
+ }
318
+ currentIndex = undefined;
319
+ }
320
+
321
+ function onMouseOver(event: MouseEvent, index: number) {
322
+ isDragSelecting.value = event.buttons > 0;
323
+ if (isDragSelecting.value) {
324
+ currentIndex = index;
325
+ throttleDown();
326
+ }
327
+ }
328
+
329
+ //#endregion
330
+
331
+ defineExpose({
332
+ show() {
333
+ visible.value = true;
334
+ }
335
+ })
336
+ </script>
337
+
338
+ <template>
339
+ <el-dialog v-model="visible" destroy-on-close draggable width="640" title="选择人员"
340
+ append-to-body
341
+ @open="onOpen">
342
+ <div class="ja-user-select-dialog">
343
+ <el-tabs class="ja-user-select-dialog__left" v-model="currentTab">
344
+ <el-tab-pane name="dept" label="按部门"
345
+ class="ja-user-select-dialog__left-dept-pane">
346
+ <el-scrollbar height="400">
347
+ <ja-department-tree v-model="currentDept"
348
+ :realm-id="localRealmId"
349
+ ></ja-department-tree>
350
+ </el-scrollbar>
351
+ </el-tab-pane>
352
+ <el-tab-pane name="group" label="按用户组">
353
+ <el-scrollbar height="400">
354
+ <ja-user-group-tree v-model="currentGroup"
355
+ :realm-id="localRealmId"></ja-user-group-tree>
356
+ </el-scrollbar>
357
+ </el-tab-pane>
358
+ <el-tab-pane name="customGroup" label="按自定义组">
359
+ <el-scrollbar height="400">
360
+ <ja-custom-group-tree v-model="currentCustomGroup"></ja-custom-group-tree>
361
+ </el-scrollbar>
362
+ </el-tab-pane>
363
+ </el-tabs>
364
+ <div class="ja-user-select-dialog__content">
365
+ <el-table-v2
366
+ ref="userTable"
367
+ :row-height="40"
368
+ :header-height="40"
369
+ :columns="columns"
370
+ :data="usersForBinding"
371
+ :width="300"
372
+ :height="450"
373
+ v-loading="loading"
374
+ fixed>
375
+ <template #header>
376
+ <div
377
+ style="display: flex; flex-direction: row; align-content: baseline; width: 100%; gap:0;">
378
+ <el-text style="flex:auto; font-weight: 600">
379
+ 人员
380
+ <el-tooltip
381
+ content="切换筛选条件,不会清除已选人员。支持鼠标拖动多选。">
382
+ <el-icon v-if="props.multiple">
383
+ <QuestionFilled></QuestionFilled>
384
+ </el-icon>
385
+ </el-tooltip>
386
+ </el-text>
387
+ <el-text style="flex:none; margin-right: 12px" v-if="props.multiple">
388
+ <el-tooltip
389
+ :content="showSelectedOnly? '显示所有人员': '只显示选中人员'">
390
+ <el-icon size="15"
391
+ @click="switchSelectOnlyView">
392
+ <Hide v-if="!showSelectedOnly"></Hide>
393
+ <View v-else></View>
394
+ </el-icon>
395
+ </el-tooltip>
396
+ </el-text>
397
+ <ja-button plain style="flex: none;" v-if="props.multiple" link
398
+ tooltip="全部选中" shortcut="Ctrl+A"
399
+ @click="selectAll">全选
400
+ </ja-button>
401
+ <ja-button plain style="flex: none;" v-if="props.multiple" link
402
+ tooltip="清除全部选中项" shortcut="Ctrl+Q"
403
+ @click="clearSelection">全消
404
+ </ja-button>
405
+ <ja-button plain style="flex: none;" v-if="props.multiple" link
406
+ tooltip="清除选中项,并选中之前未被选中项"
407
+ shortcut="Ctrl+F"
408
+ @click="inverseSelection">反选
409
+ </ja-button>
410
+ </div>
411
+ </template>
412
+ </el-table-v2>
413
+ </div>
414
+ </div>
415
+ <template #footer>
416
+ <ja-button type="primary" shortcut="Ctrl+Alt+Enter" @click="onConfirm" size="default">确定
417
+ </ja-button>
418
+ <el-button plain size="default" @click="visible=false">取消</el-button>
419
+ </template>
420
+ </el-dialog>
421
+ </template>
422
+
423
+ <style scoped lang="scss">
424
+ .ja-user-select-dialog {
425
+ display: flex;
426
+ align-content: stretch;
427
+ gap: 8px;
428
+
429
+ &__left {
430
+ flex: none;
431
+ width: 300px;
432
+ border-right: 1px solid var(--el-border-color);
433
+ padding-right: 8px;
434
+
435
+ &-dept-pane {
436
+ padding: 0 16px 0 0;
437
+ }
438
+ }
439
+
440
+ &__content {
441
+ flex: auto;
442
+
443
+ :deep(.user-row) {
444
+ user-select: none;
445
+ display: flex;
446
+ height: 100%;
447
+ width: 100%;
448
+ flex-direction: row;
449
+ gap: 4px;
450
+ align-items: center;
451
+
452
+ i {
453
+ height: 100%;
454
+ }
455
+ }
456
+ }
457
+
458
+
459
+ }
460
+
461
+
462
+ </style>