feat: 优化登陆跳转/退出跳转

master
luoer 2023-08-07 17:43:22 +08:00
parent 0e39ea474a
commit 89b1de9b02
22 changed files with 362 additions and 137 deletions

2
.env
View File

@ -2,7 +2,7 @@
# 应用配置 # 应用配置
# ===================================================================================== # =====================================================================================
# 网站标题 # 网站标题
VITE_TITLE = 绝弹管理系统 VITE_TITLE = 绝弹中心
# 网站副标题 # 网站副标题
VITE_SUBTITLE = 快速开发web应用的模板工具 VITE_SUBTITLE = 快速开发web应用的模板工具
# API接口前缀参见 axios 的 baseURL # API接口前缀参见 axios 的 baseURL

View File

@ -1,14 +1,27 @@
import { IToastOptions, toast } from "@/components"; import { IToastOptions, toast } from "@/components";
import { store, useUserStore } from "@/store"; import { store, useUserStore } from "@/store";
import { Api } from "../service/Api"; import { Api } from "../service/Api";
import { Message } from "@arco-design/web-vue";
const userStore = useUserStore(store); class Service extends Api<unknown> {
/**
*
*/
tokenExpiredHandler: () => void = () => {};
/**
*
* @param handler
*/
setTokenExpiredHandler(handler: () => void) {
this.tokenExpiredHandler = handler;
}
}
/** /**
* API * API
* @see src/api/instance/instance.ts * @see src/api/instance/instance.ts
*/ */
export const api = new Api({ export const api = new Service({
baseURL: import.meta.env.VITE_API_PREFIX, baseURL: import.meta.env.VITE_API_PREFIX,
}); });
@ -17,6 +30,7 @@ export const api = new Api({
*/ */
api.instance.interceptors.request.use( api.instance.interceptors.request.use(
(config) => { (config) => {
const userStore = useUserStore(store);
if (userStore.accessToken) { if (userStore.accessToken) {
config.headers.Authorization = `Bearer ${userStore.accessToken}`; config.headers.Authorization = `Bearer ${userStore.accessToken}`;
} }
@ -50,11 +64,18 @@ api.instance.interceptors.response.use(
return res; return res;
}, },
(error) => { (error) => {
const userStore = useUserStore(store);
error.config.closeToast?.(); error.config.closeToast?.();
if (error.response) { if (error.response) {
console.log("response error", error.response); console.log("response error", error.response);
const code = error.response.data?.code;
if (code === 4050 || code === 4051) {
userStore.clearUser();
api.tokenExpiredHandler?.();
}
} else if (error.request) { } else if (error.request) {
console.log("request error", error.request); console.log("request error", error.request);
Message.error(`提示:请求失败,检查网络状态或参数格式!`);
} }
return Promise.reject(error); return Promise.reject(error);
} }

View File

@ -1,9 +1,11 @@
<template> <template>
<div> <div>
<BreadCrumb></BreadCrumb> <BreadCrumb></BreadCrumb>
<slot name="content">
<div class="mx-4 mt-4 p-4 bg-white"> <div class="mx-4 mt-4 p-4 bg-white">
<slot></slot> <slot></slot>
</div> </div>
</slot>
</div> </div>
</template> </template>

View File

@ -61,7 +61,7 @@ export const FormModal = defineComponent({
* @description `{ message }` * @description `{ message }`
*/ */
submit: { submit: {
type: Function as PropType<(arg: { model: Record<string, any>; items: IFormItem[] }) => any | Promise<any>>, type: Function as PropType<(args: { model: any; items: IFormItem[] }) => PromiseLike<any>>,
default: () => true, default: () => true,
}, },
/** /**
@ -191,7 +191,5 @@ export const FormModal = defineComponent({
}); });
export type FormModalInstance = InstanceType<typeof FormModal>; export type FormModalInstance = InstanceType<typeof FormModal>;
export type FormModalProps = FormModalInstance["$props"]; export type FormModalProps = FormModalInstance["$props"];
export default FormModal; export default FormModal;

View File

@ -45,7 +45,12 @@ export const config = {
title: "序号", title: "序号",
width: 60, width: 60,
align: "center", align: "center",
render: ({ rowIndex }: any) => rowIndex + 1, render: ({ rowIndex }: any) => {
const table = inject<any>("ref:table");
const page = table.pagination.current;
const size = table.pagination.pageSize;
return size * (page - 1) + rowIndex + 1;
},
}, },
columnButtonBase: { columnButtonBase: {
buttonProps: { buttonProps: {
@ -63,5 +68,5 @@ export const config = {
getApiErrorMessage(error: any): string { getApiErrorMessage(error: any): string {
const message = error?.response?.data?.message || error?.message || "请求失败"; const message = error?.response?.data?.message || error?.message || "请求失败";
return message; return message;
} },
}; };

View File

@ -23,28 +23,23 @@ export interface TableColumnButton {
* *
*/ */
text?: string; text?: string;
/** /**
* *
* @description `delete` `onClick``modify` `modify` * @description `delete` `onClick``modify` `modify`
*/ */
type?: "delete" | "modify"; type?: "delete" | "modify";
/** /**
* *
*/ */
onClick?: (data: UseColumnRenderOptions, openModify?: (model: Record<string, any>) => void) => void; onClick?: (data: UseColumnRenderOptions) => any;
/** /**
* *
*/ */
disabled?: (data: UseColumnRenderOptions) => boolean; disabled?: (data: UseColumnRenderOptions) => boolean;
/** /**
* *
*/ */
visible?: (data: UseColumnRenderOptions) => boolean; visible?: (data: UseColumnRenderOptions) => boolean;
/** /**
* props * props
*/ */
@ -56,13 +51,41 @@ export interface UseTableColumn extends TableColumnData {
* *
*/ */
type?: "index" | "button"; type?: "index" | "button";
/** /**
* *
*/ */
buttons?: TableColumnButton[]; buttons?: TableColumnButton[];
} }
type ExtendedFormItem = Partial<IFormItem> & {
/**
* `create.items` `field`
*/
extend?: string;
};
type Search = Partial<
Omit<FormProps, "items"> & {
/**
*
*/
items?: ExtendedFormItem[];
}
>;
type Modify = Partial<
Omit<FormModalProps, "items"> & {
/**
* `create`
*/
extend: boolean;
/**
*
*/
items?: ExtendedFormItem[];
}
>;
export interface UseTableOptions extends Omit<TableProps, "search" | "create" | "modify" | "columns"> { export interface UseTableOptions extends Omit<TableProps, "search" | "create" | "modify" | "columns"> {
/** /**
* *
@ -73,24 +96,7 @@ export interface UseTableOptions extends Omit<TableProps, "search" | "create" |
* *
* @see FormProps * @see FormProps
*/ */
search?: Partial< search?: Search;
Omit<FormProps, "items"> & {
/**
*
*/
items?: (Partial<IFormItem> & {
/**
* `create.items`field
*/
extend?: string;
})[];
}
>;
/**
* common props for `create` and `modify` modal
* @see FormModalProps
*/
common?: Partial<FormModalProps>;
/** /**
* *
*/ */
@ -98,20 +104,7 @@ export interface UseTableOptions extends Omit<TableProps, "search" | "create" |
/** /**
* *
*/ */
modify?: Partial< modify?: Modify;
Omit<FormModalProps, "items"> & {
/**
* `create`
*/
extend: boolean;
items?: (FormModalProps["items"][number] & {
/**
* `create.items`field
*/
extend?: string;
})[];
}
>;
/** /**
* *
*/ */

View File

@ -28,13 +28,9 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
const modifyAction = column.buttons.find((i) => i.type === "modify"); const modifyAction = column.buttons.find((i) => i.type === "modify");
if (modifyAction) { if (modifyAction) {
const { onClick } = modifyAction; const { onClick } = modifyAction;
modifyAction.onClick = (columnData) => { modifyAction.onClick = async (columnData) => {
const fn = (data: Record<string, any>) => getTable()?.openModifyModal(data); const result = (await onClick?.(columnData)) || columnData;
if (isFunction(onClick)) { getTable()?.openModifyModal(result);
onClick(columnData, fn);
} else {
fn(columnData);
}
}; };
} else { } else {
column.buttons.unshift({ column.buttons.unshift({
@ -53,7 +49,10 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
onOk: async () => { onOk: async () => {
try { try {
const resData: any = await action?.onClick?.(data); const resData: any = await action?.onClick?.(data);
resData.msg && Message.success(resData?.msg || ""); const message = resData?.data?.message;
if (message) {
Message.success(`提示:${message}`);
}
getTable()?.loadData(); getTable()?.loadData();
} catch (error: any) { } catch (error: any) {
const message = error.response?.data?.message; const message = error.response?.data?.message;

View File

@ -12,13 +12,13 @@
<template #icon> <template #icon>
<i class="icon-park-outline-back"></i> <i class="icon-park-outline-back"></i>
</template> </template>
返回上页 返回
</a-button> </a-button>
<a-button type="outline" @click="router.push('/')"> <a-button type="outline" @click="router.push('/')">
<template #icon> <template #icon>
<i class="icon-park-outline-home"></i> <i class="icon-park-outline-home"></i>
</template> </template>
返回首页 首页
</a-button> </a-button>
</div> </div>
</div> </div>

View File

@ -22,7 +22,7 @@
<a-dropdown> <a-dropdown>
<span class="cursor-pointer"> <span class="cursor-pointer">
<a-avatar :size="28"> <a-avatar :size="28">
<img :src="userStore.avatar" :alt="userStore.nickname"> <img :src="userStore.avatar" :alt="userStore.nickname" />
</a-avatar> </a-avatar>
<span class="mx-2"> <span class="mx-2">
{{ userStore.nickname }} {{ userStore.nickname }}
@ -80,6 +80,7 @@ import Menu from "./components/menu.vue";
const appStore = useAppStore(); const appStore = useAppStore();
const userStore = useUserStore(); const userStore = useUserStore();
const isCollapsed = ref(false); const isCollapsed = ref(false);
const route = useRoute();
const router = useRouter(); const router = useRouter();
const themeConfig = ref({ visible: false }); const themeConfig = ref({ visible: false });
const onCollapse = (val: boolean) => { const onCollapse = (val: boolean) => {
@ -115,15 +116,9 @@ const userButtons = [
icon: "icon-park-outline-logout", icon: "icon-park-outline-logout",
text: "退出登录", text: "退出登录",
onClick: async () => { onClick: async () => {
userStore.clearUser() userStore.clearUser();
Message.loading({ Message.success("提示:已退出登陆!");
content: '提示: 正在退出,请稍后...', router.push({ path: "/login", query: { redirect: route.path } });
duration: 2000,
onClose: () => {
Message.success(`提示: 已成功退出登录!`)
router.push({ name: "_login" });
}
})
}, },
}, },
]; ];

View File

@ -65,6 +65,7 @@ const meridiem = dayjs.localeData().meridiem(dayjs().hour(), dayjs().minute());
const appStore = useAppStore(); const appStore = useAppStore();
const userStore = useUserStore(); const userStore = useUserStore();
const model = reactive({ username: "juetan", password: "juetan" }); const model = reactive({ username: "juetan", password: "juetan" });
const route = useRoute();
const router = useRouter(); const router = useRouter();
const loading = ref(false); const loading = ref(false);
const formRef = ref<InstanceType<typeof Form>>(); const formRef = ref<InstanceType<typeof Form>>();
@ -102,9 +103,8 @@ const onSubmitClick = async () => {
loading.value = true; loading.value = true;
const res = await api.auth.login(model); const res = await api.auth.login(model);
userStore.setUser(res.data.data); userStore.setUser(res.data.data);
userStore.username = res.data.data.username; Message.success(`欢迎回来,${res.data.data.username}!`);
Message.success(`欢迎回来,${res.data.data.username}!`) router.push({ path: (route.query.redirect as string) || "/" });
router.push({ path: "/" });
} catch (error: any) { } catch (error: any) {
const message = error?.response?.data?.message; const message = error?.response?.data?.message;
if (message) { if (message) {

View File

@ -1,10 +1,140 @@
<template> <template>
<bread-page class="">Demo/test Page</bread-page> <bread-page id="list-page">
<template #content>
<AList class="mx-5 mt-3 bg-white" :bordered="true">
<template #header>
<div class="flex gap-2 items-center justify-between text-sm bg-gray-50 px-5 py-4">
<div class="flex gap-4">
<ACheckbox></ACheckbox>
<AInput class="inline-block w-80" placeholder="输入名称关键字"></AInput>
</div>
<div class="flex items-center gap-4">
<ADropdown>
<span class="cursor-pointer">
上传者
<i class="icon-park-outline-down"></i>
</span>
<template #content>
<ADoption>
<AInput placeholder="用户名关键字" />
</ADoption>
<ADoption>
<AAvatar :size="20">
<img src="https://picsum.photos/seed/picsum/200/300" alt="" />
</AAvatar>
绝弹土豆
</ADoption>
</template>
</ADropdown>
<ADropdown>
<span class="cursor-pointer">
排序默认
<i class="icon-park-outline-down"></i>
</span>
<template #content>
<ADoption>
<div class="w-40">默认</div>
</ADoption>
<ADoption>
<template #icon>
<i class="icon-park-outline-sort-amount-up"></i>
</template>
按创建时间升序
</ADoption>
<ADoption>
<template #icon>
<i class="icon-park-outline-sort-amount-down"></i>
</template>
按创建时间降序
</ADoption>
<ADoption>
<template #icon>
<i class="icon-park-outline-align-text-top"></i>
</template>
按文件大小升序
</ADoption>
<ADoption>
<template #icon>
<i class="icon-park-outline-align-text-bottom"></i>
</template>
按文件大小降序
</ADoption>
</template>
</ADropdown>
<div class="space-x-1">
<span
class="inline-flex p-1 hover:bg-slate-100 rounded cursor-pointer text-gray-400 hover:text-gray-700"
>
<i class="icon-park-outline-list"></i>
</span>
<span
class="inline-flex p-1 hover:bg-slate-100 rounded cursor-pointer text-gray-400 hover:text-gray-700"
>
<i class="icon-park-outline-insert-table"></i>
</span>
<span
class="inline-flex p-1 hover:bg-slate-100 rounded cursor-pointer text-gray-400 hover:text-gray-700"
>
<i class="icon-park-outline-refresh"></i>
</span>
</div>
</div>
</div>
</template>
<AListItem v-for="i in 10">
<AListItemMeta title="测试图片.png" description="image/png 1.2MB">
<template #avatar>
<ACheckbox class="mr-3"></ACheckbox>
<AImage src="https://picsum.photos/seed/picsum/200/300" height="40">
<img src="https://picsum.photos/seed/picsum/200/300" alt="" />
</AImage>
</template>
<template #title>
<span class="hover:text-blue-500 cursor-pointer">测试图片.png</span>
</template>
<template #description>
<div class="text-xs text-gray-400">image/png 1.2MB</div>
</template>
</AListItemMeta>
<template #actions>
<span class="text-xs text-gray-400">
<i class="icon-park-outline-user !w-[14px] !h-[14px]"></i>
绝弹
</span>
<span class="text-xs text-gray-400">2023-08-17 17:00:01</span>
<ADropdown>
<span class="inline-flex p-1 hover:bg-slate-100 rounded cursor-pointer">
<i class="icon-park-outline-more"></i>
</span>
<template #content>
<ADoption class="w-32"> <div class="w-32">详情</div> </ADoption>
<ADoption class="!text-red-500 !hover-bg-red-50">
<template #icon>
<i class="icon-park-outline-delete"></i>
</template>
删除
</ADoption>
</template>
</ADropdown>
</template>
</AListItem>
</AList>
</template>
</bread-page>
</template> </template>
<script setup lang="ts"></script> <script setup lang="ts"></script>
<style scoped></style> <style lang="less">
#list-page {
.arco-list-header {
padding: 0;
}
.arco-dropdown-list {
padding: 0 4px;
}
}
</style>
<route lang="json"> <route lang="json">
{ {

View File

@ -17,9 +17,9 @@ const table = useTable({
return []; return [];
}, },
columns: [ columns: [
// { {
// type: 'index' type: "index",
// }, },
{ {
title: "姓名", title: "姓名",
dataIndex: "username", dataIndex: "username",
@ -64,7 +64,17 @@ const table = useTable({
buttons: [], buttons: [],
}, },
], ],
common: { search: {
items: [
{
field: "username",
label: "姓名",
type: "input",
},
],
},
create: {
title: "新建用户",
modalProps: { modalProps: {
width: 432, width: 432,
maskClosable: false, maskClosable: false,
@ -124,18 +134,6 @@ const table = useTable({
}, },
}, },
], ],
},
search: {
items: [
{
field: "username",
label: "姓名",
type: "input",
},
],
},
create: {
title: "新建用户",
submit: ({ model }) => { submit: ({ model }) => {
return api.user.addUser(model as any, { return api.user.addUser(model as any, {
type: ContentType.FormData, type: ContentType.FormData,

View File

@ -95,7 +95,7 @@ const table = useTable({
</div> </div>
</div> </div>
); );
} },
}, },
], ],
modalProps: { modalProps: {
@ -106,7 +106,7 @@ const table = useTable({
layout: "vertical", layout: "vertical",
}, },
submit: ({ model }) => { submit: ({ model }) => {
return api.permission.addPermission(model as any); return api.permission.addPermission(model);
}, },
}, },
modify: { modify: {

View File

@ -1,11 +1,101 @@
<template> <template>
<div class="m-4 p-4 bg-white"> <div class="m-4 p-4 bg-white">
Post Page <Table v-bind="table" />
</div> </div>
</template> </template>
<script setup lang="tsx" name="PostPage"> <script setup lang="tsx" name="PostPage">
import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/plugins";
const table = useTable({
data: async (model, paging) => {
return api.post.getPosts({ ...model, ...paging });
},
columns: [
{
type: "index",
},
{
title: "文章名称",
dataIndex: "title",
width: 200,
},
{
title: "文章描述",
dataIndex: "description",
},
{
title: "创建时间",
dataIndex: "createdAt",
width: 200,
render: ({ record }) => dayjs(record.createdAt).format(),
},
{
title: "操作",
type: "button",
width: 70,
buttons: [
{
type: "modify",
text: "修改",
},
],
},
],
search: {
items: [
{
extend: "name",
required: false,
},
],
},
create: {
title: "新建文章",
modalProps: {
width: 580,
maskClosable: false,
},
formProps: {
layout: "vertical",
},
items: [
{
field: "title",
label: "文章名称",
type: "input",
required: true,
},
{
field: "slug",
label: "文章标识",
type: "input",
},
{
field: "description",
label: "文章描述",
type: "input",
},
{
field: "content",
label: "文章内容",
type: "textarea",
},
],
submit: ({ model }) => {
return api.role.addRole(model);
},
},
modify: {
extend: true,
title: "修改文章",
submit: ({ model }) => {
return api.post.updatePost(model.id, model);
},
},
});
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -95,7 +95,7 @@ const table = useTable({
], ],
submit: ({ model }) => { submit: ({ model }) => {
return api.role.addRole(model as any); return api.role.addRole(model);
}, },
}, },
modify: { modify: {

View File

@ -130,7 +130,7 @@ const table = useTable({
}, },
], ],
submit: ({ model }) => { submit: ({ model }) => {
console.log(model); return api.user.addUser(model);
}, },
}, },
modify: { modify: {

View File

@ -2,27 +2,32 @@ import { store, useUserStore } from "@/store";
import { Message } from "@arco-design/web-vue"; import { Message } from "@arco-design/web-vue";
import { NavigationGuardWithThis } from "vue-router"; import { NavigationGuardWithThis } from "vue-router";
const whitelist = ["/404"]; const whitelist = ["/:all(.*)*"];
const signoutlist = ["/login"]; const signoutlist = ["/login"];
export const authGuard: NavigationGuardWithThis<undefined> = async function (to, from, next) { export const authGuard: NavigationGuardWithThis<undefined> = async function (to) {
// 放在外面pinia-plugin-peristedstate 插件会失效 // 放在外面pinia-plugin-peristedstate 插件会失效
const userStore = useUserStore(store); const userStore = useUserStore(store);
if (to.meta?.auth === false) { if (to.meta?.auth === false) {
return next(); return true;
} }
if (whitelist.includes(to.fullPath)) { if (whitelist.includes(to.path) || to.name === "_all") {
return next(); return true;
} }
if (signoutlist.includes(to.fullPath)) { if (signoutlist.includes(to.path)) {
if (userStore.id) { if (userStore.accessToken) {
Message.warning(`提示:您已登陆,如需重新请退出后再操作!`); Message.warning(`提示:您已登陆,如需重新请退出后再操作!`);
return next(false); return false;
} }
return next(); return true;
} }
if (!userStore.accessToken) { if (!userStore.accessToken) {
return next("/login"); return {
path: "/login",
query: {
redirect: to.path,
},
};
} }
next(); return true;
}; };

View File

@ -1,9 +1,8 @@
import { NProgress } from "@/plugins"; import { NProgress } from "@/plugins";
import { NavigationGuardWithThis, NavigationHookAfter } from "vue-router"; import { NavigationGuardWithThis, NavigationHookAfter } from "vue-router";
const before: NavigationGuardWithThis<undefined> = function (to, from, next) { const before: NavigationGuardWithThis<undefined> = function () {
NProgress.start(); NProgress.start();
next();
}; };
const after: NavigationHookAfter = function () { const after: NavigationHookAfter = function () {

View File

@ -1,10 +1,9 @@
import { store, useAppStore } from "@/store"; import { store, useAppStore } from "@/store";
import { NavigationGuardWithThis } from "vue-router"; import { NavigationHookAfter } from "vue-router";
export const titleGuard: NavigationGuardWithThis<undefined> = function (to, from, next) { export const titleGuard: NavigationHookAfter = function (to) {
const appStore = useAppStore(store); const appStore = useAppStore(store);
const title = to.meta.title || appStore.title; const title = to.meta.title || appStore.title;
const subtitle = appStore.subtitle; const subtitle = appStore.subtitle;
document.title = `${title} | ${subtitle}`; document.title = `${title} | ${subtitle}`;
next();
}; };

View File

@ -3,6 +3,7 @@ import { authGuard } from "../guards/guard-auth";
import { nprogressGuard } from "../guards/guard-nprogress"; import { nprogressGuard } from "../guards/guard-nprogress";
import { titleGuard } from "../guards/guard-title"; import { titleGuard } from "../guards/guard-title";
import { routes } from "../routes"; import { routes } from "../routes";
import { api } from "@/api";
export const router = createRouter({ export const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
@ -17,5 +18,10 @@ export const router = createRouter({
router.beforeEach(nprogressGuard.before); router.beforeEach(nprogressGuard.before);
router.afterEach(nprogressGuard.after); router.afterEach(nprogressGuard.after);
router.beforeEach(titleGuard);
router.beforeEach(authGuard); router.beforeEach(authGuard);
router.afterEach(titleGuard);
api.setTokenExpiredHandler(() => {
const redirect = router.currentRoute.value.path;
router.push({ path: "/login", query: { redirect } });
});

View File

@ -4,9 +4,7 @@ import { RouteRecordRaw } from "vue-router";
const APP_ROUTE_NAME = "_app"; const APP_ROUTE_NAME = "_app";
/** /**
* _ * _
* @param routes
* @returns
*/ */
const transformRoutes = (routes: RouteRecordRaw[]) => { const transformRoutes = (routes: RouteRecordRaw[]) => {
const topRoutes: RouteRecordRaw[] = []; const topRoutes: RouteRecordRaw[] = [];
@ -33,26 +31,7 @@ const transformRoutes = (routes: RouteRecordRaw[]) => {
appRoute.children = appRoutes; appRoute.children = appRoutes;
} }
return topRoutes; return [topRoutes, appRoutes];
}; };
/** export const [routes, appRoutes] = transformRoutes(generatedRoutes);
*
* @param routes
* @returns
*/
const getAppRoutes = (routes: RouteRecordRaw[]) => {
return routes.find((i) => i.name === APP_ROUTE_NAME)?.children || [];
};
/**
*
*/
const routes = transformRoutes(generatedRoutes);
/**
*
*/
const appRoutes = getAppRoutes(routes);
export { routes, appRoutes };

View File

@ -14,10 +14,12 @@ declare module '@vue/runtime-core' {
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox'] ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider'] AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
ADoption: typeof import('@arco-design/web-vue')['Doption'] ADoption: typeof import('@arco-design/web-vue')['Doption']
ADot: typeof import('@arco-design/web-vue')['Dot']
ADrawer: typeof import('@arco-design/web-vue')['Drawer'] ADrawer: typeof import('@arco-design/web-vue')['Drawer']
ADropdown: typeof import('@arco-design/web-vue')['Dropdown'] ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
AForm: typeof import('@arco-design/web-vue')['Form'] AForm: typeof import('@arco-design/web-vue')['Form']
AFormItem: typeof import('@arco-design/web-vue')['FormItem'] AFormItem: typeof import('@arco-design/web-vue')['FormItem']
AImage: typeof import('@arco-design/web-vue')['Image']
AInput: typeof import('@arco-design/web-vue')['Input'] AInput: typeof import('@arco-design/web-vue')['Input']
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword'] AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
ALayout: typeof import('@arco-design/web-vue')['Layout'] ALayout: typeof import('@arco-design/web-vue')['Layout']
@ -25,8 +27,12 @@ declare module '@vue/runtime-core' {
ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader'] ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider'] ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
ALink: typeof import('@arco-design/web-vue')['Link'] ALink: typeof import('@arco-design/web-vue')['Link']
AList: typeof import('@arco-design/web-vue')['List']
AListItem: typeof import('@arco-design/web-vue')['ListItem']
AListItemMeta: typeof import('@arco-design/web-vue')['ListItemMeta']
AMenu: typeof import('@arco-design/web-vue')['Menu'] AMenu: typeof import('@arco-design/web-vue')['Menu']
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem'] AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
APopover: typeof import('@arco-design/web-vue')['Popover']
ASpace: typeof import('@arco-design/web-vue')['Space'] ASpace: typeof import('@arco-design/web-vue')['Space']
ASubMenu: typeof import('@arco-design/web-vue')['SubMenu'] ASubMenu: typeof import('@arco-design/web-vue')['SubMenu']
ATag: typeof import('@arco-design/web-vue')['Tag'] ATag: typeof import('@arco-design/web-vue')['Tag']