mirror of
https://gitee.com/bootx/dax-pay-ui.git
synced 2025-10-14 14:11:10 +00:00
remove 国际化删除, 依赖精简
This commit is contained in:
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@@ -5,7 +5,6 @@
|
||||
"stylelint.vscode-stylelint",
|
||||
"esbenp.prettier-vscode",
|
||||
"mrmlnc.vscode-less",
|
||||
"lokalise.i18n-ally",
|
||||
"antfu.iconify",
|
||||
"antfu.unocss",
|
||||
"mikestead.dotenv",
|
||||
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@@ -93,15 +93,6 @@
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"i18n-ally.localesPaths": ["src/locales/lang"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.namespace": true,
|
||||
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
|
||||
"i18n-ally.enabledParsers": ["json", "ts", "js"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.displayLanguage": "zh-CN",
|
||||
"i18n-ally.enabledFrameworks": ["vue", "react"],
|
||||
"cSpell.words": [
|
||||
"antd",
|
||||
"antv",
|
||||
|
@@ -1,18 +1,18 @@
|
||||
import FileService from '../service/FileService';
|
||||
import FileService from '../service/FileService'
|
||||
|
||||
class FileController {
|
||||
private service: FileService = new FileService();
|
||||
private service: FileService = new FileService()
|
||||
|
||||
upload = async (ctx) => {
|
||||
const files = ctx.request.files.file;
|
||||
console.log(files);
|
||||
const files = ctx.request.files.file
|
||||
console.log(files)
|
||||
|
||||
if (files.length === undefined) {
|
||||
this.service.upload(ctx, files, false);
|
||||
this.service.upload(ctx, files, false)
|
||||
} else {
|
||||
this.service.upload(ctx, files, true);
|
||||
this.service.upload(ctx, files, true)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default new FileController();
|
||||
export default new FileController()
|
||||
|
@@ -1,15 +1,15 @@
|
||||
import UserService from '../service/UserService';
|
||||
import UserService from '../service/UserService'
|
||||
|
||||
class UserController {
|
||||
private service: UserService = new UserService();
|
||||
private service: UserService = new UserService()
|
||||
|
||||
login = async (ctx) => {
|
||||
ctx.body = await this.service.login();
|
||||
};
|
||||
|
||||
getUserInfoById = async (ctx) => {
|
||||
ctx.body = await this.service.getUserInfoById();
|
||||
};
|
||||
ctx.body = await this.service.login()
|
||||
}
|
||||
|
||||
export default new UserController();
|
||||
getUserInfoById = async (ctx) => {
|
||||
ctx.body = await this.service.getUserInfoById()
|
||||
}
|
||||
}
|
||||
|
||||
export default new UserController()
|
||||
|
@@ -1,22 +1,22 @@
|
||||
import Koa from 'koa';
|
||||
import path from 'path';
|
||||
import Router from 'koa-router';
|
||||
import body from 'koa-body';
|
||||
import cors from 'koa2-cors';
|
||||
import koaStatic from 'koa-static';
|
||||
import websockify from 'koa-websocket';
|
||||
import route from 'koa-route';
|
||||
import Koa from 'koa'
|
||||
import path from 'path'
|
||||
import Router from 'koa-router'
|
||||
import body from 'koa-body'
|
||||
import cors from 'koa2-cors'
|
||||
import koaStatic from 'koa-static'
|
||||
import websockify from 'koa-websocket'
|
||||
import route from 'koa-route'
|
||||
|
||||
import AppRoutes from './routes';
|
||||
import AppRoutes from './routes'
|
||||
|
||||
const PORT = 3300;
|
||||
const PORT = 3300
|
||||
|
||||
const app = websockify(new Koa());
|
||||
const app = websockify(new Koa())
|
||||
|
||||
app.ws.use(function (ctx, next) {
|
||||
ctx.websocket.send('connection succeeded!');
|
||||
return next(ctx);
|
||||
});
|
||||
ctx.websocket.send('connection succeeded!')
|
||||
return next(ctx)
|
||||
})
|
||||
|
||||
app.ws.use(
|
||||
route.all('/test', function (ctx) {
|
||||
@@ -29,20 +29,20 @@ app.ws.use(
|
||||
id: Math.ceil(Math.random() * 1000),
|
||||
time: new Date().getTime(),
|
||||
res: `${message}`,
|
||||
});
|
||||
ctx.websocket.send(data);
|
||||
})
|
||||
ctx.websocket.send(data)
|
||||
}
|
||||
console.log(message);
|
||||
});
|
||||
console.log(message)
|
||||
})
|
||||
}),
|
||||
);
|
||||
)
|
||||
|
||||
const router = new Router();
|
||||
const router = new Router()
|
||||
|
||||
// router
|
||||
AppRoutes.forEach((route) => router[route.method](route.path, route.action));
|
||||
AppRoutes.forEach((route) => router[route.method](route.path, route.action))
|
||||
|
||||
app.use(cors());
|
||||
app.use(cors())
|
||||
app.use(
|
||||
body({
|
||||
encoding: 'gzip',
|
||||
@@ -53,11 +53,11 @@ app.use(
|
||||
maxFieldsSize: 20 * 1024 * 1024,
|
||||
},
|
||||
}),
|
||||
);
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
app.use(koaStatic(path.join(__dirname)));
|
||||
)
|
||||
app.use(router.routes())
|
||||
app.use(router.allowedMethods())
|
||||
app.use(koaStatic(path.join(__dirname)))
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Application started successfully: http://localhost:${PORT}`);
|
||||
});
|
||||
console.log(`Application started successfully: http://localhost:${PORT}`)
|
||||
})
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import UserController from './controller/UserController';
|
||||
import FileController from './controller/FileController';
|
||||
import UserController from './controller/UserController'
|
||||
import FileController from './controller/FileController'
|
||||
|
||||
export default [
|
||||
// user
|
||||
@@ -20,4 +20,4 @@ export default [
|
||||
method: 'post',
|
||||
action: FileController.upload,
|
||||
},
|
||||
];
|
||||
]
|
||||
|
@@ -1,54 +1,54 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
const uploadUrl = 'http://localhost:3300/static/upload';
|
||||
const filePath = path.join(__dirname, '../static/upload/');
|
||||
const uploadUrl = 'http://localhost:3300/static/upload'
|
||||
const filePath = path.join(__dirname, '../static/upload/')
|
||||
|
||||
fs.ensureDir(filePath);
|
||||
fs.ensureDir(filePath)
|
||||
export default class FileService {
|
||||
async upload(ctx, files, isMultiple) {
|
||||
let fileReader, fileResource, writeStream;
|
||||
let fileReader, fileResource, writeStream
|
||||
|
||||
const fileFunc = function (file) {
|
||||
fileReader = fs.createReadStream(file.filepath);
|
||||
fileResource = filePath + `/${file.originalFilename}`;
|
||||
console.log(fileResource);
|
||||
fileReader = fs.createReadStream(file.filepath)
|
||||
fileResource = filePath + `/${file.originalFilename}`
|
||||
console.log(fileResource)
|
||||
|
||||
writeStream = fs.createWriteStream(fileResource);
|
||||
fileReader.pipe(writeStream);
|
||||
};
|
||||
writeStream = fs.createWriteStream(fileResource)
|
||||
fileReader.pipe(writeStream)
|
||||
}
|
||||
|
||||
const returnFunc = function (flag) {
|
||||
if (flag) {
|
||||
let url = '';
|
||||
let url = ''
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
url += uploadUrl + `/${files[i].originalFilename},`;
|
||||
url += uploadUrl + `/${files[i].originalFilename},`
|
||||
}
|
||||
url = url.replace(/,$/gi, '');
|
||||
url = url.replace(/,$/gi, '')
|
||||
ctx.body = {
|
||||
url: url,
|
||||
code: 0,
|
||||
message: 'upload Success!',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
ctx.body = {
|
||||
url: uploadUrl + `/${files.originalFilename}`,
|
||||
code: 0,
|
||||
message: 'upload Success!',
|
||||
};
|
||||
}
|
||||
};
|
||||
console.log(isMultiple, files.length);
|
||||
}
|
||||
}
|
||||
console.log(isMultiple, files.length)
|
||||
|
||||
if (isMultiple) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const f1 = files[i];
|
||||
fileFunc(f1);
|
||||
const f1 = files[i]
|
||||
fileFunc(f1)
|
||||
}
|
||||
} else {
|
||||
fileFunc(files);
|
||||
fileFunc(files)
|
||||
}
|
||||
fs.ensureDir(filePath);
|
||||
returnFunc(isMultiple);
|
||||
fs.ensureDir(filePath)
|
||||
returnFunc(isMultiple)
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Result } from '../utils';
|
||||
import { Result } from '../utils'
|
||||
|
||||
const fakeUserInfo = {
|
||||
userId: '1',
|
||||
@@ -13,13 +13,13 @@ const fakeUserInfo = {
|
||||
value: 'super',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
export default class UserService {
|
||||
async login() {
|
||||
return Result.success(fakeUserInfo);
|
||||
return Result.success(fakeUserInfo)
|
||||
}
|
||||
|
||||
async getUserInfoById() {
|
||||
return Result.success(fakeUserInfo);
|
||||
return Result.success(fakeUserInfo)
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,6 @@ export class Result {
|
||||
code: 0,
|
||||
success: true,
|
||||
result: data,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -37,10 +37,6 @@ function defineApplicationConfig(defineOptions: DefineOptions = {}) {
|
||||
base: VITE_PUBLIC_PATH,
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: 'vue-i18n',
|
||||
replacement: 'vue-i18n/dist/vue-i18n.cjs.js',
|
||||
},
|
||||
// @/xxxx => src/xxxx
|
||||
{
|
||||
find: /@\//,
|
||||
|
@@ -93,13 +93,12 @@
|
||||
"sortablejs": "^1.15.2",
|
||||
"unocss": "^0.59.4",
|
||||
"vue": "^3.4.25",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-json-pretty": "^2.4.0",
|
||||
"vue-router": "^4.3.2",
|
||||
"vue-types": "^5.1.1",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"vxe-table": "^4.7.31",
|
||||
"xe-utils": "^3.5.25"
|
||||
"xe-utils": "^3.5.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
import { defineBuildConfig } from 'unbuild'
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
@@ -7,4 +7,4 @@ export default defineBuildConfig({
|
||||
rollup: {
|
||||
emitCJS: true,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
@@ -1,7 +1,7 @@
|
||||
export * from './onMountedOrActivated';
|
||||
export * from './useAttrs';
|
||||
export * from './useRefs';
|
||||
export * from './useRequest';
|
||||
export * from './useScrollTo';
|
||||
export * from './useWindowSizeFn';
|
||||
export { useTimeoutFn } from '@vueuse/core';
|
||||
export * from './onMountedOrActivated'
|
||||
export * from './useAttrs'
|
||||
export * from './useRefs'
|
||||
export * from './useRequest'
|
||||
export * from './useScrollTo'
|
||||
export * from './useWindowSizeFn'
|
||||
export { useTimeoutFn } from '@vueuse/core'
|
||||
|
@@ -1,25 +1,25 @@
|
||||
import { type AnyFunction } from '@vben/types';
|
||||
import { nextTick, onActivated, onMounted } from 'vue';
|
||||
import { type AnyFunction } from '@vben/types'
|
||||
import { nextTick, onActivated, onMounted } from 'vue'
|
||||
|
||||
/**
|
||||
* 在 OnMounted 或者 OnActivated 时触发
|
||||
* @param hook 任何函数(包括异步函数)
|
||||
*/
|
||||
function onMountedOrActivated(hook: AnyFunction) {
|
||||
let mounted: boolean;
|
||||
let mounted: boolean
|
||||
|
||||
onMounted(() => {
|
||||
hook();
|
||||
hook()
|
||||
nextTick(() => {
|
||||
mounted = true;
|
||||
});
|
||||
});
|
||||
mounted = true
|
||||
})
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
if (mounted) {
|
||||
hook();
|
||||
hook()
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
export { onMountedOrActivated };
|
||||
export { onMountedOrActivated }
|
||||
|
@@ -1,43 +1,43 @@
|
||||
import { type Recordable } from '@vben/types';
|
||||
import { getCurrentInstance, reactive, shallowRef, watchEffect } from 'vue';
|
||||
import { type Recordable } from '@vben/types'
|
||||
import { getCurrentInstance, reactive, shallowRef, watchEffect } from 'vue'
|
||||
|
||||
interface UseAttrsOptions {
|
||||
excludeListeners?: boolean;
|
||||
excludeKeys?: string[];
|
||||
excludeDefaultKeys?: boolean;
|
||||
excludeListeners?: boolean
|
||||
excludeKeys?: string[]
|
||||
excludeDefaultKeys?: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_EXCLUDE_KEYS = ['class', 'style'];
|
||||
const LISTENER_PREFIX = /^on[A-Z]/;
|
||||
const DEFAULT_EXCLUDE_KEYS = ['class', 'style']
|
||||
const LISTENER_PREFIX = /^on[A-Z]/
|
||||
|
||||
function entries<T>(obj: Recordable<T>): [string, T][] {
|
||||
return Object.keys(obj).map((key: string) => [key, obj[key]]);
|
||||
return Object.keys(obj).map((key: string) => [key, obj[key]])
|
||||
}
|
||||
|
||||
function useAttrs(options: UseAttrsOptions = {}): Recordable<any> {
|
||||
const instance = getCurrentInstance();
|
||||
if (!instance) return {};
|
||||
const instance = getCurrentInstance()
|
||||
if (!instance) return {}
|
||||
|
||||
const { excludeListeners = false, excludeKeys = [], excludeDefaultKeys = true } = options;
|
||||
const attrs = shallowRef({});
|
||||
const allExcludeKeys = excludeKeys.concat(excludeDefaultKeys ? DEFAULT_EXCLUDE_KEYS : []);
|
||||
const { excludeListeners = false, excludeKeys = [], excludeDefaultKeys = true } = options
|
||||
const attrs = shallowRef({})
|
||||
const allExcludeKeys = excludeKeys.concat(excludeDefaultKeys ? DEFAULT_EXCLUDE_KEYS : [])
|
||||
|
||||
// Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance
|
||||
instance.attrs = reactive(instance.attrs);
|
||||
instance.attrs = reactive(instance.attrs)
|
||||
|
||||
watchEffect(() => {
|
||||
const res = entries(instance.attrs).reduce((acm, [key, val]) => {
|
||||
if (!allExcludeKeys.includes(key) && !(excludeListeners && LISTENER_PREFIX.test(key))) {
|
||||
acm[key] = val;
|
||||
acm[key] = val
|
||||
}
|
||||
|
||||
return acm;
|
||||
}, {} as Recordable<any>);
|
||||
return acm
|
||||
}, {} as Recordable<any>)
|
||||
|
||||
attrs.value = res;
|
||||
});
|
||||
attrs.value = res
|
||||
})
|
||||
|
||||
return attrs;
|
||||
return attrs
|
||||
}
|
||||
|
||||
export { useAttrs, type UseAttrsOptions };
|
||||
export { useAttrs, type UseAttrsOptions }
|
||||
|
@@ -1,24 +1,24 @@
|
||||
import type { ComponentPublicInstance, Ref } from 'vue';
|
||||
import { onBeforeUpdate, shallowRef } from 'vue';
|
||||
import type { ComponentPublicInstance, Ref } from 'vue'
|
||||
import { onBeforeUpdate, shallowRef } from 'vue'
|
||||
|
||||
function useRefs<T = HTMLElement>(): {
|
||||
refs: Ref<T[]>;
|
||||
setRefs: (index: number) => (el: Element | ComponentPublicInstance | null) => void;
|
||||
refs: Ref<T[]>
|
||||
setRefs: (index: number) => (el: Element | ComponentPublicInstance | null) => void
|
||||
} {
|
||||
const refs = shallowRef([]) as Ref<T[]>;
|
||||
const refs = shallowRef([]) as Ref<T[]>
|
||||
|
||||
onBeforeUpdate(() => {
|
||||
refs.value = [];
|
||||
});
|
||||
refs.value = []
|
||||
})
|
||||
|
||||
const setRefs = (index: number) => (el: Element | ComponentPublicInstance | null) => {
|
||||
refs.value[index] = el as T;
|
||||
};
|
||||
refs.value[index] = el as T
|
||||
}
|
||||
|
||||
return {
|
||||
refs,
|
||||
setRefs,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export { useRefs };
|
||||
export { useRefs }
|
||||
|
@@ -1,20 +1,20 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { reactive } from 'vue';
|
||||
import { reactive } from 'vue'
|
||||
|
||||
import type { FetchState, PluginReturn, Service, Subscribe, UseRequestOptions } from './types';
|
||||
import { isFunction } from './utils/isFunction';
|
||||
import type { FetchState, PluginReturn, Service, Subscribe, UseRequestOptions } from './types'
|
||||
import { isFunction } from './utils/isFunction'
|
||||
|
||||
export default class Fetch<TData, TParams extends any[]> {
|
||||
pluginImpls: PluginReturn<TData, TParams>[] = [];
|
||||
pluginImpls: PluginReturn<TData, TParams>[] = []
|
||||
|
||||
count: number = 0;
|
||||
count: number = 0
|
||||
|
||||
state: FetchState<TData, TParams> = reactive({
|
||||
loading: false,
|
||||
params: undefined,
|
||||
data: undefined,
|
||||
error: undefined,
|
||||
});
|
||||
})
|
||||
|
||||
constructor(
|
||||
public serviceRef: Service<TData, TParams>,
|
||||
@@ -22,126 +22,126 @@ export default class Fetch<TData, TParams extends any[]> {
|
||||
public subscribe: Subscribe,
|
||||
public initState: Partial<FetchState<TData, TParams>> = {},
|
||||
) {
|
||||
this.setState({ loading: !options.manual, ...initState });
|
||||
this.setState({ loading: !options.manual, ...initState })
|
||||
}
|
||||
|
||||
setState(s: Partial<FetchState<TData, TParams>> = {}) {
|
||||
Object.assign(this.state, s);
|
||||
this.subscribe();
|
||||
Object.assign(this.state, s)
|
||||
this.subscribe()
|
||||
}
|
||||
|
||||
runPluginHandler(event: keyof PluginReturn<TData, TParams>, ...rest: any[]) {
|
||||
// @ts-ignore
|
||||
const r = this.pluginImpls.map((i) => i[event]?.(...rest)).filter(Boolean);
|
||||
return Object.assign({}, ...r);
|
||||
const r = this.pluginImpls.map((i) => i[event]?.(...rest)).filter(Boolean)
|
||||
return Object.assign({}, ...r)
|
||||
}
|
||||
|
||||
async runAsync(...params: TParams): Promise<TData> {
|
||||
this.count += 1;
|
||||
const currentCount = this.count;
|
||||
this.count += 1
|
||||
const currentCount = this.count
|
||||
|
||||
const {
|
||||
stopNow = false,
|
||||
returnNow = false,
|
||||
...state
|
||||
} = this.runPluginHandler('onBefore', params);
|
||||
} = this.runPluginHandler('onBefore', params)
|
||||
|
||||
// stop request
|
||||
if (stopNow) {
|
||||
return new Promise(() => {});
|
||||
return new Promise(() => {})
|
||||
}
|
||||
|
||||
this.setState({
|
||||
loading: true,
|
||||
params,
|
||||
...state,
|
||||
});
|
||||
})
|
||||
|
||||
// return now
|
||||
if (returnNow) {
|
||||
return Promise.resolve(state.data);
|
||||
return Promise.resolve(state.data)
|
||||
}
|
||||
|
||||
this.options.onBefore?.(params);
|
||||
this.options.onBefore?.(params)
|
||||
|
||||
try {
|
||||
// replace service
|
||||
let { servicePromise } = this.runPluginHandler('onRequest', this.serviceRef, params);
|
||||
let { servicePromise } = this.runPluginHandler('onRequest', this.serviceRef, params)
|
||||
|
||||
if (!servicePromise) {
|
||||
servicePromise = this.serviceRef(...params);
|
||||
servicePromise = this.serviceRef(...params)
|
||||
}
|
||||
|
||||
const res = await servicePromise;
|
||||
const res = await servicePromise
|
||||
|
||||
if (currentCount !== this.count) {
|
||||
// prevent run.then when request is canceled
|
||||
return new Promise(() => {});
|
||||
return new Promise(() => {})
|
||||
}
|
||||
|
||||
// const formattedResult = this.options.formatResultRef.current ? this.options.formatResultRef.current(res) : res;
|
||||
|
||||
this.setState({ data: res, error: undefined, loading: false });
|
||||
this.setState({ data: res, error: undefined, loading: false })
|
||||
|
||||
this.options.onSuccess?.(res, params);
|
||||
this.runPluginHandler('onSuccess', res, params);
|
||||
this.options.onSuccess?.(res, params)
|
||||
this.runPluginHandler('onSuccess', res, params)
|
||||
|
||||
this.options.onFinally?.(params, res, undefined);
|
||||
this.options.onFinally?.(params, res, undefined)
|
||||
|
||||
if (currentCount === this.count) {
|
||||
this.runPluginHandler('onFinally', params, res, undefined);
|
||||
this.runPluginHandler('onFinally', params, res, undefined)
|
||||
}
|
||||
|
||||
return res;
|
||||
return res
|
||||
} catch (error) {
|
||||
if (currentCount !== this.count) {
|
||||
// prevent run.then when request is canceled
|
||||
return new Promise(() => {});
|
||||
return new Promise(() => {})
|
||||
}
|
||||
|
||||
this.setState({ error, loading: false });
|
||||
this.setState({ error, loading: false })
|
||||
|
||||
this.options.onError?.(error, params);
|
||||
this.runPluginHandler('onError', error, params);
|
||||
this.options.onError?.(error, params)
|
||||
this.runPluginHandler('onError', error, params)
|
||||
|
||||
this.options.onFinally?.(params, undefined, error);
|
||||
this.options.onFinally?.(params, undefined, error)
|
||||
|
||||
if (currentCount === this.count) {
|
||||
this.runPluginHandler('onFinally', params, undefined, error);
|
||||
this.runPluginHandler('onFinally', params, undefined, error)
|
||||
}
|
||||
|
||||
throw error;
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
run(...params: TParams) {
|
||||
this.runAsync(...params).catch((error) => {
|
||||
if (!this.options.onError) {
|
||||
console.error(error);
|
||||
console.error(error)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.count += 1;
|
||||
this.setState({ loading: false });
|
||||
this.count += 1
|
||||
this.setState({ loading: false })
|
||||
|
||||
this.runPluginHandler('onCancel');
|
||||
this.runPluginHandler('onCancel')
|
||||
}
|
||||
|
||||
refresh() {
|
||||
// @ts-ignore
|
||||
this.run(...(this.state.params || []));
|
||||
this.run(...(this.state.params || []))
|
||||
}
|
||||
|
||||
refreshAsync() {
|
||||
// @ts-ignore
|
||||
return this.runAsync(...(this.state.params || []));
|
||||
return this.runAsync(...(this.state.params || []))
|
||||
}
|
||||
|
||||
mutate(data?: TData | ((oldData?: TData) => TData | undefined)) {
|
||||
const targetData = isFunction(data) ? data(this.state.data) : data;
|
||||
this.runPluginHandler('onMutate', targetData);
|
||||
this.setState({ data: targetData });
|
||||
const targetData = isFunction(data) ? data(this.state.data) : data
|
||||
this.runPluginHandler('onMutate', targetData)
|
||||
this.setState({ data: targetData })
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +1,15 @@
|
||||
import useAutoRunPlugin from './plugins/useAutoRunPlugin';
|
||||
import useCachePlugin from './plugins/useCachePlugin';
|
||||
import useDebouncePlugin from './plugins/useDebouncePlugin';
|
||||
import useLoadingDelayPlugin from './plugins/useLoadingDelayPlugin';
|
||||
import usePollingPlugin from './plugins/usePollingPlugin';
|
||||
import useRefreshOnWindowFocusPlugin from './plugins/useRefreshOnWindowFocusPlugin';
|
||||
import useRetryPlugin from './plugins/useRetryPlugin';
|
||||
import useThrottlePlugin from './plugins/useThrottlePlugin';
|
||||
import type { Service, UseRequestOptions, UseRequestPlugin } from './types';
|
||||
import { useRequestImplement } from './useRequestImplement';
|
||||
import useAutoRunPlugin from './plugins/useAutoRunPlugin'
|
||||
import useCachePlugin from './plugins/useCachePlugin'
|
||||
import useDebouncePlugin from './plugins/useDebouncePlugin'
|
||||
import useLoadingDelayPlugin from './plugins/useLoadingDelayPlugin'
|
||||
import usePollingPlugin from './plugins/usePollingPlugin'
|
||||
import useRefreshOnWindowFocusPlugin from './plugins/useRefreshOnWindowFocusPlugin'
|
||||
import useRetryPlugin from './plugins/useRetryPlugin'
|
||||
import useThrottlePlugin from './plugins/useThrottlePlugin'
|
||||
import type { Service, UseRequestOptions, UseRequestPlugin } from './types'
|
||||
import { useRequestImplement } from './useRequestImplement'
|
||||
|
||||
export { clearCache } from './utils/cache';
|
||||
export { clearCache } from './utils/cache'
|
||||
|
||||
export function useRequest<TData, TParams extends any[]>(
|
||||
service: Service<TData, TParams>,
|
||||
@@ -26,5 +26,5 @@ export function useRequest<TData, TParams extends any[]>(
|
||||
useAutoRunPlugin,
|
||||
useCachePlugin,
|
||||
useRetryPlugin,
|
||||
] as UseRequestPlugin<TData, TParams>[]);
|
||||
] as UseRequestPlugin<TData, TParams>[])
|
||||
}
|
||||
|
@@ -1,52 +1,52 @@
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { ref, unref, watch } from 'vue'
|
||||
|
||||
import type { UseRequestPlugin } from '../types';
|
||||
import type { UseRequestPlugin } from '../types'
|
||||
|
||||
// support refreshDeps & ready
|
||||
const useAutoRunPlugin: UseRequestPlugin<any, any[]> = (
|
||||
fetchInstance,
|
||||
{ manual, ready = true, defaultParams = [], refreshDeps = [], refreshDepsAction },
|
||||
) => {
|
||||
const hasAutoRun = ref(false);
|
||||
const hasAutoRun = ref(false)
|
||||
|
||||
watch(
|
||||
() => unref(ready),
|
||||
(readyVal) => {
|
||||
if (!unref(manual) && readyVal) {
|
||||
hasAutoRun.value = true;
|
||||
fetchInstance.run(...defaultParams);
|
||||
hasAutoRun.value = true
|
||||
fetchInstance.run(...defaultParams)
|
||||
}
|
||||
},
|
||||
);
|
||||
)
|
||||
|
||||
if (refreshDeps.length) {
|
||||
watch(refreshDeps, () => {
|
||||
if (hasAutoRun.value) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (!manual) {
|
||||
if (refreshDepsAction) {
|
||||
refreshDepsAction();
|
||||
refreshDepsAction()
|
||||
} else {
|
||||
fetchInstance.refresh();
|
||||
fetchInstance.refresh()
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
onBefore: () => {
|
||||
if (!unref(ready)) {
|
||||
return { stopNow: true };
|
||||
return { stopNow: true }
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
useAutoRunPlugin.onInit = ({ ready = true, manual }) => {
|
||||
return {
|
||||
loading: !unref(manual) && unref(ready),
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default useAutoRunPlugin;
|
||||
export default useAutoRunPlugin
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import { onUnmounted, ref, watchEffect } from 'vue';
|
||||
import { onUnmounted, ref, watchEffect } from 'vue'
|
||||
|
||||
import type { UseRequestPlugin } from '../types';
|
||||
import type { CachedData } from '../utils/cache';
|
||||
import { getCache, setCache } from '../utils/cache';
|
||||
import { getCachePromise, setCachePromise } from '../utils/cachePromise';
|
||||
import { subscribe, trigger } from '../utils/cacheSubscribe';
|
||||
import type { UseRequestPlugin } from '../types'
|
||||
import type { CachedData } from '../utils/cache'
|
||||
import { getCache, setCache } from '../utils/cache'
|
||||
import { getCachePromise, setCachePromise } from '../utils/cachePromise'
|
||||
import { subscribe, trigger } from '../utils/cacheSubscribe'
|
||||
|
||||
const useCachePlugin: UseRequestPlugin<any, any[]> = (
|
||||
fetchInstance,
|
||||
@@ -16,52 +16,52 @@ const useCachePlugin: UseRequestPlugin<any, any[]> = (
|
||||
getCache: customGetCache,
|
||||
},
|
||||
) => {
|
||||
const unSubscribeRef = ref<() => void>();
|
||||
const currentPromiseRef = ref<Promise<any>>();
|
||||
const unSubscribeRef = ref<() => void>()
|
||||
const currentPromiseRef = ref<Promise<any>>()
|
||||
|
||||
const _setCache = (key: string, cachedData: CachedData) => {
|
||||
customSetCache ? customSetCache(cachedData) : setCache(key, cacheTime, cachedData);
|
||||
trigger(key, cachedData.data);
|
||||
};
|
||||
customSetCache ? customSetCache(cachedData) : setCache(key, cacheTime, cachedData)
|
||||
trigger(key, cachedData.data)
|
||||
}
|
||||
|
||||
const _getCache = (key: string, params: any[] = []) => {
|
||||
return customGetCache ? customGetCache(params) : getCache(key);
|
||||
};
|
||||
return customGetCache ? customGetCache(params) : getCache(key)
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (!cacheKey) return;
|
||||
if (!cacheKey) return
|
||||
|
||||
// get data from cache when init
|
||||
const cacheData = _getCache(cacheKey);
|
||||
const cacheData = _getCache(cacheKey)
|
||||
if (cacheData && Object.hasOwnProperty.call(cacheData, 'data')) {
|
||||
fetchInstance.state.data = cacheData.data;
|
||||
fetchInstance.state.params = cacheData.params;
|
||||
fetchInstance.state.data = cacheData.data
|
||||
fetchInstance.state.params = cacheData.params
|
||||
|
||||
if (staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime) {
|
||||
fetchInstance.state.loading = false;
|
||||
fetchInstance.state.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
// subscribe same cachekey update, trigger update
|
||||
unSubscribeRef.value = subscribe(cacheKey, (data) => {
|
||||
fetchInstance.setState({ data });
|
||||
});
|
||||
});
|
||||
fetchInstance.setState({ data })
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
unSubscribeRef.value?.();
|
||||
});
|
||||
unSubscribeRef.value?.()
|
||||
})
|
||||
|
||||
if (!cacheKey) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
return {
|
||||
onBefore: (params) => {
|
||||
const cacheData = _getCache(cacheKey, params);
|
||||
const cacheData = _getCache(cacheKey, params)
|
||||
|
||||
if (!cacheData || !Object.hasOwnProperty.call(cacheData, 'data')) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
// If the data is fresh, stop request
|
||||
@@ -71,57 +71,57 @@ const useCachePlugin: UseRequestPlugin<any, any[]> = (
|
||||
data: cacheData?.data,
|
||||
error: undefined,
|
||||
returnNow: true,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// If the data is stale, return data, and request continue
|
||||
return { data: cacheData?.data, error: undefined };
|
||||
return { data: cacheData?.data, error: undefined }
|
||||
}
|
||||
},
|
||||
onRequest: (service, args) => {
|
||||
let servicePromise = getCachePromise(cacheKey);
|
||||
let servicePromise = getCachePromise(cacheKey)
|
||||
|
||||
// If has servicePromise, and is not trigger by self, then use it
|
||||
if (servicePromise && servicePromise !== currentPromiseRef.value) {
|
||||
return { servicePromise };
|
||||
return { servicePromise }
|
||||
}
|
||||
|
||||
servicePromise = service(...args);
|
||||
currentPromiseRef.value = servicePromise;
|
||||
setCachePromise(cacheKey, servicePromise);
|
||||
servicePromise = service(...args)
|
||||
currentPromiseRef.value = servicePromise
|
||||
setCachePromise(cacheKey, servicePromise)
|
||||
|
||||
return { servicePromise };
|
||||
return { servicePromise }
|
||||
},
|
||||
onSuccess: (data, params) => {
|
||||
if (cacheKey) {
|
||||
// cancel subscribe, avoid trgger self
|
||||
unSubscribeRef.value?.();
|
||||
unSubscribeRef.value?.()
|
||||
|
||||
_setCache(cacheKey, { data, params, time: new Date().getTime() });
|
||||
_setCache(cacheKey, { data, params, time: new Date().getTime() })
|
||||
|
||||
// resubscribe
|
||||
unSubscribeRef.value = subscribe(cacheKey, (d) => {
|
||||
fetchInstance.setState({ data: d });
|
||||
});
|
||||
fetchInstance.setState({ data: d })
|
||||
})
|
||||
}
|
||||
},
|
||||
onMutate: (data) => {
|
||||
if (cacheKey) {
|
||||
// cancel subscribe, avoid trigger self
|
||||
unSubscribeRef.value?.();
|
||||
unSubscribeRef.value?.()
|
||||
|
||||
_setCache(cacheKey, {
|
||||
data,
|
||||
params: fetchInstance.state.params,
|
||||
time: new Date().getTime(),
|
||||
});
|
||||
})
|
||||
|
||||
// resubscribe
|
||||
unSubscribeRef.value = subscribe(cacheKey, (d) => {
|
||||
fetchInstance.setState({ data: d });
|
||||
});
|
||||
fetchInstance.setState({ data: d })
|
||||
})
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default useCachePlugin;
|
||||
export default useCachePlugin
|
||||
|
@@ -1,42 +1,42 @@
|
||||
import type { DebouncedFunc, DebounceSettings } from 'lodash-es';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { computed, ref, watchEffect } from 'vue';
|
||||
import type { DebouncedFunc, DebounceSettings } from 'lodash-es'
|
||||
import { debounce } from 'lodash-es'
|
||||
import { computed, ref, watchEffect } from 'vue'
|
||||
|
||||
import type { UseRequestPlugin } from '../types';
|
||||
import type { UseRequestPlugin } from '../types'
|
||||
|
||||
const useDebouncePlugin: UseRequestPlugin<any, any[]> = (
|
||||
fetchInstance,
|
||||
{ debounceWait, debounceLeading, debounceTrailing, debounceMaxWait },
|
||||
) => {
|
||||
const debouncedRef = ref<DebouncedFunc<any>>();
|
||||
const debouncedRef = ref<DebouncedFunc<any>>()
|
||||
|
||||
const options = computed(() => {
|
||||
const ret: DebounceSettings = {};
|
||||
const ret: DebounceSettings = {}
|
||||
|
||||
if (debounceLeading !== undefined) {
|
||||
ret.leading = debounceLeading;
|
||||
ret.leading = debounceLeading
|
||||
}
|
||||
if (debounceTrailing !== undefined) {
|
||||
ret.trailing = debounceTrailing;
|
||||
ret.trailing = debounceTrailing
|
||||
}
|
||||
if (debounceMaxWait !== undefined) {
|
||||
ret.maxWait = debounceMaxWait;
|
||||
ret.maxWait = debounceMaxWait
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
return ret
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (debounceWait) {
|
||||
const _originRunAsync = fetchInstance.runAsync.bind(fetchInstance);
|
||||
const _originRunAsync = fetchInstance.runAsync.bind(fetchInstance)
|
||||
|
||||
debouncedRef.value = debounce(
|
||||
(callback) => {
|
||||
callback();
|
||||
callback()
|
||||
},
|
||||
debounceWait,
|
||||
options.value,
|
||||
);
|
||||
)
|
||||
|
||||
// debounce runAsync should be promise
|
||||
// https://github.com/lodash/lodash/issues/4400#issuecomment-834800398
|
||||
@@ -45,27 +45,27 @@ const useDebouncePlugin: UseRequestPlugin<any, any[]> = (
|
||||
debouncedRef.value?.(() => {
|
||||
_originRunAsync(...args)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
});
|
||||
};
|
||||
.catch(reject)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
debouncedRef.value?.cancel();
|
||||
fetchInstance.runAsync = _originRunAsync;
|
||||
};
|
||||
debouncedRef.value?.cancel()
|
||||
fetchInstance.runAsync = _originRunAsync
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
if (!debounceWait) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
return {
|
||||
onCancel: () => {
|
||||
debouncedRef.value?.cancel();
|
||||
debouncedRef.value?.cancel()
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default useDebouncePlugin;
|
||||
export default useDebouncePlugin
|
||||
|
@@ -1,45 +1,45 @@
|
||||
import { ref, unref } from 'vue';
|
||||
import { ref, unref } from 'vue'
|
||||
|
||||
import type { UseRequestPlugin, UseRequestTimeout } from '../types';
|
||||
import type { UseRequestPlugin, UseRequestTimeout } from '../types'
|
||||
|
||||
const useLoadingDelayPlugin: UseRequestPlugin<any, any[]> = (
|
||||
fetchInstance,
|
||||
{ loadingDelay, ready },
|
||||
) => {
|
||||
const timerRef = ref<UseRequestTimeout>();
|
||||
const timerRef = ref<UseRequestTimeout>()
|
||||
|
||||
if (!loadingDelay) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
const cancelTimeout = () => {
|
||||
if (timerRef.value) {
|
||||
clearTimeout(timerRef.value);
|
||||
clearTimeout(timerRef.value)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
onBefore: () => {
|
||||
cancelTimeout();
|
||||
cancelTimeout()
|
||||
|
||||
// Two cases:
|
||||
// 1. ready === undefined
|
||||
// 2. ready === true
|
||||
if (unref(ready) !== false) {
|
||||
timerRef.value = setTimeout(() => {
|
||||
fetchInstance.setState({ loading: true });
|
||||
}, loadingDelay);
|
||||
fetchInstance.setState({ loading: true })
|
||||
}, loadingDelay)
|
||||
}
|
||||
|
||||
return { loading: false };
|
||||
return { loading: false }
|
||||
},
|
||||
onFinally: () => {
|
||||
cancelTimeout();
|
||||
cancelTimeout()
|
||||
},
|
||||
onCancel: () => {
|
||||
cancelTimeout();
|
||||
cancelTimeout()
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default useLoadingDelayPlugin;
|
||||
export default useLoadingDelayPlugin
|
||||
|
@@ -1,46 +1,46 @@
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
import type { UseRequestPlugin, UseRequestTimeout } from '../types';
|
||||
import { isDocumentVisible } from '../utils/isDocumentVisible';
|
||||
import subscribeReVisible from '../utils/subscribeReVisible';
|
||||
import type { UseRequestPlugin, UseRequestTimeout } from '../types'
|
||||
import { isDocumentVisible } from '../utils/isDocumentVisible'
|
||||
import subscribeReVisible from '../utils/subscribeReVisible'
|
||||
|
||||
const usePollingPlugin: UseRequestPlugin<any, any[]> = (
|
||||
fetchInstance,
|
||||
{ pollingInterval, pollingWhenHidden = true, pollingErrorRetryCount = -1 },
|
||||
) => {
|
||||
const timerRef = ref<UseRequestTimeout>();
|
||||
const unsubscribeRef = ref<() => void>();
|
||||
const countRef = ref<number>(0);
|
||||
const timerRef = ref<UseRequestTimeout>()
|
||||
const unsubscribeRef = ref<() => void>()
|
||||
const countRef = ref<number>(0)
|
||||
|
||||
const stopPolling = () => {
|
||||
if (timerRef.value) {
|
||||
clearTimeout(timerRef.value);
|
||||
clearTimeout(timerRef.value)
|
||||
}
|
||||
unsubscribeRef.value?.()
|
||||
}
|
||||
unsubscribeRef.value?.();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => pollingInterval,
|
||||
() => {
|
||||
if (!pollingInterval) {
|
||||
stopPolling();
|
||||
stopPolling()
|
||||
}
|
||||
},
|
||||
);
|
||||
)
|
||||
|
||||
if (!pollingInterval) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
return {
|
||||
onBefore: () => {
|
||||
stopPolling();
|
||||
stopPolling()
|
||||
},
|
||||
onError: () => {
|
||||
countRef.value += 1;
|
||||
countRef.value += 1
|
||||
},
|
||||
onSuccess: () => {
|
||||
countRef.value = 0;
|
||||
countRef.value = 0
|
||||
},
|
||||
onFinally: () => {
|
||||
if (
|
||||
@@ -52,20 +52,20 @@ const usePollingPlugin: UseRequestPlugin<any, any[]> = (
|
||||
// if pollingWhenHidden = false && document is hidden, then stop polling and subscribe revisible
|
||||
if (!pollingWhenHidden && !isDocumentVisible()) {
|
||||
unsubscribeRef.value = subscribeReVisible(() => {
|
||||
fetchInstance.refresh();
|
||||
});
|
||||
fetchInstance.refresh()
|
||||
})
|
||||
} else {
|
||||
fetchInstance.refresh();
|
||||
fetchInstance.refresh()
|
||||
}
|
||||
}, pollingInterval);
|
||||
}, pollingInterval)
|
||||
} else {
|
||||
countRef.value = 0;
|
||||
countRef.value = 0
|
||||
}
|
||||
},
|
||||
onCancel: () => {
|
||||
stopPolling();
|
||||
stopPolling()
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default usePollingPlugin;
|
||||
export default usePollingPlugin
|
||||
|
@@ -1,37 +1,37 @@
|
||||
import { onUnmounted, ref, watchEffect } from 'vue';
|
||||
import { onUnmounted, ref, watchEffect } from 'vue'
|
||||
|
||||
import type { UseRequestPlugin } from '../types';
|
||||
import { limit } from '../utils/limit';
|
||||
import subscribeFocus from '../utils/subscribeFocus';
|
||||
import type { UseRequestPlugin } from '../types'
|
||||
import { limit } from '../utils/limit'
|
||||
import subscribeFocus from '../utils/subscribeFocus'
|
||||
|
||||
const useRefreshOnWindowFocusPlugin: UseRequestPlugin<any, any[]> = (
|
||||
fetchInstance,
|
||||
{ refreshOnWindowFocus, focusTimespan = 5000 },
|
||||
) => {
|
||||
const unsubscribeRef = ref<() => void>();
|
||||
const unsubscribeRef = ref<() => void>()
|
||||
|
||||
const stopSubscribe = () => {
|
||||
unsubscribeRef.value?.();
|
||||
};
|
||||
unsubscribeRef.value?.()
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (refreshOnWindowFocus) {
|
||||
const limitRefresh = limit(fetchInstance.refresh.bind(fetchInstance), focusTimespan);
|
||||
const limitRefresh = limit(fetchInstance.refresh.bind(fetchInstance), focusTimespan)
|
||||
unsubscribeRef.value = subscribeFocus(() => {
|
||||
limitRefresh();
|
||||
});
|
||||
limitRefresh()
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
stopSubscribe();
|
||||
};
|
||||
});
|
||||
stopSubscribe()
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopSubscribe();
|
||||
});
|
||||
stopSubscribe()
|
||||
})
|
||||
|
||||
return {};
|
||||
};
|
||||
return {}
|
||||
}
|
||||
|
||||
export default useRefreshOnWindowFocusPlugin;
|
||||
export default useRefreshOnWindowFocusPlugin
|
||||
|
@@ -1,54 +1,54 @@
|
||||
import { ref } from 'vue';
|
||||
import { ref } from 'vue'
|
||||
|
||||
import type { UseRequestPlugin, UseRequestTimeout } from '../types';
|
||||
import type { UseRequestPlugin, UseRequestTimeout } from '../types'
|
||||
|
||||
const useRetryPlugin: UseRequestPlugin<any, any[]> = (
|
||||
fetchInstance,
|
||||
{ retryInterval, retryCount },
|
||||
) => {
|
||||
const timerRef = ref<UseRequestTimeout>();
|
||||
const countRef = ref(0);
|
||||
const timerRef = ref<UseRequestTimeout>()
|
||||
const countRef = ref(0)
|
||||
|
||||
const triggerByRetry = ref(false);
|
||||
const triggerByRetry = ref(false)
|
||||
|
||||
if (!retryCount) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
return {
|
||||
onBefore: () => {
|
||||
if (!triggerByRetry.value) {
|
||||
countRef.value = 0;
|
||||
countRef.value = 0
|
||||
}
|
||||
triggerByRetry.value = false;
|
||||
triggerByRetry.value = false
|
||||
|
||||
if (timerRef.value) {
|
||||
clearTimeout(timerRef.value);
|
||||
clearTimeout(timerRef.value)
|
||||
}
|
||||
},
|
||||
onSuccess: () => {
|
||||
countRef.value = 0;
|
||||
countRef.value = 0
|
||||
},
|
||||
onError: () => {
|
||||
countRef.value += 1;
|
||||
countRef.value += 1
|
||||
if (retryCount === -1 || countRef.value <= retryCount) {
|
||||
// Exponential backoff
|
||||
const timeout = retryInterval ?? Math.min(1000 * 2 ** countRef.value, 30000);
|
||||
const timeout = retryInterval ?? Math.min(1000 * 2 ** countRef.value, 30000)
|
||||
timerRef.value = setTimeout(() => {
|
||||
triggerByRetry.value = true;
|
||||
fetchInstance.refresh();
|
||||
}, timeout);
|
||||
triggerByRetry.value = true
|
||||
fetchInstance.refresh()
|
||||
}, timeout)
|
||||
} else {
|
||||
countRef.value = 0;
|
||||
countRef.value = 0
|
||||
}
|
||||
},
|
||||
onCancel: () => {
|
||||
countRef.value = 0;
|
||||
countRef.value = 0
|
||||
if (timerRef.value) {
|
||||
clearTimeout(timerRef.value);
|
||||
clearTimeout(timerRef.value)
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default useRetryPlugin;
|
||||
export default useRetryPlugin
|
||||
|
@@ -1,34 +1,34 @@
|
||||
import type { DebouncedFunc, ThrottleSettings } from 'lodash-es';
|
||||
import { throttle } from 'lodash-es';
|
||||
import { ref, watchEffect } from 'vue';
|
||||
import type { DebouncedFunc, ThrottleSettings } from 'lodash-es'
|
||||
import { throttle } from 'lodash-es'
|
||||
import { ref, watchEffect } from 'vue'
|
||||
|
||||
import type { UseRequestPlugin } from '../types';
|
||||
import type { UseRequestPlugin } from '../types'
|
||||
|
||||
const useThrottlePlugin: UseRequestPlugin<any, any[]> = (
|
||||
fetchInstance,
|
||||
{ throttleWait, throttleLeading, throttleTrailing },
|
||||
) => {
|
||||
const throttledRef = ref<DebouncedFunc<any>>();
|
||||
const throttledRef = ref<DebouncedFunc<any>>()
|
||||
|
||||
const options: ThrottleSettings = {};
|
||||
const options: ThrottleSettings = {}
|
||||
if (throttleLeading !== undefined) {
|
||||
options.leading = throttleLeading;
|
||||
options.leading = throttleLeading
|
||||
}
|
||||
if (throttleTrailing !== undefined) {
|
||||
options.trailing = throttleTrailing;
|
||||
options.trailing = throttleTrailing
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (throttleWait) {
|
||||
const _originRunAsync = fetchInstance.runAsync.bind(fetchInstance);
|
||||
const _originRunAsync = fetchInstance.runAsync.bind(fetchInstance)
|
||||
|
||||
throttledRef.value = throttle(
|
||||
(callback) => {
|
||||
callback();
|
||||
callback()
|
||||
},
|
||||
throttleWait,
|
||||
options,
|
||||
);
|
||||
)
|
||||
|
||||
// throttle runAsync should be promise
|
||||
// https://github.com/lodash/lodash/issues/4400#issuecomment-834800398
|
||||
@@ -37,27 +37,27 @@ const useThrottlePlugin: UseRequestPlugin<any, any[]> = (
|
||||
throttledRef.value?.(() => {
|
||||
_originRunAsync(...args)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
});
|
||||
};
|
||||
.catch(reject)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
fetchInstance.runAsync = _originRunAsync;
|
||||
throttledRef.value?.cancel();
|
||||
};
|
||||
fetchInstance.runAsync = _originRunAsync
|
||||
throttledRef.value?.cancel()
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
if (!throttleWait) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
return {
|
||||
onCancel: () => {
|
||||
throttledRef.value?.cancel();
|
||||
throttledRef.value?.cancel()
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default useThrottlePlugin;
|
||||
export default useThrottlePlugin
|
||||
|
@@ -1,93 +1,93 @@
|
||||
import type { MaybeRef, Ref, WatchSource } from 'vue';
|
||||
import type { MaybeRef, Ref, WatchSource } from 'vue'
|
||||
|
||||
import type Fetch from './Fetch';
|
||||
import type { CachedData } from './utils/cache';
|
||||
import type Fetch from './Fetch'
|
||||
import type { CachedData } from './utils/cache'
|
||||
|
||||
export type Service<TData, TParams extends any[]> = (...args: TParams) => Promise<TData>;
|
||||
export type Subscribe = () => void;
|
||||
export type Service<TData, TParams extends any[]> = (...args: TParams) => Promise<TData>
|
||||
export type Subscribe = () => void
|
||||
|
||||
// for Fetch
|
||||
export interface FetchState<TData, TParams extends any[]> {
|
||||
loading: boolean;
|
||||
params?: TParams;
|
||||
data?: TData;
|
||||
error?: Error;
|
||||
loading: boolean
|
||||
params?: TParams
|
||||
data?: TData
|
||||
error?: Error
|
||||
}
|
||||
|
||||
export interface PluginReturn<TData, TParams extends any[]> {
|
||||
onBefore?: (params: TParams) =>
|
||||
| ({
|
||||
stopNow?: boolean;
|
||||
returnNow?: boolean;
|
||||
stopNow?: boolean
|
||||
returnNow?: boolean
|
||||
} & Partial<FetchState<TData, TParams>>)
|
||||
| void;
|
||||
| void
|
||||
|
||||
onRequest?: (
|
||||
service: Service<TData, TParams>,
|
||||
params: TParams,
|
||||
) => {
|
||||
servicePromise?: Promise<TData>;
|
||||
};
|
||||
servicePromise?: Promise<TData>
|
||||
}
|
||||
|
||||
onSuccess?: (data: TData, params: TParams) => void;
|
||||
onError?: (e: Error, params: TParams) => void;
|
||||
onFinally?: (params: TParams, data?: TData, e?: Error) => void;
|
||||
onCancel?: () => void;
|
||||
onMutate?: (data: TData) => void;
|
||||
onSuccess?: (data: TData, params: TParams) => void
|
||||
onError?: (e: Error, params: TParams) => void
|
||||
onFinally?: (params: TParams, data?: TData, e?: Error) => void
|
||||
onCancel?: () => void
|
||||
onMutate?: (data: TData) => void
|
||||
}
|
||||
|
||||
// for useRequestImplement
|
||||
export interface UseRequestOptions<TData, TParams extends any[]> {
|
||||
manual?: MaybeRef<boolean>;
|
||||
manual?: MaybeRef<boolean>
|
||||
|
||||
onBefore?: (params: TParams) => void;
|
||||
onSuccess?: (data: TData, params: TParams) => void;
|
||||
onError?: (e: Error, params: TParams) => void;
|
||||
onBefore?: (params: TParams) => void
|
||||
onSuccess?: (data: TData, params: TParams) => void
|
||||
onError?: (e: Error, params: TParams) => void
|
||||
// formatResult?: (res: any) => TData;
|
||||
onFinally?: (params: TParams, data?: TData, e?: Error) => void;
|
||||
onFinally?: (params: TParams, data?: TData, e?: Error) => void
|
||||
|
||||
defaultParams?: TParams;
|
||||
defaultParams?: TParams
|
||||
|
||||
// refreshDeps
|
||||
refreshDeps?: WatchSource<any>[];
|
||||
refreshDepsAction?: () => void;
|
||||
refreshDeps?: WatchSource<any>[]
|
||||
refreshDepsAction?: () => void
|
||||
|
||||
// loading delay
|
||||
loadingDelay?: number;
|
||||
loadingDelay?: number
|
||||
|
||||
// polling
|
||||
pollingInterval?: number;
|
||||
pollingWhenHidden?: boolean;
|
||||
pollingErrorRetryCount?: number;
|
||||
pollingInterval?: number
|
||||
pollingWhenHidden?: boolean
|
||||
pollingErrorRetryCount?: number
|
||||
|
||||
// refresh on window focus
|
||||
refreshOnWindowFocus?: boolean;
|
||||
focusTimespan?: number;
|
||||
refreshOnWindowFocus?: boolean
|
||||
focusTimespan?: number
|
||||
|
||||
// debounce
|
||||
debounceWait?: number;
|
||||
debounceLeading?: boolean;
|
||||
debounceTrailing?: boolean;
|
||||
debounceMaxWait?: number;
|
||||
debounceWait?: number
|
||||
debounceLeading?: boolean
|
||||
debounceTrailing?: boolean
|
||||
debounceMaxWait?: number
|
||||
|
||||
// throttle
|
||||
throttleWait?: number;
|
||||
throttleLeading?: boolean;
|
||||
throttleTrailing?: boolean;
|
||||
throttleWait?: number
|
||||
throttleLeading?: boolean
|
||||
throttleTrailing?: boolean
|
||||
|
||||
// cache
|
||||
cacheKey?: string;
|
||||
cacheTime?: number;
|
||||
staleTime?: number;
|
||||
setCache?: (data: CachedData<TData, TParams>) => void;
|
||||
getCache?: (params: TParams) => CachedData<TData, TParams> | undefined;
|
||||
cacheKey?: string
|
||||
cacheTime?: number
|
||||
staleTime?: number
|
||||
setCache?: (data: CachedData<TData, TParams>) => void
|
||||
getCache?: (params: TParams) => CachedData<TData, TParams> | undefined
|
||||
|
||||
// retry
|
||||
retryCount?: number;
|
||||
retryInterval?: number;
|
||||
retryCount?: number
|
||||
retryInterval?: number
|
||||
|
||||
// ready
|
||||
ready?: MaybeRef<boolean>;
|
||||
ready?: MaybeRef<boolean>
|
||||
|
||||
// [key: string]: any;
|
||||
}
|
||||
@@ -97,8 +97,8 @@ export interface UseRequestPlugin<TData, TParams extends any[]> {
|
||||
(
|
||||
fetchInstance: Fetch<TData, TParams>,
|
||||
options: UseRequestOptions<TData, TParams>,
|
||||
): PluginReturn<TData, TParams>;
|
||||
onInit?: (options: UseRequestOptions<TData, TParams>) => Partial<FetchState<TData, TParams>>;
|
||||
): PluginReturn<TData, TParams>
|
||||
onInit?: (options: UseRequestOptions<TData, TParams>) => Partial<FetchState<TData, TParams>>
|
||||
}
|
||||
|
||||
// for index
|
||||
@@ -109,16 +109,16 @@ export interface UseRequestPlugin<TData, TParams extends any[]> {
|
||||
// };
|
||||
|
||||
export interface UseRequestResult<TData, TParams extends any[]> {
|
||||
loading: Ref<boolean>;
|
||||
data: Ref<TData>;
|
||||
error: Ref<Error>;
|
||||
params: Ref<TParams | []>;
|
||||
cancel: Fetch<TData, TParams>['cancel'];
|
||||
refresh: Fetch<TData, TParams>['refresh'];
|
||||
refreshAsync: Fetch<TData, TParams>['refreshAsync'];
|
||||
run: Fetch<TData, TParams>['run'];
|
||||
runAsync: Fetch<TData, TParams>['runAsync'];
|
||||
mutate: Fetch<TData, TParams>['mutate'];
|
||||
loading: Ref<boolean>
|
||||
data: Ref<TData>
|
||||
error: Ref<Error>
|
||||
params: Ref<TParams | []>
|
||||
cancel: Fetch<TData, TParams>['cancel']
|
||||
refresh: Fetch<TData, TParams>['refresh']
|
||||
refreshAsync: Fetch<TData, TParams>['refreshAsync']
|
||||
run: Fetch<TData, TParams>['run']
|
||||
runAsync: Fetch<TData, TParams>['runAsync']
|
||||
mutate: Fetch<TData, TParams>['mutate']
|
||||
}
|
||||
|
||||
export type UseRequestTimeout = ReturnType<typeof setTimeout>;
|
||||
export type UseRequestTimeout = ReturnType<typeof setTimeout>
|
||||
|
@@ -1,41 +1,41 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { onMounted, onUnmounted, toRefs } from 'vue';
|
||||
import { onMounted, onUnmounted, toRefs } from 'vue'
|
||||
|
||||
import Fetch from './Fetch';
|
||||
import type { Service, UseRequestOptions, UseRequestPlugin, UseRequestResult } from './types';
|
||||
import Fetch from './Fetch'
|
||||
import type { Service, UseRequestOptions, UseRequestPlugin, UseRequestResult } from './types'
|
||||
|
||||
export function useRequestImplement<TData, TParams extends any[]>(
|
||||
service: Service<TData, TParams>,
|
||||
options: UseRequestOptions<TData, TParams> = {},
|
||||
plugins: UseRequestPlugin<TData, TParams>[] = [],
|
||||
) {
|
||||
const { manual = false, ...rest } = options;
|
||||
const fetchOptions = { manual, ...rest };
|
||||
const { manual = false, ...rest } = options
|
||||
const fetchOptions = { manual, ...rest }
|
||||
|
||||
const initState = plugins.map((p) => p?.onInit?.(fetchOptions)).filter(Boolean);
|
||||
const initState = plugins.map((p) => p?.onInit?.(fetchOptions)).filter(Boolean)
|
||||
|
||||
const fetchInstance = new Fetch<TData, TParams>(
|
||||
service,
|
||||
fetchOptions,
|
||||
() => {},
|
||||
Object.assign({}, ...initState),
|
||||
);
|
||||
)
|
||||
|
||||
fetchInstance.options = fetchOptions;
|
||||
fetchInstance.options = fetchOptions
|
||||
// run all plugins hooks
|
||||
fetchInstance.pluginImpls = plugins.map((p) => p(fetchInstance, fetchOptions));
|
||||
fetchInstance.pluginImpls = plugins.map((p) => p(fetchInstance, fetchOptions))
|
||||
|
||||
onMounted(() => {
|
||||
if (!manual) {
|
||||
const params = fetchInstance.state.params || options.defaultParams || [];
|
||||
const params = fetchInstance.state.params || options.defaultParams || []
|
||||
// @ts-ignore
|
||||
fetchInstance.run(...params);
|
||||
fetchInstance.run(...params)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
fetchInstance.cancel();
|
||||
});
|
||||
fetchInstance.cancel()
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(fetchInstance.state),
|
||||
@@ -45,5 +45,5 @@ export function useRequestImplement<TData, TParams extends any[]>(
|
||||
refreshAsync: fetchInstance.refreshAsync.bind(fetchInstance),
|
||||
run: fetchInstance.run.bind(fetchInstance),
|
||||
runAsync: fetchInstance.runAsync.bind(fetchInstance),
|
||||
} as UseRequestResult<TData, TParams>;
|
||||
} as UseRequestResult<TData, TParams>
|
||||
}
|
||||
|
@@ -1,48 +1,48 @@
|
||||
type Timer = ReturnType<typeof setTimeout>;
|
||||
type CachedKey = string | number;
|
||||
type Timer = ReturnType<typeof setTimeout>
|
||||
type CachedKey = string | number
|
||||
|
||||
export interface CachedData<TData = any, TParams = any> {
|
||||
data: TData;
|
||||
params: TParams;
|
||||
time: number;
|
||||
data: TData
|
||||
params: TParams
|
||||
time: number
|
||||
}
|
||||
|
||||
interface RecordData extends CachedData {
|
||||
timer: Timer | undefined;
|
||||
timer: Timer | undefined
|
||||
}
|
||||
|
||||
const cache = new Map<CachedKey, RecordData>();
|
||||
const cache = new Map<CachedKey, RecordData>()
|
||||
|
||||
export const setCache = (key: CachedKey, cacheTime: number, cachedData: CachedData) => {
|
||||
const currentCache = cache.get(key);
|
||||
const currentCache = cache.get(key)
|
||||
if (currentCache?.timer) {
|
||||
clearTimeout(currentCache.timer);
|
||||
clearTimeout(currentCache.timer)
|
||||
}
|
||||
|
||||
let timer: Timer | undefined = undefined;
|
||||
let timer: Timer | undefined = undefined
|
||||
|
||||
if (cacheTime > -1) {
|
||||
// if cache out, clear it
|
||||
timer = setTimeout(() => {
|
||||
cache.delete(key);
|
||||
}, cacheTime);
|
||||
cache.delete(key)
|
||||
}, cacheTime)
|
||||
}
|
||||
|
||||
cache.set(key, {
|
||||
...cachedData,
|
||||
timer,
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
export const getCache = (key: CachedKey) => {
|
||||
return cache.get(key);
|
||||
};
|
||||
return cache.get(key)
|
||||
}
|
||||
|
||||
export const clearCache = (key?: string | string[]) => {
|
||||
if (key) {
|
||||
const cacheKeys = Array.isArray(key) ? key : [key];
|
||||
cacheKeys.forEach((cacheKey) => cache.delete(cacheKey));
|
||||
const cacheKeys = Array.isArray(key) ? key : [key]
|
||||
cacheKeys.forEach((cacheKey) => cache.delete(cacheKey))
|
||||
} else {
|
||||
cache.clear();
|
||||
cache.clear()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -1,23 +1,23 @@
|
||||
type CachedKey = string | number;
|
||||
type CachedKey = string | number
|
||||
|
||||
const cachePromise = new Map<CachedKey, Promise<any>>();
|
||||
const cachePromise = new Map<CachedKey, Promise<any>>()
|
||||
|
||||
export const getCachePromise = (cacheKey: CachedKey) => {
|
||||
return cachePromise.get(cacheKey);
|
||||
};
|
||||
return cachePromise.get(cacheKey)
|
||||
}
|
||||
|
||||
export const setCachePromise = (cacheKey: CachedKey, promise: Promise<any>) => {
|
||||
// Should cache the same promise, cannot be promise.finally
|
||||
// Because the promise.finally will change the reference of the promise
|
||||
cachePromise.set(cacheKey, promise);
|
||||
cachePromise.set(cacheKey, promise)
|
||||
|
||||
// no use promise.finally for compatibility
|
||||
promise
|
||||
.then((res) => {
|
||||
cachePromise.delete(cacheKey);
|
||||
return res;
|
||||
cachePromise.delete(cacheKey)
|
||||
return res
|
||||
})
|
||||
.catch(() => {
|
||||
cachePromise.delete(cacheKey);
|
||||
});
|
||||
};
|
||||
cachePromise.delete(cacheKey)
|
||||
})
|
||||
}
|
||||
|
@@ -1,22 +1,22 @@
|
||||
type Listener = (data: any) => void;
|
||||
type Listener = (data: any) => void
|
||||
|
||||
const listeners: Record<string, Listener[]> = {};
|
||||
const listeners: Record<string, Listener[]> = {}
|
||||
|
||||
export const trigger = (key: string, data: any) => {
|
||||
if (listeners[key]) {
|
||||
listeners[key].forEach((item) => item(data));
|
||||
listeners[key].forEach((item) => item(data))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const subscribe = (key: string, listener: Listener) => {
|
||||
if (!listeners[key]) {
|
||||
listeners[key] = [];
|
||||
listeners[key] = []
|
||||
}
|
||||
|
||||
listeners[key].push(listener);
|
||||
listeners[key].push(listener)
|
||||
|
||||
return function unsubscribe() {
|
||||
const index = listeners[key].indexOf(listener);
|
||||
listeners[key].splice(index, 1);
|
||||
};
|
||||
};
|
||||
const index = listeners[key].indexOf(listener)
|
||||
listeners[key].splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
@@ -2,4 +2,4 @@ export const isBrowser = !!(
|
||||
typeof window !== 'undefined' &&
|
||||
window.document &&
|
||||
window.document.createElement
|
||||
);
|
||||
)
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { isBrowser } from './isBrowser';
|
||||
import { isBrowser } from './isBrowser'
|
||||
|
||||
export function isDocumentVisible(): boolean {
|
||||
if (isBrowser) {
|
||||
return document.visibilityState !== 'hidden';
|
||||
return document.visibilityState !== 'hidden'
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
@@ -1,2 +1,2 @@
|
||||
export const isFunction = (value: unknown): value is (...args: any) => any =>
|
||||
typeof value === 'function';
|
||||
typeof value === 'function'
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { isBrowser } from './isBrowser';
|
||||
import { isBrowser } from './isBrowser'
|
||||
|
||||
export function isOnline(): boolean {
|
||||
if (isBrowser && typeof navigator.onLine !== 'undefined') {
|
||||
return navigator.onLine;
|
||||
return navigator.onLine
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
export function limit(fn: any, timespan: number) {
|
||||
let pending = false;
|
||||
let pending = false
|
||||
|
||||
return (...args: any[]) => {
|
||||
if (pending) return;
|
||||
pending = true;
|
||||
fn(...args);
|
||||
if (pending) return
|
||||
pending = true
|
||||
fn(...args)
|
||||
setTimeout(() => {
|
||||
pending = false;
|
||||
}, timespan);
|
||||
};
|
||||
pending = false
|
||||
}, timespan)
|
||||
}
|
||||
}
|
||||
|
@@ -1,30 +1,30 @@
|
||||
import { isBrowser } from './isBrowser';
|
||||
import { isDocumentVisible } from './isDocumentVisible';
|
||||
import { isOnline } from './isOnline';
|
||||
import { isBrowser } from './isBrowser'
|
||||
import { isDocumentVisible } from './isDocumentVisible'
|
||||
import { isOnline } from './isOnline'
|
||||
|
||||
type Listener = () => void;
|
||||
type Listener = () => void
|
||||
|
||||
const listeners: Listener[] = [];
|
||||
const listeners: Listener[] = []
|
||||
|
||||
if (isBrowser) {
|
||||
const revalidate = () => {
|
||||
if (!isDocumentVisible() || !isOnline()) return;
|
||||
if (!isDocumentVisible() || !isOnline()) return
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
const listener = listeners[i];
|
||||
listener();
|
||||
const listener = listeners[i]
|
||||
listener()
|
||||
}
|
||||
};
|
||||
window.addEventListener('visibilitychange', revalidate, false);
|
||||
window.addEventListener('focus', revalidate, false);
|
||||
}
|
||||
window.addEventListener('visibilitychange', revalidate, false)
|
||||
window.addEventListener('focus', revalidate, false)
|
||||
}
|
||||
|
||||
export default function subscribe(listener: Listener) {
|
||||
listeners.push(listener);
|
||||
listeners.push(listener)
|
||||
|
||||
return function unsubscribe() {
|
||||
const index = listeners.indexOf(listener);
|
||||
const index = listeners.indexOf(listener)
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1);
|
||||
listeners.splice(index, 1)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -1,25 +1,25 @@
|
||||
import { isBrowser } from './isBrowser';
|
||||
import { isDocumentVisible } from './isDocumentVisible';
|
||||
import { isBrowser } from './isBrowser'
|
||||
import { isDocumentVisible } from './isDocumentVisible'
|
||||
|
||||
type Listener = () => void;
|
||||
type Listener = () => void
|
||||
|
||||
const listeners: Listener[] = [];
|
||||
const listeners: Listener[] = []
|
||||
|
||||
if (isBrowser) {
|
||||
const revalidate = () => {
|
||||
if (!isDocumentVisible()) return;
|
||||
if (!isDocumentVisible()) return
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
const listener = listeners[i];
|
||||
listener();
|
||||
const listener = listeners[i]
|
||||
listener()
|
||||
}
|
||||
};
|
||||
window.addEventListener('visibilitychange', revalidate, false);
|
||||
}
|
||||
window.addEventListener('visibilitychange', revalidate, false)
|
||||
}
|
||||
|
||||
export default function subscribe(listener: Listener) {
|
||||
listeners.push(listener);
|
||||
listeners.push(listener)
|
||||
return function unsubscribe() {
|
||||
const index = listeners.indexOf(listener);
|
||||
listeners.splice(index, 1);
|
||||
};
|
||||
const index = listeners.indexOf(listener)
|
||||
listeners.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
@@ -1,60 +1,60 @@
|
||||
import { shallowRef, unref } from 'vue';
|
||||
import { shallowRef, unref } from 'vue'
|
||||
|
||||
interface UseScrollToOptions {
|
||||
el: any;
|
||||
to: number;
|
||||
duration?: number;
|
||||
callback?: () => any;
|
||||
el: any
|
||||
to: number
|
||||
duration?: number
|
||||
callback?: () => any
|
||||
}
|
||||
|
||||
function easeInOutQuad(t: number, b: number, c: number, d: number) {
|
||||
t /= d / 2;
|
||||
t /= d / 2
|
||||
if (t < 1) {
|
||||
return (c / 2) * t * t + b;
|
||||
return (c / 2) * t * t + b
|
||||
}
|
||||
t--;
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b;
|
||||
t--
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b
|
||||
}
|
||||
|
||||
function move(el: HTMLElement, amount: number) {
|
||||
el.scrollTop = amount;
|
||||
el.scrollTop = amount
|
||||
}
|
||||
|
||||
const position = (el: HTMLElement) => {
|
||||
return el.scrollTop;
|
||||
};
|
||||
return el.scrollTop
|
||||
}
|
||||
function useScrollTo({ el, to, duration = 500, callback }: UseScrollToOptions) {
|
||||
const isActiveRef = shallowRef(false);
|
||||
const start = position(el);
|
||||
const change = to - start;
|
||||
const increment = 20;
|
||||
let currentTime = 0;
|
||||
const isActiveRef = shallowRef(false)
|
||||
const start = position(el)
|
||||
const change = to - start
|
||||
const increment = 20
|
||||
let currentTime = 0
|
||||
|
||||
const animateScroll = function () {
|
||||
if (!unref(isActiveRef)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
currentTime += increment;
|
||||
const val = easeInOutQuad(currentTime, start, change, duration);
|
||||
move(el, val);
|
||||
currentTime += increment
|
||||
const val = easeInOutQuad(currentTime, start, change, duration)
|
||||
move(el, val)
|
||||
if (currentTime < duration && unref(isActiveRef)) {
|
||||
requestAnimationFrame(animateScroll);
|
||||
requestAnimationFrame(animateScroll)
|
||||
} else {
|
||||
if (callback && typeof callback === 'function') {
|
||||
callback();
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const run = () => {
|
||||
isActiveRef.value = true;
|
||||
animateScroll();
|
||||
};
|
||||
isActiveRef.value = true
|
||||
animateScroll()
|
||||
}
|
||||
|
||||
const stop = () => {
|
||||
isActiveRef.value = false;
|
||||
};
|
||||
|
||||
return { start: run, stop };
|
||||
isActiveRef.value = false
|
||||
}
|
||||
|
||||
export { useScrollTo, type UseScrollToOptions };
|
||||
return { start: run, stop }
|
||||
}
|
||||
|
||||
export { useScrollTo, type UseScrollToOptions }
|
||||
|
@@ -1,40 +1,40 @@
|
||||
import { type AnyFunction } from '@vben/types';
|
||||
import { tryOnMounted, tryOnUnmounted, useDebounceFn } from '@vueuse/core';
|
||||
import { type AnyFunction } from '@vben/types'
|
||||
import { tryOnMounted, tryOnUnmounted, useDebounceFn } from '@vueuse/core'
|
||||
|
||||
interface UseWindowSizeOptions {
|
||||
wait?: number;
|
||||
once?: boolean;
|
||||
immediate?: boolean;
|
||||
listenerOptions?: AddEventListenerOptions | boolean;
|
||||
wait?: number
|
||||
once?: boolean
|
||||
immediate?: boolean
|
||||
listenerOptions?: AddEventListenerOptions | boolean
|
||||
}
|
||||
|
||||
function useWindowSizeFn(fn: AnyFunction, options: UseWindowSizeOptions = {}) {
|
||||
const { wait = 150, immediate } = options;
|
||||
const { wait = 150, immediate } = options
|
||||
let handler = () => {
|
||||
fn();
|
||||
};
|
||||
const handleSize = useDebounceFn(handler, wait);
|
||||
handler = handleSize;
|
||||
fn()
|
||||
}
|
||||
const handleSize = useDebounceFn(handler, wait)
|
||||
handler = handleSize
|
||||
|
||||
const start = () => {
|
||||
if (immediate) {
|
||||
handler();
|
||||
handler()
|
||||
}
|
||||
window.addEventListener('resize', handler)
|
||||
}
|
||||
window.addEventListener('resize', handler);
|
||||
};
|
||||
|
||||
const stop = () => {
|
||||
window.removeEventListener('resize', handler);
|
||||
};
|
||||
|
||||
tryOnMounted(() => {
|
||||
start();
|
||||
});
|
||||
|
||||
tryOnUnmounted(() => {
|
||||
stop();
|
||||
});
|
||||
return { start, stop };
|
||||
window.removeEventListener('resize', handler)
|
||||
}
|
||||
|
||||
export { useWindowSizeFn, type UseWindowSizeOptions };
|
||||
tryOnMounted(() => {
|
||||
start()
|
||||
})
|
||||
|
||||
tryOnUnmounted(() => {
|
||||
stop()
|
||||
})
|
||||
return { start, stop }
|
||||
}
|
||||
|
||||
export { useWindowSizeFn, type UseWindowSizeOptions }
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
import { defineBuildConfig } from 'unbuild'
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
@@ -7,4 +7,4 @@ export default defineBuildConfig({
|
||||
rollup: {
|
||||
emitCJS: true,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
@@ -1 +1 @@
|
||||
export * from './utils';
|
||||
export * from './utils'
|
||||
|
@@ -1,49 +1,49 @@
|
||||
/**
|
||||
* 任意类型的异步函数
|
||||
*/
|
||||
type AnyPromiseFunction = (...arg: any[]) => PromiseLike<any>;
|
||||
type AnyPromiseFunction = (...arg: any[]) => PromiseLike<any>
|
||||
|
||||
/**
|
||||
* 任意类型的普通函数
|
||||
*/
|
||||
type AnyNormalFunction = (...arg: any[]) => any;
|
||||
type AnyNormalFunction = (...arg: any[]) => any
|
||||
|
||||
/**
|
||||
* 任意类型的函数
|
||||
*/
|
||||
type AnyFunction = AnyNormalFunction | AnyPromiseFunction;
|
||||
type AnyFunction = AnyNormalFunction | AnyPromiseFunction
|
||||
|
||||
/**
|
||||
* T | null 包装
|
||||
*/
|
||||
type Nullable<T> = T | null;
|
||||
type Nullable<T> = T | null
|
||||
|
||||
/**
|
||||
* T | Not null 包装
|
||||
*/
|
||||
type NonNullable<T> = T extends null | undefined ? never : T;
|
||||
type NonNullable<T> = T extends null | undefined ? never : T
|
||||
|
||||
/**
|
||||
* 字符串类型对象
|
||||
*/
|
||||
type Recordable<T = any> = Record<string, T>;
|
||||
type Recordable<T = any> = Record<string, T>
|
||||
|
||||
/**
|
||||
* 字符串类型对象(只读)
|
||||
*/
|
||||
interface ReadonlyRecordable<T = any> {
|
||||
readonly [key: string]: T;
|
||||
readonly [key: string]: T
|
||||
}
|
||||
|
||||
/**
|
||||
* setTimeout 返回值类型
|
||||
*/
|
||||
type TimeoutHandle = ReturnType<typeof setTimeout>;
|
||||
type TimeoutHandle = ReturnType<typeof setTimeout>
|
||||
|
||||
/**
|
||||
* setInterval 返回值类型
|
||||
*/
|
||||
type IntervalHandle = ReturnType<typeof setInterval>;
|
||||
type IntervalHandle = ReturnType<typeof setInterval>
|
||||
|
||||
export {
|
||||
type AnyFunction,
|
||||
@@ -55,4 +55,4 @@ export {
|
||||
type ReadonlyRecordable,
|
||||
type Recordable,
|
||||
type TimeoutHandle,
|
||||
};
|
||||
}
|
||||
|
49
pnpm-lock.yaml
generated
49
pnpm-lock.yaml
generated
@@ -83,9 +83,6 @@ importers:
|
||||
vue:
|
||||
specifier: ^3.4.25
|
||||
version: 3.4.25(typescript@5.4.5)
|
||||
vue-i18n:
|
||||
specifier: ^9.13.1
|
||||
version: 9.13.1(vue@3.4.25(typescript@5.4.5))
|
||||
vue-json-pretty:
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0(vue@3.4.25(typescript@5.4.5))
|
||||
@@ -102,8 +99,8 @@ importers:
|
||||
specifier: ^4.7.31
|
||||
version: 4.7.40
|
||||
xe-utils:
|
||||
specifier: ^3.5.25
|
||||
version: 3.5.25
|
||||
specifier: ^3.5.28
|
||||
version: 3.5.28
|
||||
devDependencies:
|
||||
'@commitlint/cli':
|
||||
specifier: ^19.3.0
|
||||
@@ -1081,18 +1078,6 @@ packages:
|
||||
'@iconify/utils@2.1.23':
|
||||
resolution: {integrity: sha512-YGNbHKM5tyDvdWZ92y2mIkrfvm5Fvhe6WJSkWu7vvOFhMtYDP0casZpoRz0XEHZCrYsR4stdGT3cZ52yp5qZdQ==}
|
||||
|
||||
'@intlify/core-base@9.13.1':
|
||||
resolution: {integrity: sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/message-compiler@9.13.1':
|
||||
resolution: {integrity: sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/shared@9.13.1':
|
||||
resolution: {integrity: sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -5914,12 +5899,6 @@ packages:
|
||||
peerDependencies:
|
||||
eslint: '>=6.0.0'
|
||||
|
||||
vue-i18n@9.13.1:
|
||||
resolution: {integrity: sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==}
|
||||
engines: {node: '>= 16'}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
vue-json-pretty@2.4.0:
|
||||
resolution: {integrity: sha512-e9bP41DYYIc2tWaB6KuwqFJq5odZ8/GkE6vHQuGcbPn37kGk4a3n1RNw3ZYeDrl66NWXgTlOfS+M6NKkowmkWw==}
|
||||
engines: {node: '>= 10.0.0', npm: '>= 5.0.0'}
|
||||
@@ -6076,9 +6055,6 @@ packages:
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
xe-utils@3.5.25:
|
||||
resolution: {integrity: sha512-d/ty5eo4hXtho/3195XAvqereIoSYJ+XfC52f3ZEPxTaCeyLFivDZTyX6gTdsR65ISH1Irvn85H0bSL60dUhSQ==}
|
||||
|
||||
xe-utils@3.5.28:
|
||||
resolution: {integrity: sha512-oeLLJ0b54QdOSSgYQ9TiKW/xAGrc9r0weCA/5UfyGdm3n3js4cNOuuf9Tml7UwgBQpl4uWMbMwUZKLh2yqPF3A==}
|
||||
|
||||
@@ -6759,18 +6735,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@intlify/core-base@9.13.1':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 9.13.1
|
||||
'@intlify/shared': 9.13.1
|
||||
|
||||
'@intlify/message-compiler@9.13.1':
|
||||
dependencies:
|
||||
'@intlify/shared': 9.13.1
|
||||
source-map-js: 1.2.0
|
||||
|
||||
'@intlify/shared@9.13.1': {}
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
dependencies:
|
||||
string-width: 5.1.2
|
||||
@@ -12351,13 +12315,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vue-i18n@9.13.1(vue@3.4.25(typescript@5.4.5)):
|
||||
dependencies:
|
||||
'@intlify/core-base': 9.13.1
|
||||
'@intlify/shared': 9.13.1
|
||||
'@vue/devtools-api': 6.6.1
|
||||
vue: 3.4.25(typescript@5.4.5)
|
||||
|
||||
vue-json-pretty@2.4.0(vue@3.4.25(typescript@5.4.5)):
|
||||
dependencies:
|
||||
vue: 3.4.25(typescript@5.4.5)
|
||||
@@ -12504,8 +12461,6 @@ snapshots:
|
||||
|
||||
ws@8.16.0: {}
|
||||
|
||||
xe-utils@3.5.25: {}
|
||||
|
||||
xe-utils@3.5.28: {}
|
||||
|
||||
xml-name-validator@4.0.0: {}
|
||||
|
@@ -40,7 +40,10 @@ export const getFileDownloadUrl = (id) => {
|
||||
* @param params
|
||||
* @param onUploadProgress
|
||||
*/
|
||||
export function uploadFile(params: UploadFileParams, onUploadProgress: (progressEvent: AxiosProgressEvent) => void) {
|
||||
export function uploadFile(
|
||||
params: UploadFileParams,
|
||||
onUploadProgress: (progressEvent: AxiosProgressEvent) => void,
|
||||
) {
|
||||
return defHttp.uploadFile<UploadApiResult>(
|
||||
{
|
||||
url: VITE_GLOB_API_URL + VITE_GLOB_API_URL_PREFIX + '/file/upload',
|
||||
|
@@ -18,5 +18,8 @@ export const getMenuList = () => {
|
||||
* 获取菜单和权限码
|
||||
*/
|
||||
export const getPermissions = (clientCode: string) => {
|
||||
return defHttp.get<Result<MenuAndResource>>({ url: '/role/menu/getPermissions', params: { clientCode } })
|
||||
return defHttp.get<Result<MenuAndResource>>({
|
||||
url: '/role/menu/getPermissions',
|
||||
params: { clientCode },
|
||||
})
|
||||
}
|
||||
|
@@ -9,7 +9,10 @@ const { uploadUrl = '' } = useGlobSetting()
|
||||
/**
|
||||
* @description: Upload interface
|
||||
*/
|
||||
export function uploadApi(params: UploadFileParams, onUploadProgress: (progressEvent: AxiosProgressEvent) => void) {
|
||||
export function uploadApi(
|
||||
params: UploadFileParams,
|
||||
onUploadProgress: (progressEvent: AxiosProgressEvent) => void,
|
||||
) {
|
||||
return defHttp.uploadFile<UploadApiResult>(
|
||||
{
|
||||
url: uploadUrl,
|
||||
|
@@ -40,7 +40,11 @@
|
||||
const { title } = useGlobSetting()
|
||||
const go = useGo()
|
||||
|
||||
const getAppLogoClass = computed(() => [prefixCls, props.theme, { 'collapsed-show-title': unref(getCollapsedShowTitle) }])
|
||||
const getAppLogoClass = computed(() => [
|
||||
prefixCls,
|
||||
props.theme,
|
||||
{ 'collapsed-show-title': unref(getCollapsedShowTitle) },
|
||||
])
|
||||
|
||||
const getTitleClass = computed(() => [
|
||||
`${prefixCls}__title`,
|
||||
|
@@ -45,7 +45,12 @@
|
||||
if (!unref(isSetState)) {
|
||||
isSetState.value = true
|
||||
const {
|
||||
menuSetting: { type: menuType, mode: menuMode, collapsed: menuCollapsed, split: menuSplit },
|
||||
menuSetting: {
|
||||
type: menuType,
|
||||
mode: menuMode,
|
||||
collapsed: menuCollapsed,
|
||||
split: menuSplit,
|
||||
},
|
||||
} = appStore.getProjectConfig
|
||||
appStore.setProjectConfig({
|
||||
menuSetting: {
|
||||
|
@@ -4,7 +4,13 @@
|
||||
<div :class="getClass" @click.stop v-if="visible">
|
||||
<div :class="`${prefixCls}-content`" v-click-outside="handleClose">
|
||||
<div :class="`${prefixCls}-input__wrapper`">
|
||||
<a-input :class="`${prefixCls}-input`" :placeholder="'搜索'" ref="inputRef" allow-clear @change="handleSearch">
|
||||
<a-input
|
||||
:class="`${prefixCls}-input`"
|
||||
:placeholder="'搜索'"
|
||||
ref="inputRef"
|
||||
allow-clear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
@@ -34,7 +40,11 @@
|
||||
</div>
|
||||
<div :class="`${prefixCls}-list__item-text`">
|
||||
<!-- 搜索结果包含的字符着色 -->
|
||||
<span v-for="(each, i) in item.chars" :key="i" :class="{ highlight: each.highlight }">
|
||||
<span
|
||||
v-for="(each, i) in item.chars"
|
||||
:key="i"
|
||||
:class="{ highlight: each.highlight }"
|
||||
>
|
||||
{{ each.char }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -74,7 +84,8 @@
|
||||
const { refs, setRefs } = useRefs()
|
||||
const { getIsMobile } = useAppInject()
|
||||
|
||||
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } = useMenuSearch(refs, scrollWrap, emit)
|
||||
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } =
|
||||
useMenuSearch(refs, scrollWrap, emit)
|
||||
|
||||
const getIsNotData = computed(() => !keyword || unref(searchResult).length === 0)
|
||||
|
||||
|
@@ -66,7 +66,11 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref, emit: A
|
||||
const ret: SearchResult[] = []
|
||||
filterMenu.forEach((item) => {
|
||||
const { name, path, icon, children, hideMenu, meta } = item
|
||||
if (!hideMenu && reg.test(name?.toLowerCase() ?? '') && (!children?.length || meta?.hideChildrenInMenu)) {
|
||||
if (
|
||||
!hideMenu &&
|
||||
reg.test(name?.toLowerCase() ?? '') &&
|
||||
(!children?.length || meta?.hideChildrenInMenu)
|
||||
) {
|
||||
const chars: { char: string; highlight: boolean }[] = []
|
||||
|
||||
// 显示字符串
|
||||
@@ -84,7 +88,10 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref, emit: A
|
||||
// 优先查找完整关键词的匹配
|
||||
if (label.toLowerCase().includes(keyword.value.toLowerCase())) {
|
||||
while (includePointer < labelChars.length) {
|
||||
if (label.toLowerCase().slice(includePointer, includePointer + keywordLength) === keyword.value.toLowerCase()) {
|
||||
if (
|
||||
label.toLowerCase().slice(includePointer, includePointer + keywordLength) ===
|
||||
keyword.value.toLowerCase()
|
||||
) {
|
||||
chars.push(
|
||||
...label
|
||||
.substring(labelPointer, includePointer)
|
||||
@@ -116,7 +123,10 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref, emit: A
|
||||
keywordPointer = 0
|
||||
while (keywordPointer < keywordChars.length) {
|
||||
if (keywordChars[keywordPointer] !== void 0 && labelChars[labelPointer] !== void 0) {
|
||||
if (keywordChars[keywordPointer].toLowerCase() === labelChars[labelPointer].toLowerCase()) {
|
||||
if (
|
||||
keywordChars[keywordPointer].toLowerCase() ===
|
||||
labelChars[labelPointer].toLowerCase()
|
||||
) {
|
||||
chars.push({
|
||||
char: labelChars[labelPointer],
|
||||
highlight: true,
|
||||
@@ -148,12 +158,17 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref, emit: A
|
||||
|
||||
// 排序
|
||||
return ret.sort((a, b) => {
|
||||
if (a.name.toLowerCase().includes(keyword.value.toLowerCase()) && b.name.toLowerCase().includes(keyword.value.toLowerCase())) {
|
||||
if (
|
||||
a.name.toLowerCase().includes(keyword.value.toLowerCase()) &&
|
||||
b.name.toLowerCase().includes(keyword.value.toLowerCase())
|
||||
) {
|
||||
// 两者都存在完整关键词的匹配
|
||||
|
||||
// 匹配数量
|
||||
const ca = a.name.toLowerCase().match(new RegExp(keyword.value.toLowerCase(), 'g'))?.length ?? 0
|
||||
const cb = b.name.toLowerCase().match(new RegExp(keyword.value.toLowerCase(), 'g'))?.length ?? 0
|
||||
const ca =
|
||||
a.name.toLowerCase().match(new RegExp(keyword.value.toLowerCase(), 'g'))?.length ?? 0
|
||||
const cb =
|
||||
b.name.toLowerCase().match(new RegExp(keyword.value.toLowerCase(), 'g'))?.length ?? 0
|
||||
|
||||
// 匹配数量越多的优先显示,数量相同的按字符串排序
|
||||
return ca === cb ? a.name.toLowerCase().localeCompare(b.name.toLowerCase()) : cb - ca
|
||||
|
@@ -48,7 +48,9 @@
|
||||
setup(props, { slots }) {
|
||||
const { prefixCls } = useDesign('basic-help')
|
||||
|
||||
const getTooltipStyle = computed((): CSSProperties => ({ color: props.color, fontSize: props.fontSize }))
|
||||
const getTooltipStyle = computed(
|
||||
(): CSSProperties => ({ color: props.color, fontSize: props.fontSize }),
|
||||
)
|
||||
|
||||
const getOverlayStyle = computed((): CSSProperties => ({ maxWidth: props.maxWidth }))
|
||||
|
||||
|
@@ -7,7 +7,16 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { type PropType, ref, onMounted, onUnmounted, watchEffect, watch, unref, nextTick } from 'vue'
|
||||
import {
|
||||
type PropType,
|
||||
ref,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
watchEffect,
|
||||
watch,
|
||||
unref,
|
||||
nextTick,
|
||||
} from 'vue'
|
||||
import type { Nullable } from '@vben/types'
|
||||
import { useWindowSizeFn } from '@vben/hooks'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
@@ -81,7 +90,10 @@
|
||||
)
|
||||
|
||||
function setTheme() {
|
||||
unref(editor)?.setOption('theme', appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight')
|
||||
unref(editor)?.setOption(
|
||||
'theme',
|
||||
appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight',
|
||||
)
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
|
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<Scrollbar ref="scrollbarRef" class="scroll-container" :scrollHeight="scrollHeight" v-bind="$attrs">
|
||||
<Scrollbar
|
||||
ref="scrollbarRef"
|
||||
class="scroll-container"
|
||||
:scrollHeight="scrollHeight"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot></slot>
|
||||
</Scrollbar>
|
||||
</template>
|
||||
|
@@ -33,7 +33,9 @@
|
||||
<div class={`${unref(_prefixCls)}__action`}>
|
||||
{slots.action
|
||||
? slots.action({ expand: props.show, onClick: () => emit('expand') })
|
||||
: props.canExpand && <BasicArrow up expand={props.show} onClick={() => emit('expand')} />}
|
||||
: props.canExpand && (
|
||||
<BasicArrow up expand={props.show} onClick={() => emit('expand')} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@@ -31,7 +31,11 @@
|
||||
const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
|
||||
const { item } = props
|
||||
return (
|
||||
<span style="display: inline-block; width: 100%; " class="px-4" onClick={props.handler.bind(null, item)}>
|
||||
<span
|
||||
style="display: inline-block; width: 100%; "
|
||||
class="px-4"
|
||||
onClick={props.handler.bind(null, item)}
|
||||
>
|
||||
{props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />}
|
||||
<span>{item.label}</span>
|
||||
</span>
|
||||
@@ -123,7 +127,13 @@
|
||||
}
|
||||
const { items } = props
|
||||
return (
|
||||
<Menu inlineIndent={12} mode="vertical" class={prefixCls} ref={wrapRef} style={unref(getStyle)}>
|
||||
<Menu
|
||||
inlineIndent={12}
|
||||
mode="vertical"
|
||||
class={prefixCls}
|
||||
ref={wrapRef}
|
||||
style={unref(getStyle)}
|
||||
>
|
||||
{renderMenuItem(items)}
|
||||
</Menu>
|
||||
)
|
||||
|
@@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<div :class="getClass" :style="getWrapperStyle">
|
||||
<img v-show="isReady" ref="imgElRef" :src="src" :alt="alt" :crossorigin="crossorigin" :style="getImageStyle" />
|
||||
<img
|
||||
v-show="isReady"
|
||||
ref="imgElRef"
|
||||
:src="src"
|
||||
:alt="alt"
|
||||
:crossorigin="crossorigin"
|
||||
:style="getImageStyle"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
@@ -2,15 +2,31 @@
|
||||
<div :class="getClass" :style="getStyle">
|
||||
<div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal()">
|
||||
<div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle">
|
||||
<Icon icon="ant-design:cloud-upload-outlined" :size="getIconWidth" :style="getImageWrapperStyle" color="#d6d6d6" />
|
||||
<Icon
|
||||
icon="ant-design:cloud-upload-outlined"
|
||||
:size="getIconWidth"
|
||||
:style="getImageWrapperStyle"
|
||||
color="#d6d6d6"
|
||||
/>
|
||||
</div>
|
||||
<img :src="sourceValue" v-if="sourceValue" alt="avatar" />
|
||||
</div>
|
||||
<a-button :class="`${prefixCls}-upload-btn`" @click="openModal" v-if="showBtn" v-bind="btnProps">
|
||||
<a-button
|
||||
:class="`${prefixCls}-upload-btn`"
|
||||
@click="openModal"
|
||||
v-if="showBtn"
|
||||
v-bind="btnProps"
|
||||
>
|
||||
{{ btnText ? btnText : '选择图片' }}
|
||||
</a-button>
|
||||
|
||||
<CropperModal @register="register" @upload-success="handleUploadSuccess" :uploadApi="uploadApi" :src="sourceValue" :size="size" />
|
||||
<CropperModal
|
||||
@register="register"
|
||||
@upload-success="handleUploadSuccess"
|
||||
:uploadApi="uploadApi"
|
||||
:src="sourceValue"
|
||||
:size="size"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@@ -52,7 +68,9 @@
|
||||
|
||||
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }))
|
||||
|
||||
const getImageWrapperStyle = computed((): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }))
|
||||
const getImageWrapperStyle = computed(
|
||||
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }),
|
||||
)
|
||||
|
||||
watchEffect(() => {
|
||||
sourceValue.value = props.value || ''
|
||||
|
@@ -1,9 +1,24 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="register" title="头像上传" width="800px" :canFullscreen="false" @ok="handleOk" okText="确认并上传">
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="register"
|
||||
title="头像上传"
|
||||
width="800px"
|
||||
:canFullscreen="false"
|
||||
@ok="handleOk"
|
||||
okText="确认并上传"
|
||||
>
|
||||
<div :class="prefixCls">
|
||||
<div :class="`${prefixCls}-left`">
|
||||
<div :class="`${prefixCls}-cropper`">
|
||||
<CropperImage v-if="src" :src="src" height="300px" :circled="circled" @cropend="handleCropend" @ready="handleReady" />
|
||||
<CropperImage
|
||||
v-if="src"
|
||||
:src="src"
|
||||
height="300px"
|
||||
:circled="circled"
|
||||
@cropend="handleCropend"
|
||||
@ready="handleReady"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div :class="`${prefixCls}-toolbar`">
|
||||
@@ -41,10 +56,22 @@
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="水平翻转" placement="bottom">
|
||||
<a-button type="primary" preIcon="vaadin:arrows-long-h" size="small" :disabled="!src" @click="handlerToolbar('scaleX')" />
|
||||
<a-button
|
||||
type="primary"
|
||||
preIcon="vaadin:arrows-long-h"
|
||||
size="small"
|
||||
:disabled="!src"
|
||||
@click="handlerToolbar('scaleX')"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="垂直翻转" placement="bottom">
|
||||
<a-button type="primary" preIcon="vaadin:arrows-long-v" size="small" :disabled="!src" @click="handlerToolbar('scaleY')" />
|
||||
<a-button
|
||||
type="primary"
|
||||
preIcon="vaadin:arrows-long-v"
|
||||
size="small"
|
||||
:disabled="!src"
|
||||
@click="handlerToolbar('scaleY')"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="放大" placement="bottom">
|
||||
<a-button
|
||||
@@ -192,8 +219,20 @@
|
||||
&-cropper {
|
||||
height: 300px;
|
||||
background: #eee;
|
||||
background-image: linear-gradient(45deg, rgb(0 0 0 / 25%) 25%, transparent 0, transparent 75%, rgb(0 0 0 / 25%) 0),
|
||||
linear-gradient(45deg, rgb(0 0 0 / 25%) 25%, transparent 0, transparent 75%, rgb(0 0 0 / 25%) 0);
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
rgb(0 0 0 / 25%) 25%,
|
||||
transparent 0,
|
||||
transparent 75%,
|
||||
rgb(0 0 0 / 25%) 0
|
||||
),
|
||||
linear-gradient(
|
||||
45deg,
|
||||
rgb(0 0 0 / 25%) 25%,
|
||||
transparent 0,
|
||||
transparent 75%,
|
||||
rgb(0 0 0 / 25%) 0
|
||||
);
|
||||
background-position:
|
||||
0 0,
|
||||
12px 12px;
|
||||
|
@@ -8,7 +8,15 @@
|
||||
import { Descriptions } from 'ant-design-vue'
|
||||
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions'
|
||||
import { get } from 'lodash-es'
|
||||
import { computed, defineComponent, ref, toRefs, unref, type CSSProperties, type PropType } from 'vue'
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
ref,
|
||||
toRefs,
|
||||
unref,
|
||||
type CSSProperties,
|
||||
type PropType,
|
||||
} from 'vue'
|
||||
import type { DescInstance, DescItem, DescriptionProps } from './typing'
|
||||
|
||||
const props = {
|
||||
|
@@ -12,7 +12,10 @@ export interface DescItem {
|
||||
span?: number
|
||||
show?: (...arg: any) => boolean
|
||||
// render
|
||||
render?: (val: any, data: Recordable) => VNode | undefined | JSX.Element | Element | string | number
|
||||
render?: (
|
||||
val: any,
|
||||
data: Recordable,
|
||||
) => VNode | undefined | JSX.Element | Element | string | number
|
||||
}
|
||||
|
||||
export interface DescriptionProps extends DescriptionsProps {
|
||||
|
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<Drawer v-bind="getBindValues" :class="prefixCls" @close="onClose">
|
||||
<template #title v-if="!$slots.title">
|
||||
<DrawerHeader :title="getMergeProps.title" :isDetail="isDetail" :showDetailBack="showDetailBack" @close="onClose">
|
||||
<DrawerHeader
|
||||
:title="getMergeProps.title"
|
||||
:isDetail="isDetail"
|
||||
:showDetailBack="showDetailBack"
|
||||
@close="onClose"
|
||||
>
|
||||
<template #titleToolbar>
|
||||
<slot name="titleToolbar"></slot>
|
||||
</template>
|
||||
@@ -11,7 +16,11 @@
|
||||
<slot name="title"></slot>
|
||||
</template>
|
||||
|
||||
<ScrollContainer :style="getScrollContentStyle" v-loading="getLoading" :loading-tip="loadingText || '加载中...'">
|
||||
<ScrollContainer
|
||||
:style="getScrollContentStyle"
|
||||
v-loading="getLoading"
|
||||
:loading-tip="loadingText || '加载中...'"
|
||||
>
|
||||
<slot></slot>
|
||||
</ScrollContainer>
|
||||
<DrawerFooter v-bind="getProps" @close="onClose" @ok="handleOk" :height="getFooterHeight">
|
||||
|
@@ -6,7 +6,14 @@
|
||||
{{ cancelText }}
|
||||
</a-button>
|
||||
<slot name="centerFooter"></slot>
|
||||
<a-button :type="okType" @click="handleOk" v-bind="okButtonProps" class="mr-2" :loading="confirmLoading" v-if="showOkBtn">
|
||||
<a-button
|
||||
:type="okType"
|
||||
@click="handleOk"
|
||||
v-bind="okButtonProps"
|
||||
class="mr-2"
|
||||
:loading="confirmLoading"
|
||||
v-if="showOkBtn"
|
||||
>
|
||||
{{ okText }}
|
||||
</a-button>
|
||||
<slot name="appendFooter"></slot>
|
||||
|
@@ -1,5 +1,20 @@
|
||||
import type { UseDrawerReturnType, DrawerInstance, ReturnMethods, DrawerProps, UseDrawerInnerReturnType } from './typing'
|
||||
import { ref, getCurrentInstance, unref, reactive, watchEffect, nextTick, toRaw, computed } from 'vue'
|
||||
import type {
|
||||
UseDrawerReturnType,
|
||||
DrawerInstance,
|
||||
ReturnMethods,
|
||||
DrawerProps,
|
||||
UseDrawerInnerReturnType,
|
||||
} from './typing'
|
||||
import {
|
||||
ref,
|
||||
getCurrentInstance,
|
||||
unref,
|
||||
reactive,
|
||||
watchEffect,
|
||||
nextTick,
|
||||
toRaw,
|
||||
computed,
|
||||
} from 'vue'
|
||||
import { isProdMode } from '@/utils/env'
|
||||
import { isFunction } from '@/utils/is'
|
||||
import { tryOnUnmounted } from '@vueuse/core'
|
||||
|
@@ -6,8 +6,16 @@
|
||||
<template #overlay>
|
||||
<a-menu :selectedKeys="selectedKeys">
|
||||
<template v-for="item in dropMenuList" :key="`${item.event}`">
|
||||
<a-menu-item v-bind="getAttr(item.event)" @click="handleClickMenu(item)" :disabled="item.disabled">
|
||||
<a-popconfirm v-if="popconfirm && item.popConfirm" v-bind="getPopConfirmAttrs(item.popConfirm)" :disabled="item.disabled">
|
||||
<a-menu-item
|
||||
v-bind="getAttr(item.event)"
|
||||
@click="handleClickMenu(item)"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-popconfirm
|
||||
v-if="popconfirm && item.popConfirm"
|
||||
v-bind="getPopConfirmAttrs(item.popConfirm)"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<template #icon v-if="item.popConfirm.icon">
|
||||
<Icon :icon="item.popConfirm.icon" />
|
||||
</template>
|
||||
@@ -78,8 +86,10 @@
|
||||
const getPopConfirmAttrs = computed(() => {
|
||||
return (attrs) => {
|
||||
const originAttrs = omit(attrs, ['confirm', 'cancel', 'icon'])
|
||||
if (!attrs.onConfirm && attrs.confirm && isFunction(attrs.confirm)) originAttrs['onConfirm'] = attrs.confirm
|
||||
if (!attrs.onCancel && attrs.cancel && isFunction(attrs.cancel)) originAttrs['onCancel'] = attrs.cancel
|
||||
if (!attrs.onConfirm && attrs.confirm && isFunction(attrs.confirm))
|
||||
originAttrs['onConfirm'] = attrs.confirm
|
||||
if (!attrs.onCancel && attrs.cancel && isFunction(attrs.cancel))
|
||||
originAttrs['onCancel'] = attrs.cancel
|
||||
return originAttrs
|
||||
}
|
||||
})
|
||||
|
@@ -2,7 +2,8 @@
|
||||
export const cancelAnimationFrame = window.cancelAnimationFrame
|
||||
// 使用 requestAnimationFrame 模拟 setTimeout 和 setInterval
|
||||
export function rafTimeout(fn: Function, delay = 0, interval = false): object {
|
||||
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : () => {}
|
||||
const requestAnimationFrame =
|
||||
typeof window !== 'undefined' ? window.requestAnimationFrame : () => {}
|
||||
let start: any = null
|
||||
function timeElapse(timestamp: number) {
|
||||
/*
|
||||
@@ -31,7 +32,8 @@ export function rafTimeout(fn: Function, delay = 0, interval = false): object {
|
||||
}
|
||||
// 用于取消 rafTimeout 函数
|
||||
export function cancelRaf(raf: { id: number }): void {
|
||||
const cancelAnimationFrame = typeof window !== 'undefined' ? window.cancelAnimationFrame : () => {}
|
||||
const cancelAnimationFrame =
|
||||
typeof window !== 'undefined' ? window.cancelAnimationFrame : () => {}
|
||||
if (raf && raf.id) {
|
||||
cancelAnimationFrame(raf.id)
|
||||
}
|
||||
|
@@ -1,6 +1,17 @@
|
||||
<template>
|
||||
<SvgIcon :size="size" :name="getSvgIcon" v-if="isSvgIcon" :class="[$attrs.class, 'anticon']" :spin="spin" />
|
||||
<span v-else ref="elRef" :class="[$attrs.class, 'app-iconify anticon', spin && 'app-iconify-spin']" :style="getWrapStyle"></span>
|
||||
<SvgIcon
|
||||
:size="size"
|
||||
:name="getSvgIcon"
|
||||
v-if="isSvgIcon"
|
||||
:class="[$attrs.class, 'anticon']"
|
||||
:spin="spin"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
ref="elRef"
|
||||
:class="[$attrs.class, 'app-iconify anticon', spin && 'app-iconify-spin']"
|
||||
:style="getWrapStyle"
|
||||
></span>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue'
|
||||
|
@@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<svg :class="[prefixCls, $attrs.class, spin && 'svg-icon-spin']" :style="getStyle" aria-hidden="true">
|
||||
<svg
|
||||
:class="[prefixCls, $attrs.class, spin && 'svg-icon-spin']"
|
||||
:style="getStyle"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<use :xlink:href="symbolId" />
|
||||
</svg>
|
||||
</template>
|
||||
|
@@ -16,7 +16,9 @@ interface Fn {
|
||||
export function useLoading(props: Partial<LoadingProps>): [Fn, Fn, (string) => void]
|
||||
export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn, (string) => void]
|
||||
|
||||
export function useLoading(opt: Partial<LoadingProps> | Partial<UseLoadingOptions>): [Fn, Fn, (string) => void] {
|
||||
export function useLoading(
|
||||
opt: Partial<LoadingProps> | Partial<UseLoadingOptions>,
|
||||
): [Fn, Fn, (string) => void] {
|
||||
let props: Partial<LoadingProps>
|
||||
let target: HTMLElement | Ref<ElRef> = document.body
|
||||
|
||||
|
@@ -57,12 +57,20 @@
|
||||
|
||||
const { currentRoute } = useRouter()
|
||||
|
||||
const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys(menuState, items, mode as any, accordion)
|
||||
const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys(
|
||||
menuState,
|
||||
items,
|
||||
mode as any,
|
||||
accordion,
|
||||
)
|
||||
|
||||
const getIsTopMenu = computed(() => {
|
||||
const { type, mode } = props
|
||||
|
||||
return (type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL) || (props.isHorizontal && unref(getSplit))
|
||||
return (
|
||||
(type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL) ||
|
||||
(props.isHorizontal && unref(getSplit))
|
||||
)
|
||||
})
|
||||
|
||||
const getMenuClass = computed(() => {
|
||||
@@ -123,7 +131,8 @@
|
||||
isClickGo.value = false
|
||||
return
|
||||
}
|
||||
const path = (route || unref(currentRoute)).meta?.currentActiveMenu || (route || unref(currentRoute)).path
|
||||
const path =
|
||||
(route || unref(currentRoute)).meta?.currentActiveMenu || (route || unref(currentRoute)).path
|
||||
setOpenKeys(path)
|
||||
if (unref(currentActiveMenu)) return
|
||||
if (props.isHorizontal && unref(getSplit)) {
|
||||
|
@@ -7,7 +7,12 @@ import { uniq } from 'lodash-es'
|
||||
import { useMenuSetting } from '@/hooks/setting/useMenuSetting'
|
||||
import { getAllParentPath } from '@/router/helper/menuHelper'
|
||||
|
||||
export function useOpenKeys(menuState: MenuState, menus: Ref<MenuType[]>, mode: Ref<MenuModeEnum>, accordion: Ref<boolean>) {
|
||||
export function useOpenKeys(
|
||||
menuState: MenuState,
|
||||
menus: Ref<MenuType[]>,
|
||||
mode: Ref<MenuModeEnum>,
|
||||
accordion: Ref<boolean>,
|
||||
) {
|
||||
const { getCollapsed, getIsMixSidebar } = useMenuSetting()
|
||||
|
||||
async function setOpenKeys(path: string) {
|
||||
|
@@ -10,7 +10,11 @@
|
||||
</template>
|
||||
|
||||
<template #title v-if="!$slots.title">
|
||||
<ModalHeader :helpMessage="getProps.helpMessage" :title="getMergeProps.title" @dblclick="handleTitleDbClick" />
|
||||
<ModalHeader
|
||||
:helpMessage="getProps.helpMessage"
|
||||
:title="getMergeProps.title"
|
||||
@dblclick="handleTitleDbClick"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #footer v-if="!$slots.footer">
|
||||
@@ -46,7 +50,17 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { ModalProps, ModalMethods } from './typing'
|
||||
import { computed, ref, watch, unref, watchEffect, toRef, getCurrentInstance, nextTick, useAttrs } from 'vue'
|
||||
import {
|
||||
computed,
|
||||
ref,
|
||||
watch,
|
||||
unref,
|
||||
watchEffect,
|
||||
toRef,
|
||||
getCurrentInstance,
|
||||
nextTick,
|
||||
useAttrs,
|
||||
} from 'vue'
|
||||
import Modal from './components/Modal'
|
||||
import ModalWrapper from './components/ModalWrapper.vue'
|
||||
import ModalClose from './components/ModalClose.vue'
|
||||
@@ -63,7 +77,15 @@
|
||||
|
||||
const props = defineProps(basicProps)
|
||||
|
||||
const emit = defineEmits(['open-change', 'height-change', 'cancel', 'ok', 'register', 'update:open', 'fullscreen'])
|
||||
const emit = defineEmits([
|
||||
'open-change',
|
||||
'height-change',
|
||||
'cancel',
|
||||
'ok',
|
||||
'register',
|
||||
'update:open',
|
||||
'fullscreen',
|
||||
])
|
||||
|
||||
const attrs = useAttrs()
|
||||
const openRef = ref(false)
|
||||
|
@@ -5,7 +5,13 @@
|
||||
{{ cancelText }}
|
||||
</a-button>
|
||||
<slot name="centerFooter"></slot>
|
||||
<a-button :type="okType" @click="handleOk" :loading="confirmLoading" v-bind="okButtonProps" v-if="showOkBtn">
|
||||
<a-button
|
||||
:type="okType"
|
||||
@click="handleOk"
|
||||
:loading="confirmLoading"
|
||||
v-bind="okButtonProps"
|
||||
v-if="showOkBtn"
|
||||
>
|
||||
{{ okText }}
|
||||
</a-button>
|
||||
<slot name="appendFooter"></slot>
|
||||
|
@@ -114,7 +114,12 @@
|
||||
|
||||
const modalRect = getComputedStyle(modalDom as Element).top
|
||||
const modalTop = Number.parseInt(modalRect)
|
||||
let maxHeight = window.innerHeight - modalTop * 2 + (props.footerOffset! || 0) - props.modalFooterHeight - props.modalHeaderHeight
|
||||
let maxHeight =
|
||||
window.innerHeight -
|
||||
modalTop * 2 +
|
||||
(props.footerOffset! || 0) -
|
||||
props.modalFooterHeight -
|
||||
props.modalHeaderHeight
|
||||
|
||||
// 距离顶部过进会出现滚动条
|
||||
if (modalTop < 40) {
|
||||
@@ -130,9 +135,14 @@
|
||||
// }
|
||||
|
||||
if (props.fullScreen) {
|
||||
realHeightRef.value = window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 28
|
||||
realHeightRef.value =
|
||||
window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 28
|
||||
} else {
|
||||
realHeightRef.value = props.height ? props.height : realHeight.value > maxHeight ? maxHeight : realHeight.value
|
||||
realHeightRef.value = props.height
|
||||
? props.height
|
||||
: realHeight.value > maxHeight
|
||||
? maxHeight
|
||||
: realHeight.value
|
||||
}
|
||||
emit('height-change', unref(realHeightRef))
|
||||
} catch (error) {
|
||||
|
@@ -1,5 +1,21 @@
|
||||
import type { UseModalReturnType, ModalMethods, ModalProps, ReturnMethods, UseModalInnerReturnType } from '../typing'
|
||||
import { ref, onUnmounted, unref, getCurrentInstance, reactive, watchEffect, nextTick, toRaw, computed } from 'vue'
|
||||
import type {
|
||||
UseModalReturnType,
|
||||
ModalMethods,
|
||||
ModalProps,
|
||||
ReturnMethods,
|
||||
UseModalInnerReturnType,
|
||||
} from '../typing'
|
||||
import {
|
||||
ref,
|
||||
onUnmounted,
|
||||
unref,
|
||||
getCurrentInstance,
|
||||
reactive,
|
||||
watchEffect,
|
||||
nextTick,
|
||||
toRaw,
|
||||
computed,
|
||||
} from 'vue'
|
||||
import { isProdMode } from '@/utils/env'
|
||||
import { isFunction } from '@/utils/is'
|
||||
import { isEqual } from 'lodash-es'
|
||||
|
@@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<div :class="getClass" :style="getStyle" ref="wrapperRef">
|
||||
<PageHeader :ghost="ghost" :title="title" v-bind="omit($attrs, 'class')" :style="getHeaderStyle" ref="headerRef" v-if="getShowHeader">
|
||||
<PageHeader
|
||||
:ghost="ghost"
|
||||
:title="title"
|
||||
v-bind="omit($attrs, 'class')"
|
||||
:style="getHeaderStyle"
|
||||
ref="headerRef"
|
||||
v-if="getShowHeader"
|
||||
>
|
||||
<template #default>
|
||||
<template v-if="content">
|
||||
{{ content }}
|
||||
@@ -34,7 +41,17 @@
|
||||
import { PageHeader } from 'ant-design-vue'
|
||||
import { omit, debounce } from 'lodash-es'
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import { CSSProperties, PropType, computed, provide, ref, unref, useAttrs, useSlots, watch } from 'vue'
|
||||
import {
|
||||
CSSProperties,
|
||||
PropType,
|
||||
computed,
|
||||
provide,
|
||||
ref,
|
||||
unref,
|
||||
useAttrs,
|
||||
useSlots,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import PageFooter from './PageFooter.vue'
|
||||
|
||||
defineOptions({
|
||||
@@ -122,7 +139,9 @@
|
||||
}
|
||||
})
|
||||
|
||||
const getShowHeader = computed(() => props.content || slots?.headerContent || props.title || getHeaderSlots.value.length)
|
||||
const getShowHeader = computed(
|
||||
() => props.content || slots?.headerContent || props.title || getHeaderSlots.value.length,
|
||||
)
|
||||
|
||||
const getShowFooter = computed(() => slots?.leftFooter || slots?.rightFooter)
|
||||
|
||||
|
@@ -320,7 +320,11 @@
|
||||
})
|
||||
|
||||
const handleMaskClick = (e: MouseEvent) => {
|
||||
if (props.maskClosable && e.target && (e.target as HTMLDivElement).classList.contains(`${prefixCls}-content`)) {
|
||||
if (
|
||||
props.maskClosable &&
|
||||
e.target &&
|
||||
(e.target as HTMLDivElement).classList.contains(`${prefixCls}-content`)
|
||||
) {
|
||||
handleClose(e)
|
||||
}
|
||||
}
|
||||
@@ -349,10 +353,16 @@
|
||||
const renderController = () => {
|
||||
return (
|
||||
<div class={`${prefixCls}__controller`}>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-getScaleStep.value)}>
|
||||
<div
|
||||
class={`${prefixCls}__controller-item`}
|
||||
onClick={() => scaleFunc(-getScaleStep.value)}
|
||||
>
|
||||
<img src={unScaleSvg} />
|
||||
</div>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(getScaleStep.value)}>
|
||||
<div
|
||||
class={`${prefixCls}__controller-item`}
|
||||
onClick={() => scaleFunc(getScaleStep.value)}
|
||||
>
|
||||
<img src={scaleSvg} />
|
||||
</div>
|
||||
<div class={`${prefixCls}__controller-item`} onClick={resume}>
|
||||
@@ -382,7 +392,12 @@
|
||||
return () => {
|
||||
return (
|
||||
imgState.show && (
|
||||
<div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp} onClick={handleMaskClick}>
|
||||
<div
|
||||
class={prefixCls}
|
||||
ref={wrapElRef}
|
||||
onMouseup={handleMouseUp}
|
||||
onClick={handleMaskClick}
|
||||
>
|
||||
<div class={`${prefixCls}-content`}>
|
||||
{/*<Spin*/}
|
||||
{/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/}
|
||||
@@ -396,7 +411,10 @@
|
||||
{/*/>*/}
|
||||
<img
|
||||
style={unref(getImageStyle)}
|
||||
class={[`${prefixCls}-image`, imgState.status === StatueEnum.DONE ? '' : 'hidden']}
|
||||
class={[
|
||||
`${prefixCls}-image`,
|
||||
imgState.status === StatueEnum.DONE ? '' : 'hidden',
|
||||
]}
|
||||
ref={imgElRef}
|
||||
src={imgState.currentUrl}
|
||||
onMousedown={handleAddMoveListener}
|
||||
|
@@ -3,7 +3,12 @@ import type { QRCodeRenderersOptions } from 'qrcode'
|
||||
import { RenderQrCodeParams, ContentType } from './typing'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
export const renderQrCode = ({ canvas, content, width = 0, options: params = {} }: RenderQrCodeParams) => {
|
||||
export const renderQrCode = ({
|
||||
canvas,
|
||||
content,
|
||||
width = 0,
|
||||
options: params = {},
|
||||
}: RenderQrCodeParams) => {
|
||||
const options = cloneDeep(params)
|
||||
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
|
||||
options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content)
|
||||
|
@@ -8,7 +8,14 @@ export const drawLogo = ({ canvas, logo }: RenderQrCodeParams) => {
|
||||
})
|
||||
}
|
||||
const canvasWidth = (canvas as HTMLCanvasElement).width
|
||||
const { logoSize = 0.15, bgColor = '#ffffff', borderSize = 0.05, crossOrigin, borderRadius = 8, logoRadius = 0 } = logo as LogoType
|
||||
const {
|
||||
logoSize = 0.15,
|
||||
bgColor = '#ffffff',
|
||||
borderSize = 0.05,
|
||||
crossOrigin,
|
||||
borderRadius = 8,
|
||||
logoRadius = 0,
|
||||
} = logo as LogoType
|
||||
|
||||
const logoSrc: string = isString(logo) ? logo : logo.src
|
||||
const logoWidth = canvasWidth * logoSize
|
||||
|
@@ -17,7 +17,16 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onBeforeUnmount, nextTick, provide, unref, watch, type PropType } from 'vue'
|
||||
import {
|
||||
ref,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
nextTick,
|
||||
provide,
|
||||
unref,
|
||||
watch,
|
||||
type PropType,
|
||||
} from 'vue'
|
||||
import type { StyleValue } from '@/utils/types'
|
||||
import { addResizeListener, removeResizeListener } from '@/utils/event'
|
||||
import componentSetting from '@/settings/componentSetting'
|
||||
|
@@ -1,4 +1,13 @@
|
||||
import { defineComponent, h, computed, ref, getCurrentInstance, onUnmounted, inject, Ref } from 'vue'
|
||||
import {
|
||||
defineComponent,
|
||||
h,
|
||||
computed,
|
||||
ref,
|
||||
getCurrentInstance,
|
||||
onUnmounted,
|
||||
inject,
|
||||
Ref,
|
||||
} from 'vue'
|
||||
import { on, off } from '@/utils/domUtils'
|
||||
import { renderThumbStyle, BAR_MAP } from './util'
|
||||
|
||||
@@ -30,10 +39,14 @@ export default defineComponent({
|
||||
return
|
||||
}
|
||||
|
||||
const offset = (instance?.vnode.el?.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) * -1
|
||||
const offset =
|
||||
(instance?.vnode.el?.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) *
|
||||
-1
|
||||
const thumbClickPosition = thumb.value[bar.value.offset] - prevPage
|
||||
const thumbPositionPercentage = ((offset - thumbClickPosition) * 100) / instance?.vnode.el?.[bar.value.offset]
|
||||
wrap.value[bar.value.scroll] = (thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100
|
||||
const thumbPositionPercentage =
|
||||
((offset - thumbClickPosition) * 100) / instance?.vnode.el?.[bar.value.offset]
|
||||
wrap.value[bar.value.scroll] =
|
||||
(thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100
|
||||
}
|
||||
|
||||
const startDrag = (e: any) => {
|
||||
@@ -52,15 +65,20 @@ export default defineComponent({
|
||||
window.getSelection()?.removeAllRanges()
|
||||
startDrag(e)
|
||||
barStore.value[bar.value.axis] =
|
||||
e.currentTarget[bar.value.offset] - (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction])
|
||||
e.currentTarget[bar.value.offset] -
|
||||
(e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction])
|
||||
}
|
||||
|
||||
const clickTrackHandler = (e: any) => {
|
||||
const offset = Math.abs(e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client])
|
||||
const offset = Math.abs(
|
||||
e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client],
|
||||
)
|
||||
const thumbHalf = thumb.value[bar.value.offset] / 2
|
||||
const thumbPositionPercentage = ((offset - thumbHalf) * 100) / instance?.vnode.el?.[bar.value.offset]
|
||||
const thumbPositionPercentage =
|
||||
((offset - thumbHalf) * 100) / instance?.vnode.el?.[bar.value.offset]
|
||||
|
||||
wrap.value[bar.value.scroll] = (thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100
|
||||
wrap.value[bar.value.scroll] =
|
||||
(thumbPositionPercentage * wrap.value[bar.value.scrollSize]) / 100
|
||||
}
|
||||
|
||||
function mouseUpDocumentHandler() {
|
||||
|
@@ -8,7 +8,12 @@
|
||||
@select="handleSelect"
|
||||
>
|
||||
<template v-for="item in items" :key="item.path">
|
||||
<SimpleSubMenu :item="item" :parent="true" :collapsedShowTitle="collapsedShowTitle" :collapse="collapse" />
|
||||
<SimpleSubMenu
|
||||
:item="item"
|
||||
:parent="true"
|
||||
:collapsedShowTitle="collapsedShowTitle"
|
||||
:collapse="collapse"
|
||||
/>
|
||||
</template>
|
||||
</Menu>
|
||||
</template>
|
||||
@@ -63,7 +68,13 @@
|
||||
const { prefixCls } = useDesign('simple-menu')
|
||||
const { items, accordion, mixSider, collapse } = toRefs(props)
|
||||
|
||||
const { setOpenKeys, getOpenKeys } = useOpenKeys(menuState, items, accordion, mixSider as any, collapse as any)
|
||||
const { setOpenKeys, getOpenKeys } = useOpenKeys(
|
||||
menuState,
|
||||
items,
|
||||
accordion,
|
||||
mixSider as any,
|
||||
collapse as any,
|
||||
)
|
||||
|
||||
const getBindValues = computed(() => ({ ...attrs, ...props }))
|
||||
|
||||
|
@@ -7,7 +7,16 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue'
|
||||
import type { SubMenuProvider } from './types'
|
||||
import { ref, computed, onMounted, watchEffect, watch, nextTick, getCurrentInstance, provide } from 'vue'
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
onMounted,
|
||||
watchEffect,
|
||||
watch,
|
||||
nextTick,
|
||||
getCurrentInstance,
|
||||
provide,
|
||||
} from 'vue'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { createSimpleRootMenuContext, type MenuEmitterEvents } from './useSimpleMenuContext'
|
||||
|
@@ -3,7 +3,11 @@
|
||||
<template v-if="!getCollapse">
|
||||
<div :class="`${prefixCls}-submenu-title`" @click.stop="handleClick" :style="getItemStyle">
|
||||
<slot name="title"></slot>
|
||||
<Icon icon="eva:arrow-ios-downward-outline" :size="14" :class="`${prefixCls}-submenu-title-icon`" />
|
||||
<Icon
|
||||
icon="eva:arrow-ios-downward-outline"
|
||||
:size="14"
|
||||
:class="`${prefixCls}-submenu-title-icon`"
|
||||
/>
|
||||
</div>
|
||||
<CollapseTransition>
|
||||
<ul :class="prefixCls" v-show="state.opened">
|
||||
@@ -33,7 +37,12 @@
|
||||
>
|
||||
<slot name="title"></slot>
|
||||
</div>
|
||||
<Icon v-if="getParentSubMenu" icon="eva:arrow-ios-downward-outline" :size="14" :class="`${prefixCls}-submenu-title-icon`" />
|
||||
<Icon
|
||||
v-if="getParentSubMenu"
|
||||
icon="eva:arrow-ios-downward-outline"
|
||||
:size="14"
|
||||
:class="`${prefixCls}-submenu-title-icon`"
|
||||
/>
|
||||
</div>
|
||||
<!-- eslint-disable-next-line -->
|
||||
<template #content v-show="state.opened">
|
||||
@@ -51,7 +60,15 @@
|
||||
import { type TimeoutHandle, type Recordable } from '@vben/types'
|
||||
import type { CSSProperties, PropType } from 'vue'
|
||||
import type { SubMenuProvider } from './types'
|
||||
import { computed, unref, getCurrentInstance, reactive, provide, onBeforeMount, inject } from 'vue'
|
||||
import {
|
||||
computed,
|
||||
unref,
|
||||
getCurrentInstance,
|
||||
reactive,
|
||||
provide,
|
||||
onBeforeMount,
|
||||
inject,
|
||||
} from 'vue'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useMenuItem } from './useMenu'
|
||||
@@ -241,7 +258,9 @@
|
||||
clearTimeout(data.timeout!)
|
||||
})
|
||||
|
||||
rootMenuEmitter.on('on-update-opened', (data: boolean | (string | number)[] | Recordable<any>) => {
|
||||
rootMenuEmitter.on(
|
||||
'on-update-opened',
|
||||
(data: boolean | (string | number)[] | Recordable<any>) => {
|
||||
if (unref(getCollapse)) return
|
||||
if (isBoolean(data)) {
|
||||
state.opened = data
|
||||
@@ -260,7 +279,8 @@
|
||||
if (props.name && Array.isArray(data)) {
|
||||
state.opened = (data as (string | number)[]).includes(props.name)
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {
|
||||
if (instance?.uid) {
|
||||
|
@@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<div :class="prefixCls" class="relative">
|
||||
<Input.Password v-if="showInput" v-bind="$attrs" allowClear :value="innerValueRef" @change="handleChange" :disabled="disabled">
|
||||
<Input.Password
|
||||
v-if="showInput"
|
||||
v-bind="$attrs"
|
||||
allowClear
|
||||
:value="innerValueRef"
|
||||
@change="handleChange"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)">
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
|
@@ -11,7 +11,8 @@
|
||||
defineOptions({ name: 'Time' })
|
||||
|
||||
const props = defineProps({
|
||||
value: propTypes.oneOfType([propTypes.number, propTypes.instanceOf(Date), propTypes.string]).isRequired,
|
||||
value: propTypes.oneOfType([propTypes.number, propTypes.instanceOf(Date), propTypes.string])
|
||||
.isRequired,
|
||||
step: propTypes.number.def(60),
|
||||
mode: propTypes.oneOf(['date', 'datetime', 'relative']).def('relative'),
|
||||
})
|
||||
|
@@ -16,6 +16,12 @@ export const SlideXReverseTransition = createSimpleTransition('slide-x-reverse-t
|
||||
export const ScrollXReverseTransition = createSimpleTransition('scroll-x-reverse-transition')
|
||||
export const ScaleRotateTransition = createSimpleTransition('scale-rotate-transition')
|
||||
|
||||
export const ExpandXTransition = createJavascriptTransition('expand-x-transition', ExpandTransitionGenerator('', true))
|
||||
export const ExpandXTransition = createJavascriptTransition(
|
||||
'expand-x-transition',
|
||||
ExpandTransitionGenerator('', true),
|
||||
)
|
||||
|
||||
export const ExpandTransition = createJavascriptTransition('expand-transition', ExpandTransitionGenerator(''))
|
||||
export const ExpandTransition = createJavascriptTransition(
|
||||
'expand-transition',
|
||||
ExpandTransitionGenerator(''),
|
||||
)
|
||||
|
@@ -38,7 +38,11 @@ export function createSimpleTransition(name: string, origin = 'top center 0', mo
|
||||
},
|
||||
})
|
||||
}
|
||||
export function createJavascriptTransition(name: string, functions: Recordable, mode: Mode = 'in-out') {
|
||||
export function createJavascriptTransition(
|
||||
name: string,
|
||||
functions: Recordable,
|
||||
mode: Mode = 'in-out',
|
||||
) {
|
||||
return defineComponent({
|
||||
name,
|
||||
props: {
|
||||
|
@@ -1,8 +1,25 @@
|
||||
<script lang="tsx">
|
||||
import type { CSSProperties } from 'vue'
|
||||
import type { FieldNames, TreeState, TreeItem, KeyType, CheckKeys, TreeActionType } from './types/tree'
|
||||
import type {
|
||||
FieldNames,
|
||||
TreeState,
|
||||
TreeItem,
|
||||
KeyType,
|
||||
CheckKeys,
|
||||
TreeActionType,
|
||||
} from './types/tree'
|
||||
|
||||
import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw, watch, onMounted } from 'vue'
|
||||
import {
|
||||
defineComponent,
|
||||
reactive,
|
||||
computed,
|
||||
unref,
|
||||
ref,
|
||||
watchEffect,
|
||||
toRaw,
|
||||
watch,
|
||||
onMounted,
|
||||
} from 'vue'
|
||||
import TreeHeader from './components/TreeHeader.vue'
|
||||
import { Tree, Spin, Empty } from 'ant-design-vue'
|
||||
import { TreeIcon } from './TreeIcon'
|
||||
@@ -93,7 +110,9 @@
|
||||
return omit(propsData, 'treeData', 'class') as TreeProps
|
||||
})
|
||||
|
||||
const getTreeSearchData = computed((): TreeItem[] => (searchState.startSearch ? searchState.searchData : unref(treeDataRef)))
|
||||
const getTreeSearchData = computed((): TreeItem[] =>
|
||||
searchState.startSearch ? searchState.searchData : unref(treeDataRef),
|
||||
)
|
||||
|
||||
const getNotFound = computed((): boolean => {
|
||||
return !getTreeSearchData.value || getTreeSearchData.value.length === 0
|
||||
@@ -206,7 +225,8 @@
|
||||
searchState.startSearch = false
|
||||
return
|
||||
}
|
||||
const { filterFn, checkable, expandOnSearch, checkOnSearch, selectedOnSearch } = unref(props)
|
||||
const { filterFn, checkable, expandOnSearch, checkOnSearch, selectedOnSearch } =
|
||||
unref(props)
|
||||
searchState.startSearch = true
|
||||
const { title: titleField, key: keyField } = unref(getFieldNames)
|
||||
|
||||
@@ -214,7 +234,9 @@
|
||||
searchState.searchData = filter(
|
||||
unref(treeDataRef),
|
||||
(node) => {
|
||||
const result = filterFn ? filterFn(searchValue, node, unref(getFieldNames)) : node[titleField]?.includes(searchValue) ?? false
|
||||
const result = filterFn
|
||||
? filterFn(searchValue, node, unref(getFieldNames))
|
||||
: node[titleField]?.includes(searchValue) ?? false
|
||||
if (result) {
|
||||
matchedKeys.push(node[keyField])
|
||||
}
|
||||
@@ -359,7 +381,8 @@
|
||||
const title = get(item, titleField)
|
||||
|
||||
const searchIdx = searchText ? title.indexOf(searchText) : -1
|
||||
const isHighlight = searchState.startSearch && !isEmpty(searchText) && highlight && searchIdx !== -1
|
||||
const isHighlight =
|
||||
searchState.startSearch && !isEmpty(searchText) && highlight && searchIdx !== -1
|
||||
const highlightStyle = `color: ${isBoolean(highlight) ? '#f50' : highlight}`
|
||||
|
||||
const titleDom = isHighlight ? (
|
||||
@@ -372,10 +395,17 @@
|
||||
title
|
||||
)
|
||||
|
||||
const iconDom = icon ? <TreeIcon icon={icon} /> : slots.icon ? <span class="mr-2">{getSlot(slots, 'icon')}</span> : null
|
||||
const iconDom = icon ? (
|
||||
<TreeIcon icon={icon} />
|
||||
) : slots.icon ? (
|
||||
<span class="mr-2">{getSlot(slots, 'icon')}</span>
|
||||
) : null
|
||||
|
||||
item[titleField] = (
|
||||
<span class={`${bem('title')}`} onClick={handleClickNode.bind(null, item[keyField], item[childrenField])}>
|
||||
<span
|
||||
class={`${bem('title')}`}
|
||||
onClick={handleClickNode.bind(null, item[keyField], item[childrenField])}
|
||||
>
|
||||
{slots?.title ? (
|
||||
<>
|
||||
{iconDom}
|
||||
@@ -419,13 +449,21 @@
|
||||
{extendSlots(slots)}
|
||||
</TreeHeader>
|
||||
)}
|
||||
<Spin wrapperClassName={unref(props.treeWrapperClassName)} spinning={unref(props.loading)} tip="加载中...">
|
||||
<Spin
|
||||
wrapperClassName={unref(props.treeWrapperClassName)}
|
||||
spinning={unref(props.loading)}
|
||||
tip="加载中..."
|
||||
>
|
||||
<ScrollContainer style={scrollStyle} v-show={!unref(getNotFound)}>
|
||||
<Tree {...unref(getBindValues)} showIcon={false} treeData={treeData.value}>
|
||||
{extendSlots(slots, ['title'])}
|
||||
</Tree>
|
||||
</ScrollContainer>
|
||||
<Empty v-show={unref(getNotFound)} image={Empty.PRESENTED_IMAGE_SIMPLE} class="!mt-4" />
|
||||
<Empty
|
||||
v-show={unref(getNotFound)}
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
class="!mt-4"
|
||||
/>
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
|
@@ -12,7 +12,14 @@ export enum ToolbarEnum {
|
||||
CHECK_UN_STRICTLY,
|
||||
}
|
||||
|
||||
export const treeEmits = ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'change', 'check', 'update:searchValue']
|
||||
export const treeEmits = [
|
||||
'update:expandedKeys',
|
||||
'update:selectedKeys',
|
||||
'update:value',
|
||||
'change',
|
||||
'check',
|
||||
'update:searchValue',
|
||||
]
|
||||
|
||||
export interface TreeState {
|
||||
expandedKeys: KeyType[]
|
||||
@@ -29,7 +36,9 @@ export interface FieldNames {
|
||||
|
||||
export type KeyType = string | number
|
||||
|
||||
export type CheckKeys = KeyType[] | { checked: string[] | number[]; halfChecked: string[] | number[] }
|
||||
export type CheckKeys =
|
||||
| KeyType[]
|
||||
| { checked: string[] | number[]; halfChecked: string[] | number[] }
|
||||
|
||||
export const treeProps = buildProps({
|
||||
value: {
|
||||
@@ -105,7 +114,9 @@ export const treeProps = buildProps({
|
||||
},
|
||||
// 自定义数据过滤判断方法(注: 不是整个过滤方法,而是内置过滤的判断方法,用于增强原本仅能通过title进行过滤的方式)
|
||||
filterFn: {
|
||||
type: Function as PropType<(searchValue: any, node: TreeItem, fieldNames: FieldNames) => boolean>,
|
||||
type: Function as PropType<
|
||||
(searchValue: any, node: TreeItem, fieldNames: FieldNames) => boolean
|
||||
>,
|
||||
default: undefined,
|
||||
},
|
||||
// 高亮搜索值,仅高亮具体匹配值(通过title)值为true时使用默认色值,值为#xxx时使用此值替代且高亮开启
|
||||
@@ -177,5 +188,9 @@ export interface TreeActionType {
|
||||
updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void
|
||||
setSearchValue: (value: string) => void
|
||||
getSearchValue: () => string
|
||||
getSelectedNode: (key: KeyType, treeList?: TreeItem[], selectNode?: TreeItem | null) => TreeItem | null
|
||||
getSelectedNode: (
|
||||
key: KeyType,
|
||||
treeList?: TreeItem[],
|
||||
selectNode?: TreeItem | null,
|
||||
) => TreeItem | null
|
||||
}
|
||||
|
@@ -1,5 +1,16 @@
|
||||
<script lang="tsx">
|
||||
import { defineComponent, computed, ref, unref, reactive, onMounted, watch, nextTick, CSSProperties, PropType } from 'vue'
|
||||
import {
|
||||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
unref,
|
||||
reactive,
|
||||
onMounted,
|
||||
watch,
|
||||
nextTick,
|
||||
CSSProperties,
|
||||
PropType,
|
||||
} from 'vue'
|
||||
import { useEventListener } from '@/hooks/event/useEventListener'
|
||||
import { getSlot } from '@/utils/helper/tsxHelper'
|
||||
|
||||
|
@@ -112,7 +112,12 @@ export default defineComponent({
|
||||
|
||||
return (
|
||||
<div class={`h-full flex flex-col bg-white ${this.getWrapperClass}`}>
|
||||
<VxeGrid ref="tableElRef" class={`vxe-grid_scrollbar px-6 py-4 ${tableClass}`} style={tableStyle} {...this.getBindGridValues}>
|
||||
<VxeGrid
|
||||
ref="tableElRef"
|
||||
class={`vxe-grid_scrollbar px-6 py-4 ${tableClass}`}
|
||||
style={tableStyle}
|
||||
{...this.getBindGridValues}
|
||||
>
|
||||
{extendSlots(this.$slots)}
|
||||
</VxeGrid>
|
||||
</div>
|
||||
|
@@ -1,4 +1,10 @@
|
||||
import { createEditRender, createDefaultRender, createFilterRender, createDefaultFilterRender, createFormItemRender } from './common'
|
||||
import {
|
||||
createEditRender,
|
||||
createDefaultRender,
|
||||
createFilterRender,
|
||||
createDefaultFilterRender,
|
||||
createFormItemRender,
|
||||
} from './common'
|
||||
|
||||
export default {
|
||||
autofocus: 'input.ant-input',
|
||||
|
@@ -1,12 +1,19 @@
|
||||
import { h } from 'vue'
|
||||
import { FormItemContentRenderParams, FormItemRenderOptions, VxeGlobalRendererHandles } from 'vxe-table'
|
||||
import {
|
||||
FormItemContentRenderParams,
|
||||
FormItemRenderOptions,
|
||||
VxeGlobalRendererHandles,
|
||||
} from 'vxe-table'
|
||||
import XEUtils from 'xe-utils'
|
||||
import { cellText, createEvents, createProps, getComponent } from './common'
|
||||
|
||||
const COMPONENT_NAME = 'AButton'
|
||||
|
||||
export function createEditRender() {
|
||||
return function (renderOpts: VxeGlobalRendererHandles.RenderEditOptions, params: VxeGlobalRendererHandles.RenderEditParams) {
|
||||
return function (
|
||||
renderOpts: VxeGlobalRendererHandles.RenderEditOptions,
|
||||
params: VxeGlobalRendererHandles.RenderEditParams,
|
||||
) {
|
||||
const { attrs } = renderOpts
|
||||
const Component = getComponent(COMPONENT_NAME)
|
||||
|
||||
@@ -21,7 +28,10 @@ export function createEditRender() {
|
||||
}
|
||||
|
||||
export function createDefaultRender() {
|
||||
return function (renderOpts: VxeGlobalRendererHandles.RenderEditOptions, params: VxeGlobalRendererHandles.RenderEditParams) {
|
||||
return function (
|
||||
renderOpts: VxeGlobalRendererHandles.RenderEditOptions,
|
||||
params: VxeGlobalRendererHandles.RenderEditParams,
|
||||
) {
|
||||
const { attrs } = renderOpts
|
||||
const Component = getComponent(COMPONENT_NAME)
|
||||
|
||||
@@ -77,7 +87,10 @@ export function createFormItemRender() {
|
||||
}
|
||||
|
||||
function createToolbarButtonRender() {
|
||||
return function (renderOpts: VxeGlobalRendererHandles.RenderToolOptions, params: VxeGlobalRendererHandles.RenderButtonParams) {
|
||||
return function (
|
||||
renderOpts: VxeGlobalRendererHandles.RenderToolOptions,
|
||||
params: VxeGlobalRendererHandles.RenderButtonParams,
|
||||
) {
|
||||
const { attrs } = renderOpts
|
||||
const { button } = params
|
||||
const props = createProps(renderOpts, null)
|
||||
|
@@ -1,24 +1,38 @@
|
||||
import { FormItemContentRenderParams, FormItemRenderOptions, VxeGlobalRendererHandles } from 'vxe-table'
|
||||
import {
|
||||
FormItemContentRenderParams,
|
||||
FormItemRenderOptions,
|
||||
VxeGlobalRendererHandles,
|
||||
} from 'vxe-table'
|
||||
import { createDefaultRender, createEditRender, createFormItemRender } from './AButton'
|
||||
|
||||
function createEditButtonRender() {
|
||||
return function (renderOpts: VxeGlobalRendererHandles.RenderEditOptions, params: VxeGlobalRendererHandles.RenderEditParams) {
|
||||
return function (
|
||||
renderOpts: VxeGlobalRendererHandles.RenderEditOptions,
|
||||
params: VxeGlobalRendererHandles.RenderEditParams,
|
||||
) {
|
||||
const buttonEditRender = createEditRender()
|
||||
const { children } = renderOpts
|
||||
if (children) {
|
||||
return children.map((childRenderOpts: VxeGlobalRendererHandles.RenderEditOptions) => buttonEditRender(childRenderOpts, params)[0])
|
||||
return children.map(
|
||||
(childRenderOpts: VxeGlobalRendererHandles.RenderEditOptions) =>
|
||||
buttonEditRender(childRenderOpts, params)[0],
|
||||
)
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function createDefaultButtonRender() {
|
||||
return function (renderOpts: VxeGlobalRendererHandles.RenderDefaultOptions, params: VxeGlobalRendererHandles.RenderDefaultParams) {
|
||||
return function (
|
||||
renderOpts: VxeGlobalRendererHandles.RenderDefaultOptions,
|
||||
params: VxeGlobalRendererHandles.RenderDefaultParams,
|
||||
) {
|
||||
const buttonDefaultRender = createDefaultRender()
|
||||
const { children } = renderOpts
|
||||
if (children) {
|
||||
return children.map(
|
||||
(childRenderOpts: VxeGlobalRendererHandles.RenderDefaultOptions) => buttonDefaultRender(childRenderOpts, params)[0],
|
||||
(childRenderOpts: VxeGlobalRendererHandles.RenderDefaultOptions) =>
|
||||
buttonDefaultRender(childRenderOpts, params)[0],
|
||||
)
|
||||
}
|
||||
return []
|
||||
@@ -30,7 +44,9 @@ function createButtonItemRender() {
|
||||
const buttonItemRender = createFormItemRender()
|
||||
const { children } = renderOpts
|
||||
if (children) {
|
||||
return children.map((childRenderOpts: FormItemRenderOptions) => buttonItemRender(childRenderOpts, params)[0])
|
||||
return children.map(
|
||||
(childRenderOpts: FormItemRenderOptions) => buttonItemRender(childRenderOpts, params)[0],
|
||||
)
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { VxeGlobalRendererHandles } from 'vxe-table'
|
||||
import XEUtils from 'xe-utils'
|
||||
import { createEditRender, createCellRender, createFormItemRender, createExportMethod } from './common'
|
||||
import {
|
||||
createEditRender,
|
||||
createCellRender,
|
||||
createFormItemRender,
|
||||
createExportMethod,
|
||||
} from './common'
|
||||
|
||||
function matchCascaderData(index: number, list: any[], values: any[], labels: any[]) {
|
||||
const val = values[index]
|
||||
@@ -14,14 +19,19 @@ function matchCascaderData(index: number, list: any[], values: any[], labels: an
|
||||
}
|
||||
}
|
||||
|
||||
function getCascaderCellValue(renderOpts: VxeGlobalRendererHandles.RenderOptions, params: VxeGlobalRendererHandles.RenderCellParams) {
|
||||
function getCascaderCellValue(
|
||||
renderOpts: VxeGlobalRendererHandles.RenderOptions,
|
||||
params: VxeGlobalRendererHandles.RenderCellParams,
|
||||
) {
|
||||
const { props = {} } = renderOpts
|
||||
const { row, column } = params
|
||||
const cellValue = XEUtils.get(row, column.field as string)
|
||||
const values = cellValue || []
|
||||
const labels: Array<any> = []
|
||||
matchCascaderData(0, props.options, values, labels)
|
||||
return (props.showAllLevels === false ? labels.slice(labels.length - 1, labels.length) : labels).join(` ${props.separator || '/'} `)
|
||||
return (
|
||||
props.showAllLevels === false ? labels.slice(labels.length - 1, labels.length) : labels
|
||||
).join(` ${props.separator || '/'} `)
|
||||
}
|
||||
|
||||
export default {
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { VxeGlobalRendererHandles } from 'vxe-table'
|
||||
import XEUtils from 'xe-utils'
|
||||
import { createCellRender, createEditRender, createExportMethod, createFormItemRender } from './common'
|
||||
import {
|
||||
createCellRender,
|
||||
createEditRender,
|
||||
createExportMethod,
|
||||
createFormItemRender,
|
||||
} from './common'
|
||||
|
||||
export function getDatePickerCellValue(
|
||||
renderOpts: VxeGlobalRendererHandles.RenderOptions,
|
||||
|
@@ -1,4 +1,10 @@
|
||||
import { createEditRender, createDefaultRender, createFilterRender, createDefaultFilterRender, createFormItemRender } from './common'
|
||||
import {
|
||||
createEditRender,
|
||||
createDefaultRender,
|
||||
createFilterRender,
|
||||
createDefaultFilterRender,
|
||||
createFormItemRender,
|
||||
} from './common'
|
||||
|
||||
export default {
|
||||
autofocus: 'input.ant-input',
|
||||
|
@@ -1,4 +1,10 @@
|
||||
import { createEditRender, createFilterRender, createFormItemRender, createDefaultFilterRender, createDefaultRender } from './common'
|
||||
import {
|
||||
createEditRender,
|
||||
createFilterRender,
|
||||
createFormItemRender,
|
||||
createDefaultFilterRender,
|
||||
createDefaultRender,
|
||||
} from './common'
|
||||
|
||||
export default {
|
||||
autofocus: 'input.ant-input-number-input',
|
||||
|
@@ -1,5 +1,10 @@
|
||||
import { getDatePickerCellValue } from './ADatePicker'
|
||||
import { createCellRender, createEditRender, createExportMethod, createFormItemRender } from './common'
|
||||
import {
|
||||
createCellRender,
|
||||
createEditRender,
|
||||
createExportMethod,
|
||||
createFormItemRender,
|
||||
} from './common'
|
||||
|
||||
export default {
|
||||
renderEdit: createEditRender(),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user