create-young-proj 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +5 -3
- package/dist/index.mjs +9 -9
- package/package.json +1 -1
- package/template-nuxt-admin/Dockerfile +41 -0
- package/template-nuxt-admin/README.md +35 -0
- package/template-nuxt-admin/app.vue +41 -0
- package/template-nuxt-admin/boot.mjs +16 -0
- package/template-nuxt-admin/components/ScreenFull.vue +18 -0
- package/template-nuxt-admin/components/TopSearch.vue +73 -0
- package/template-nuxt-admin/components/TopUser.vue +69 -0
- package/template-nuxt-admin/components/YoungChangePassword.vue +87 -0
- package/template-nuxt-admin/components/YoungCodeInput.vue +60 -0
- package/template-nuxt-admin/components/YoungLink.vue +23 -0
- package/template-nuxt-admin/components/YoungLoading.vue +39 -0
- package/template-nuxt-admin/components/layout/Footer.vue +29 -0
- package/template-nuxt-admin/components/layout/Logo.vue +52 -0
- package/template-nuxt-admin/components/layout/Main.vue +41 -0
- package/template-nuxt-admin/components/layout/NavBar.vue +77 -0
- package/template-nuxt-admin/components/layout/SideBar.vue +90 -0
- package/template-nuxt-admin/components/layout/SubMenu.vue +46 -0
- package/template-nuxt-admin/components/layout/TabsBar.vue +183 -0
- package/template-nuxt-admin/composables/api.ts +104 -0
- package/template-nuxt-admin/composables/apis/delete.ts +37 -0
- package/template-nuxt-admin/composables/apis/get.ts +83 -0
- package/template-nuxt-admin/composables/apis/index.ts +10 -0
- package/template-nuxt-admin/composables/apis/patch.ts +74 -0
- package/template-nuxt-admin/composables/apis/post.ts +85 -0
- package/template-nuxt-admin/composables/config.ts +13 -0
- package/template-nuxt-admin/composables/icon.ts +27 -0
- package/template-nuxt-admin/composables/nav.ts +60 -0
- package/template-nuxt-admin/composables/tags.ts +108 -0
- package/template-nuxt-admin/composables/user.ts +29 -0
- package/template-nuxt-admin/config/.devrc +1 -0
- package/template-nuxt-admin/config/.onlinerc +1 -0
- package/template-nuxt-admin/config/.testrc +1 -0
- package/template-nuxt-admin/env.d.ts +47 -0
- package/template-nuxt-admin/error.vue +53 -0
- package/template-nuxt-admin/layouts/blank.vue +9 -0
- package/template-nuxt-admin/layouts/default.vue +124 -0
- package/template-nuxt-admin/middleware/auth.global.ts +39 -0
- package/template-nuxt-admin/nuxt.config.ts +101 -0
- package/template-nuxt-admin/package.json +44 -0
- package/template-nuxt-admin/pages/home/[id].vue +28 -0
- package/template-nuxt-admin/pages/index.vue +20 -0
- package/template-nuxt-admin/pages/login.vue +179 -0
- package/template-nuxt-admin/pages/system/api.vue +166 -0
- package/template-nuxt-admin/pages/system/hooks/useRole.ts +336 -0
- package/template-nuxt-admin/pages/system/menuList.vue +329 -0
- package/template-nuxt-admin/pages/system/role.vue +117 -0
- package/template-nuxt-admin/pages/system/user.vue +214 -0
- package/template-nuxt-admin/plugins/directive.ts +26 -0
- package/template-nuxt-admin/public/default_avatar.svg +1 -0
- package/template-nuxt-admin/public/favicon.ico +0 -0
- package/template-nuxt-admin/public/image_placeholder.svg +15 -0
- package/template-nuxt-admin/public/tabbar_bg.png +0 -0
- package/template-nuxt-admin/rome.json +26 -0
- package/template-nuxt-admin/server/api/[...all].ts +10 -0
- package/template-nuxt-admin/server/plugins/env.ts +89 -0
- package/template-nuxt-admin/server/routes/get/env.ts +13 -0
- package/template-nuxt-admin/server/tsconfig.json +3 -0
- package/template-nuxt-admin/server/utils/index.ts +35 -0
- package/template-nuxt-admin/styles/element.scss +30 -0
- package/template-nuxt-admin/styles/index.scss +59 -0
- package/template-nuxt-admin/styles/variable.scss +103 -0
- package/template-nuxt-admin/tsconfig.json +7 -0
- package/template-nuxt-admin/typings/global.d.ts +16 -0
- package/template-nuxt-admin/typings/system.d.ts +66 -0
- package/template-nuxt-admin/typings/user.d.ts +19 -0
- package/template-nuxt-admin/uno.config.ts +40 -0
- package/template-nuxt-admin/utils/tool.ts +207 -0
- package/template-nuxt-admin/yarn.lock +6849 -0
- package/template-uni-app/_npmrc +2 -0
- package/template-uni-app/src/layouts/tabbar.vue +6 -6
- package/template-uni-app/src/pages/index.vue +16 -12
- package/template-uni-app/src/pages/my.vue +7 -11
@@ -0,0 +1,87 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2023-07-24 14:21:55
|
4
|
+
* @LastEditTime: 2023-07-24 14:59:34
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<script lang="ts" setup>
|
8
|
+
import { isMobile } from '@bluesyoung/utils';
|
9
|
+
import type { FormInstance } from 'element-plus';
|
10
|
+
|
11
|
+
type Props = {
|
12
|
+
onDestroy?: Function;
|
13
|
+
};
|
14
|
+
|
15
|
+
const props = withDefaults(defineProps<Props>(), {
|
16
|
+
onDestroy: () => console.log('为了节省性能,此时应该销毁dom'),
|
17
|
+
});
|
18
|
+
|
19
|
+
const showPopup = ref(false);
|
20
|
+
|
21
|
+
const show = () => {
|
22
|
+
showPopup.value = true;
|
23
|
+
};
|
24
|
+
|
25
|
+
const hide = () => {
|
26
|
+
showPopup.value = false;
|
27
|
+
props.onDestroy();
|
28
|
+
};
|
29
|
+
|
30
|
+
const form = reactive<LoginForm>({
|
31
|
+
mobile: '',
|
32
|
+
vercode: '',
|
33
|
+
password: '',
|
34
|
+
});
|
35
|
+
|
36
|
+
const { cookie } = storeToRefs(useUserStore());
|
37
|
+
|
38
|
+
const formRef = ref<FormInstance>();
|
39
|
+
const sure = () => {
|
40
|
+
formRef.value?.validate(async (valid) => {
|
41
|
+
if (valid) {
|
42
|
+
await apis.post.changePassword(form);
|
43
|
+
hide();
|
44
|
+
await showDialog({
|
45
|
+
message: '密码修改成功,请重新登录'
|
46
|
+
})
|
47
|
+
// @ts-ignore
|
48
|
+
cookie.value = undefined;
|
49
|
+
const redirect = location.pathname === '/login' ? '/' : encodeURIComponent(location.href.replace(location.origin, ''));
|
50
|
+
navigateTo(`/login?redirect=${redirect}`);
|
51
|
+
}
|
52
|
+
});
|
53
|
+
};
|
54
|
+
|
55
|
+
defineExpose({
|
56
|
+
show
|
57
|
+
});
|
58
|
+
</script>
|
59
|
+
|
60
|
+
<template>
|
61
|
+
<ElDialog v-model="showPopup" title="修改密码" :before-close="hide" width="430px" lt-sm="!w-[96%]">
|
62
|
+
<template #default>
|
63
|
+
<ElForm ref="formRef" :model="form">
|
64
|
+
<ElFormItem prop="mobile" :rules="[
|
65
|
+
{ required: true, trigger: 'blur', message: '请输入手机号' },
|
66
|
+
{ message: '请输入合法的手机号', trigger: 'blur', validator: (_: any, v: string) => isMobile(v) }
|
67
|
+
]" class="mt-20px">
|
68
|
+
<ElInput v-model="form.mobile" placeholder="请输入手机号" maxlength="11" class="!h-52px" clearable />
|
69
|
+
</ElFormItem>
|
70
|
+
<ElFormItem prop="vercode" :rules="[{ required: true, trigger: 'blur', message: '请输入验证码' }]">
|
71
|
+
<YoungCodeInput v-model="form.vercode" :tel="form.mobile" />
|
72
|
+
</ElFormItem>
|
73
|
+
<ElFormItem prop="password" :rules="[
|
74
|
+
{ required: true, trigger: 'blur', message: '请输入密码' },
|
75
|
+
{ min: 8, max: 16, trigger: 'blur', message: '请输入8-16位字符!' }
|
76
|
+
]">
|
77
|
+
<ElInput type="password" v-model="form.password" minlength="8" maxlength="16" placeholder="请输入密码"
|
78
|
+
class="!h-52px" clearable show-password @keyup.enter="sure" />
|
79
|
+
</ElFormItem>
|
80
|
+
</ElForm>
|
81
|
+
</template>
|
82
|
+
<template #footer>
|
83
|
+
<ElButton @click="hide">取消</ElButton>
|
84
|
+
<ElButton type="primary" @click="sure">确认</ElButton>
|
85
|
+
</template>
|
86
|
+
</ElDialog>
|
87
|
+
</template>
|
@@ -0,0 +1,60 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2022-08-19 13:52:58
|
4
|
+
* @LastEditTime: 2023-07-24 11:57:12
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<script lang="ts" setup>
|
8
|
+
import { YoungSlideVerify } from '@bluesyoung/ui-vue3';
|
9
|
+
import { useVerifyCode } from '@bluesyoung/ui-vue3-element-plus';
|
10
|
+
import { isNumber } from '@bluesyoung/utils';
|
11
|
+
type Props = {
|
12
|
+
modelValue: string;
|
13
|
+
tel: string;
|
14
|
+
maxlength?: number;
|
15
|
+
};
|
16
|
+
|
17
|
+
const emit = defineEmits<{
|
18
|
+
(e: 'update:modelValue', v: string): void;
|
19
|
+
(e: 'enter'): void;
|
20
|
+
}>();
|
21
|
+
|
22
|
+
const props = withDefaults(defineProps<Props>(), {
|
23
|
+
maxlength: 6
|
24
|
+
});
|
25
|
+
|
26
|
+
const canGet = computed(() => {
|
27
|
+
return props.tel.length === 11;
|
28
|
+
});
|
29
|
+
|
30
|
+
const {
|
31
|
+
getCode,
|
32
|
+
tip,
|
33
|
+
showSlider,
|
34
|
+
pass,
|
35
|
+
cancel
|
36
|
+
} = useVerifyCode(async () => {
|
37
|
+
await apis.get.sendCode(props.tel);
|
38
|
+
ElMessage.success('短信已发送至您的手机,请注意查收!');
|
39
|
+
});
|
40
|
+
|
41
|
+
</script>
|
42
|
+
<template>
|
43
|
+
<div class="w-full">
|
44
|
+
<ElInput :model-value="modelValue" :maxlength="maxlength" @update:model-value="(e) => emit('update:modelValue', e)"
|
45
|
+
class="!h-52px" placeholder="请输入验证码" @keyup.enter="emit('enter')">
|
46
|
+
<template #suffix>
|
47
|
+
<ElLink v-if="isNumber(+tip)" type="primary" :disabled="!canGet" :underline="false" @click="getCode">{{ tip }}
|
48
|
+
</ElLink>
|
49
|
+
<div v-else>{{ tip }}</div>
|
50
|
+
</template>
|
51
|
+
</ElInput>
|
52
|
+
<YoungSlideVerify :show="showSlider" @success="pass" @close="cancel" />
|
53
|
+
</div>
|
54
|
+
</template>
|
55
|
+
|
56
|
+
<style>
|
57
|
+
.vue-puzzle-vcode {
|
58
|
+
z-index: 10001 !important;
|
59
|
+
}
|
60
|
+
</style>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2023-07-21 14:47:12
|
4
|
+
* @LastEditTime: 2023-07-26 10:39:40
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<script lang="ts" setup>
|
8
|
+
import { isHttpUrl } from '@bluesyoung/utils';
|
9
|
+
defineProps<{ to: string }>();
|
10
|
+
</script>
|
11
|
+
|
12
|
+
<template>
|
13
|
+
<a v-if="isHttpUrl(to)" :href="to" target="_blank" rel="noopener" referrerpolicy="no-referrer" class="w-full">
|
14
|
+
<div class="flex items-center">
|
15
|
+
<slot />
|
16
|
+
</div>
|
17
|
+
</a>
|
18
|
+
<NuxtLink v-else :to="to" class="w-full">
|
19
|
+
<div class="flex items-center">
|
20
|
+
<slot />
|
21
|
+
</div>
|
22
|
+
</NuxtLink>
|
23
|
+
</template>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2023-07-07 11:57:22
|
4
|
+
* @LastEditTime: 2023-07-26 10:42:39
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<template>
|
8
|
+
<div class="w-100vw h-100vh flex justify-center items-center bg-[#333] opacity-80 fixed top-0 z-999999">
|
9
|
+
<svg class="nuxt-spa-loading" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37 25" fill="none" width="80">
|
10
|
+
<path d="M24.236 22.006h10.742L25.563 5.822l-8.979 14.31a4 4 0 0 1-3.388 1.874H2.978l11.631-20 5.897 10.567"></path>
|
11
|
+
</svg>
|
12
|
+
</div>
|
13
|
+
</template>
|
14
|
+
|
15
|
+
<style>
|
16
|
+
.nuxt-spa-loading {
|
17
|
+
position: fixed;
|
18
|
+
top: 50%;
|
19
|
+
left: 50%;
|
20
|
+
transform: translate(-50%, -50%)
|
21
|
+
}
|
22
|
+
|
23
|
+
.nuxt-spa-loading>path {
|
24
|
+
fill: none;
|
25
|
+
stroke: #00DC82;
|
26
|
+
stroke-width: 4px;
|
27
|
+
stroke-linecap: round;
|
28
|
+
stroke-linejoin: round;
|
29
|
+
stroke-dasharray: 128;
|
30
|
+
stroke-dashoffset: 128;
|
31
|
+
animation: nuxt-spa-loading-move 3s linear infinite
|
32
|
+
}
|
33
|
+
|
34
|
+
@keyframes nuxt-spa-loading-move {
|
35
|
+
100% {
|
36
|
+
stroke-dashoffset: -128
|
37
|
+
}
|
38
|
+
}
|
39
|
+
</style>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2023-07-21 12:03:00
|
4
|
+
* @LastEditTime: 2023-07-24 10:05:41
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<script setup lang="ts">
|
8
|
+
const fullYear = new Date().getFullYear();
|
9
|
+
</script>
|
10
|
+
|
11
|
+
<template>
|
12
|
+
<div class="layout-footer-container">
|
13
|
+
©{{ fullYear }} - Current
|
14
|
+
By <a href="https://gitee.com/BluesYoung-web" target="_blank">BluesYoung-web</a>
|
15
|
+
</div>
|
16
|
+
</template>
|
17
|
+
|
18
|
+
<style lang="scss" scoped>
|
19
|
+
.layout-footer-container {
|
20
|
+
display: flex;
|
21
|
+
align-items: center;
|
22
|
+
justify-content: center;
|
23
|
+
min-height: $base-app-footer-height;
|
24
|
+
padding: 0 20px;
|
25
|
+
color: rgb(0 0 0 / 45%);
|
26
|
+
background: var(--el-color-white);
|
27
|
+
border-top: 1px dashed #dcdfe6;
|
28
|
+
}
|
29
|
+
</style>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2023-07-21 12:21:02
|
4
|
+
* @LastEditTime: 2023-07-28 16:53:30
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<script lang="ts" setup>
|
8
|
+
const { isCollapse } = storeToRefs(useNavStore());
|
9
|
+
|
10
|
+
const title = window.__YOUNG_ENV__.NUXT_PUBLIC_TITLE;
|
11
|
+
const logo = window.__YOUNG_ENV__.NUXT_PUBLIC_LOGIN_LOGO;
|
12
|
+
</script>
|
13
|
+
|
14
|
+
<template>
|
15
|
+
<div class="logo-container flex-center">
|
16
|
+
<NuxtLink to="/">
|
17
|
+
<img class="logo" alt="logo" :src="logo" />
|
18
|
+
<h1 class="title" v-if="!(isCollapse || WindowSize['lt-lg'])">{{ title }}</h1>
|
19
|
+
</NuxtLink>
|
20
|
+
</div>
|
21
|
+
</template>
|
22
|
+
|
23
|
+
<style scoped lang="scss">
|
24
|
+
.logo-container {
|
25
|
+
position: relative;
|
26
|
+
display: flex;
|
27
|
+
justify-content: center;
|
28
|
+
|
29
|
+
// padding-left: 24px;
|
30
|
+
height: 60px;
|
31
|
+
overflow: hidden;
|
32
|
+
line-height: 60px;
|
33
|
+
background: transparent;
|
34
|
+
|
35
|
+
.title {
|
36
|
+
display: inline-block;
|
37
|
+
margin-left: 12px;
|
38
|
+
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
|
39
|
+
font-size: 20px;
|
40
|
+
font-weight: 600;
|
41
|
+
color: #fff;
|
42
|
+
vertical-align: middle;
|
43
|
+
}
|
44
|
+
|
45
|
+
.logo {
|
46
|
+
display: inline-block;
|
47
|
+
width: 32px;
|
48
|
+
height: 32px;
|
49
|
+
vertical-align: middle;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
</style>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2023-07-21 11:55:27
|
4
|
+
* @LastEditTime: 2023-07-28 17:15:07
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<script lang="ts" setup>
|
8
|
+
const { cachedViews, visitedViews } = storeToRefs(useTagsStore());
|
9
|
+
const route = useRoute();
|
10
|
+
const name = computed(() => route.name);
|
11
|
+
const isCached = computed(() => {
|
12
|
+
// 无需缓存的界面
|
13
|
+
if (route.meta.noCache) {
|
14
|
+
return false;
|
15
|
+
}
|
16
|
+
const arr = visitedViews.value.map((view) => view.name);
|
17
|
+
// 已经缓存,或者首次打开(防止二次初始化)
|
18
|
+
return cachedViews.value.includes(name.value) || !arr.includes(name.value as unknown as string);
|
19
|
+
});
|
20
|
+
</script>
|
21
|
+
|
22
|
+
<template>
|
23
|
+
<main class="app-main-height w-full">
|
24
|
+
<NuxtPage :keepalive="isCached" />
|
25
|
+
</main>
|
26
|
+
</template>
|
27
|
+
|
28
|
+
<style scoped lang="scss">
|
29
|
+
.app-main-height {
|
30
|
+
min-height: $base-app-main-height;
|
31
|
+
background-color: inherit;
|
32
|
+
padding: 20px;
|
33
|
+
|
34
|
+
@screen lt-lg {
|
35
|
+
& {
|
36
|
+
min-height: -webkit-calc($base-app-main-height + $base-tabs-bar-height) !important;
|
37
|
+
min-height: calc($base-app-main-height + $base-tabs-bar-height) !important;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
</style>
|
@@ -0,0 +1,77 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2023-07-21 16:38:20
|
4
|
+
* @LastEditTime: 2023-07-28 16:37:04
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<script lang="ts" setup>
|
8
|
+
const { isCollapse } = storeToRefs(useNavStore());
|
9
|
+
|
10
|
+
const collapseHandler = () => {
|
11
|
+
if (WindowSize['lt-lg']) {
|
12
|
+
/**
|
13
|
+
* 触发 Ctrl + K 事件
|
14
|
+
*/
|
15
|
+
const event = new KeyboardEvent('keydown', {
|
16
|
+
key: 'k',
|
17
|
+
ctrlKey: true,
|
18
|
+
bubbles: true,
|
19
|
+
cancelable: true,
|
20
|
+
composed: true,
|
21
|
+
view: window,
|
22
|
+
});
|
23
|
+
|
24
|
+
document.body.dispatchEvent(event);
|
25
|
+
} else {
|
26
|
+
isCollapse.value = !isCollapse.value;
|
27
|
+
}
|
28
|
+
};
|
29
|
+
</script>
|
30
|
+
|
31
|
+
<template>
|
32
|
+
<div class="nav-bar-container flex justify-between">
|
33
|
+
<div class="left-panel">
|
34
|
+
<div class="fold-unfold i-ep-expand" :class="[isCollapse ? '' : 'rotate-180']"
|
35
|
+
:title="`${(isCollapse ? '展开' : '收起')}菜单`" @click="collapseHandler" />
|
36
|
+
</div>
|
37
|
+
<div class="right-panel">
|
38
|
+
<TopSearch />
|
39
|
+
<ScreenFull />
|
40
|
+
<TopUser />
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
</template>
|
44
|
+
|
45
|
+
<style scoped lang="scss">
|
46
|
+
.nav-bar-container {
|
47
|
+
position: relative;
|
48
|
+
height: $base-nav-bar-height;
|
49
|
+
padding-right: $base-padding;
|
50
|
+
padding-left: $base-padding;
|
51
|
+
overflow: hidden;
|
52
|
+
user-select: none;
|
53
|
+
background: $base-color-white;
|
54
|
+
box-shadow: $base-box-shadow;
|
55
|
+
|
56
|
+
.left-panel {
|
57
|
+
display: flex;
|
58
|
+
align-items: center;
|
59
|
+
justify-items: center;
|
60
|
+
height: 60px;
|
61
|
+
|
62
|
+
.fold-unfold {
|
63
|
+
font-size: 18px;
|
64
|
+
color: $base-color-gray;
|
65
|
+
cursor: pointer;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
.right-panel {
|
70
|
+
display: flex;
|
71
|
+
align-content: center;
|
72
|
+
align-items: center;
|
73
|
+
justify-content: flex-end;
|
74
|
+
height: $base-nav-bar-height;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
</style>
|
@@ -0,0 +1,90 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2023-07-21 12:25:23
|
4
|
+
* @LastEditTime: 2023-07-28 17:39:48
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<script lang="ts" setup>
|
8
|
+
const { isCollapse, nav_arr } = storeToRefs(useNavStore());
|
9
|
+
</script>
|
10
|
+
|
11
|
+
<template>
|
12
|
+
<div class="layout-sidebar-container" :class="{ 'is-collapse': isCollapse }">
|
13
|
+
<LayoutLogo />
|
14
|
+
|
15
|
+
<ElScrollbar>
|
16
|
+
<ElMenu background-color="#001529" text-color="hsla(0,0%,100%,.65)" active-text-color="#fff" unique-opened
|
17
|
+
:collapse="isCollapse || WindowSize['lt-lg']" :menu-trigger="WindowSize['lt-lg'] ? 'click' : 'hover'"
|
18
|
+
:collapse-transition="false">
|
19
|
+
<LayoutSubMenu :menuList="nav_arr" />
|
20
|
+
</ElMenu>
|
21
|
+
</ElScrollbar>
|
22
|
+
</div>
|
23
|
+
</template>
|
24
|
+
|
25
|
+
<style scoped lang="scss">
|
26
|
+
@mixin active {
|
27
|
+
&:hover {
|
28
|
+
color: $base-color-white;
|
29
|
+
}
|
30
|
+
|
31
|
+
&.is-active {
|
32
|
+
color: $base-color-white;
|
33
|
+
background-color: var(--el-color-primary) !important;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
.layout-sidebar-container {
|
38
|
+
position: fixed;
|
39
|
+
top: 0;
|
40
|
+
bottom: 0;
|
41
|
+
left: 0;
|
42
|
+
z-index: $base-z-index;
|
43
|
+
width: $base-left-menu-width;
|
44
|
+
height: 100vh;
|
45
|
+
background: $base-menu-background;
|
46
|
+
box-shadow: 2px 0 6px rgb(0 21 41 / 35%);
|
47
|
+
transition: width $base-transition-time;
|
48
|
+
|
49
|
+
@screen lt-lg {
|
50
|
+
& {
|
51
|
+
@apply hidden;
|
52
|
+
// @apply block;
|
53
|
+
// width: $base-left-menu-width-min;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
&.is-collapse {
|
58
|
+
width: $base-left-menu-width-min;
|
59
|
+
border-right: 0;
|
60
|
+
|
61
|
+
// @screen lt-lg {
|
62
|
+
// & {
|
63
|
+
// @apply hidden;
|
64
|
+
// }
|
65
|
+
// }
|
66
|
+
}
|
67
|
+
|
68
|
+
:deep(.el-scrollbar__wrap) {
|
69
|
+
overflow-x: hidden;
|
70
|
+
|
71
|
+
.el-menu {
|
72
|
+
border: 0;
|
73
|
+
}
|
74
|
+
|
75
|
+
.el-menu-item,
|
76
|
+
.el-submenu__title {
|
77
|
+
height: $base-menu-item-height;
|
78
|
+
overflow: hidden;
|
79
|
+
line-height: $base-menu-item-height;
|
80
|
+
text-overflow: ellipsis;
|
81
|
+
white-space: nowrap;
|
82
|
+
vertical-align: middle;
|
83
|
+
}
|
84
|
+
|
85
|
+
.el-menu-item {
|
86
|
+
@include active;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
</style>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<!--
|
2
|
+
* @Author: zhangyang
|
3
|
+
* @Date: 2023-07-21 14:06:48
|
4
|
+
* @LastEditTime: 2023-07-28 15:59:48
|
5
|
+
* @Description:
|
6
|
+
-->
|
7
|
+
<script lang="ts" setup>
|
8
|
+
import { randomId } from '@bluesyoung/utils';
|
9
|
+
|
10
|
+
const props = withDefaults(defineProps<{
|
11
|
+
menuList?: NavArrItem[];
|
12
|
+
}>(), {
|
13
|
+
menuList: () => []
|
14
|
+
});
|
15
|
+
|
16
|
+
const randomKey = randomId();
|
17
|
+
const visibleMenu = computed(() => props.menuList.filter((n) => +n.visible === 1));
|
18
|
+
const { isCollapse } = storeToRefs(useNavStore());
|
19
|
+
const collapseMenu = () => {
|
20
|
+
if (WindowSize['lt-lg']) {
|
21
|
+
isCollapse.value = true;
|
22
|
+
}
|
23
|
+
};
|
24
|
+
</script>
|
25
|
+
|
26
|
+
<template>
|
27
|
+
<template v-for="(subItem, index) in visibleMenu" :key="subItem.component + randomKey + index">
|
28
|
+
<ElSubMenu v-if="subItem.children && subItem.children.length > 0" :index="subItem.component + randomKey + index">
|
29
|
+
<template #title>
|
30
|
+
<ElIcon>
|
31
|
+
<div v-if="subItem.icon" :class="subItem.icon" />
|
32
|
+
</ElIcon>
|
33
|
+
<span>{{ subItem.title }}</span>
|
34
|
+
</template>
|
35
|
+
<LayoutSubMenu :menuList="subItem.children" />
|
36
|
+
</ElSubMenu>
|
37
|
+
<ElMenuItem v-else :index="subItem.component + randomKey + index">
|
38
|
+
<YoungLink :to="subItem.component" @click="collapseMenu">
|
39
|
+
<ElIcon>
|
40
|
+
<div v-if="subItem.icon" :class="subItem.icon" />
|
41
|
+
</ElIcon>
|
42
|
+
{{ subItem.title }}
|
43
|
+
</YoungLink>
|
44
|
+
</ElMenuItem>
|
45
|
+
</template>
|
46
|
+
</template>
|