yuang-framework-ui-common 1.0.113 → 1.0.115
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.
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { ref, watch, computed } from 'vue';
|
|
2
|
+
|
|
3
|
+
interface InfoProps {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 通用弹窗选择器基类 Hooks
|
|
10
|
+
* 解决:
|
|
11
|
+
* - v-model 两次赋值
|
|
12
|
+
* - 输入框缓存
|
|
13
|
+
* - 防抖刷新
|
|
14
|
+
* - 加载状态
|
|
15
|
+
* - 弹窗开关
|
|
16
|
+
*/
|
|
17
|
+
export function useBaseDialog(
|
|
18
|
+
props: any,
|
|
19
|
+
emit: any,
|
|
20
|
+
options: {
|
|
21
|
+
/** 单选获取详情接口 */
|
|
22
|
+
selectInfo: (id: string) => Promise<any>;
|
|
23
|
+
/** 批量获取列表接口 */
|
|
24
|
+
selectInfoList: (ids: string[]) => Promise<any[]>;
|
|
25
|
+
/** 配置映射字段 */
|
|
26
|
+
infoProps?: InfoProps;
|
|
27
|
+
}
|
|
28
|
+
) {
|
|
29
|
+
const { selectInfo, selectInfoList, infoProps = { value: 'id', label: 'name' } } = options;
|
|
30
|
+
|
|
31
|
+
// v-model的值
|
|
32
|
+
// const modelValue = defineModel<string>();
|
|
33
|
+
// defineModel 只能在vue文件中使用
|
|
34
|
+
// ✅ 修复:不在 hook 里用 defineModel,改为外部传入
|
|
35
|
+
const modelValue = computed({
|
|
36
|
+
get() {
|
|
37
|
+
if (typeof props.modelValue == 'undefined') {
|
|
38
|
+
throw new Error('需要在props中定义modelValue');
|
|
39
|
+
}
|
|
40
|
+
return props.modelValue;
|
|
41
|
+
},
|
|
42
|
+
set(v) {
|
|
43
|
+
emit('update:modelValue', v);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
// 输入框的值
|
|
47
|
+
const inputValue = ref<string>('');
|
|
48
|
+
// 初始化列表
|
|
49
|
+
const initList = ref<string[]>([]);
|
|
50
|
+
// 是否显示弹出框
|
|
51
|
+
const isShowDialog = ref<boolean>(false);
|
|
52
|
+
// 是否初始化成功
|
|
53
|
+
const isInitSuccess = ref<boolean>(false);
|
|
54
|
+
|
|
55
|
+
// 是否锁定的
|
|
56
|
+
let isLocked = true;
|
|
57
|
+
|
|
58
|
+
// 使用 computed 创建一个只读的、组合了 props 的本地配置,这样更简洁,且具有响应式
|
|
59
|
+
const componentParam = computed(() => ({
|
|
60
|
+
isMultiple: true,
|
|
61
|
+
isShowInput: true,
|
|
62
|
+
isDisabled: false,
|
|
63
|
+
isReadonly: false,
|
|
64
|
+
profileId: 'dev',
|
|
65
|
+
...props.param,
|
|
66
|
+
initList: initList.value
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
// ========== 初始化(防抖版) ==========
|
|
70
|
+
const init = async () => {
|
|
71
|
+
inputValue.value = '';
|
|
72
|
+
initList.value = [];
|
|
73
|
+
isInitSuccess.value = false;
|
|
74
|
+
|
|
75
|
+
if (!modelValue.value) {
|
|
76
|
+
isInitSuccess.value = true;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// 单选
|
|
82
|
+
if (!componentParam.value?.isMultiple) {
|
|
83
|
+
if (modelValue.value !== '0') {
|
|
84
|
+
const infoData = await selectInfo(modelValue.value);
|
|
85
|
+
inputValue.value = infoData[infoProps.label];
|
|
86
|
+
|
|
87
|
+
initList.value = [infoData];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// 多选
|
|
91
|
+
else {
|
|
92
|
+
const idList = modelValue.value.split('|').filter(Boolean);
|
|
93
|
+
if (idList.length) {
|
|
94
|
+
const listData = await selectInfoList(idList);
|
|
95
|
+
inputValue.value = listData.map((item) => item[infoProps.label]).join('、');
|
|
96
|
+
initList.value = listData;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error(err);
|
|
101
|
+
} finally {
|
|
102
|
+
isInitSuccess.value = true;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// 监听组件值变化(解决弹窗/重复赋值/缓存问题)
|
|
107
|
+
watch(
|
|
108
|
+
modelValue,
|
|
109
|
+
() => {
|
|
110
|
+
isLocked = true;
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
if (isLocked) {
|
|
113
|
+
init();
|
|
114
|
+
isLocked = false;
|
|
115
|
+
}
|
|
116
|
+
}, 50);
|
|
117
|
+
},
|
|
118
|
+
{ immediate: true }
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// ========== 弹窗 ==========
|
|
122
|
+
const showDialog = () => {
|
|
123
|
+
isShowDialog.value = true;
|
|
124
|
+
};
|
|
125
|
+
const hideDialog = () => {
|
|
126
|
+
if (!isInitSuccess.value) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
isShowDialog.value = false;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// ========== 选择完成 ==========
|
|
133
|
+
const handleChange = (selections) => {
|
|
134
|
+
modelValue.value = '';
|
|
135
|
+
inputValue.value = '';
|
|
136
|
+
|
|
137
|
+
if (selections.length) {
|
|
138
|
+
if (!componentParam.value?.isMultiple) {
|
|
139
|
+
modelValue.value = selections[0][infoProps.value];
|
|
140
|
+
inputValue.value = selections[0][infoProps.label];
|
|
141
|
+
} else {
|
|
142
|
+
modelValue.value = '|' + selections.map((s) => s[infoProps.value]).join('|');
|
|
143
|
+
inputValue.value = selections.map((s) => s[infoProps.label]).join('、');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
emit('change', selections);
|
|
148
|
+
hideDialog();
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
modelValue,
|
|
153
|
+
inputValue,
|
|
154
|
+
initList,
|
|
155
|
+
isShowDialog,
|
|
156
|
+
isInitSuccess,
|
|
157
|
+
componentParam,
|
|
158
|
+
showDialog,
|
|
159
|
+
hideDialog,
|
|
160
|
+
handleChange
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -14,7 +14,7 @@ const CACHE_KEY = '__global_status_enum_list__';
|
|
|
14
14
|
if (!(window as any)[CACHE_KEY]) {
|
|
15
15
|
(window as any)[CACHE_KEY] = {
|
|
16
16
|
data: ref<StatusEnumItem[]>([]),
|
|
17
|
-
|
|
17
|
+
isLoading: ref(false),
|
|
18
18
|
isLoaded: false, // 🔥 核心:标记是否【真正加载完成】
|
|
19
19
|
promise: null as Promise<any> | null,
|
|
20
20
|
};
|
|
@@ -36,7 +36,7 @@ export const useStatusEnumList = () => {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// 3. 真正开始请求
|
|
39
|
-
cache.
|
|
39
|
+
cache.isLoading.value = true;
|
|
40
40
|
cache.promise = http.get('/framework-api/union/uims-user/getStatusEnumList', {});
|
|
41
41
|
|
|
42
42
|
try {
|
|
@@ -47,7 +47,7 @@ export const useStatusEnumList = () => {
|
|
|
47
47
|
console.error('加载枚举失败', err);
|
|
48
48
|
cache.data.value = [];
|
|
49
49
|
} finally {
|
|
50
|
-
cache.
|
|
50
|
+
cache.isLoading.value = false;
|
|
51
51
|
cache.promise = null;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -59,6 +59,6 @@ export const useStatusEnumList = () => {
|
|
|
59
59
|
|
|
60
60
|
return {
|
|
61
61
|
statusEnumList: cache.data,
|
|
62
|
-
isLoadingEnum: cache.
|
|
62
|
+
isLoadingEnum: cache.isLoading,
|
|
63
63
|
};
|
|
64
64
|
};
|
package/lib/hooks/sso/ssoUser.ts
CHANGED
|
@@ -1,50 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { ref } from 'vue';
|
|
2
|
+
import { http } from '../../../lib/config/httpConfig';
|
|
3
|
+
|
|
4
|
+
// 枚举项类型
|
|
3
5
|
export interface StatusEnumItem {
|
|
4
|
-
// 根据实际返回的字段补充类型,比如:
|
|
5
6
|
value: string;
|
|
6
7
|
name: string;
|
|
7
|
-
// 兼容对象中未显式定义的任意字段,避免 TS 报错
|
|
8
8
|
[key: string]: any;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// ==================== 全局单例缓存(核心) ====================
|
|
12
|
+
const GLOBAL_ENUM_CACHE = '__sso_user_status_enum_list__';
|
|
13
|
+
|
|
14
|
+
// 全局唯一初始化
|
|
15
|
+
if (!(window as any)[GLOBAL_ENUM_CACHE]) {
|
|
16
|
+
(window as any)[GLOBAL_ENUM_CACHE] = {
|
|
17
|
+
data: ref<StatusEnumItem[]>([]),
|
|
18
|
+
isLoading: ref(false),
|
|
19
|
+
isLoaded: false,
|
|
20
|
+
promise: null as Promise<any> | null,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const cache = (window as any)[GLOBAL_ENUM_CACHE];
|
|
13
25
|
|
|
14
|
-
|
|
15
|
-
* 提供“获取状态枚举列表”的能力
|
|
16
|
-
*
|
|
17
|
-
* Vue3 官方明确规定,组合式函数必须以 use 开头(参考:Vue3 组合式函数文档),目的是让开发者一眼识别:“这是一个可复用的组合式逻辑,返回响应式能力”
|
|
18
|
-
*/
|
|
26
|
+
// ==================== Hook 本体 ====================
|
|
19
27
|
export const useStatusEnumList = () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const getStatusEnumList = async () => {
|
|
29
|
+
// 1. 已经加载过 → 直接返回
|
|
30
|
+
if (cache.isLoaded) return cache.data.value;
|
|
31
|
+
|
|
32
|
+
// 2. 正在请求 → 等待同一个请求
|
|
33
|
+
if (cache.promise) {
|
|
34
|
+
await cache.promise;
|
|
35
|
+
return cache.data.value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 3. 真正发起请求
|
|
39
|
+
cache.isLoading.value = true;
|
|
40
|
+
cache.promise = http.get('/framework-api/union/sso-user/getStatusEnumList', {});
|
|
41
|
+
|
|
29
42
|
try {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
statusEnumList.value = res.data.data;
|
|
43
|
+
const res = await cache.promise;
|
|
44
|
+
cache.data.value = res.data.data || [];
|
|
45
|
+
cache.isLoaded = true; // 标记已加载
|
|
34
46
|
} catch (ex) {
|
|
35
47
|
console.error('获取状态枚举列表失败:', ex);
|
|
36
|
-
|
|
48
|
+
cache.data.value = [];
|
|
37
49
|
} finally {
|
|
38
|
-
|
|
50
|
+
cache.isLoading.value = false;
|
|
51
|
+
cache.promise = null;
|
|
39
52
|
}
|
|
53
|
+
|
|
54
|
+
return cache.data.value;
|
|
40
55
|
};
|
|
41
56
|
|
|
42
|
-
//
|
|
57
|
+
// 自动加载
|
|
43
58
|
getStatusEnumList();
|
|
44
59
|
|
|
45
|
-
//
|
|
60
|
+
// 保持你原来的返回结构,不用改业务代码
|
|
46
61
|
return {
|
|
47
|
-
statusEnumList,
|
|
48
|
-
isLoadingEnum
|
|
62
|
+
statusEnumList: cache.data,
|
|
63
|
+
isLoadingEnum: cache.isLoading,
|
|
49
64
|
};
|
|
50
65
|
};
|
|
@@ -1,50 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { ref } from 'vue';
|
|
2
|
+
import { http } from '../../../lib/config/httpConfig';
|
|
3
|
+
|
|
4
|
+
// 枚举项类型
|
|
3
5
|
export interface StatusEnumItem {
|
|
4
|
-
// 根据实际返回的字段补充类型,比如:
|
|
5
6
|
value: string;
|
|
6
7
|
name: string;
|
|
7
|
-
// 兼容对象中未显式定义的任意字段,避免 TS 报错
|
|
8
8
|
[key: string]: any;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// ==================== 全局单例缓存(核心) ====================
|
|
12
|
+
const GLOBAL_ENUM_CACHE = '__uims_user_status_enum_list__';
|
|
13
|
+
|
|
14
|
+
// 全局唯一初始化
|
|
15
|
+
if (!(window as any)[GLOBAL_ENUM_CACHE]) {
|
|
16
|
+
(window as any)[GLOBAL_ENUM_CACHE] = {
|
|
17
|
+
data: ref<StatusEnumItem[]>([]),
|
|
18
|
+
isLoading: ref(false),
|
|
19
|
+
isLoaded: false,
|
|
20
|
+
promise: null as Promise<any> | null,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const cache = (window as any)[GLOBAL_ENUM_CACHE];
|
|
13
25
|
|
|
14
|
-
|
|
15
|
-
* 提供“获取状态枚举列表”的能力
|
|
16
|
-
*
|
|
17
|
-
* Vue3 官方明确规定,组合式函数必须以 use 开头(参考:Vue3 组合式函数文档),目的是让开发者一眼识别:“这是一个可复用的组合式逻辑,返回响应式能力”
|
|
18
|
-
*/
|
|
26
|
+
// ==================== Hook 本体 ====================
|
|
19
27
|
export const useStatusEnumList = () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const getStatusEnumList = async () => {
|
|
29
|
+
// 1. 已经加载过 → 直接返回
|
|
30
|
+
if (cache.isLoaded) return cache.data.value;
|
|
31
|
+
|
|
32
|
+
// 2. 正在请求 → 等待同一个请求
|
|
33
|
+
if (cache.promise) {
|
|
34
|
+
await cache.promise;
|
|
35
|
+
return cache.data.value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 3. 真正发起请求
|
|
39
|
+
cache.isLoading.value = true;
|
|
40
|
+
cache.promise = http.get('/framework-api/union/uims-user/getStatusEnumList', {});
|
|
41
|
+
|
|
29
42
|
try {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
statusEnumList.value = res.data.data;
|
|
43
|
+
const res = await cache.promise;
|
|
44
|
+
cache.data.value = res.data.data || [];
|
|
45
|
+
cache.isLoaded = true; // 标记已加载
|
|
34
46
|
} catch (ex) {
|
|
35
47
|
console.error('获取状态枚举列表失败:', ex);
|
|
36
|
-
|
|
48
|
+
cache.data.value = [];
|
|
37
49
|
} finally {
|
|
38
|
-
|
|
50
|
+
cache.isLoading.value = false;
|
|
51
|
+
cache.promise = null;
|
|
39
52
|
}
|
|
53
|
+
|
|
54
|
+
return cache.data.value;
|
|
40
55
|
};
|
|
41
56
|
|
|
42
|
-
//
|
|
57
|
+
// 自动加载
|
|
43
58
|
getStatusEnumList();
|
|
44
59
|
|
|
45
|
-
//
|
|
60
|
+
// 保持你原来的返回结构,不用改业务代码
|
|
46
61
|
return {
|
|
47
|
-
statusEnumList,
|
|
48
|
-
isLoadingEnum
|
|
62
|
+
statusEnumList: cache.data,
|
|
63
|
+
isLoadingEnum: cache.isLoading,
|
|
49
64
|
};
|
|
50
65
|
};
|