create-young-proj 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +13 -2
  3. package/dist/index.mjs +18 -18
  4. package/index.mjs +3 -3
  5. package/package.json +10 -12
  6. package/template-admin-server/.editorconfig +11 -0
  7. package/template-admin-server/.nvmrc +1 -0
  8. package/template-admin-server/.vscode/extensions.json +6 -0
  9. package/template-admin-server/.vscode/settings.json +4 -0
  10. package/template-admin-server/README.md +73 -0
  11. package/template-admin-server/_gitignore +15 -0
  12. package/template-admin-server/boot.mjs +11 -0
  13. package/template-admin-server/package.json +60 -0
  14. package/template-admin-server/rome.json +22 -0
  15. package/template-admin-server/src/config/config.default.ts +56 -0
  16. package/template-admin-server/src/configuration.ts +47 -0
  17. package/template-admin-server/src/controller/admin.controller.ts +397 -0
  18. package/template-admin-server/src/controller/api.controller.ts +98 -0
  19. package/template-admin-server/src/controller/base.controller.ts +70 -0
  20. package/template-admin-server/src/controller/dto/api.ts +47 -0
  21. package/template-admin-server/src/controller/dto/index.ts +36 -0
  22. package/template-admin-server/src/controller/dto/menu.ts +41 -0
  23. package/template-admin-server/src/controller/dto/role.ts +41 -0
  24. package/template-admin-server/src/controller/dto/user.ts +52 -0
  25. package/template-admin-server/src/controller/menu.controller.ts +138 -0
  26. package/template-admin-server/src/controller/role.controller.ts +116 -0
  27. package/template-admin-server/src/controller/user.controller.ts +108 -0
  28. package/template-admin-server/src/entities/Api.ts +29 -0
  29. package/template-admin-server/src/entities/BaseCreate.ts +30 -0
  30. package/template-admin-server/src/entities/Menu.ts +39 -0
  31. package/template-admin-server/src/entities/Role.ts +36 -0
  32. package/template-admin-server/src/entities/User.ts +35 -0
  33. package/template-admin-server/src/entities/index.ts +10 -0
  34. package/template-admin-server/src/filter/default.filter.ts +22 -0
  35. package/template-admin-server/src/filter/notfound.filter.ts +23 -0
  36. package/template-admin-server/src/middleware/helper.middleware.ts +28 -0
  37. package/template-admin-server/src/middleware/index.ts +9 -0
  38. package/template-admin-server/src/middleware/jwt.middleware.ts +32 -0
  39. package/template-admin-server/src/middleware/report.middleware.ts +26 -0
  40. package/template-admin-server/src/service/api.service.ts +174 -0
  41. package/template-admin-server/src/service/basic.ts +118 -0
  42. package/template-admin-server/src/service/index.ts +10 -0
  43. package/template-admin-server/src/service/menu.service.ts +139 -0
  44. package/template-admin-server/src/service/role.service.ts +286 -0
  45. package/template-admin-server/src/service/user.service.ts +124 -0
  46. package/template-admin-server/src/strategy/jwt.strategy.ts +26 -0
  47. package/template-admin-server/src/types/index.ts +42 -0
  48. package/template-admin-server/src/types/types.d.ts +31 -0
  49. package/template-admin-server/tsconfig.json +24 -0
  50. package/template-vue-admin/.vscode/extensions.json +10 -0
  51. package/template-vue-admin/.vscode/list-add.code-snippets +108 -0
  52. package/template-vue-admin/.vscode/list-export.code-snippets +72 -0
  53. package/template-vue-admin/.vscode/list.code-snippets +61 -0
  54. package/template-vue-admin/.vscode/settings.json +7 -0
  55. package/template-vue-admin/Dockerfile +42 -0
  56. package/template-vue-admin/README.md +75 -0
  57. package/template-vue-admin/_env +8 -0
  58. package/template-vue-admin/_gitignore +30 -0
  59. package/template-vue-admin/boot.mjs +16 -0
  60. package/template-vue-admin/config/.devrc +2 -0
  61. package/template-vue-admin/config/.onlinerc +2 -0
  62. package/template-vue-admin/config/.testrc +2 -0
  63. package/template-vue-admin/index.html +21 -0
  64. package/template-vue-admin/nitro.config.ts +19 -0
  65. package/template-vue-admin/package.json +50 -0
  66. package/template-vue-admin/plugins/env.ts +26 -0
  67. package/template-vue-admin/public/vite.svg +1 -0
  68. package/template-vue-admin/rome.json +26 -0
  69. package/template-vue-admin/routes/api/[...all].ts +49 -0
  70. package/template-vue-admin/routes/get/env.ts +18 -0
  71. package/template-vue-admin/src/App.vue +14 -0
  72. package/template-vue-admin/src/apis/delete.ts +36 -0
  73. package/template-vue-admin/src/apis/get.ts +84 -0
  74. package/template-vue-admin/src/apis/index.ts +10 -0
  75. package/template-vue-admin/src/apis/patch.ts +79 -0
  76. package/template-vue-admin/src/apis/post.ts +77 -0
  77. package/template-vue-admin/src/assets/img/login_background.jpg +0 -0
  78. package/template-vue-admin/src/auto-components.d.ts +36 -0
  79. package/template-vue-admin/src/auto-imports.d.ts +282 -0
  80. package/template-vue-admin/src/layouts/blank.vue +9 -0
  81. package/template-vue-admin/src/layouts/default/components/Link.vue +23 -0
  82. package/template-vue-admin/src/layouts/default/components/Logo.vue +20 -0
  83. package/template-vue-admin/src/layouts/default/components/Menu.vue +54 -0
  84. package/template-vue-admin/src/layouts/default/components/NavSearch.vue +52 -0
  85. package/template-vue-admin/src/layouts/default/components/ScrollPane.vue +79 -0
  86. package/template-vue-admin/src/layouts/default/components/TagsView.vue +137 -0
  87. package/template-vue-admin/src/layouts/default/components/TopMenu.vue +21 -0
  88. package/template-vue-admin/src/layouts/default/components/UserCenter.vue +50 -0
  89. package/template-vue-admin/src/layouts/default/index.vue +95 -0
  90. package/template-vue-admin/src/main.ts +44 -0
  91. package/template-vue-admin/src/modules/1-router.ts +66 -0
  92. package/template-vue-admin/src/modules/2-pinia.ts +10 -0
  93. package/template-vue-admin/src/modules/3-net.ts +75 -0
  94. package/template-vue-admin/src/modules/4-auth.ts +122 -0
  95. package/template-vue-admin/src/stores/index.ts +9 -0
  96. package/template-vue-admin/src/stores/local/index.ts +23 -0
  97. package/template-vue-admin/src/stores/session/index.ts +63 -0
  98. package/template-vue-admin/src/stores/tags.ts +109 -0
  99. package/template-vue-admin/src/typings/global.d.ts +70 -0
  100. package/template-vue-admin/src/typings/index.ts +50 -0
  101. package/template-vue-admin/src/views/403.vue +32 -0
  102. package/template-vue-admin/src/views/[...all_404].vue +556 -0
  103. package/template-vue-admin/src/views/base/login.vue +193 -0
  104. package/template-vue-admin/src/views/dashboard/[name].vue +23 -0
  105. package/template-vue-admin/src/views/index.vue +19 -0
  106. package/template-vue-admin/src/views/system/api.vue +161 -0
  107. package/template-vue-admin/src/views/system/hooks/useRole.ts +286 -0
  108. package/template-vue-admin/src/views/system/menuList.vue +195 -0
  109. package/template-vue-admin/src/views/system/role.vue +132 -0
  110. package/template-vue-admin/src/views/system/user.vue +193 -0
  111. package/template-vue-admin/src/vite-env.d.ts +52 -0
  112. package/template-vue-admin/tsconfig.json +21 -0
  113. package/template-vue-admin/tsconfig.node.json +9 -0
  114. package/template-vue-admin/unocss.config.ts +47 -0
  115. package/template-vue-admin/vite.config.ts +77 -0
  116. package/template-vue-thin/package.json +14 -13
  117. package/template-vue-thin/vite.config.ts +1 -6
@@ -0,0 +1,122 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-03-01 19:40:13
4
+ * @LastEditTime: 2023-01-09 09:19:41
5
+ * @Description: 权限校验
6
+ */
7
+ import { router } from './1-router';
8
+ import type { RouteLocationNormalized } from 'vue-router';
9
+ import { useNavStore, useTagsStore, useUserStore, getToken } from '@/stores';
10
+ import { apis } from './3-net';
11
+
12
+ const changeTitle = (route: RouteLocationNormalized) => {
13
+ document.title =
14
+ (route.meta.title as string) || window.__YOUNG_VITE_ENV__.VITE_TITLE || '管理后台';
15
+ };
16
+
17
+ const { CurrUserInfo } = useUserStore();
18
+ const { NavArrAll, RoleRoute, FlatNavArr, RawNav } = useNavStore();
19
+
20
+ let role_route: string[] = [];
21
+ const generateRoleRoute = (arr: NavArrItem[], num?: number): string[] => {
22
+ if (num === 1) {
23
+ role_route = [];
24
+ FlatNavArr.value = [];
25
+ }
26
+ for (const item of arr) {
27
+ if (item.component) {
28
+ role_route.push(item.component);
29
+ +item.visible === 1 && FlatNavArr.value.push(item);
30
+ }
31
+ // 子节点递归遍历
32
+ if (Array.isArray(item.children) && item.children.length > 0) {
33
+ const part = JSON.parse(JSON.stringify(item.children));
34
+ // 尾递归优化
35
+ generateRoleRoute(part);
36
+ }
37
+ }
38
+ return role_route;
39
+ };
40
+ // 清除没有子元素的children
41
+ export const clearChildren = <T extends Record<string, any>>(arr: T[]) => {
42
+ for (const item of arr) {
43
+ if (item?.children.length === 0) {
44
+ delete item.children;
45
+ } else if (item.children) {
46
+ clearChildren(item.children);
47
+ }
48
+ }
49
+ return arr;
50
+ };
51
+
52
+ export const generateNavData = async () => {
53
+ const info = await apis.post.getCurrUserInfo();
54
+
55
+ if (!info) {
56
+ return;
57
+ }
58
+
59
+ const menu = Object.values(await apis.get.getMenuTree());
60
+ CurrUserInfo.value = info;
61
+ RawNav.value = menu;
62
+
63
+ // 后端获取用户可见节点
64
+ const navArr: NavArrItem[] = menu;
65
+ const routes: string[] = generateRoleRoute(menu, 1);
66
+
67
+ // 导航数组
68
+ NavArrAll.value = navArr.filter((item) => +item.visible === 1);
69
+ // 生成角色有权访问的路由
70
+ RoleRoute.value = routes.slice();
71
+ };
72
+
73
+ export const hasPermission = (path: string) => {
74
+ const { RoleRoute } = useNavStore();
75
+ const roleRoute = RoleRoute.value;
76
+ return roleRoute.includes(path);
77
+ };
78
+
79
+ export const install: UserModule = (app) => {
80
+ router.beforeEach(async (to, from) => {
81
+ /* 授权登录,暂未启用
82
+ const { code, state } = Object.fromEntries(new URLSearchParams(location.search));
83
+ if (code && state) {
84
+ (await casdoorLogin(code, state)) as UserKey;
85
+ getToken() && (await generateNavData());
86
+ location.search = '';
87
+ changeTitle(to);
88
+ return true;
89
+ } else
90
+ */
91
+ if (to.path !== '/base/login') {
92
+ // 页面无需权限
93
+ if (!to.meta.authPath) {
94
+ return true;
95
+ }
96
+ // 已登录
97
+ if (getToken()) {
98
+ // 生成权限树
99
+ await generateNavData();
100
+ if (hasPermission(to.meta.authPath)) {
101
+ // 拥有对应页面的权限
102
+ changeTitle(to);
103
+ return true;
104
+ }
105
+ if (!hasPermission(to.meta.authPath)) {
106
+ // 已登录,并且无权限
107
+ changeTitle(from);
108
+ return '/403';
109
+ }
110
+ } else {
111
+ return '/base/login';
112
+ }
113
+ } else {
114
+ return true;
115
+ }
116
+ });
117
+
118
+ router.afterEach((to) => {
119
+ const { addView } = useTagsStore();
120
+ addView(to);
121
+ });
122
+ };
@@ -0,0 +1,9 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-03-01 14:42:57
4
+ * @LastEditTime: 2023-01-09 09:19:05
5
+ * @Description:
6
+ */
7
+ export * from './tags';
8
+ export * from './session';
9
+ export * from './local';
@@ -0,0 +1,23 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-10-20 08:52:37
4
+ * @LastEditTime: 2023-01-09 09:18:45
5
+ * @Description:
6
+ */
7
+ import { YoungLocalStorage } from '@bluesyoung/utils';
8
+
9
+ export const YoungStorage = new YoungLocalStorage();
10
+
11
+ const TOKEN_KEY = 'bluesyoung-web.com/music/admin';
12
+
13
+ export const getToken = () => {
14
+ return YoungStorage.get<UserKey>(TOKEN_KEY)?.token;
15
+ };
16
+
17
+ export const setToken = (data: UserKey) => {
18
+ return YoungStorage.set(TOKEN_KEY, data);
19
+ };
20
+
21
+ export const removeToken = () => {
22
+ return YoungStorage.remove(TOKEN_KEY);
23
+ };
@@ -0,0 +1,63 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-03-02 18:42:56
4
+ * @LastEditTime: 2023-01-04 15:14:46
5
+ * @Description: 存储界面相关的数据
6
+ */
7
+ const SESSION_PREFIX = '__young_admin_';
8
+
9
+ export const SESSION_KEYS = {
10
+ collapsed: `${SESSION_PREFIX}collapsed`,
11
+ topIndex: `${SESSION_PREFIX}topIndex`,
12
+ expandNodeKeys: `${SESSION_PREFIX}expandNodeKeys`,
13
+ userInfo: `${SESSION_PREFIX}userInfo`,
14
+ rawNavArr: `${SESSION_PREFIX}rawNavArr`,
15
+ navArr: `${SESSION_PREFIX}navArr`,
16
+ flatNavArr: `${SESSION_PREFIX}flatNavArr`,
17
+ roleRoute: `${SESSION_PREFIX}roleRoute`,
18
+ };
19
+
20
+ export const isCollapse = useSessionStorage(SESSION_KEYS.collapsed, false);
21
+
22
+ export const TopIndex = useSessionStorage(SESSION_KEYS.topIndex, '0');
23
+
24
+ export const expandNodeKeys = useSessionStorage<number[]>(SESSION_KEYS.expandNodeKeys, []);
25
+
26
+ export const useUserStore = () => {
27
+ const CurrUserInfo = useSessionStorage<CurrUserInfo>(
28
+ SESSION_KEYS.userInfo,
29
+ {} as unknown as CurrUserInfo,
30
+ );
31
+ const reset = () => (CurrUserInfo.value = {} as unknown as CurrUserInfo);
32
+ return {
33
+ CurrUserInfo,
34
+ reset,
35
+ };
36
+ };
37
+
38
+ export const useNavStore = () => {
39
+ const RawNav = useSessionStorage<NavArrItem[]>(SESSION_KEYS.rawNavArr, []);
40
+ const NavArrAll = useSessionStorage<NavArrItem[]>(SESSION_KEYS.navArr, []);
41
+ const FlatNavArr = useSessionStorage<NavArrItem[]>(SESSION_KEYS.flatNavArr, []);
42
+ const RoleRoute = useSessionStorage<string[]>(SESSION_KEYS.roleRoute, []);
43
+ const TopMenu = computed(() => RawNav.value.filter((nav) => nav.visible === 1));
44
+ const NavArr = computed(() =>
45
+ TopIndex.value ? NavArrAll.value[+TopIndex.value]?.children || [] : [],
46
+ );
47
+
48
+ const reset = () => {
49
+ NavArrAll.value = [];
50
+ FlatNavArr.value = [];
51
+ RoleRoute.value = [];
52
+ RawNav.value = [];
53
+ };
54
+ return {
55
+ RawNav,
56
+ NavArrAll,
57
+ NavArr,
58
+ FlatNavArr,
59
+ RoleRoute,
60
+ TopMenu,
61
+ reset,
62
+ };
63
+ };
@@ -0,0 +1,109 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-02-24 15:55:05
4
+ * @LastEditTime: 2023-01-04 19:47:49
5
+ * @Description: 存储标签页的状态
6
+ */
7
+ import { defineStore, acceptHMRUpdate } from 'pinia';
8
+ import type { RouteLocationNormalized } from 'vue-router';
9
+
10
+ type CachedView = string | symbol | null | undefined;
11
+
12
+ interface TagsViewState {
13
+ /**
14
+ * 访问过的页面,路由对象
15
+ */
16
+ visitedViews: RouteLocationNormalized[];
17
+ /**
18
+ * 已缓存的页面名称
19
+ */
20
+ cachedViews: CachedView[];
21
+ }
22
+ // @ts-ignore
23
+ export const useTagsStore = defineStore('useTagsStore', {
24
+ state: () => {
25
+ const state = reactive<TagsViewState>({
26
+ visitedViews: [],
27
+ cachedViews: [],
28
+ });
29
+ return state;
30
+ },
31
+ actions: {
32
+ /**
33
+ * 打开某个页面
34
+ */
35
+ addView(view: RouteLocationNormalized) {
36
+ if (view.path === '/') {
37
+ return;
38
+ }
39
+
40
+ // 非我族类其心必异,布局不是默认布局的页面,不缓存
41
+ if ((view.meta?.layout ?? 'default') !== 'default') {
42
+ return;
43
+ }
44
+ // 查询是否已经访问过
45
+ if (!this.visitedViews.some((v) => v.path === view.path)) {
46
+ // 添加到已访问
47
+ const { name, path, fullPath, meta } = view;
48
+ this.visitedViews.push({ name, path, fullPath, meta } as RouteLocationNormalized);
49
+ // 添加到缓存
50
+ this.addToCache(view);
51
+ }
52
+ },
53
+ /**
54
+ * 添加页面到缓存
55
+ */
56
+ addToCache(view: RouteLocationNormalized) {
57
+ // 查询该标签是否已缓存
58
+ if (this.cachedViews.includes(view.name)) {
59
+ null;
60
+ } else if (!view.meta?.noCache) {
61
+ this.cachedViews.push(view.name);
62
+ }
63
+ },
64
+ /**
65
+ * 关闭某个页面
66
+ */
67
+ delView(view: RouteLocationNormalized) {
68
+ // 固定页不能关闭
69
+ if (view.meta.affix) {
70
+ ElMessage.warning('固定页无法关闭!');
71
+ return false;
72
+ }
73
+ // 删除访问记录
74
+ const index = this.visitedViews.findIndex((r) => r.path === view.path);
75
+ index > -1 && this.visitedViews.splice(index, 1);
76
+ // 删除缓存
77
+ this.delCachedView(view);
78
+ return true;
79
+ },
80
+ /**
81
+ * 删除页面缓存
82
+ */
83
+ delCachedView(view: RouteLocationNormalized) {
84
+ const index = this.cachedViews.indexOf(view.name);
85
+ index > -1 && this.cachedViews.splice(index, 1);
86
+ },
87
+ /**
88
+ * 关闭其他页面
89
+ */
90
+ delOtherViews(view: RouteLocationNormalized) {
91
+ this.visitedViews = this.visitedViews.filter((v) => {
92
+ return v?.meta?.affix || v.path === view.path;
93
+ });
94
+ this.cachedViews = this.cachedViews.filter((v) => {
95
+ return v === view.name;
96
+ });
97
+ },
98
+ /**
99
+ * 关闭所有页面
100
+ */
101
+ delAllViews() {
102
+ const affixTags = this.visitedViews.filter((tag) => tag?.meta?.affix);
103
+ this.visitedViews = affixTags;
104
+ this.cachedViews.length = 0;
105
+ },
106
+ },
107
+ });
108
+
109
+ import.meta.hot && import.meta.hot.accept(acceptHMRUpdate(useTagsStore, import.meta.hot));
@@ -0,0 +1,70 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-03-09 17:55:56
4
+ * @LastEditTime: 2023-01-09 10:05:44
5
+ * @Description: 自定义表格组件相关
6
+ */
7
+ import type { App, VNode } from 'vue';
8
+
9
+ declare global {
10
+ interface BaseQuery {
11
+ pageNum: number;
12
+ pageSize: number;
13
+ total: number;
14
+ noPagination?: boolean;
15
+ }
16
+
17
+ type PagesData = {
18
+ list: any[];
19
+ } & BaseQuery;
20
+
21
+ type EnableWrite<T extends any> = {
22
+ -readonly [p in keyof T]: T[p];
23
+ };
24
+
25
+ type UserModule = (ctx: App<Element>, ...args: any[]) => void;
26
+ type Cbk = () => void;
27
+
28
+ type ResponseMsg = {
29
+ code: number;
30
+ msg: string;
31
+ data: any;
32
+ };
33
+
34
+ type UserKey = {
35
+ expires: string;
36
+ token: string;
37
+ };
38
+
39
+ type NavArrItem = {
40
+ breadcrumb: number;
41
+ component: string;
42
+ createdAt: string;
43
+ creator: string;
44
+ icon?: any;
45
+ id: number;
46
+ name: string;
47
+ not_dev: number;
48
+ parentId: number;
49
+ path: string;
50
+ permission: string;
51
+ redirect: string;
52
+ sort: number;
53
+ status: number;
54
+ title?: string;
55
+ updatedAt: string;
56
+ visible: number;
57
+ children?: NavArrItem[] | [];
58
+ } & Record<string, any>;
59
+
60
+ type CurrUserInfo = {
61
+ avatar: string;
62
+ id: number;
63
+ introduction: string;
64
+ mobile: string;
65
+ nickname: string;
66
+ roles: string[];
67
+ roleSort: number;
68
+ username: string;
69
+ };
70
+ }
@@ -0,0 +1,50 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-03-01 14:03:04
4
+ * @LastEditTime: 2023-01-06 16:47:05
5
+ * @Description: 部分类型定义
6
+ */
7
+ export type UserItem = {
8
+ id: number;
9
+ username: string;
10
+ nickname: string;
11
+ mobile: string;
12
+ roleId: number;
13
+ status: number;
14
+ role_name?: string;
15
+ creator?: string;
16
+ newPassword?: string;
17
+ initPassword?: string;
18
+ };
19
+
20
+ export type RoleItem = {
21
+ createdAt?: string;
22
+ creator?: string;
23
+ desc: string;
24
+ id: number;
25
+ keyword: string;
26
+ name: string;
27
+ not_dev?: number;
28
+ sort?: number;
29
+ status: number;
30
+ updatedAt?: string;
31
+ };
32
+
33
+ export const MethodObj = {
34
+ GET: 'info',
35
+ POST: 'success',
36
+ PATCH: 'warning',
37
+ PUT: '',
38
+ DELETE: 'danger',
39
+ } as const;
40
+
41
+ export type ApiItem = {
42
+ id: number;
43
+ path: string;
44
+ desc: string;
45
+ category: string;
46
+ method: keyof typeof MethodObj;
47
+ roleIds: number[];
48
+ creator?: string;
49
+ title?: string;
50
+ };
@@ -0,0 +1,32 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2022-10-27 11:32:57
4
+ * @LastEditTime: 2023-01-04 19:42:03
5
+ * @Description:
6
+ -->
7
+ <route lang="yaml">
8
+ meta:
9
+ title: 403
10
+ </route>
11
+
12
+ <script lang="ts" setup>
13
+ import { router } from '@/modules/1-router';
14
+ import { useUserStore, useNavStore, removeToken } from '@/stores';
15
+
16
+ const loginOut = async () => {
17
+ removeToken();
18
+ useUserStore().reset();
19
+ useNavStore().reset();
20
+ router.replace('/base/login');
21
+ };
22
+ </script>
23
+
24
+ <template>
25
+ <div class="mt-20">
26
+ <ElResult icon="error" title="403 禁止访问" sub-title="总有些门是对你关闭的" />
27
+
28
+ <div class="text-center">
29
+ <ElButton type="primary" @click="loginOut">重新登录</ElButton>
30
+ </div>
31
+ </div>
32
+ </template>