feat: 优化登陆跳转/退出跳转
parent
0e39ea474a
commit
89b1de9b02
2
.env
2
.env
|
|
@ -2,7 +2,7 @@
|
|||
# 应用配置
|
||||
# =====================================================================================
|
||||
# 网站标题
|
||||
VITE_TITLE = 绝弹管理系统
|
||||
VITE_TITLE = 绝弹中心
|
||||
# 网站副标题
|
||||
VITE_SUBTITLE = 快速开发web应用的模板工具
|
||||
# API接口前缀:参见 axios 的 baseURL
|
||||
|
|
|
|||
|
|
@ -1,14 +1,27 @@
|
|||
import { IToastOptions, toast } from "@/components";
|
||||
import { store, useUserStore } from "@/store";
|
||||
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 接口实例
|
||||
* @see src/api/instance/instance.ts
|
||||
*/
|
||||
export const api = new Api({
|
||||
export const api = new Service({
|
||||
baseURL: import.meta.env.VITE_API_PREFIX,
|
||||
});
|
||||
|
||||
|
|
@ -17,6 +30,7 @@ export const api = new Api({
|
|||
*/
|
||||
api.instance.interceptors.request.use(
|
||||
(config) => {
|
||||
const userStore = useUserStore(store);
|
||||
if (userStore.accessToken) {
|
||||
config.headers.Authorization = `Bearer ${userStore.accessToken}`;
|
||||
}
|
||||
|
|
@ -50,11 +64,18 @@ api.instance.interceptors.response.use(
|
|||
return res;
|
||||
},
|
||||
(error) => {
|
||||
const userStore = useUserStore(store);
|
||||
error.config.closeToast?.();
|
||||
if (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) {
|
||||
console.log("request error", error.request);
|
||||
Message.error(`提示:请求失败,检查网络状态或参数格式!`);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
<template>
|
||||
<div>
|
||||
<BreadCrumb></BreadCrumb>
|
||||
<div class="mx-4 mt-4 p-4 bg-white">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="content">
|
||||
<div class="mx-4 mt-4 p-4 bg-white">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export const FormModal = defineComponent({
|
|||
* @description 可返回`{ message }`类型,用于显示提示信息
|
||||
*/
|
||||
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,
|
||||
},
|
||||
/**
|
||||
|
|
@ -191,7 +191,5 @@ export const FormModal = defineComponent({
|
|||
});
|
||||
|
||||
export type FormModalInstance = InstanceType<typeof FormModal>;
|
||||
|
||||
export type FormModalProps = FormModalInstance["$props"];
|
||||
|
||||
export default FormModal;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,12 @@ export const config = {
|
|||
title: "序号",
|
||||
width: 60,
|
||||
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: {
|
||||
buttonProps: {
|
||||
|
|
@ -63,5 +68,5 @@ export const config = {
|
|||
getApiErrorMessage(error: any): string {
|
||||
const message = error?.response?.data?.message || error?.message || "请求失败";
|
||||
return message;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,28 +23,23 @@ export interface TableColumnButton {
|
|||
* 按钮文本
|
||||
*/
|
||||
text?: string;
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
* @description `delete` 需配置`onClick`属性,`modify` 需配置根对象下的 `modify` 属性
|
||||
*/
|
||||
type?: "delete" | "modify";
|
||||
|
||||
/**
|
||||
* 处理函数
|
||||
*/
|
||||
onClick?: (data: UseColumnRenderOptions, openModify?: (model: Record<string, any>) => void) => void;
|
||||
|
||||
onClick?: (data: UseColumnRenderOptions) => any;
|
||||
/**
|
||||
* 是否禁用按钮
|
||||
*/
|
||||
disabled?: (data: UseColumnRenderOptions) => boolean;
|
||||
|
||||
/**
|
||||
* 是否显示按钮
|
||||
*/
|
||||
visible?: (data: UseColumnRenderOptions) => boolean;
|
||||
|
||||
/**
|
||||
* 传递给按钮的props
|
||||
*/
|
||||
|
|
@ -56,13 +51,41 @@ export interface UseTableColumn extends TableColumnData {
|
|||
* 表格列类型
|
||||
*/
|
||||
type?: "index" | "button";
|
||||
|
||||
/**
|
||||
* 按钮配置列表
|
||||
*/
|
||||
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"> {
|
||||
/**
|
||||
* 表格列配置
|
||||
|
|
@ -73,24 +96,7 @@ export interface UseTableOptions extends Omit<TableProps, "search" | "create" |
|
|||
* 搜索表单配置
|
||||
* @see FormProps
|
||||
*/
|
||||
search?: Partial<
|
||||
Omit<FormProps, "items"> & {
|
||||
/**
|
||||
* 表单项
|
||||
*/
|
||||
items?: (Partial<IFormItem> & {
|
||||
/**
|
||||
* 继承`create.items`中指定field值的项
|
||||
*/
|
||||
extend?: string;
|
||||
})[];
|
||||
}
|
||||
>;
|
||||
/**
|
||||
* common props for `create` and `modify` modal
|
||||
* @see FormModalProps
|
||||
*/
|
||||
common?: Partial<FormModalProps>;
|
||||
search?: Search;
|
||||
/**
|
||||
* 新建弹窗配置
|
||||
*/
|
||||
|
|
@ -98,20 +104,7 @@ export interface UseTableOptions extends Omit<TableProps, "search" | "create" |
|
|||
/**
|
||||
* 新建弹窗配置
|
||||
*/
|
||||
modify?: Partial<
|
||||
Omit<FormModalProps, "items"> & {
|
||||
/**
|
||||
* 是否继承`create`弹窗配置
|
||||
*/
|
||||
extend: boolean;
|
||||
items?: (FormModalProps["items"][number] & {
|
||||
/**
|
||||
* 继承`create.items`弹窗配置中指定field值的项
|
||||
*/
|
||||
extend?: string;
|
||||
})[];
|
||||
}
|
||||
>;
|
||||
modify?: Modify;
|
||||
/**
|
||||
* 详情弹窗配置
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -28,13 +28,9 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
|
|||
const modifyAction = column.buttons.find((i) => i.type === "modify");
|
||||
if (modifyAction) {
|
||||
const { onClick } = modifyAction;
|
||||
modifyAction.onClick = (columnData) => {
|
||||
const fn = (data: Record<string, any>) => getTable()?.openModifyModal(data);
|
||||
if (isFunction(onClick)) {
|
||||
onClick(columnData, fn);
|
||||
} else {
|
||||
fn(columnData);
|
||||
}
|
||||
modifyAction.onClick = async (columnData) => {
|
||||
const result = (await onClick?.(columnData)) || columnData;
|
||||
getTable()?.openModifyModal(result);
|
||||
};
|
||||
} else {
|
||||
column.buttons.unshift({
|
||||
|
|
@ -53,7 +49,10 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
|
|||
onOk: async () => {
|
||||
try {
|
||||
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();
|
||||
} catch (error: any) {
|
||||
const message = error.response?.data?.message;
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@
|
|||
<template #icon>
|
||||
<i class="icon-park-outline-back"></i>
|
||||
</template>
|
||||
返回上页
|
||||
返回
|
||||
</a-button>
|
||||
<a-button type="outline" @click="router.push('/')">
|
||||
<template #icon>
|
||||
<i class="icon-park-outline-home"></i>
|
||||
</template>
|
||||
返回首页
|
||||
首页
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<a-dropdown>
|
||||
<span class="cursor-pointer">
|
||||
<a-avatar :size="28">
|
||||
<img :src="userStore.avatar" :alt="userStore.nickname">
|
||||
<img :src="userStore.avatar" :alt="userStore.nickname" />
|
||||
</a-avatar>
|
||||
<span class="mx-2">
|
||||
{{ userStore.nickname }}
|
||||
|
|
@ -80,6 +80,7 @@ import Menu from "./components/menu.vue";
|
|||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const isCollapsed = ref(false);
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const themeConfig = ref({ visible: false });
|
||||
const onCollapse = (val: boolean) => {
|
||||
|
|
@ -115,15 +116,9 @@ const userButtons = [
|
|||
icon: "icon-park-outline-logout",
|
||||
text: "退出登录",
|
||||
onClick: async () => {
|
||||
userStore.clearUser()
|
||||
Message.loading({
|
||||
content: '提示: 正在退出,请稍后...',
|
||||
duration: 2000,
|
||||
onClose: () => {
|
||||
Message.success(`提示: 已成功退出登录!`)
|
||||
router.push({ name: "_login" });
|
||||
}
|
||||
})
|
||||
userStore.clearUser();
|
||||
Message.success("提示:已退出登陆!");
|
||||
router.push({ path: "/login", query: { redirect: route.path } });
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ const meridiem = dayjs.localeData().meridiem(dayjs().hour(), dayjs().minute());
|
|||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const model = reactive({ username: "juetan", password: "juetan" });
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const loading = ref(false);
|
||||
const formRef = ref<InstanceType<typeof Form>>();
|
||||
|
|
@ -102,9 +103,8 @@ const onSubmitClick = async () => {
|
|||
loading.value = true;
|
||||
const res = await api.auth.login(model);
|
||||
userStore.setUser(res.data.data);
|
||||
userStore.username = res.data.data.username;
|
||||
Message.success(`欢迎回来,${res.data.data.username}!`)
|
||||
router.push({ path: "/" });
|
||||
Message.success(`欢迎回来,${res.data.data.username}!`);
|
||||
router.push({ path: (route.query.redirect as string) || "/" });
|
||||
} catch (error: any) {
|
||||
const message = error?.response?.data?.message;
|
||||
if (message) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,140 @@
|
|||
<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>
|
||||
|
||||
<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">
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ const table = useTable({
|
|||
return [];
|
||||
},
|
||||
columns: [
|
||||
// {
|
||||
// type: 'index'
|
||||
// },
|
||||
{
|
||||
type: "index",
|
||||
},
|
||||
{
|
||||
title: "姓名",
|
||||
dataIndex: "username",
|
||||
|
|
@ -64,7 +64,17 @@ const table = useTable({
|
|||
buttons: [],
|
||||
},
|
||||
],
|
||||
common: {
|
||||
search: {
|
||||
items: [
|
||||
{
|
||||
field: "username",
|
||||
label: "姓名",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
},
|
||||
create: {
|
||||
title: "新建用户",
|
||||
modalProps: {
|
||||
width: 432,
|
||||
maskClosable: false,
|
||||
|
|
@ -124,18 +134,6 @@ const table = useTable({
|
|||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
search: {
|
||||
items: [
|
||||
{
|
||||
field: "username",
|
||||
label: "姓名",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
},
|
||||
create: {
|
||||
title: "新建用户",
|
||||
submit: ({ model }) => {
|
||||
return api.user.addUser(model as any, {
|
||||
type: ContentType.FormData,
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ const table = useTable({
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
modalProps: {
|
||||
|
|
@ -106,7 +106,7 @@ const table = useTable({
|
|||
layout: "vertical",
|
||||
},
|
||||
submit: ({ model }) => {
|
||||
return api.permission.addPermission(model as any);
|
||||
return api.permission.addPermission(model);
|
||||
},
|
||||
},
|
||||
modify: {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,101 @@
|
|||
<template>
|
||||
<div class="m-4 p-4 bg-white">
|
||||
Post Page
|
||||
<Table v-bind="table" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<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>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ const table = useTable({
|
|||
],
|
||||
|
||||
submit: ({ model }) => {
|
||||
return api.role.addRole(model as any);
|
||||
return api.role.addRole(model);
|
||||
},
|
||||
},
|
||||
modify: {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ const table = useTable({
|
|||
},
|
||||
],
|
||||
submit: ({ model }) => {
|
||||
console.log(model);
|
||||
return api.user.addUser(model);
|
||||
},
|
||||
},
|
||||
modify: {
|
||||
|
|
|
|||
|
|
@ -2,27 +2,32 @@ import { store, useUserStore } from "@/store";
|
|||
import { Message } from "@arco-design/web-vue";
|
||||
import { NavigationGuardWithThis } from "vue-router";
|
||||
|
||||
const whitelist = ["/404"];
|
||||
const whitelist = ["/:all(.*)*"];
|
||||
const signoutlist = ["/login"];
|
||||
|
||||
export const authGuard: NavigationGuardWithThis<undefined> = async function (to, from, next) {
|
||||
export const authGuard: NavigationGuardWithThis<undefined> = async function (to) {
|
||||
// 放在外面,pinia-plugin-peristedstate 插件会失效
|
||||
const userStore = useUserStore(store);
|
||||
if (to.meta?.auth === false) {
|
||||
return next();
|
||||
return true;
|
||||
}
|
||||
if (whitelist.includes(to.fullPath)) {
|
||||
return next();
|
||||
if (whitelist.includes(to.path) || to.name === "_all") {
|
||||
return true;
|
||||
}
|
||||
if (signoutlist.includes(to.fullPath)) {
|
||||
if (userStore.id) {
|
||||
if (signoutlist.includes(to.path)) {
|
||||
if (userStore.accessToken) {
|
||||
Message.warning(`提示:您已登陆,如需重新请退出后再操作!`);
|
||||
return next(false);
|
||||
return false;
|
||||
}
|
||||
return next();
|
||||
return true;
|
||||
}
|
||||
if (!userStore.accessToken) {
|
||||
return next("/login");
|
||||
return {
|
||||
path: "/login",
|
||||
query: {
|
||||
redirect: to.path,
|
||||
},
|
||||
};
|
||||
}
|
||||
next();
|
||||
return true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { NProgress } from "@/plugins";
|
||||
import { NavigationGuardWithThis, NavigationHookAfter } from "vue-router";
|
||||
|
||||
const before: NavigationGuardWithThis<undefined> = function (to, from, next) {
|
||||
const before: NavigationGuardWithThis<undefined> = function () {
|
||||
NProgress.start();
|
||||
next();
|
||||
};
|
||||
|
||||
const after: NavigationHookAfter = function () {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
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 title = to.meta.title || appStore.title;
|
||||
const subtitle = appStore.subtitle;
|
||||
document.title = `${title} | ${subtitle}`;
|
||||
next();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { authGuard } from "../guards/guard-auth";
|
|||
import { nprogressGuard } from "../guards/guard-nprogress";
|
||||
import { titleGuard } from "../guards/guard-title";
|
||||
import { routes } from "../routes";
|
||||
import { api } from "@/api";
|
||||
|
||||
export const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
|
|
@ -17,5 +18,10 @@ export const router = createRouter({
|
|||
|
||||
router.beforeEach(nprogressGuard.before);
|
||||
router.afterEach(nprogressGuard.after);
|
||||
router.beforeEach(titleGuard);
|
||||
router.beforeEach(authGuard);
|
||||
router.afterEach(titleGuard);
|
||||
|
||||
api.setTokenExpiredHandler(() => {
|
||||
const redirect = router.currentRoute.value.path;
|
||||
router.push({ path: "/login", query: { redirect } });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import { RouteRecordRaw } from "vue-router";
|
|||
const APP_ROUTE_NAME = "_app";
|
||||
|
||||
/**
|
||||
* 转换一维路由为二维路由,其中以 _ 开头的路由为顶级路由,其余为应用路由
|
||||
* @param routes 路由配置
|
||||
* @returns
|
||||
* 转换一维路由为二维路由,以 _ 开头的路由为顶级路由,其余为应用路由
|
||||
*/
|
||||
const transformRoutes = (routes: RouteRecordRaw[]) => {
|
||||
const topRoutes: RouteRecordRaw[] = [];
|
||||
|
|
@ -33,26 +31,7 @@ const transformRoutes = (routes: RouteRecordRaw[]) => {
|
|||
appRoute.children = appRoutes;
|
||||
}
|
||||
|
||||
return topRoutes;
|
||||
return [topRoutes, appRoutes];
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取应用路由
|
||||
* @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 };
|
||||
export const [routes, appRoutes] = transformRoutes(generatedRoutes);
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@ declare module '@vue/runtime-core' {
|
|||
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
||||
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
|
||||
ADoption: typeof import('@arco-design/web-vue')['Doption']
|
||||
ADot: typeof import('@arco-design/web-vue')['Dot']
|
||||
ADrawer: typeof import('@arco-design/web-vue')['Drawer']
|
||||
ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
|
||||
AForm: typeof import('@arco-design/web-vue')['Form']
|
||||
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
||||
AImage: typeof import('@arco-design/web-vue')['Image']
|
||||
AInput: typeof import('@arco-design/web-vue')['Input']
|
||||
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
||||
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']
|
||||
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
|
||||
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']
|
||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
||||
APopover: typeof import('@arco-design/web-vue')['Popover']
|
||||
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||
ASubMenu: typeof import('@arco-design/web-vue')['SubMenu']
|
||||
ATag: typeof import('@arco-design/web-vue')['Tag']
|
||||
|
|
|
|||
Loading…
Reference in New Issue