@jari-ace/element-plus-component 0.2.1 → 0.2.3
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/dist/components/form/JaForm.vue.d.ts +3 -0
- package/dist/components/form/JaForm.vue.d.ts.map +1 -1
- package/dist/components/form/JaForm.vue.js +22 -68
- package/dist/components/form/JaForm.vue.js.map +1 -1
- package/dist/components/formItem/JaFormItem.vue.d.ts +4 -0
- package/dist/components/formItem/JaFormItem.vue.d.ts.map +1 -1
- package/dist/components/formItem/JaFormItem.vue.js +26 -8
- package/dist/components/formItem/JaFormItem.vue.js.map +1 -1
- package/dist/components/rolePicker/baseRolePicker.vue.d.ts.map +1 -1
- package/dist/components/rolePicker/baseRolePicker.vue.js +4 -2
- package/dist/components/rolePicker/baseRolePicker.vue.js.map +1 -1
- package/dist/components/userPicker/src/JaUserList.vue.d.ts.map +1 -1
- package/dist/components/userPicker/src/JaUserList.vue.js +41 -7
- package/dist/components/userPicker/src/JaUserList.vue.js.map +1 -1
- package/dist/components/userPicker/src/JaUserPicker.vue.d.ts +9 -2
- package/dist/components/userPicker/src/JaUserPicker.vue.d.ts.map +1 -1
- package/dist/components/userPicker/src/JaUserPicker.vue.js +7 -2
- package/dist/components/userPicker/src/JaUserPicker.vue.js.map +1 -1
- package/dist/components/userPicker/src/UserPicker.vue.d.ts +33 -112
- package/dist/components/userPicker/src/UserPicker.vue.d.ts.map +1 -1
- package/dist/components/userPicker/src/UserPicker.vue.js +262 -288
- package/dist/components/userPicker/src/UserPicker.vue.js.map +1 -1
- package/dist/utils/formUtils.d.ts +7 -0
- package/dist/utils/formUtils.d.ts.map +1 -0
- package/dist/utils/formUtils.js +54 -0
- package/dist/utils/formUtils.js.map +1 -0
- package/lib/index.css +1 -1
- package/lib/index.js +1965 -1929
- package/lib/index.umd.cjs +2 -2
- package/package.json +2 -2
- package/packages/components/form/JaForm.vue +29 -69
- package/packages/components/formItem/JaFormItem.vue +35 -16
- package/packages/components/rolePicker/baseRolePicker.vue +6 -2
- package/packages/components/userPicker/src/JaUserList.vue +45 -11
- package/packages/components/userPicker/src/JaUserPicker.vue +4 -1
- package/packages/components/userPicker/src/UserPicker.vue +129 -33
- package/packages/utils/formUtils.ts +57 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jari-ace/element-plus-component",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"main": "lib/index.umd.cjs",
|
|
6
6
|
"module": "lib/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"lodash-es": "^4.17.21",
|
|
50
50
|
"vue": "^3.5.11",
|
|
51
51
|
"vue-i18n": "^9.13.1",
|
|
52
|
-
"@jari-ace/app-bolts": "0.
|
|
52
|
+
"@jari-ace/app-bolts": "0.5.10"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"dev": "vite --port 3400",
|
|
@@ -3,7 +3,7 @@ import {type PropType, provide, ref, computed, watch} from "vue";
|
|
|
3
3
|
import {ElForm, type FormInstance, type FormRules} from "element-plus";
|
|
4
4
|
import {type ValidationInstance} from "../../hooks/useBackendValidations";
|
|
5
5
|
import type {JaFormModel} from "./types";
|
|
6
|
-
|
|
6
|
+
import {mergeRules} from '../../utils/formUtils'
|
|
7
7
|
interface Props {
|
|
8
8
|
model: JaFormModel,
|
|
9
9
|
inline?:boolean,
|
|
@@ -20,6 +20,7 @@ interface Props {
|
|
|
20
20
|
disabled?: boolean,
|
|
21
21
|
scrollToError?: boolean,
|
|
22
22
|
validator: ValidationInstance,
|
|
23
|
+
requiredPosition?: 'left' | 'right'
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const formRef = ref<FormInstance | undefined>(undefined);
|
|
@@ -35,82 +36,26 @@ const props = withDefaults(defineProps<Props>(),{
|
|
|
35
36
|
validateOnRuleChange:true,
|
|
36
37
|
size:'small',
|
|
37
38
|
scrollToError:true,
|
|
38
|
-
|
|
39
|
+
requiredPosition:'right',
|
|
39
40
|
})
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// required: true,
|
|
45
|
-
// },
|
|
46
|
-
// inline: {
|
|
47
|
-
// type: Boolean,
|
|
48
|
-
// default: false,
|
|
49
|
-
// },
|
|
50
|
-
// labelPosition: {
|
|
51
|
-
// type: String as PropType<"left" | "top" | "right">,
|
|
52
|
-
// default: "top",
|
|
53
|
-
// },
|
|
54
|
-
// labelWidth: {
|
|
55
|
-
// type: [String, Number],
|
|
56
|
-
// },
|
|
57
|
-
// labelSuffix: {
|
|
58
|
-
// type: String,
|
|
59
|
-
// },
|
|
60
|
-
// hideRequiredAsterisk: {
|
|
61
|
-
// type: Boolean,
|
|
62
|
-
// default: false,
|
|
63
|
-
// },
|
|
64
|
-
// showMessage: {
|
|
65
|
-
// type: Boolean,
|
|
66
|
-
// default: true,
|
|
67
|
-
// },
|
|
68
|
-
// inlineMessage: {
|
|
69
|
-
// type: Boolean,
|
|
70
|
-
// default: true,
|
|
71
|
-
// },
|
|
72
|
-
// statusIcon: {
|
|
73
|
-
// type: Boolean,
|
|
74
|
-
// default: false,
|
|
75
|
-
// },
|
|
76
|
-
// validateOnRuleChange: {
|
|
77
|
-
// type: Boolean,
|
|
78
|
-
// default: true,
|
|
79
|
-
// },
|
|
80
|
-
// rules: {
|
|
81
|
-
// type: Object as PropType<FormRules>,
|
|
82
|
-
// default: {},
|
|
83
|
-
// },
|
|
84
|
-
// size: {
|
|
85
|
-
// type: String as PropType<"small" | "default" | "large">,
|
|
86
|
-
// default: "small",
|
|
87
|
-
// },
|
|
88
|
-
// disabled: {
|
|
89
|
-
// type: Boolean,
|
|
90
|
-
// default: false,
|
|
91
|
-
// },
|
|
92
|
-
// scrollToError: {
|
|
93
|
-
// type: Boolean,
|
|
94
|
-
// default: true,
|
|
95
|
-
// },
|
|
96
|
-
// validator: {
|
|
97
|
-
// type: Object as PropType<ValidationInstance>,
|
|
98
|
-
// required: true,
|
|
99
|
-
// },
|
|
100
|
-
// });
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
101
45
|
|
|
102
46
|
defineEmits(["validate", "click"]);
|
|
103
47
|
provide("aceFormValidator", props.validator);
|
|
104
48
|
provide("aceFormModel", props.model);
|
|
105
49
|
provide("aceFormSize", props.size);
|
|
50
|
+
provide("aceFormRequiredPosition", props.requiredPosition);
|
|
51
|
+
const rules = ref<FormRules>({})
|
|
106
52
|
|
|
107
|
-
const
|
|
108
|
-
return
|
|
109
|
-
...props.rules,
|
|
110
|
-
...props.validator.rules,
|
|
111
|
-
};
|
|
53
|
+
const model = computed(() => {
|
|
54
|
+
return props.model.formData?props.model.formData:props.model;
|
|
112
55
|
});
|
|
113
56
|
|
|
57
|
+
|
|
58
|
+
|
|
114
59
|
defineExpose({
|
|
115
60
|
elForm: formRef,
|
|
116
61
|
getElForm:():FormInstance|undefined=>formRef.value,
|
|
@@ -119,18 +64,21 @@ defineExpose({
|
|
|
119
64
|
//先清空之前的校验
|
|
120
65
|
formRef.value.clearValidate();
|
|
121
66
|
//判断是否有传入rules如果有则先执行 formRef.value!.validate()
|
|
122
|
-
if(props.rules){
|
|
67
|
+
if(props.rules && Object.keys(props.rules).length>0){
|
|
123
68
|
await formRef.value.validate((valid,fields)=>{
|
|
124
69
|
//判断校验是否成功
|
|
125
70
|
if(valid){
|
|
126
71
|
//前端校验已成功,执行异步submit
|
|
127
72
|
submit()
|
|
128
73
|
.then(() => {
|
|
74
|
+
//成功
|
|
129
75
|
//校验通过清楚所有的错误校验信息
|
|
130
76
|
props.validator?.clearFiledErrors();
|
|
131
77
|
})
|
|
132
78
|
.catch(() => {
|
|
133
79
|
//失败
|
|
80
|
+
//合并rules
|
|
81
|
+
rules.value = mergeRules(props.rules, props.validator.rules);
|
|
134
82
|
formRef.value.validate();
|
|
135
83
|
if (onFail) onFail();
|
|
136
84
|
});
|
|
@@ -144,6 +92,9 @@ defineExpose({
|
|
|
144
92
|
props.validator?.clearFiledErrors();
|
|
145
93
|
})
|
|
146
94
|
.catch(async () => {
|
|
95
|
+
//失败
|
|
96
|
+
//合并rules
|
|
97
|
+
rules.value = mergeRules(props.rules, props.validator.rules);
|
|
147
98
|
await formRef.value.validate();
|
|
148
99
|
if (onFail) onFail();
|
|
149
100
|
});
|
|
@@ -152,17 +103,26 @@ defineExpose({
|
|
|
152
103
|
|
|
153
104
|
|
|
154
105
|
},
|
|
106
|
+
getRules: () => rules.value,
|
|
155
107
|
});
|
|
156
108
|
|
|
109
|
+
|
|
157
110
|
watch(() => props.model.formData, () => {
|
|
158
111
|
props.validator?.clearFiledErrors();
|
|
159
112
|
})
|
|
113
|
+
|
|
114
|
+
watch(() => props.rules, () => {
|
|
115
|
+
//合并rules
|
|
116
|
+
rules.value = mergeRules(props.rules, props.validator.rules);
|
|
117
|
+
}, { immediate: true })
|
|
118
|
+
|
|
119
|
+
|
|
160
120
|
</script>
|
|
161
121
|
|
|
162
122
|
<template>
|
|
163
123
|
<el-form
|
|
164
124
|
ref="formRef"
|
|
165
|
-
:model="model
|
|
125
|
+
:model="model"
|
|
166
126
|
:rules="rules"
|
|
167
127
|
:inline="inline"
|
|
168
128
|
:label-position="labelPosition"
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {provide, ref, withDefaults, defineProps, defineExpose} from
|
|
2
|
+
import { provide, ref, withDefaults, defineProps, defineExpose, inject, computed } from "vue";
|
|
3
3
|
import {ElFormItem, ElIcon, ElText, type FormItemRule, ElTooltip} from 'element-plus'
|
|
4
4
|
import {QuestionFilled} from "@element-plus/icons-vue";
|
|
5
5
|
|
|
6
|
+
type RequiredPosition = 'left' | 'right'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const aceFormRequiredPosition = inject('aceFormRequiredPosition')
|
|
6
11
|
const props = withDefaults(defineProps<{
|
|
7
12
|
prop?: string | string[]
|
|
8
13
|
rules?: FormItemRule | FormItemRule[]
|
|
@@ -14,15 +19,23 @@ const props = withDefaults(defineProps<{
|
|
|
14
19
|
showMessage?: boolean
|
|
15
20
|
inlineMessage?: boolean
|
|
16
21
|
size?: 'small' | 'default' | 'large'
|
|
22
|
+
requiredPosition?: RequiredPosition
|
|
23
|
+
labelGap?:string
|
|
17
24
|
}>(), {
|
|
18
25
|
required: false,
|
|
19
26
|
showMessage: true,
|
|
20
27
|
inlineMessage: true,
|
|
21
|
-
size: 'small'
|
|
28
|
+
size: 'small',
|
|
29
|
+
labelGap:'4px'
|
|
22
30
|
})
|
|
23
31
|
|
|
24
32
|
provide('aceFormItemProp', props.prop)
|
|
25
33
|
|
|
34
|
+
const requiredPosition = computed(()=>{
|
|
35
|
+
return props.requiredPosition || aceFormRequiredPosition
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
|
|
26
39
|
const formItem = ref<typeof ElFormItem | null>(null)
|
|
27
40
|
defineExpose({
|
|
28
41
|
resetField: () => {
|
|
@@ -46,20 +59,26 @@ defineExpose({
|
|
|
46
59
|
:inline-message="inlineMessage"
|
|
47
60
|
:size="size">
|
|
48
61
|
<template #label>
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<el-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
<div style="display: flex;" :style="{
|
|
63
|
+
gap: labelGap
|
|
64
|
+
}">
|
|
65
|
+
<span style="color:red" v-if="props.required && requiredPosition==='left'">*</span>
|
|
66
|
+
<el-text class="el-form-item__label">
|
|
67
|
+
{{ props.label }}
|
|
68
|
+
<el-tooltip
|
|
69
|
+
v-if="labelRemark"
|
|
70
|
+
placement="top">
|
|
71
|
+
<el-icon>
|
|
72
|
+
<question-filled/>
|
|
73
|
+
</el-icon>
|
|
74
|
+
<template #content>
|
|
75
|
+
<div v-html="labelRemark"/>
|
|
76
|
+
</template>
|
|
77
|
+
</el-tooltip>
|
|
78
|
+
</el-text>
|
|
79
|
+
<span style="color:red" v-if="props.required && requiredPosition==='right'">*</span>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
63
82
|
</template>
|
|
64
83
|
<slot></slot>
|
|
65
84
|
<slot name="error"></slot>
|
|
@@ -21,8 +21,12 @@ const roles = ref<ProjectedRole[]>([]);
|
|
|
21
21
|
const loadData = async () => {
|
|
22
22
|
if (!props.appName) return;
|
|
23
23
|
currentApp.value = await appApi.getByName(props.appName);
|
|
24
|
-
roles.value =
|
|
25
|
-
|
|
24
|
+
roles.value = (
|
|
25
|
+
await api.getAllRole(currentApp.value.id)
|
|
26
|
+
).map(r => {
|
|
27
|
+
r.name = r.appName + "." + r.name
|
|
28
|
+
return r
|
|
29
|
+
});
|
|
26
30
|
if (selectedRoleName.value) {
|
|
27
31
|
currentRole.value = findRole(selectedRoleName.value, roles.value);
|
|
28
32
|
} else {
|
|
@@ -31,7 +31,7 @@ const handleScroll = () => {
|
|
|
31
31
|
const scrollElement = scrollbar.value?.wrapRef;
|
|
32
32
|
if (scrollElement) {
|
|
33
33
|
const isBottom = scrollElement.scrollHeight - scrollElement.scrollTop
|
|
34
|
-
|
|
34
|
+
<= scrollElement.clientHeight + tolerance;
|
|
35
35
|
|
|
36
36
|
if (isBottom) {
|
|
37
37
|
if (page.value < props.total / props.pageSize) {
|
|
@@ -55,7 +55,15 @@ function switchUserSelect(u: UserReference) {
|
|
|
55
55
|
selectedUsers.value.splice(index, 1);
|
|
56
56
|
}
|
|
57
57
|
} else {
|
|
58
|
-
selectedUsers.value
|
|
58
|
+
if (Array.isArray(selectedUsers.value)) {
|
|
59
|
+
throw new Error("UserPicker组件设置为单选模式时,v-model必须绑定UserReference对象或null")
|
|
60
|
+
}
|
|
61
|
+
if(selectedUsers.value!==null&&u.id===selectedUsers.value.id){
|
|
62
|
+
selectedUsers.value = null;
|
|
63
|
+
}else{
|
|
64
|
+
selectedUsers.value = u;
|
|
65
|
+
}
|
|
66
|
+
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
|
|
@@ -74,6 +82,7 @@ function onMouseUp() {
|
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
function onMouseEnter(u: UserReference) {
|
|
85
|
+
console.log("点击")
|
|
77
86
|
if (isDragSelecting.value) {
|
|
78
87
|
switchUserSelect(u);
|
|
79
88
|
}
|
|
@@ -140,31 +149,56 @@ function onMouseOver(event: MouseEvent, index: number) {
|
|
|
140
149
|
}
|
|
141
150
|
|
|
142
151
|
function userSelected(u: UserReference) {
|
|
143
|
-
|
|
152
|
+
if (props.multiple) {
|
|
153
|
+
if (!Array.isArray(selectedUsers.value)) {
|
|
154
|
+
throw new Error("UserPicker组件设置为多选模式(multiple)时,v-model必须绑定UserReference数组")
|
|
155
|
+
}
|
|
156
|
+
return selectedUsers.value.some(su => su.id === u.id);
|
|
157
|
+
}else{
|
|
158
|
+
if (Array.isArray(selectedUsers.value)) {
|
|
159
|
+
throw new Error("UserPicker组件设置为单选模式时,v-model必须绑定UserReference对象")
|
|
160
|
+
}
|
|
161
|
+
return selectedUsers.value?.id === u.id;
|
|
162
|
+
}
|
|
163
|
+
|
|
144
164
|
}
|
|
145
165
|
|
|
146
166
|
const userTagTheme = (u: UserReference) => {
|
|
147
|
-
if (selectedForBinding.value
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
167
|
+
if (Array.isArray(selectedForBinding.value)) {
|
|
168
|
+
if (selectedForBinding.value.some(su => su.id === u.id)) {
|
|
169
|
+
return "autumnOrange"
|
|
170
|
+
} else {
|
|
171
|
+
return "skyGray"
|
|
172
|
+
}
|
|
173
|
+
}else {
|
|
174
|
+
if (selectedForBinding.value?.id === u.id) {
|
|
175
|
+
return "autumnOrange"
|
|
176
|
+
} else {
|
|
177
|
+
return "skyGray"
|
|
178
|
+
}
|
|
151
179
|
}
|
|
152
180
|
}
|
|
153
181
|
|
|
154
182
|
const selectedForBinding = computed(() => {
|
|
155
|
-
|
|
183
|
+
|
|
184
|
+
const isArray:boolean = Array.isArray(selectedUsers.value);
|
|
185
|
+
if (props.multiple && isArray) {
|
|
156
186
|
return selectedUsers.value;
|
|
157
|
-
|
|
187
|
+
|
|
188
|
+
} else if (!props.multiple && !isArray) {
|
|
158
189
|
if (selectedUsers.value) {
|
|
159
190
|
return [selectedUsers.value as UserReference];
|
|
160
191
|
} else {
|
|
161
|
-
return []
|
|
192
|
+
return [] ;
|
|
162
193
|
}
|
|
163
|
-
} else {
|
|
194
|
+
} else if(!props.multiple && isArray) {
|
|
195
|
+
return selectedUsers.value;
|
|
196
|
+
} else{
|
|
164
197
|
throw new Error("UserPicker组件设置为多选模式(multiple)时,v-model必须绑定UserReference数组")
|
|
165
198
|
}
|
|
166
199
|
})
|
|
167
200
|
|
|
201
|
+
|
|
168
202
|
function getUserItemClass(index: number) {
|
|
169
203
|
return index === activeIndex.value ? "active" : "";
|
|
170
204
|
}
|
|
@@ -19,6 +19,7 @@ function onChange(value: UserReference[]) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const mv = ref(getValue(model.formData, prop) as UserReference[] ?? []);
|
|
22
|
+
|
|
22
23
|
watch(() => model.formData, () => {
|
|
23
24
|
mv.value = getValue(model.formData, prop) as UserReference[] ?? []
|
|
24
25
|
}, {
|
|
@@ -29,7 +30,9 @@ watch(() => model.formData, () => {
|
|
|
29
30
|
<template>
|
|
30
31
|
<user-picker v-model="mv"
|
|
31
32
|
@change="onChange"
|
|
32
|
-
v-bind="$attrs"
|
|
33
|
+
v-bind="$attrs">
|
|
34
|
+
<slot name="default"></slot>
|
|
35
|
+
</user-picker>
|
|
33
36
|
</template>
|
|
34
37
|
|
|
35
38
|
<style scoped lang="scss">
|
|
@@ -5,9 +5,12 @@ import {
|
|
|
5
5
|
ElInput,
|
|
6
6
|
ElPopover,
|
|
7
7
|
ElText,
|
|
8
|
-
ElSpace
|
|
8
|
+
ElSpace,
|
|
9
9
|
} from "element-plus";
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
import type { PopoverInstance } from 'element-plus'
|
|
12
|
+
|
|
13
|
+
import {computed, ref, shallowRef, watch} from "vue"
|
|
11
14
|
import {
|
|
12
15
|
Delete,
|
|
13
16
|
Finished,
|
|
@@ -16,6 +19,9 @@ import {
|
|
|
16
19
|
Search, Setting,
|
|
17
20
|
StarFilled
|
|
18
21
|
} from "@element-plus/icons-vue";
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
19
25
|
import {
|
|
20
26
|
type BooleanResultExpression,
|
|
21
27
|
createAxiosWithoutCache,
|
|
@@ -32,46 +38,102 @@ import {JaScrollbar} from "../../scrollbar";
|
|
|
32
38
|
import CustomGroupManager from "./CustomGroupManager.vue";
|
|
33
39
|
import JaUserList from "./JaUserList.vue";
|
|
34
40
|
import {useRealms} from '../../../hooks/useRealms'
|
|
35
|
-
|
|
41
|
+
|
|
42
|
+
const props = withDefaults( defineProps<{
|
|
43
|
+
/**
|
|
44
|
+
* Popover 弹出框的触发方式
|
|
45
|
+
*/
|
|
46
|
+
trigger?:'click' | 'focus' | 'hover' | 'contextmenu',
|
|
36
47
|
/**
|
|
37
48
|
* 可选择用户的用户域ID
|
|
38
49
|
*/
|
|
39
|
-
realmId
|
|
50
|
+
realmId?:string,
|
|
40
51
|
/**
|
|
41
52
|
* 弹出下拉选框的快捷键
|
|
42
53
|
*/
|
|
43
|
-
shortcut
|
|
54
|
+
shortcut?:string,
|
|
44
55
|
/**
|
|
45
56
|
* 是否多选模式
|
|
46
57
|
*/
|
|
47
|
-
multiple
|
|
58
|
+
multiple?:boolean
|
|
48
59
|
/**
|
|
49
60
|
* 密级过滤
|
|
50
61
|
*/
|
|
51
|
-
classificationLevel
|
|
62
|
+
classificationLevel?:number
|
|
52
63
|
/**
|
|
53
64
|
* 自定义筛选过滤条件,可以在此回调方法内使用qUser参数构造自定义的查询条件。例如 qUser => qUser.fullName.startWith('王')
|
|
54
65
|
*/
|
|
55
|
-
customFilter:
|
|
56
|
-
type: Function as PropType<(qUser: InstanceType<typeof QUser>) => BooleanResultExpression>,
|
|
57
|
-
required: false
|
|
58
|
-
},
|
|
66
|
+
customFilter?:(qUser: InstanceType<typeof QUser>) => BooleanResultExpression,
|
|
59
67
|
/**
|
|
60
68
|
* 最多显示多少已选中的用户数量
|
|
61
69
|
*/
|
|
62
|
-
maxShowCount
|
|
70
|
+
maxShowCount?:number,
|
|
63
71
|
/**
|
|
64
72
|
* 高度,默认100%
|
|
65
73
|
*/
|
|
66
|
-
height
|
|
74
|
+
height?:number|string
|
|
67
75
|
/**
|
|
68
76
|
* 最大高度
|
|
69
77
|
*/
|
|
70
|
-
maxHeight
|
|
78
|
+
maxHeight?: number|string,
|
|
79
|
+
/**
|
|
80
|
+
* 是否显示已选用户标签
|
|
81
|
+
*/
|
|
82
|
+
showUserTag?:boolean
|
|
83
|
+
|
|
84
|
+
}>(),{
|
|
85
|
+
trigger:'hover',
|
|
86
|
+
shortcut: "Ctrl+Alt+U",
|
|
87
|
+
multiple:false,
|
|
88
|
+
classificationLevel: 0,
|
|
89
|
+
maxShowCount: 20,
|
|
90
|
+
maxHeight: '100%',
|
|
91
|
+
showUserTag: true,
|
|
71
92
|
})
|
|
72
93
|
|
|
94
|
+
// const props = defineProps({
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 可选择用户的用户域ID
|
|
98
|
+
*/
|
|
99
|
+
// realmId: {type: String, required: false},
|
|
100
|
+
/**
|
|
101
|
+
* 弹出下拉选框的快捷键
|
|
102
|
+
*/
|
|
103
|
+
// shortcut: {type: String, required: false, default: "Ctrl+Alt+U"},
|
|
104
|
+
/**
|
|
105
|
+
* 是否多选模式
|
|
106
|
+
*/
|
|
107
|
+
// multiple: {type: Boolean, required: false, default: false},
|
|
108
|
+
/**
|
|
109
|
+
* 密级过滤
|
|
110
|
+
*/
|
|
111
|
+
// classificationLevel: {type: Number, required: false, default: 0},
|
|
112
|
+
/**
|
|
113
|
+
* 自定义筛选过滤条件,可以在此回调方法内使用qUser参数构造自定义的查询条件。例如 qUser => qUser.fullName.startWith('王')
|
|
114
|
+
*/
|
|
115
|
+
// customFilter: {
|
|
116
|
+
// type: Function as PropType<(qUser: InstanceType<typeof QUser>) => BooleanResultExpression>,
|
|
117
|
+
// required: false
|
|
118
|
+
// },
|
|
119
|
+
/**
|
|
120
|
+
* 最多显示多少已选中的用户数量
|
|
121
|
+
*/
|
|
122
|
+
// maxShowCount: {type: Number, required: false, default: 20},
|
|
123
|
+
/**
|
|
124
|
+
* 高度,默认100%
|
|
125
|
+
*/
|
|
126
|
+
// height: {type: [Number, String], required: false, default: '100%'},
|
|
127
|
+
/**
|
|
128
|
+
* 最大高度
|
|
129
|
+
*/
|
|
130
|
+
// maxHeight: {type: [Number, String], required: false},
|
|
131
|
+
// })
|
|
132
|
+
|
|
73
133
|
const localRealmId = ref<string>();
|
|
74
134
|
|
|
135
|
+
const bookmarkDropdown = ref<PopoverInstance>()
|
|
136
|
+
|
|
75
137
|
async function loadRealmId() {
|
|
76
138
|
if (props.realmId) {
|
|
77
139
|
localRealmId.value = props.realmId;
|
|
@@ -126,7 +188,14 @@ function switchUserSelect(u: UserReference) {
|
|
|
126
188
|
selectedUsers.value.splice(index, 1);
|
|
127
189
|
}
|
|
128
190
|
} else {
|
|
129
|
-
selectedUsers.value
|
|
191
|
+
if (Array.isArray(selectedUsers.value)) {
|
|
192
|
+
throw new Error("UserPicker组件设置为单选模式时,v-model必须绑定UserReference对象或者null")
|
|
193
|
+
}
|
|
194
|
+
if(selectedUsers.value!==null&&u.id===selectedUsers.value.id){
|
|
195
|
+
selectedUsers.value = null;
|
|
196
|
+
}else{
|
|
197
|
+
selectedUsers.value = u;
|
|
198
|
+
}
|
|
130
199
|
}
|
|
131
200
|
}
|
|
132
201
|
|
|
@@ -152,6 +221,7 @@ function onPopoverShow() {
|
|
|
152
221
|
}
|
|
153
222
|
|
|
154
223
|
function onPopoverHide() {
|
|
224
|
+
popoverVisible.value = false
|
|
155
225
|
btn.value?.$el.focus();
|
|
156
226
|
}
|
|
157
227
|
|
|
@@ -226,8 +296,15 @@ function showUserSelectDialog() {
|
|
|
226
296
|
popoverVisible.value = false;
|
|
227
297
|
}
|
|
228
298
|
|
|
299
|
+
function handleMouseOver() {
|
|
300
|
+
popoverVisible.value = true
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
229
304
|
const selectedUsersForRender = computed(() => selectedForBinding.value.slice(0, props.maxShowCount))
|
|
230
305
|
|
|
306
|
+
|
|
307
|
+
|
|
231
308
|
watch(()=>props.classificationLevel, ()=>{
|
|
232
309
|
queryParams.value.classificationLevel = props.classificationLevel
|
|
233
310
|
})
|
|
@@ -238,13 +315,22 @@ watch(()=>props.classificationLevel, ()=>{
|
|
|
238
315
|
<ja-scrollbar :max-height="props.maxHeight" :height="props.height" v-bind="$attrs"
|
|
239
316
|
style="overflow-x: hidden;">
|
|
240
317
|
<div class="ja-user-picker__root" v-shortcut:[props.shortcut]="popup">
|
|
318
|
+
|
|
241
319
|
<el-popover ref="bookmarkDropdown" @show="onPopoverShow" @hide="onPopoverHide"
|
|
320
|
+
v-model:visible="popoverVisible"
|
|
242
321
|
width="auto" teleported
|
|
243
|
-
|
|
322
|
+
:trigger="props.trigger"
|
|
323
|
+
>
|
|
244
324
|
<template #reference>
|
|
245
|
-
<
|
|
246
|
-
|
|
247
|
-
|
|
325
|
+
<div style="display: flex;align-items: center;">
|
|
326
|
+
<slot
|
|
327
|
+
v-if="$slots.default"
|
|
328
|
+
name="default"></slot>
|
|
329
|
+
<el-button v-else circle :icon="Plus" :size="'small'" ref="btn"
|
|
330
|
+
@mouseover="handleMouseOver">
|
|
331
|
+
</el-button>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
248
334
|
</template>
|
|
249
335
|
<template #default>
|
|
250
336
|
<div class="ja-user-picker-bookmark__dropdown">
|
|
@@ -301,31 +387,35 @@ watch(()=>props.classificationLevel, ()=>{
|
|
|
301
387
|
ref="userList"
|
|
302
388
|
:total="queryResult.total"
|
|
303
389
|
:empty-text="queryParams.name && queryParams.name.length > minLengthToTriggerUserQuery ? undefined : '无可选常用人员'"
|
|
304
|
-
multiple
|
|
305
390
|
:page-size="pageParams.pageSize"
|
|
306
391
|
:loading="loading"
|
|
307
392
|
:users="users"
|
|
308
393
|
:height="300"
|
|
309
394
|
v-model="selectedUsers"
|
|
310
395
|
v-model:page="pageParams.currentPage"
|
|
396
|
+
:multiple="props.multiple"
|
|
311
397
|
@item-clicked="onArrowKeyDown"
|
|
312
398
|
@arrow-key-down="onArrowKeyDown"></ja-user-list>
|
|
313
399
|
</div>
|
|
314
400
|
</template>
|
|
315
401
|
</el-popover>
|
|
316
|
-
<
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
<
|
|
402
|
+
<div v-if="props.showUserTag" class="ja-user-picker__tag-container">
|
|
403
|
+
<ja-user-info-tag v-for="u in selectedUsersForRender" :key="u.id" :user-id="u.id"
|
|
404
|
+
:full-name="u.fullName" closable :has-avatar="u.hasAvatar"
|
|
405
|
+
@closed="onDeselected">
|
|
406
|
+
</ja-user-info-tag>
|
|
407
|
+
<div class="more-tag" v-if="selectedForBinding.length > props.maxShowCount">
|
|
408
|
+
+{{ selectedForBinding.length - props.maxShowCount }}人
|
|
409
|
+
</div>
|
|
410
|
+
<div v-if="selectedForBinding.length === 0">
|
|
411
|
+
<el-text type="info"> <暂无选中人员></el-text>
|
|
412
|
+
</div>
|
|
325
413
|
</div>
|
|
414
|
+
|
|
326
415
|
<ja-user-select-dialog ref="dialogUserSelector"
|
|
327
416
|
:multiple="props.multiple"
|
|
328
417
|
:realm-id="localRealmId"
|
|
418
|
+
:classificationLevel="props.classificationLevel"
|
|
329
419
|
:customFilter="props.customFilter"
|
|
330
420
|
v-model="selectedUsers"></ja-user-select-dialog>
|
|
331
421
|
</div>
|
|
@@ -337,12 +427,18 @@ watch(()=>props.classificationLevel, ()=>{
|
|
|
337
427
|
<style scoped lang="scss">
|
|
338
428
|
.ja-user-picker__root {
|
|
339
429
|
display: flex;
|
|
340
|
-
flex-wrap: wrap;
|
|
430
|
+
//flex-wrap: wrap;
|
|
341
431
|
justify-content: left;
|
|
342
|
-
align-items: center;
|
|
343
|
-
gap:
|
|
432
|
+
//align-items: center;
|
|
433
|
+
gap: 8px;
|
|
344
434
|
padding: 8px 0;
|
|
345
|
-
|
|
435
|
+
.ja-user-picker__tag-container{
|
|
436
|
+
display: flex;
|
|
437
|
+
flex-wrap: wrap;
|
|
438
|
+
justify-content: left;
|
|
439
|
+
align-items: center;
|
|
440
|
+
gap: 4px;
|
|
441
|
+
}
|
|
346
442
|
.more-tag {
|
|
347
443
|
border-radius: 28px;
|
|
348
444
|
height: 28px;
|