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,54 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2022-10-26 09:24:29
4
+ * @LastEditTime: 2023-01-06 16:42:20
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ import { useNavStore } from '@/stores';
9
+ import Link from './Link.vue';
10
+ const { NavArr } = useNavStore();
11
+ const activeIndex = ref('0');
12
+ const haldleSelected = (index: string) => {
13
+ activeIndex.value = index;
14
+ };
15
+ </script>
16
+ <template>
17
+ <ElMenu :default-active="activeIndex" background-color="rgb(48, 65, 86)" text-color="#fff" mode="vertical"
18
+ style="border-right: none;" router @select="haldleSelected">
19
+ <div v-for="(nav, index) in NavArr" :key="index + 'adskjgkjer'">
20
+ <ElSubMenu v-if="NavArr[index]?.children?.filter((n) => +n.visible === 1).length"
21
+ :index="index + 'adskjgkjer' + nav.component">
22
+ <template #title>
23
+ <Link v-if="nav.component" :to="nav.component">
24
+ <span class="ml-1">{{ nav.title }}</span>
25
+ </Link>
26
+ <span v-else class="ml-1">{{ nav.title }}</span>
27
+ </template>
28
+ <ElMenuItemGroup>
29
+ <ElMenuItem style="padding: 0;" v-for="(sub, idx) in NavArr[index].children?.filter((n) => +n.visible === 1)"
30
+ :key="index + '-' + idx + 'fdjahsuy'" :index="sub.component" :route="sub.component">
31
+ <template #title>
32
+ <Link class="block w-full text-left box-border pl-[40px] pr-[20px]" :to="sub.component">
33
+ <span class="ml-1">{{ sub.title }}</span>
34
+ </Link>
35
+ </template>
36
+ </ElMenuItem>
37
+ </ElMenuItemGroup>
38
+ </ElSubMenu>
39
+ <ElMenuItem v-else :index="nav.component">
40
+ <template #title>
41
+ <Link :to="nav.component">
42
+ <span class="ml-1">{{ nav.title }}</span>
43
+ </Link>
44
+ </template>
45
+ </ElMenuItem>
46
+ </div>
47
+ </ElMenu>
48
+ </template>
49
+
50
+ <style>
51
+ .el-menu-item-group__title {
52
+ display: none;
53
+ }
54
+ </style>
@@ -0,0 +1,52 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2022-10-26 10:08:07
4
+ * @LastEditTime: 2023-01-09 10:27:03
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ import type { SelectOptionItem } from '@bluesyoung/ui-vue3-element-plus';
9
+ import { YoungCmdPopup } from '@bluesyoung/ui-vue3';
10
+ import { useNavStore } from '@/stores';
11
+
12
+ const { FlatNavArr } = useNavStore();
13
+
14
+ const options = ref<SelectOptionItem<string>[]>([]);
15
+ const searchStr = ref('');
16
+ const remoteMethod = (query: string) => {
17
+ if (query) {
18
+ options.value = FlatNavArr.value
19
+ .filter((item) => +item.visible === 1 && item.title?.toLowerCase().includes(query.toLowerCase()))
20
+ .map((item) => ({ label: item.title || '', value: item.component }));
21
+ } else {
22
+ options.value = [];
23
+ }
24
+ };
25
+
26
+ const router = useRouter();
27
+
28
+ const cmdRef = ref()
29
+
30
+ const goPage = (url: string) => {
31
+ if (url) {
32
+ router.push(url);
33
+ searchStr.value = '';
34
+ cmdRef.value?.hide()
35
+ }
36
+ };
37
+ </script>
38
+ <template>
39
+ <YoungCmdPopup ref="cmdRef">
40
+ <template #default="{ el }">
41
+ <div class="flex flex-col items-center">
42
+ <div class="text-xl mb-4">
43
+ 快捷菜单搜索:
44
+ </div>
45
+ <ElSelect :ref="el" class="w-260px" v-model="searchStr" filterable remote reserve-keyword
46
+ placeholder="请输入菜单关键字,快捷键 Ctrl + K" :remote-method="remoteMethod" @change="goPage">
47
+ <ElOption v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
48
+ </ElSelect>
49
+ </div>
50
+ </template>
51
+ </YoungCmdPopup>
52
+ </template>
@@ -0,0 +1,79 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2020-12-11 11:02:54
4
+ * @LastEditTime: 2023-01-05 16:07:55
5
+ * @Description: 滚动容器
6
+ -->
7
+ <template>
8
+ <div ref="scrollWrapper" class="max-w-[calc(90vw)] flex-nowrap whitespace-nowrap overflow-x-auto"
9
+ @wheel.prevent="scrollHandler">
10
+ <span>
11
+ <slot />
12
+ </span>
13
+ </div>
14
+ </template>
15
+ <script lang="ts" setup>
16
+ type EL = Element | null;
17
+ /**
18
+ * 标签间距
19
+ */
20
+ const tagSpacing = 4;
21
+ const scrollWrapper = ref<HTMLElement>();
22
+ // 滚动鼠标滚轮
23
+ const scrollHandler = (e: WheelEvent) => {
24
+ const eventData = e.deltaY * 40;
25
+ const wrap = scrollWrapper.value;
26
+ if (wrap) {
27
+ wrap.scrollLeft = wrap.scrollLeft + eventData / 4;
28
+ }
29
+ };
30
+ const moveToTarget = () => {
31
+ if (scrollWrapper.value) {
32
+ // 外层包裹
33
+ const wrap: HTMLElement = scrollWrapper.value as any;
34
+ // 容器宽度
35
+ const containerWidth = 1704;
36
+ // 标签列表
37
+ const tagList: HTMLCollection = (wrap.firstElementChild as HTMLElement)?.children;
38
+
39
+ let firstChild: EL = null, lastChild: EL = null, targetElement: EL = null;
40
+ if (tagList.length > 0) {
41
+ firstChild = tagList[0];
42
+ lastChild = tagList[tagList.length - 1];
43
+ for (const element of Array.from(tagList)) {
44
+ if (Array.from(element.classList).includes('el-tag--success')) {
45
+ targetElement = element;
46
+ break;
47
+ }
48
+ }
49
+
50
+ if (firstChild === targetElement) {
51
+ wrap.scrollLeft = 0;
52
+ } else if (lastChild === targetElement) {
53
+ wrap.scrollLeft = wrap.scrollWidth - containerWidth;
54
+ } else {
55
+ const currentIndex = Array.from(tagList).findIndex((item) => item === targetElement);
56
+
57
+ const preElement = tagList[currentIndex - 1];
58
+ const nextElement = tagList[currentIndex + 1];
59
+ if (!preElement || !nextElement) {
60
+ return;
61
+ }
62
+
63
+ const afterNextTagOffsetLeft = (nextElement as HTMLElement).offsetLeft + (nextElement as HTMLElement).offsetWidth + tagSpacing;
64
+ const beforePrevTagOffsetLeft = (preElement as HTMLElement).offsetLeft - tagSpacing;
65
+
66
+ if (afterNextTagOffsetLeft > wrap.scrollLeft + containerWidth) {
67
+ wrap.scrollLeft = afterNextTagOffsetLeft - containerWidth;
68
+ } else if (beforePrevTagOffsetLeft < wrap.scrollLeft) {
69
+ wrap.scrollLeft = beforePrevTagOffsetLeft;
70
+ }
71
+ }
72
+ } else {
73
+ return;
74
+ }
75
+ }
76
+ };
77
+ // 暴露给其父级组件通过 ref 使用
78
+ defineExpose({ moveToTarget });
79
+ </script>
@@ -0,0 +1,137 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2020-12-11 13:35:58
4
+ * @LastEditTime: 2023-01-05 16:00:11
5
+ * @Description: 标签选项卡组件
6
+ -->
7
+ <template>
8
+ <div class="tags-view-container px-2">
9
+ <ScrollPane ref="scrollPane" class="tags-view-wrapper">
10
+ <ElTag v-for="(tag, index) in visitedViews" :key="index + 'fadsrhewioru'" effect="dark" size="large"
11
+ class="inline-block mx-1 my-0.2vh hover:cursor-pointer" :type="isActive(tag) ? 'success' : 'info'"
12
+ :closable="!tag.meta.affix && visitedViews.length > 1" @click="router.push(tag.fullPath)"
13
+ @close.stop="menuHandlers['closeThis'](tag)" @contextmenu.prevent="openContextMenu(tag, $event)">
14
+ {{ tag.meta.title }}
15
+ </ElTag>
16
+ </ScrollPane>
17
+
18
+ <YoungContextMenu v-model="showContextMenu" :menu-list="menuList" @clickItem="clickItemHandler" />
19
+ </div>
20
+ </template>
21
+ <script lang="ts" setup>
22
+ import { YoungContextMenu } from '@bluesyoung/ui-vue3';
23
+ import ScrollPane from './ScrollPane.vue';
24
+ import { storeToRefs } from 'pinia';
25
+ import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
26
+ import { useTagsStore } from '@/stores';
27
+
28
+ const showContextMenu = ref(false);
29
+
30
+ const tagsState = useTagsStore();
31
+ const { visitedViews, cachedViews } = storeToRefs(tagsState);
32
+ const menuList = computed(() => {
33
+ let baseMenu = [
34
+ { title: '关闭此页面', handlerName: 'closeThis' },
35
+ { title: '关闭其他页面', handlerName: 'closeOthers' }
36
+ ];
37
+ if (visitedViews.value.length === 1) {
38
+ baseMenu = [];
39
+ }
40
+ const { name } = route;
41
+ const s_name = selectedTag.value?.name ?? '';
42
+ // 当前页为激活状态才允许刷新
43
+ const res = (cachedViews.value.includes(s_name) && name === s_name) ? [{ title: '刷新此页面', handlerName: 'refresh' }].concat(baseMenu) : baseMenu;
44
+ return res;
45
+ });
46
+ const router = useRouter();
47
+ const route = useRoute();
48
+ /**
49
+ * 当前选中的标签
50
+ */
51
+ const selectedTag = ref<RouteLocationNormalized | null>(null);
52
+ /**
53
+ * 所有固定的标签(禁止关闭)
54
+ */
55
+ const affixTags = ref<RouteLocationNormalized[]>([]);
56
+
57
+ const isActive = (view: RouteLocationNormalized) => view.path === route.path;
58
+ const isAffix = (route: RouteLocationNormalized | RouteRecordRaw) => route?.meta?.affix ?? false;
59
+
60
+ const toLastView = (visitedViews: RouteLocationNormalized[], view: RouteLocationNormalized) => {
61
+ const lastView = visitedViews.slice(-1)[0];
62
+ if (lastView) {
63
+ router.push(lastView.fullPath);
64
+ }
65
+ };
66
+
67
+ const scrollPane = ref<any | null>(null);
68
+
69
+ const moveToCurrentTag = () => {
70
+ nextTick(() => {
71
+ const SP = scrollPane.value;
72
+ SP?.moveToTarget?.();
73
+ });
74
+ };
75
+
76
+ const menuHandlers: Record<string, Function> = {
77
+ 'refresh': () => {
78
+ const tag = selectedTag.value;
79
+ tag && tagsState.delCachedView(tag);
80
+ },
81
+ 'closeThis': (tag = selectedTag.value) => {
82
+ if (tag) {
83
+ if (!isAffix(tag)) {
84
+ tagsState.delView(tag);
85
+ isActive(tag) && toLastView(visitedViews.value, tag);
86
+ }
87
+ }
88
+ },
89
+ 'closeOthers': () => {
90
+ const tag = selectedTag.value;
91
+ if (tag) {
92
+ router.push(tag);
93
+ tagsState.delOtherViews(tag);
94
+ }
95
+ },
96
+ 'closeAll': () => {
97
+ tagsState.delAllViews();
98
+ const sTag = selectedTag.value;
99
+ if (sTag) {
100
+ if (affixTags.value.some((tag) => tag.path === sTag.path)) {
101
+ return;
102
+ }
103
+ toLastView(visitedViews.value, sTag);
104
+ }
105
+ }
106
+ };
107
+ const clickItemHandler = (handler: string) => {
108
+ menuHandlers?.[handler]?.();
109
+ showContextMenu.value = false;
110
+ };
111
+ const openContextMenu = (tag: RouteLocationNormalized, e: MouseEvent) => {
112
+ nextTick(() => {
113
+ if (menuList.value.length === 0) {
114
+ return;
115
+ }
116
+ showContextMenu.value = true;
117
+ selectedTag.value = tag;
118
+ });
119
+ };
120
+
121
+ router.afterEach((to) => {
122
+ tagsState.addView(to);
123
+ moveToCurrentTag();
124
+ });
125
+
126
+ onMounted(() => {
127
+ tagsState.addView(route);
128
+ });
129
+ </script>
130
+
131
+ <style lang="scss" scoped>
132
+ .tags-view-container {
133
+ background: #fff;
134
+ border-bottom: 1px solid #d8dce5;
135
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
136
+ }
137
+ </style>
@@ -0,0 +1,21 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2022-12-07 14:13:25
4
+ * @LastEditTime: 2023-01-05 16:08:30
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ import { useNavStore, TopIndex } from '@/stores';
9
+
10
+ const { TopMenu } = useNavStore();
11
+ </script>
12
+ <template>
13
+ <div>
14
+ <ElMenu mode="horizontal" :default-active="TopIndex" :ellipsis="(TopMenu.length > 10)">
15
+ <ElMenuItem v-for="(nav, index) in TopMenu" :key="index + 'fdsjakr'" :index="index + ''"
16
+ @click="(TopIndex = index + '')">
17
+ {{ nav.title }}
18
+ </ElMenuItem>
19
+ </ElMenu>
20
+ </div>
21
+ </template>
@@ -0,0 +1,50 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2020-12-10 11:30:30
4
+ * @LastEditTime: 2023-01-05 14:16:52
5
+ * @Description: 顶部导航栏组件
6
+ -->
7
+ <script lang="ts" setup>
8
+ import NavSearch from './NavSearch.vue';
9
+ import TopMenu from './TopMenu.vue';
10
+ import { useUserStore, useNavStore, removeToken } from '@/stores';
11
+
12
+ const router = useRouter();
13
+ const { CurrUserInfo } = useUserStore();
14
+
15
+ const loginOut = async () => {
16
+ removeToken();
17
+ useUserStore().reset();
18
+ useNavStore().reset();
19
+ router.replace('/base/login');
20
+ };
21
+ </script>
22
+
23
+ <template>
24
+ <!-- 快捷菜单 -->
25
+ <NavSearch />
26
+ <div v-bind="$attrs" class="flex w-full justify-between px-5">
27
+ <!-- 顶部导航栏 -->
28
+ <TopMenu />
29
+ <!-- 右侧个人中心下拉框 -->
30
+ <ElDropdown class="avatar-container" trigger="click">
31
+ <div class="inline-flex justify-center items-center mx-2 hover:cursor-pointer">
32
+ <ElAvatar v-if="CurrUserInfo.avatar" :src="CurrUserInfo.avatar" />
33
+ <ElAvatar v-else color="transparent">
34
+ <div class="text-xl i-noto-v1-man-technologist-light-skin-tone" />
35
+ </ElAvatar>
36
+
37
+ <div class="text-sm ml-2">{{ CurrUserInfo.nickname }}</div>
38
+ <div class="mr-2 text-sm i-ion-md-arrow-dropdown" />
39
+ </div>
40
+ <template #dropdown>
41
+ <ElDropdownMenu class="user-dropdown">
42
+ <ElDropdownItem @click="loginOut">
43
+ <div>退出登录</div>
44
+ </ElDropdownItem>
45
+ </ElDropdownMenu>
46
+ </template>
47
+ </ElDropdown>
48
+ </div>
49
+ </template>
50
+
@@ -0,0 +1,95 @@
1
+ <!--
2
+ * @Author: zhangyang
3
+ * @Date: 2022-10-25 17:43:30
4
+ * @LastEditTime: 2023-01-09 09:08:54
5
+ * @Description:
6
+ -->
7
+ <script lang="ts" setup>
8
+ import Logo from './components/Logo.vue';
9
+ import Menu from './components/Menu.vue';
10
+ import UserCenter from './components/UserCenter.vue';
11
+ import TagsView from './components/TagsView.vue';
12
+ import { useTagsStore } from '@/stores';
13
+
14
+ const { cachedViews, visitedViews } = storeToRefs(useTagsStore());
15
+ const route = useRoute();
16
+ const path = computed(() => route.path);
17
+ const name = computed(() => route.name);
18
+ const isCached = computed(() => {
19
+ // 无需缓存的界面
20
+ if (route.meta.noCache) {
21
+ return false;
22
+ }
23
+ const arr = visitedViews.value.map((view) => view.name);
24
+ // 已经缓存,或者首次打开(防止二次初始化)
25
+ return cachedViews.value.includes(name.value) || !arr.includes(name.value as unknown as string);
26
+ });
27
+ </script>
28
+ <template>
29
+ <div class="parent">
30
+ <div class="left">
31
+ <div class="logo">
32
+ <Logo />
33
+ </div>
34
+ <div class="menu">
35
+ <Menu />
36
+ </div>
37
+ </div>
38
+ <div class="right">
39
+ <div class="top">
40
+ <UserCenter />
41
+ </div>
42
+ <div class="tags">
43
+ <TagsView />
44
+ </div>
45
+ <div class="main">
46
+ <RouterView v-slot="{ Component: PageComponent }">
47
+ <Transition name="fade-transform" mode="out-in">
48
+ <div>
49
+ <KeepAlive>
50
+ <Component v-if="isCached" :is="PageComponent" :key="path" />
51
+ </KeepAlive>
52
+ <Component v-if="!isCached" :is="PageComponent" :key="path" />
53
+ </div>
54
+ </Transition>
55
+ </RouterView>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </template>
60
+
61
+ <style lang="scss" scoped>
62
+ .parent {
63
+ @apply w-100vw h-100vh overflow-auto flex;
64
+
65
+ .left {
66
+ @apply h-full flex flex-col w-210px;
67
+ background-color: rgb(48, 65, 86);
68
+
69
+ .logo {
70
+ @apply w-full h-60px flex justify-center items-center bg-[#2b2f3a] text-white;
71
+ }
72
+
73
+ .menu {
74
+ @apply flex-1 overflow-y-auto overflow-x-hidden;
75
+ background-color: rgb(48, 65, 86);
76
+ }
77
+ }
78
+
79
+ .right {
80
+ @apply flex-1;
81
+
82
+ .top {
83
+ @apply h-60px w-full flex items-center;
84
+ }
85
+
86
+ .tags {
87
+ @apply h-30px w-full relative z-10;
88
+ }
89
+
90
+ .main {
91
+ @apply p-5 h-[calc(100vh-100px)] w-[calc(100vw-210px)] overflow-auto;
92
+ }
93
+ }
94
+ }
95
+ </style>
@@ -0,0 +1,44 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-12-03 15:57:40
4
+ * @LastEditTime: 2023-01-04 14:30:48
5
+ * @Description:
6
+ */
7
+ // polyfill
8
+ import 'core-js/stable';
9
+ import 'regenerator-runtime/runtime';
10
+ // 统一浏览器样式
11
+ import '@unocss/reset/tailwind.css';
12
+ import 'uno.css';
13
+ import 'element-plus/dist/index.css';
14
+
15
+ import { createApp } from 'vue';
16
+ import App from './App.vue';
17
+
18
+ (async () => {
19
+ // 获取环境变量
20
+ let viteEnv;
21
+ // 注入此环境变量,可以兼容现有的部署方式
22
+ if (import.meta.env.VITE_USE_DEFAULT_DEPLOY_METHOD) {
23
+ viteEnv = import.meta.env;
24
+ console.log('🚀 ~ file: main.ts ~ line 19 ~ viteEnv', viteEnv);
25
+ } else if (import.meta.env.DEV) {
26
+ // 开发环境,内网其他设备访问请切换为局域网 ip
27
+ // 端口号与环境变量的 SERVER_PORT 保持一致
28
+ viteEnv = await (await fetch(`http://127.0.0.1:3000/get/env`)).json();
29
+ console.log('🚀 ~ file: main.ts ~ line 24 ~ viteEnv', viteEnv);
30
+ } else {
31
+ // 部署环境,需要配合 nginx 使用
32
+ viteEnv = await (await fetch(`/get/env`)).json();
33
+ console.log('🚀 ~ file: main.ts ~ line 28 ~ viteEnv', viteEnv);
34
+ }
35
+ window.__YOUNG_VITE_ENV__ = viteEnv;
36
+
37
+ const app = createApp(App);
38
+ Object.values(
39
+ // 模块按数字命名,确保安装的顺序
40
+ import.meta.glob<{ install: UserModule }>('./modules/*.ts', { eager: true }),
41
+ ).map(({ install }) => install(app));
42
+
43
+ app.mount('#app');
44
+ })();
@@ -0,0 +1,66 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2023-01-04 10:15:03
4
+ * @LastEditTime: 2023-01-05 14:26:22
5
+ * @Description: 路由模块
6
+ */
7
+ import { createRouter, createWebHashHistory, type RouteRecordRaw } from 'vue-router';
8
+ import { setupLayouts } from 'virtual:generated-layouts';
9
+ import routes from '~pages';
10
+
11
+ /**
12
+ * 路由元数据类型扩展
13
+ */
14
+ declare module 'vue-router' {
15
+ interface RouteMeta {
16
+ /**
17
+ * 页面标题
18
+ */
19
+ title: string;
20
+ /**
21
+ * 是否固定
22
+ */
23
+ affix?: boolean;
24
+ /**
25
+ * 是否禁用缓存
26
+ */
27
+ noCache?: boolean;
28
+ /**
29
+ * 鉴权路径,不设置则为白名单页面
30
+ */
31
+ authPath?: string;
32
+ /**
33
+ * 页面布局,对应 layouts 目录下的布局页面,不写默认为 default/index
34
+ */
35
+ layout?: 'blank';
36
+ }
37
+ }
38
+
39
+ export const finalRoutes = setupLayouts(routes);
40
+ export const navMap = new Map<string, string>();
41
+
42
+ /**
43
+ * 生成权限节点映射表
44
+ */
45
+ const generateNavMap = (raw: RouteRecordRaw[], base = '') => {
46
+ for (const route of raw) {
47
+ if (route.children) {
48
+ generateNavMap(route.children, route.path);
49
+ } else {
50
+ const meta = route.meta;
51
+ if (meta && meta.authPath) {
52
+ navMap.set(meta.authPath, `${base}${route.path.startsWith('/') ? '' : '/'}${route.path}`);
53
+ }
54
+ }
55
+ }
56
+ };
57
+ generateNavMap(finalRoutes);
58
+
59
+ export const router = createRouter({
60
+ history: createWebHashHistory(),
61
+ routes: finalRoutes,
62
+ });
63
+
64
+ export const install: UserModule = (app) => {
65
+ app.use(router);
66
+ };
@@ -0,0 +1,10 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-03-01 14:01:31
4
+ * @LastEditTime: 2023-01-04 10:22:53
5
+ * @Description: 状态管理
6
+ */
7
+ export const install: UserModule = (app) => {
8
+ const pinia = createPinia();
9
+ app.use(pinia);
10
+ };
@@ -0,0 +1,75 @@
1
+ /*
2
+ * @Author: zhangyang
3
+ * @Date: 2022-03-01 14:01:31
4
+ * @LastEditTime: 2023-01-09 09:19:53
5
+ * @Description: 网络请求
6
+ */
7
+ import { useHttp } from '@bluesyoung/http';
8
+ import { getToken } from '@/stores';
9
+ import type { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
10
+ import { useGetReq, usePostReq, usePatchReq, useDeleteReq } from '@/apis';
11
+
12
+ let loading: LoadingInstance;
13
+
14
+ const startLoading = () => {
15
+ loading = ElLoadingService({
16
+ lock: true,
17
+ text: '拼命加载中...',
18
+ background: 'rgba(0, 0, 0, 0.7)',
19
+ });
20
+ };
21
+
22
+ const endLoading = () => loading?.close?.();
23
+
24
+ export const http = useHttp<ResponseMsg>({
25
+ timeout: -1,
26
+ loading: {
27
+ start: startLoading,
28
+ end: endLoading,
29
+ },
30
+ fail: (err) => {
31
+ console.log('🚀 ~ file: 3-net.ts:60 ~ err', err, typeof err);
32
+ if (typeof err === 'string') {
33
+ // 通用失败,弹出提示信息
34
+ ElMessage.error(err);
35
+ }
36
+
37
+ if (err instanceof Error) {
38
+ ElMessage.error(
39
+ // @ts-ignore 接口出错
40
+ err?.response?.data?.message || err?.response?.data?.msg || err.message || '网络错误!',
41
+ );
42
+ }
43
+
44
+ throw err;
45
+ },
46
+ checkFn: ({ code, msg, data }) => {
47
+ if (code === 0) {
48
+ // 通用成功
49
+ return data;
50
+ } else if (code === -1) {
51
+ // 通用失败
52
+ throw msg;
53
+ } else {
54
+ // 特殊状态码
55
+ throw code;
56
+ }
57
+ },
58
+ headers: {
59
+ getAuthHeaders: () => {
60
+ const token = getToken();
61
+ return {
62
+ Authorization: `Bearer ${token}`,
63
+ };
64
+ },
65
+ },
66
+ });
67
+
68
+ export const apis = http.__mixin__({
69
+ get: useGetReq(),
70
+ post: usePostReq(),
71
+ patch: usePatchReq(),
72
+ delete: useDeleteReq(),
73
+ });
74
+
75
+ export const install: UserModule = (ctx) => {};