create-young-proj 0.10.1 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +6 -0
- package/dist/index.mjs +9 -9
- package/package.json +1 -1
- package/template-nuxt-admin/README.md +42 -1
- package/template-nuxt-admin/components/TopUser.vue +4 -4
- package/template-nuxt-admin/components/YoungChangePassword.vue +5 -5
- package/template-nuxt-admin/components/layout/Main.vue +2 -2
- package/template-nuxt-admin/components/layout/NavBar.vue +11 -2
- package/template-nuxt-admin/components/layout/SideBar.vue +7 -2
- package/template-nuxt-admin/components/layout/SubMenu.vue +9 -2
- package/template-nuxt-admin/components/layout/TabsBar.vue +4 -5
- package/template-nuxt-admin/composables/api.ts +20 -21
- package/template-nuxt-admin/composables/nav.ts +20 -3
- package/template-nuxt-admin/composables/tags.ts +29 -1
- package/template-nuxt-admin/composables/user.ts +21 -3
- package/template-nuxt-admin/middleware/auth.global.ts +26 -7
- package/template-nuxt-admin/nuxt.config.ts +5 -1
- package/template-nuxt-admin/package.json +1 -1
- package/template-nuxt-admin/pages/home/[id].vue +2 -2
- package/template-nuxt-admin/pages/index.vue +2 -7
- package/template-nuxt-admin/pages/login.vue +5 -3
- package/template-nuxt-admin/pages/system/menuList.vue +0 -1
- package/template-nuxt-admin/public/bg.webp +0 -0
- package/template-nuxt-admin/public/favicon.svg +2 -0
- package/template-nuxt-admin/public/logo.svg +2 -0
- package/template-nuxt-admin/server/plugins/env.ts +3 -4
- package/template-nuxt-admin/utils/tool.ts +62 -70
- package/template-nuxt-admin/public/favicon.ico +0 -0
- package/template-vue-admin/.vscode/extensions.json +0 -10
- package/template-vue-admin/.vscode/list-add.code-snippets +0 -108
- package/template-vue-admin/.vscode/list-export.code-snippets +0 -72
- package/template-vue-admin/.vscode/list.code-snippets +0 -61
- package/template-vue-admin/.vscode/settings.json +0 -7
- package/template-vue-admin/Dockerfile +0 -42
- package/template-vue-admin/README.md +0 -75
- package/template-vue-admin/_env +0 -8
- package/template-vue-admin/_gitignore +0 -30
- package/template-vue-admin/boot.mjs +0 -16
- package/template-vue-admin/build/custom-plugin.ts +0 -57
- package/template-vue-admin/build/index.ts +0 -7
- package/template-vue-admin/build/plugins.ts +0 -59
- package/template-vue-admin/config/.devrc +0 -2
- package/template-vue-admin/config/.onlinerc +0 -2
- package/template-vue-admin/config/.testrc +0 -2
- package/template-vue-admin/index.html +0 -21
- package/template-vue-admin/nitro.config.ts +0 -19
- package/template-vue-admin/package.json +0 -51
- package/template-vue-admin/plugins/init.ts +0 -31
- package/template-vue-admin/public/vite.svg +0 -1
- package/template-vue-admin/rome.json +0 -26
- package/template-vue-admin/routes/api/[...all].ts +0 -49
- package/template-vue-admin/routes/get/env.ts +0 -18
- package/template-vue-admin/src/App.vue +0 -14
- package/template-vue-admin/src/apis/delete.ts +0 -36
- package/template-vue-admin/src/apis/get.ts +0 -83
- package/template-vue-admin/src/apis/index.ts +0 -10
- package/template-vue-admin/src/apis/patch.ts +0 -78
- package/template-vue-admin/src/apis/post.ts +0 -76
- package/template-vue-admin/src/assets/img/login_background.jpg +0 -0
- package/template-vue-admin/src/auto-components.d.ts +0 -38
- package/template-vue-admin/src/auto-imports.d.ts +0 -302
- package/template-vue-admin/src/layouts/blank.vue +0 -9
- package/template-vue-admin/src/layouts/default/components/Link.vue +0 -23
- package/template-vue-admin/src/layouts/default/components/Logo.vue +0 -20
- package/template-vue-admin/src/layouts/default/components/Menu.vue +0 -54
- package/template-vue-admin/src/layouts/default/components/NavSearch.vue +0 -52
- package/template-vue-admin/src/layouts/default/components/ScrollPane.vue +0 -79
- package/template-vue-admin/src/layouts/default/components/TagsView.vue +0 -137
- package/template-vue-admin/src/layouts/default/components/TopMenu.vue +0 -21
- package/template-vue-admin/src/layouts/default/components/UserCenter.vue +0 -50
- package/template-vue-admin/src/layouts/default/index.vue +0 -95
- package/template-vue-admin/src/main.ts +0 -46
- package/template-vue-admin/src/modules/1-router.ts +0 -48
- package/template-vue-admin/src/modules/2-pinia.ts +0 -10
- package/template-vue-admin/src/modules/3-net.ts +0 -79
- package/template-vue-admin/src/modules/4-auth.ts +0 -124
- package/template-vue-admin/src/modules/5-checkupdate.ts +0 -38
- package/template-vue-admin/src/shims.d.ts +0 -12
- package/template-vue-admin/src/stores/index.ts +0 -9
- package/template-vue-admin/src/stores/local/index.ts +0 -31
- package/template-vue-admin/src/stores/session/index.ts +0 -63
- package/template-vue-admin/src/stores/tags.ts +0 -109
- package/template-vue-admin/src/typings/global.d.ts +0 -70
- package/template-vue-admin/src/typings/index.ts +0 -13
- package/template-vue-admin/src/typings/system.d.ts +0 -46
- package/template-vue-admin/src/views/403.vue +0 -33
- package/template-vue-admin/src/views/[...all_404].vue +0 -557
- package/template-vue-admin/src/views/base/login.vue +0 -194
- package/template-vue-admin/src/views/dashboard/[name].vue +0 -28
- package/template-vue-admin/src/views/index.vue +0 -25
- package/template-vue-admin/src/views/system/api.vue +0 -160
- package/template-vue-admin/src/views/system/hooks/useRole.ts +0 -286
- package/template-vue-admin/src/views/system/menuList.vue +0 -194
- package/template-vue-admin/src/views/system/role.vue +0 -131
- package/template-vue-admin/src/views/system/user.vue +0 -192
- package/template-vue-admin/src/vite-env.d.ts +0 -52
- package/template-vue-admin/tsconfig.json +0 -21
- package/template-vue-admin/tsconfig.node.json +0 -9
- package/template-vue-admin/unocss.config.ts +0 -47
- package/template-vue-admin/vite.config.ts +0 -32
@@ -1,54 +0,0 @@
|
|
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>
|
@@ -1,52 +0,0 @@
|
|
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>
|
@@ -1,79 +0,0 @@
|
|
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>
|
@@ -1,137 +0,0 @@
|
|
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>
|
@@ -1,21 +0,0 @@
|
|
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>
|
@@ -1,50 +0,0 @@
|
|
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
|
-
|
@@ -1,95 +0,0 @@
|
|
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>
|
@@ -1,46 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* @Author: zhangyang
|
3
|
-
* @Date: 2022-12-03 15:57:40
|
4
|
-
* @LastEditTime: 2023-01-10 15:09:29
|
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 { server } from 'virtual:local-server';
|
17
|
-
import App from './App.vue';
|
18
|
-
import { CurrentVersion } from './stores';
|
19
|
-
|
20
|
-
(async () => {
|
21
|
-
// 获取环境变量
|
22
|
-
let viteEnv;
|
23
|
-
// 注入此环境变量,可以兼容现有的部署方式
|
24
|
-
if (import.meta.env.VITE_USE_DEFAULT_DEPLOY_METHOD) {
|
25
|
-
viteEnv = import.meta.env;
|
26
|
-
console.log('🚀 ~ file: main.ts ~ line 19 ~ viteEnv', viteEnv);
|
27
|
-
} else if (import.meta.env.DEV) {
|
28
|
-
// 开发环境,局域网 ip
|
29
|
-
viteEnv = await (await fetch(server + '/get/env')).json();
|
30
|
-
console.log('🚀 ~ file: main.ts ~ line 24 ~ viteEnv', viteEnv);
|
31
|
-
} else {
|
32
|
-
// 部署环境,需要配合 nginx 使用
|
33
|
-
viteEnv = await (await fetch(`/get/env`)).json();
|
34
|
-
console.log('🚀 ~ file: main.ts ~ line 28 ~ viteEnv', viteEnv);
|
35
|
-
}
|
36
|
-
window.__YOUNG_VITE_ENV__ = viteEnv;
|
37
|
-
CurrentVersion.value = viteEnv.VITE_CURRENT_VERSION;
|
38
|
-
|
39
|
-
const app = createApp(App);
|
40
|
-
Object.values(
|
41
|
-
// 模块按数字命名,确保安装的顺序
|
42
|
-
import.meta.glob<{ install: UserModule }>('./modules/*.ts', { eager: true }),
|
43
|
-
).map(({ install }) => install(app));
|
44
|
-
|
45
|
-
app.mount('#app');
|
46
|
-
})();
|
@@ -1,48 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* @Author: zhangyang
|
3
|
-
* @Date: 2023-01-04 10:15:03
|
4
|
-
* @LastEditTime: 2023-05-18 15:45:18
|
5
|
-
* @Description: 路由模块
|
6
|
-
*/
|
7
|
-
import { createRouter, createWebHashHistory } 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
|
-
* 设置 false 不参与鉴权,否则鉴权路径为当前路由
|
30
|
-
*/
|
31
|
-
auth?: false;
|
32
|
-
/**
|
33
|
-
* 页面布局,对应 layouts 目录下的布局页面,不写默认为 default/index
|
34
|
-
*/
|
35
|
-
layout?: 'blank';
|
36
|
-
}
|
37
|
-
}
|
38
|
-
|
39
|
-
export const finalRoutes = setupLayouts(routes);
|
40
|
-
|
41
|
-
export const router = createRouter({
|
42
|
-
history: createWebHashHistory(),
|
43
|
-
routes: finalRoutes,
|
44
|
-
});
|
45
|
-
|
46
|
-
export const install: UserModule = (app) => {
|
47
|
-
app.use(router);
|
48
|
-
};
|
@@ -1,79 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* @Author: zhangyang
|
3
|
-
* @Date: 2022-03-01 14:01:31
|
4
|
-
* @LastEditTime: 2023-05-18 16:21:55
|
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: any) => {
|
31
|
-
console.log('🚀 ~ file: 3-net.ts:60 ~ err', err, typeof err);
|
32
|
-
if (err?.response?.status === 401) {
|
33
|
-
ElMessage.error('无权限,请联系管理员');
|
34
|
-
}
|
35
|
-
|
36
|
-
if (typeof err === 'string') {
|
37
|
-
// 通用失败,弹出提示信息
|
38
|
-
ElMessage.error(err);
|
39
|
-
}
|
40
|
-
|
41
|
-
if (err instanceof Error) {
|
42
|
-
ElMessage.error(
|
43
|
-
// @ts-ignore 接口出错
|
44
|
-
err?.response?.data?.message || err?.response?.data?.msg || err.message || '网络错误!',
|
45
|
-
);
|
46
|
-
}
|
47
|
-
|
48
|
-
throw err;
|
49
|
-
},
|
50
|
-
checkFn: ({ code, msg, data }) => {
|
51
|
-
if (code === 0) {
|
52
|
-
// 通用成功
|
53
|
-
return data;
|
54
|
-
} else if (code === -1) {
|
55
|
-
// 通用失败
|
56
|
-
throw msg;
|
57
|
-
} else {
|
58
|
-
// 特殊状态码
|
59
|
-
throw code;
|
60
|
-
}
|
61
|
-
},
|
62
|
-
headers: {
|
63
|
-
getAuthHeaders: () => {
|
64
|
-
const token = getToken();
|
65
|
-
return {
|
66
|
-
Authorization: `Bearer ${token}`,
|
67
|
-
};
|
68
|
-
},
|
69
|
-
},
|
70
|
-
});
|
71
|
-
|
72
|
-
export const apis = http.__mixin__({
|
73
|
-
get: useGetReq(),
|
74
|
-
post: usePostReq(),
|
75
|
-
patch: usePatchReq(),
|
76
|
-
delete: useDeleteReq(),
|
77
|
-
});
|
78
|
-
|
79
|
-
export const install: UserModule = (ctx) => {};
|