befly-admin 3.13.2 → 3.13.4
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/package.json +4 -4
- package/src/components/pagedTableDetail.vue +6 -12
- package/src/layouts/default.vue +6 -5
- package/src/plugins/http.js +77 -142
- package/src/plugins/router.js +7 -9
- package/src/plugins/storage.js +0 -139
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly-admin",
|
|
3
|
-
"version": "3.13.
|
|
4
|
-
"gitHead": "
|
|
3
|
+
"version": "3.13.4",
|
|
4
|
+
"gitHead": "aa84ccde6c76bc45727464c30d4680c379815110",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Befly Admin - 基于 Vue3 + TDesign Vue Next 的后台管理系统",
|
|
7
7
|
"files": [
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"axios": "^1.13.5",
|
|
32
|
-
"befly-admin-ui": "1.8.
|
|
33
|
-
"befly-vite": "^1.5.
|
|
32
|
+
"befly-admin-ui": "1.8.15",
|
|
33
|
+
"befly-vite": "^1.5.1",
|
|
34
34
|
"pinia": "^3.0.4",
|
|
35
35
|
"tdesign-icons-vue-next": "^0.4.0",
|
|
36
36
|
"tdesign-vue-next": "^1.18.2",
|
|
@@ -296,10 +296,7 @@ async function loadList(options) {
|
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
const res = await $Http.
|
|
300
|
-
dropValues: listEndpoint.dropValues,
|
|
301
|
-
dropKeyValue: listEndpoint.dropKeyValue
|
|
302
|
-
});
|
|
299
|
+
const res = await $Http.json(listEndpoint.path, data, listEndpoint.dropValues, listEndpoint.dropKeyValue);
|
|
303
300
|
|
|
304
301
|
// 并发保护:旧请求返回后不应覆盖新请求的状态
|
|
305
302
|
if (seq !== requestSeq) {
|
|
@@ -332,7 +329,7 @@ async function loadList(options) {
|
|
|
332
329
|
if (seq !== requestSeq) {
|
|
333
330
|
return;
|
|
334
331
|
}
|
|
335
|
-
|
|
332
|
+
MessagePlugin.error("加载数据失败");
|
|
336
333
|
} finally {
|
|
337
334
|
if (seq === requestSeq) {
|
|
338
335
|
$Data.loading = false;
|
|
@@ -352,13 +349,13 @@ async function reload(options) {
|
|
|
352
349
|
|
|
353
350
|
function onPageChange(info) {
|
|
354
351
|
$Data.pager.currentPage = info.currentPage;
|
|
355
|
-
|
|
352
|
+
reload({ keepSelection: true });
|
|
356
353
|
}
|
|
357
354
|
|
|
358
355
|
function onPageSizeChange(info) {
|
|
359
356
|
$Data.pager.limit = info.pageSize;
|
|
360
357
|
$Data.pager.currentPage = 1;
|
|
361
|
-
|
|
358
|
+
reload({ keepSelection: false });
|
|
362
359
|
}
|
|
363
360
|
|
|
364
361
|
function onActiveChange(value, context) {
|
|
@@ -463,10 +460,7 @@ async function deleteRow(row) {
|
|
|
463
460
|
}
|
|
464
461
|
}
|
|
465
462
|
|
|
466
|
-
await $Http.
|
|
467
|
-
dropValues: ep.dropValues,
|
|
468
|
-
dropKeyValue: ep.dropKeyValue
|
|
469
|
-
});
|
|
463
|
+
await $Http.json(ep.path, data, ep.dropValues, ep.dropKeyValue);
|
|
470
464
|
|
|
471
465
|
MessagePlugin.success("删除成功");
|
|
472
466
|
destroy();
|
|
@@ -509,7 +503,7 @@ defineExpose({
|
|
|
509
503
|
|
|
510
504
|
onMounted(() => {
|
|
511
505
|
if (!props.autoLoad) return;
|
|
512
|
-
|
|
506
|
+
reload({ keepSelection: false });
|
|
513
507
|
});
|
|
514
508
|
</script>
|
|
515
509
|
|
package/src/layouts/default.vue
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
<span>系统设置</span>
|
|
46
46
|
</div>
|
|
47
47
|
<div class="footer-user">
|
|
48
|
-
<t-upload :action="$Config.uploadUrl" :headers="
|
|
48
|
+
<t-upload :action="$Config.uploadUrl" :headers="uploadHeaders" :show-upload-list="false" accept="image/*" @success="onAvatarUploadSuccess">
|
|
49
49
|
<div class="user-avatar" :class="{ 'has-avatar': $Data.userInfo.avatar }">
|
|
50
50
|
<img v-if="$Data.userInfo.avatar" :src="$Data.userInfo.avatar" alt="avatar" />
|
|
51
51
|
<UserIcon v-else style="width: 16px; height: 16px; color: #fff" />
|
|
@@ -82,6 +82,7 @@ import { reactive } from "vue";
|
|
|
82
82
|
|
|
83
83
|
const router = useRouter();
|
|
84
84
|
const route = useRoute();
|
|
85
|
+
const uploadHeaders = { Authorization: localStorage.getItem("yicode-token") || "" };
|
|
85
86
|
|
|
86
87
|
const loginPath = "/core/login";
|
|
87
88
|
|
|
@@ -129,7 +130,7 @@ const $Data = reactive({
|
|
|
129
130
|
|
|
130
131
|
async function fetchUserMenus() {
|
|
131
132
|
try {
|
|
132
|
-
const { data } = await $Http.
|
|
133
|
+
const { data } = await $Http.json("/core/menu/all");
|
|
133
134
|
const lists = Array.isArray(data?.lists) ? data.lists : [];
|
|
134
135
|
|
|
135
136
|
const normalizedLists = lists.map((menu) => {
|
|
@@ -144,7 +145,7 @@ async function fetchUserMenus() {
|
|
|
144
145
|
$Data.userMenus = treeResult.tree;
|
|
145
146
|
setActiveMenu();
|
|
146
147
|
} catch (error) {
|
|
147
|
-
|
|
148
|
+
MessagePlugin.error(error.msg || error.message || "获取用户菜单失败");
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
|
|
@@ -213,7 +214,7 @@ async function handleLogout() {
|
|
|
213
214
|
}
|
|
214
215
|
|
|
215
216
|
try {
|
|
216
|
-
|
|
217
|
+
localStorage.removeItem("yicode-token");
|
|
217
218
|
await router.push(loginPath);
|
|
218
219
|
MessagePlugin.success("退出成功");
|
|
219
220
|
destroy();
|
|
@@ -239,7 +240,7 @@ function handleSettings() {
|
|
|
239
240
|
function onAvatarUploadSuccess(res) {
|
|
240
241
|
if (res.response?.code === 0 && res.response?.data?.url) {
|
|
241
242
|
$Data.userInfo.avatar = res.response.data.url;
|
|
242
|
-
|
|
243
|
+
MessagePlugin.success("头像上传成功");
|
|
243
244
|
}
|
|
244
245
|
}
|
|
245
246
|
|
package/src/plugins/http.js
CHANGED
|
@@ -1,157 +1,92 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const params = new URLSearchParams();
|
|
15
|
-
for (const key of Object.keys(queryData)) {
|
|
16
|
-
const value = queryData[key];
|
|
17
|
-
|
|
18
|
-
if (value === null || value === undefined) {
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (Array.isArray(value)) {
|
|
23
|
-
for (const item of value) {
|
|
24
|
-
if (item === null || item === undefined) {
|
|
25
|
-
continue;
|
|
1
|
+
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
|
|
2
|
+
async function requestPost(tokenGetter, url, data, dropValues, dropKeyValue) {
|
|
3
|
+
try {
|
|
4
|
+
const fullUrl = `${API_BASE_URL}${url}`;
|
|
5
|
+
const isForm = data instanceof FormData;
|
|
6
|
+
const dropList = Array.isArray(dropValues) ? dropValues : [null, undefined];
|
|
7
|
+
const dropKeyMap = dropKeyValue && typeof dropKeyValue === "object" ? dropKeyValue : {};
|
|
8
|
+
const shouldDrop = (key, value) => {
|
|
9
|
+
for (const dropValue of dropList) {
|
|
10
|
+
if (Object.is(value, dropValue)) {
|
|
11
|
+
return true;
|
|
26
12
|
}
|
|
27
|
-
params.append(key, String(item));
|
|
28
13
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
finalUrl = buildUrl(finalUrl, queryData);
|
|
59
|
-
} else {
|
|
60
|
-
finalUrl = buildUrl(finalUrl);
|
|
61
|
-
|
|
62
|
-
let body = data;
|
|
63
|
-
if (data === undefined) {
|
|
64
|
-
body = {};
|
|
14
|
+
if (Array.isArray(dropKeyMap[key])) {
|
|
15
|
+
for (const dropValue of dropKeyMap[key]) {
|
|
16
|
+
if (Object.is(value, dropValue)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
};
|
|
23
|
+
let payloadData = data ?? {};
|
|
24
|
+
if (isForm) {
|
|
25
|
+
const nextForm = new FormData();
|
|
26
|
+
for (const entry of payloadData.entries()) {
|
|
27
|
+
const key = entry[0];
|
|
28
|
+
const value = entry[1];
|
|
29
|
+
if (!shouldDrop(key, value)) {
|
|
30
|
+
nextForm.append(key, value);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
payloadData = nextForm;
|
|
34
|
+
} else if (payloadData && typeof payloadData === "object") {
|
|
35
|
+
const nextData = {};
|
|
36
|
+
for (const key of Object.keys(payloadData)) {
|
|
37
|
+
const value = payloadData[key];
|
|
38
|
+
if (!shouldDrop(key, value)) {
|
|
39
|
+
nextData[key] = value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
payloadData = nextData;
|
|
65
43
|
}
|
|
66
|
-
|
|
67
|
-
if (!
|
|
68
|
-
const cleaned = typeof body === "object" && body !== null && !Array.isArray(body) ? cleanParams(body, options?.dropValues ?? [], options?.dropKeyValue) : body;
|
|
69
|
-
const finalBody = typeof cleaned === "object" && cleaned !== null && !Array.isArray(cleaned) && Object.keys(cleaned).length === 0 ? {} : cleaned;
|
|
44
|
+
const headers = new Headers();
|
|
45
|
+
if (!isForm) {
|
|
70
46
|
headers.set("Content-Type", "application/json");
|
|
71
|
-
requestConfig.body = JSON.stringify(finalBody);
|
|
72
|
-
} else {
|
|
73
|
-
requestConfig.body = body;
|
|
74
47
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
requestConfig.headers = headers;
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const response = await fetch(finalUrl, requestConfig);
|
|
82
|
-
if (!response.ok) {
|
|
83
|
-
throw new Error(`HTTP ${response.status}`, {
|
|
84
|
-
cause: null,
|
|
85
|
-
code: "runtime",
|
|
86
|
-
subsystem: "adminHttp",
|
|
87
|
-
operation: "request"
|
|
88
|
-
});
|
|
48
|
+
const tokenValue = typeof tokenGetter === "function" ? tokenGetter() : "";
|
|
49
|
+
if (tokenValue) {
|
|
50
|
+
headers.set("Authorization", "Bearer " + tokenValue);
|
|
89
51
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
52
|
+
const init = {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: headers,
|
|
55
|
+
body: isForm ? payloadData : JSON.stringify(payloadData ?? {})
|
|
56
|
+
};
|
|
57
|
+
const res = await fetch(fullUrl, init);
|
|
58
|
+
let payload;
|
|
59
|
+
try {
|
|
60
|
+
payload = await res.json();
|
|
61
|
+
} catch {
|
|
62
|
+
if (res.ok) {
|
|
63
|
+
return Promise.reject({ msg: "响应解析失败" });
|
|
64
|
+
}
|
|
65
|
+
payload = {
|
|
66
|
+
code: res.status,
|
|
67
|
+
msg: `请求失败:HTTP ${res.status}`
|
|
68
|
+
};
|
|
99
69
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
type: "api",
|
|
106
|
-
apiCode: res.code,
|
|
107
|
-
apiData: res.data
|
|
108
|
-
},
|
|
109
|
-
code: "runtime",
|
|
110
|
-
subsystem: "adminHttp",
|
|
111
|
-
operation: "request"
|
|
70
|
+
if (payload.code !== 0) {
|
|
71
|
+
return Promise.reject({
|
|
72
|
+
msg: payload.msg ?? "请求失败",
|
|
73
|
+
code: payload.code,
|
|
74
|
+
detail: payload.detail
|
|
112
75
|
});
|
|
113
76
|
}
|
|
114
|
-
|
|
115
|
-
return res;
|
|
77
|
+
return Promise.resolve(payload);
|
|
116
78
|
} catch (error) {
|
|
117
|
-
if (error
|
|
118
|
-
|
|
79
|
+
if (error && typeof error === "object") {
|
|
80
|
+
return Promise.reject(error);
|
|
119
81
|
}
|
|
120
|
-
|
|
121
|
-
throw new Error("网络连接失败", {
|
|
122
|
-
cause: error,
|
|
123
|
-
code: "runtime",
|
|
124
|
-
subsystem: "adminHttp",
|
|
125
|
-
operation: "request"
|
|
126
|
-
});
|
|
82
|
+
return Promise.reject({ msg: "网络连接失败" });
|
|
127
83
|
}
|
|
128
84
|
}
|
|
129
|
-
|
|
130
|
-
async function httpGet(url, data, options) {
|
|
131
|
-
return request("GET", url, data, options);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function httpPost(url, data, options) {
|
|
135
|
-
return request("POST", url, data, options);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* 统一的 HTTP 请求对象(仅支持 GET 和 POST)
|
|
140
|
-
* - 调用方式:$Http.get(url, data?, options?) / $Http.post(url, data?, options?)
|
|
141
|
-
* - 重要行为:
|
|
142
|
-
* - 未传 data / 清洗为空时:仍会发送空对象(GET: params={}, POST: body={})
|
|
143
|
-
* - 原因:部分后端接口会基于“参数结构存在”触发默认逻辑/签名校验/中间件约束;
|
|
144
|
-
* 因此这里不做“省略空对象”的优化。
|
|
145
|
-
* - 传入 plain object 时:默认强制移除 null / undefined
|
|
146
|
-
* - 可选参数清洗(第三参,且可与 axios config 混用):
|
|
147
|
-
* - dropValues:全局丢弃值列表(仅对未配置 dropKeyValue 的 key 生效)
|
|
148
|
-
* - dropKeyValue:按 key 精确配置丢弃值列表(覆盖全局 dropValues)
|
|
149
|
-
*
|
|
150
|
-
* 例子:保留 page=0,但丢弃 keyword="",并且其它字段应用全局 dropValues
|
|
151
|
-
* - dropValues: [0, ""]
|
|
152
|
-
* - dropKeyValue: { page: [], keyword: [""] }
|
|
153
|
-
*/
|
|
154
85
|
export const $Http = {
|
|
155
|
-
|
|
156
|
-
|
|
86
|
+
json: async (url, data, dropValues, dropKeyValue) => {
|
|
87
|
+
return requestPost(() => localStorage.getItem("yicode-token") ?? "", url, data ?? {}, dropValues, dropKeyValue);
|
|
88
|
+
},
|
|
89
|
+
form: async (url, formData, dropValues, dropKeyValue) => {
|
|
90
|
+
return requestPost(() => localStorage.getItem("yicode-token") ?? "", url, formData, dropValues, dropKeyValue);
|
|
91
|
+
}
|
|
157
92
|
};
|
package/src/plugins/router.js
CHANGED
|
@@ -2,8 +2,6 @@ import { Layouts } from "befly-vite";
|
|
|
2
2
|
import { createRouter, createWebHashHistory } from "vue-router";
|
|
3
3
|
import { routes } from "vue-router/auto-routes";
|
|
4
4
|
|
|
5
|
-
import { $Storage } from "./storage";
|
|
6
|
-
|
|
7
5
|
// 应用自定义布局系统(同时可选注入根路径重定向)
|
|
8
6
|
const finalRoutes = Layouts(routes, $Config.homePath, (layoutName) => {
|
|
9
7
|
if (layoutName === "default") {
|
|
@@ -23,31 +21,31 @@ export const $Router = createRouter({
|
|
|
23
21
|
});
|
|
24
22
|
|
|
25
23
|
// 路由守卫 - 基础鉴权(最小实现:public 放行;未登录跳登录;已登录访问登录页跳首页)
|
|
26
|
-
$Router.beforeEach((to, _from
|
|
27
|
-
const token =
|
|
24
|
+
$Router.beforeEach((to, _from) => {
|
|
25
|
+
const token = localStorage.getItem("yicode-token");
|
|
28
26
|
const toPath = to.path;
|
|
29
27
|
|
|
30
28
|
// 根路径:按是否登录分流(兜底,避免 / 永远重定向到首页)
|
|
31
29
|
if (toPath === "/") {
|
|
32
|
-
return
|
|
30
|
+
return token ? $Config.homePath : $Config.loginPath;
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
// 公开路由放行
|
|
36
34
|
if (to.meta?.["public"] === true) {
|
|
37
|
-
return
|
|
35
|
+
return true;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
// 未登录访问非公开路由 → 登录页
|
|
41
39
|
if (!token && toPath !== $Config.loginPath) {
|
|
42
|
-
return
|
|
40
|
+
return $Config.loginPath;
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
// 已登录访问登录页 → 首页
|
|
46
44
|
if (token && toPath === $Config.loginPath) {
|
|
47
|
-
return
|
|
45
|
+
return $Config.homePath;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
return true;
|
|
51
49
|
});
|
|
52
50
|
|
|
53
51
|
// 路由就绪后处理
|
package/src/plugins/storage.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage 封装
|
|
3
|
-
* 支持命名空间隔离,统一管理 localStorage 和 sessionStorage
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// 获取命名空间
|
|
7
|
-
const NAMESPACE = import.meta.env["VITE_STORAGE_NAMESPACE"] || "befly";
|
|
8
|
-
|
|
9
|
-
class MemoryStorage {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.map = new Map();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
get length() {
|
|
15
|
-
return this.map.size;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
clear() {
|
|
19
|
-
for (const key of this.map.keys()) {
|
|
20
|
-
delete this[key];
|
|
21
|
-
}
|
|
22
|
-
this.map.clear();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
getItem(key) {
|
|
26
|
-
return this.map.get(key) ?? null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
key(index) {
|
|
30
|
-
const keys = Array.from(this.map.keys());
|
|
31
|
-
return keys[index] ?? null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
removeItem(key) {
|
|
35
|
-
this.map.delete(key);
|
|
36
|
-
delete this[key];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
setItem(key, value) {
|
|
40
|
-
this.map.set(key, value);
|
|
41
|
-
this[key] = value;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getBrowserStorage(kind) {
|
|
46
|
-
const win = globalThis;
|
|
47
|
-
const storage = win.window?.[kind];
|
|
48
|
-
if (storage) {
|
|
49
|
-
return storage;
|
|
50
|
-
}
|
|
51
|
-
return new MemoryStorage();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 存储操作类
|
|
56
|
-
*/
|
|
57
|
-
class StorageManager {
|
|
58
|
-
constructor(namespace = NAMESPACE) {
|
|
59
|
-
this.localStorage = getBrowserStorage("localStorage");
|
|
60
|
-
this.sessionStorage = getBrowserStorage("sessionStorage");
|
|
61
|
-
this.namespace = namespace;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
getKey(key) {
|
|
65
|
-
return `${this.namespace}:${key}`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
createStorageOps(storage) {
|
|
69
|
-
return {
|
|
70
|
-
set: (key, value) => {
|
|
71
|
-
try {
|
|
72
|
-
const fullKey = this.getKey(key);
|
|
73
|
-
const serializedValue = JSON.stringify(value);
|
|
74
|
-
storage.setItem(fullKey, serializedValue);
|
|
75
|
-
} catch {
|
|
76
|
-
// ignore
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
get: (key, defaultValue) => {
|
|
81
|
-
try {
|
|
82
|
-
const fullKey = this.getKey(key);
|
|
83
|
-
const value = storage.getItem(fullKey);
|
|
84
|
-
if (value === null) {
|
|
85
|
-
return defaultValue ?? null;
|
|
86
|
-
}
|
|
87
|
-
return JSON.parse(value);
|
|
88
|
-
} catch {
|
|
89
|
-
return defaultValue ?? null;
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
remove: (key) => {
|
|
94
|
-
try {
|
|
95
|
-
const fullKey = this.getKey(key);
|
|
96
|
-
storage.removeItem(fullKey);
|
|
97
|
-
} catch {
|
|
98
|
-
// ignore
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
clear: () => {
|
|
103
|
-
try {
|
|
104
|
-
const keys = Object.keys(storage);
|
|
105
|
-
const prefix = `${this.namespace}:`;
|
|
106
|
-
keys.forEach((key) => {
|
|
107
|
-
if (key.startsWith(prefix)) {
|
|
108
|
-
storage.removeItem(key);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
} catch {
|
|
112
|
-
// ignore
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
has: (key) => {
|
|
117
|
-
const fullKey = this.getKey(key);
|
|
118
|
-
return storage.getItem(fullKey) !== null;
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
keys: () => {
|
|
122
|
-
const keys = Object.keys(storage);
|
|
123
|
-
const prefix = `${this.namespace}:`;
|
|
124
|
-
return keys.filter((key) => key.startsWith(prefix)).map((key) => key.substring(prefix.length));
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
get local() {
|
|
130
|
-
return this.createStorageOps(this.localStorage);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
get session() {
|
|
134
|
-
return this.createStorageOps(this.sessionStorage);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 导出单例
|
|
139
|
-
export const $Storage = new StorageManager();
|