@@ -107,29 +107,6 @@ class PreferenceManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// const debounceWaterState = useDebounceFn(() => {
|
||||
// const newFlattenedState = flattenObject(this.state);
|
||||
// for (const k in newFlattenedState) {
|
||||
// const key = k as FlattenObjectKeys<Preferences>;
|
||||
// this.flattenedState[key] = newFlattenedState[key];
|
||||
// }
|
||||
// this.savePreferences(this.state);
|
||||
// }, 16);
|
||||
|
||||
// const debounceWaterFlattenedState = useDebounceFn(
|
||||
// (val: Flatten<Preferences>) => {
|
||||
// this.updateState(val);
|
||||
// this.savePreferences(this.state);
|
||||
// },
|
||||
// 16,
|
||||
// );
|
||||
|
||||
// 监听 state 的变化
|
||||
// watch(this.state, debounceWaterState, { deep: true });
|
||||
|
||||
// 监听 flattenedState 的变化并触发 set 方法
|
||||
// watch(this.flattenedState, debounceWaterFlattenedState, { deep: true });
|
||||
|
||||
// 监听断点,判断是否移动端
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind);
|
||||
const isMobile = breakpoints.smaller('md');
|
||||
@@ -210,16 +187,6 @@ class PreferenceManager {
|
||||
updateCSSVariables(colorVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新状态
|
||||
* 将新的扁平对象转换为嵌套对象,并与当前状态合并。
|
||||
* @param {FlattenObject<Preferences>} newValue - 新的扁平对象
|
||||
*/
|
||||
// private updateState(newValue: Flatten<Preferences>) {
|
||||
// const nestObj = nestedObject(newValue, 2);
|
||||
// Object.assign(this.state, merge(nestObj, this.state));
|
||||
// }
|
||||
|
||||
/**
|
||||
* 更新主题
|
||||
* @param preferences - 当前偏好设置对象,它的主题值将被用来设置文档的主题。
|
||||
@@ -278,9 +245,11 @@ class PreferenceManager {
|
||||
}
|
||||
}
|
||||
|
||||
// public getFlatPreferences() {
|
||||
// return this.flattenedState;
|
||||
// }
|
||||
clearCache() {
|
||||
[STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach((key) => {
|
||||
this.cache?.removeItem(key);
|
||||
});
|
||||
}
|
||||
|
||||
public getInitialPreferences() {
|
||||
return this.initialPreferences;
|
||||
@@ -308,8 +277,8 @@ class PreferenceManager {
|
||||
// 加载并合并当前存储的偏好设置
|
||||
const mergedPreference = merge(
|
||||
{},
|
||||
this.loadCachedPreferences(),
|
||||
this.initialPreferences,
|
||||
overrides,
|
||||
this.loadCachedPreferences() || defaultPreferences,
|
||||
);
|
||||
|
||||
// 更新偏好设置
|
||||
@@ -352,8 +321,6 @@ class PreferenceManager {
|
||||
|
||||
Object.assign(this.state, mergedState);
|
||||
|
||||
// Object.assign(this.flattenedState, flattenObject(this.state));
|
||||
|
||||
// 根据更新的键值执行相应的操作
|
||||
this.handleUpdates(updates);
|
||||
this.savePreferences(this.state);
|
||||
|
@@ -1,127 +0,0 @@
|
||||
import type { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { AxiosCanceler } from './canceler';
|
||||
|
||||
describe('axiosCanceler', () => {
|
||||
let axiosCanceler: AxiosCanceler;
|
||||
|
||||
beforeEach(() => {
|
||||
axiosCanceler = new AxiosCanceler();
|
||||
});
|
||||
|
||||
it('should generate a unique request key', () => {
|
||||
const config: AxiosRequestConfig = {
|
||||
data: { name: 'test' },
|
||||
method: 'get',
|
||||
params: { id: 1 },
|
||||
url: '/test',
|
||||
};
|
||||
const requestKey = axiosCanceler.getRequestKey(config);
|
||||
expect(requestKey).toBe('get:/test:{"id":1}:{"name":"test"}');
|
||||
});
|
||||
|
||||
it('should add a request and create an AbortController', () => {
|
||||
const config: InternalAxiosRequestConfig = {
|
||||
data: { name: 'test' },
|
||||
method: 'get',
|
||||
params: { id: 1 },
|
||||
url: '/test',
|
||||
} as InternalAxiosRequestConfig;
|
||||
|
||||
const updatedConfig = axiosCanceler.addRequest(config);
|
||||
expect(updatedConfig.signal).toBeInstanceOf(AbortSignal);
|
||||
});
|
||||
|
||||
it('should cancel an existing request if a duplicate is added', () => {
|
||||
const config: InternalAxiosRequestConfig = {
|
||||
data: { name: 'test' },
|
||||
method: 'get',
|
||||
params: { id: 1 },
|
||||
url: '/test',
|
||||
} as InternalAxiosRequestConfig;
|
||||
|
||||
axiosCanceler.addRequest(config);
|
||||
const controller = axiosCanceler.pending.get(
|
||||
'get:/test:{"id":1}:{"name":"test"}',
|
||||
);
|
||||
expect(controller).toBeDefined();
|
||||
|
||||
if (controller) {
|
||||
const spy = vi.spyOn(controller, 'abort');
|
||||
|
||||
axiosCanceler.addRequest(config);
|
||||
expect(spy).toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
|
||||
it('should remove a request', () => {
|
||||
const config: AxiosRequestConfig = {
|
||||
data: { name: 'test' },
|
||||
method: 'get',
|
||||
params: { id: 1 },
|
||||
url: '/test',
|
||||
};
|
||||
|
||||
axiosCanceler.addRequest(config as InternalAxiosRequestConfig);
|
||||
axiosCanceler.removeRequest(config);
|
||||
expect(axiosCanceler.pending.size).toBe(0);
|
||||
});
|
||||
|
||||
it('should remove all pending requests', () => {
|
||||
const config1: InternalAxiosRequestConfig = {
|
||||
data: { name: 'test1' },
|
||||
method: 'get',
|
||||
params: { id: 1 },
|
||||
url: '/test1',
|
||||
} as InternalAxiosRequestConfig;
|
||||
|
||||
const config2: InternalAxiosRequestConfig = {
|
||||
data: { name: 'test2' },
|
||||
method: 'get',
|
||||
params: { id: 2 },
|
||||
url: '/test2',
|
||||
} as InternalAxiosRequestConfig;
|
||||
|
||||
axiosCanceler.addRequest(config1);
|
||||
axiosCanceler.addRequest(config2);
|
||||
|
||||
axiosCanceler.removeAllPending();
|
||||
expect(axiosCanceler.pending.size).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle empty config gracefully', () => {
|
||||
const config = {} as InternalAxiosRequestConfig;
|
||||
const updatedConfig = axiosCanceler.addRequest(config);
|
||||
expect(updatedConfig.signal).toBeInstanceOf(AbortSignal);
|
||||
});
|
||||
|
||||
it('should handle undefined params and data gracefully', () => {
|
||||
const config: InternalAxiosRequestConfig = {
|
||||
method: 'get',
|
||||
url: '/test',
|
||||
} as InternalAxiosRequestConfig;
|
||||
|
||||
const requestKey = axiosCanceler.getRequestKey(config);
|
||||
expect(requestKey).toBe('get:/test:{}:{}');
|
||||
});
|
||||
|
||||
it('should not abort if no controller exists for the request key', () => {
|
||||
const config: InternalAxiosRequestConfig = {
|
||||
data: { name: 'test' },
|
||||
method: 'get',
|
||||
params: { id: 1 },
|
||||
url: '/test',
|
||||
} as InternalAxiosRequestConfig;
|
||||
|
||||
const requestKey = axiosCanceler.getRequestKey(config);
|
||||
const spy = vi.spyOn(AbortController.prototype, 'abort');
|
||||
|
||||
axiosCanceler.addRequest(config);
|
||||
axiosCanceler.pending.delete(requestKey);
|
||||
axiosCanceler.addRequest(config);
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
@@ -1,52 +0,0 @@
|
||||
import type {
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
InternalAxiosRequestConfig,
|
||||
} from 'axios';
|
||||
|
||||
class AxiosCanceler {
|
||||
public pending: Map<string, AbortController> = new Map();
|
||||
|
||||
// 添加请求
|
||||
public addRequest(
|
||||
config: InternalAxiosRequestConfig,
|
||||
): InternalAxiosRequestConfig {
|
||||
const requestKey = this.getRequestKey(config);
|
||||
if (this.pending.has(requestKey)) {
|
||||
// 如果存在相同的请求,取消前一个请求
|
||||
const controller = this.pending.get(requestKey);
|
||||
controller?.abort();
|
||||
}
|
||||
|
||||
// 创建新的AbortController并添加到pending中
|
||||
const controller = new AbortController();
|
||||
config.signal = controller.signal;
|
||||
this.pending.set(requestKey, controller);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// 生成请求的唯一标识
|
||||
public getRequestKey(config: AxiosRequestConfig): string {
|
||||
const { data = {}, method, params = {}, url } = config;
|
||||
return `${method}:${url}:${JSON.stringify(params)}:${JSON.stringify(data)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有等待中的请求
|
||||
*/
|
||||
public removeAllPending(): void {
|
||||
for (const [, abortController] of this.pending) {
|
||||
abortController?.abort();
|
||||
}
|
||||
this.pending.clear();
|
||||
}
|
||||
|
||||
// 移除请求
|
||||
public removeRequest(config: AxiosRequestConfig | AxiosResponse): void {
|
||||
const requestKey = this.getRequestKey(config);
|
||||
this.pending.delete(requestKey);
|
||||
}
|
||||
}
|
||||
|
||||
export { AxiosCanceler };
|
@@ -12,7 +12,6 @@ import { merge } from '@vben-core/toolkit';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
import { AxiosCanceler } from './modules/canceler';
|
||||
import { FileDownloader } from './modules/downloader';
|
||||
import { InterceptorManager } from './modules/interceptor';
|
||||
import { FileUploader } from './modules/uploader';
|
||||
@@ -97,8 +96,6 @@ class RequestClient {
|
||||
private setupInterceptors() {
|
||||
// 默认拦截器
|
||||
this.setupAuthorizationInterceptor();
|
||||
// 设置取消请求的拦截器
|
||||
this.setupCancelerInterceptor();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,28 +165,6 @@ class RequestClient {
|
||||
throw error.response ? error.response.data : error;
|
||||
}
|
||||
}
|
||||
|
||||
public setupCancelerInterceptor() {
|
||||
const axiosCanceler = new AxiosCanceler();
|
||||
// 注册取消重复请求的请求拦截器
|
||||
this.addRequestInterceptor((config: InternalAxiosRequestConfig) => {
|
||||
return axiosCanceler.addRequest(config);
|
||||
}, this.errorHandler);
|
||||
|
||||
// 注册移除请求的响应拦截器
|
||||
this.addResponseInterceptor(
|
||||
(response: AxiosResponse) => {
|
||||
axiosCanceler.removeRequest(response);
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
if (error.config) {
|
||||
axiosCanceler.removeRequest(error.config);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { RequestClient };
|
||||
|
@@ -22,7 +22,7 @@ describe('useAccessStore', () => {
|
||||
expect(store.userInfo).toBeNull();
|
||||
expect(store.userRoles).toEqual([]);
|
||||
|
||||
const userInfo: any = { name: 'John Doe', roles: [{ value: 'admin' }] };
|
||||
const userInfo: any = { name: 'John Doe', roles: ['admin'] };
|
||||
store.setUserInfo(userInfo);
|
||||
|
||||
expect(store.userInfo).toEqual(userInfo);
|
||||
|
@@ -49,7 +49,7 @@
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"radix-vue": "^1.8.5",
|
||||
"radix-vue": "^1.9.0",
|
||||
"vue": "^3.4.31",
|
||||
"vue-sonner": "^1.1.3"
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showHome: true,
|
||||
showHome: false,
|
||||
showIcon: false,
|
||||
type: 'normal',
|
||||
});
|
||||
|
@@ -16,8 +16,8 @@ import Preferences from './preferences.vue';
|
||||
:app-semi-dark-menu="preferences.app.semiDarkMenu"
|
||||
:breadcrumb-enable="preferences.breadcrumb.enable"
|
||||
:breadcrumb-hide-only-one="preferences.breadcrumb.hideOnlyOne"
|
||||
:breadcrumb-home="preferences.breadcrumb.showHome"
|
||||
:breadcrumb-icon="preferences.breadcrumb.showIcon"
|
||||
:breadcrumb-show-home="preferences.breadcrumb.showHome"
|
||||
:breadcrumb-show-icon="preferences.breadcrumb.showIcon"
|
||||
:breadcrumb-style-type="preferences.breadcrumb.styleType"
|
||||
:footer-enable="preferences.footer.enable"
|
||||
:footer-fixed="preferences.footer.fixed"
|
||||
|
@@ -190,7 +190,7 @@ preferences:
|
||||
icon: 显示面包屑图标
|
||||
home: 显示首页按钮
|
||||
style: 面包屑风格
|
||||
hide-only-one: 只有一个时隐藏
|
||||
hide-only-one: 仅有一个时隐藏
|
||||
background: 背景
|
||||
animation:
|
||||
title: 动画
|
||||
|
Reference in New Issue
Block a user