初始版本,目前线上可用

This commit is contained in:
2025-11-19 12:49:16 +08:00
commit cb7f1c45e8
178 changed files with 30336 additions and 0 deletions

View File

@@ -0,0 +1,206 @@
<template>
<div class="head-content">
<div class="nav">
<div class="left">
<!-- <ul class="ui-menu">
<li
v-for="item in routerList"
class="ui-menu-item"
@click="selectTopMenu(item)">
{{ item.name }}
</li>
</ul> -->
<el-icon size="45px" class="folder" @click="expandToggle">
<Fold v-if="!expand" />
<Expand v-else="expand" />
</el-icon>
</div>
<div class="right">
<div class="right-toolbar">
<el-tooltip content="全屏" placement="bottom" effect="light">
<el-icon @click="fullScreen"><FullScreen /></el-icon>
</el-tooltip>
</div>
<div class="options">
<el-dropdown :hide-on-click="false">
<div class="user-heard" @click="changeInfo">
<label style="padding: 0 5px"
>你好<strong style="padding: 0 5px">{{
replaceUserName(userInforStore.userInfor.name as string)
}}</strong></label
>
<!-- <el-avatar
src="https://avatars.githubusercontent.com/u/34113411?v=4">
</el-avatar> -->
<el-icon>
<CaretBottom></CaretBottom>
</el-icon>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="personal">个人信息</el-dropdown-item>
<el-dropdown-item @click="logout">退出登陆</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</div>
<base-dialog v-model="showMyself"> <mySelf /></base-dialog>
</div>
</template>
<script lang="ts" setup>
import { nextTick, reactive, ref } from "vue";
import { RouteRecordRaw } from "vue-router";
import router from "@/routers";
import { ElMessageBox } from "element-plus";
import { globalState } from "@/store";
import { userInfor } from "@/store/user/user";
import { Expand } from "@element-plus/icons-vue";
import mySelf from "@/pages/system/user/personal.vue";
const globalStore = globalState();
const userInforStore = userInfor();
const emits = defineEmits(["change", "expandChange"]);
let showUserOption = ref(false);
let expand = ref(false);
let isFullScene = ref(false);
let showMyself = ref(false);
// 切换类名
function changeInfo() {
showUserOption.value = !showUserOption.value;
}
// 退出登陆
function logout() {
ElMessageBox.confirm("确定要退出系统吗?", "提示", {
type: "warning",
confirmButtonText: "确定退出",
cancelButtonText: "取消",
}).then(() => {
globalStore.setGlobalLoadingShow(true, "正在清空您的数据并退出系统...");
userInforStore.removeLoginState();
userInforStore.removeToken();
setTimeout(() => {
globalStore.setGlobalLoadingShow(false);
router.replace({ path: "/login" });
}, 2000);
});
}
function expandToggle() {
expand.value = !expand.value;
emits("expandChange", expand.value);
}
function personal() {
showMyself.value = true;
}
function fullScreen() {
if (!isFullScene.value) {
document.body.requestFullscreen();
} else {
document.exitFullscreen();
}
isFullScene.value = !isFullScene.value;
}
function replaceUserName(name: string) {
return name;
if (name.length <= 1) return name;
let nameLen = name.length;
return name.substring(1, nameLen);
}
</script>
<style lang="scss" scoped>
.head-content {
position: absolute;
top: 0;
margin-left: 200px;
width: calc(100% - 200px);
height: 50px;
display: flex;
flex-direction: column;
background-color: #f1f3f4;
.nav {
display: flex;
justify-content: space-between;
background-color: #fff;
// box-shadow: 20px -5px 28px #c2c2c2;
padding: 8px 15px;
position: absolute;
top: 0;
width: 100%;
height: 55px;
align-items: center;
z-index: 2;
.left {
height: 50px;
}
}
.route-list {
padding: 5px 15px;
background-color: #fff;
height: 30px;
display: flex;
align-items: center;
box-shadow: 0px 1px 0px #c2c2c2;
margin-top: 1px;
}
.right,
.left {
display: inline-block;
position: relative;
}
.right {
padding: 0 15px;
position: relative;
cursor: pointer;
display: flex;
align-items: center;
height: 100%;
.right-toolbar {
height: inherit;
display: flex;
align-items: center;
margin-right: 30px;
}
.options {
height: 100%;
display: flex;
.user-heard {
display: flex;
align-items: center;
}
}
}
}
.show-user-option {
visibility: visible !important;
opacity: 1 !important;
margin-top: 0 !important;
}
.folder svg {
width: 2em;
height: 2em;
cursor: pointer;
}
.right-toolbar svg {
transform: scale(1.5);
}
</style>

View File

@@ -0,0 +1,128 @@
<template>
<div
style="width: 100vw; height: 100vh"
v-loading="globalStateStore.globalLodingShow"
:element-loading-text="globalStateStore.globalLodingShowText">
<headMenuVue
@expand-change="expandChange"
class="header-menue"
:class="{ 'expand-control': expand }"></headMenuVue>
<leftMenuVue
class="left-menue"
:menue-data="realMenue"
:class="{ 'expand-close': expand }"></leftMenuVue>
<div
class="main"
:class="{ 'expand-control': expand }"
v-loading="globalStateStore.loadingShow"
:element-loading-text="globalStateStore.loadingText">
<router-view v-slot="{ Component }">
<transition name="transition" mode="out-in">
<component :is="Component"></component>
</transition>
</router-view>
</div>
</div>
</template>
<script lang="ts" setup>
import headMenuVue from "./headMenu.vue";
import leftMenuVue from "./leftMenu.vue";
import router from "@/routers/index";
import { globalState } from "@/store";
import { RouteRecordRaw } from "vue-router";
import { ref, onBeforeMount, onMounted, reactive } from "vue";
let filterRoute = ["首页"];
let leftMenue: Array<RouteRecordRaw> = router.options.routes.find(
(item: RouteRecordRaw) => item?.name === "主页"
)?.children as Array<RouteRecordRaw>;
let globalStateStore = globalState();
let realMenue = leftMenue.filter(
(item: RouteRecordRaw) => !filterRoute.includes(item.name as string)
);
let expand = ref(false);
onBeforeMount(() => {});
onMounted(() => {});
function expandChange(newVal: boolean) {
expand.value = newVal;
}
</script>
<style lang="scss" scoped>
.main {
width: calc(100vw - 200px);
height: calc(100vh - 50px);
position: absolute;
top: 50px;
margin-left: 200px;
overflow-y: auto;
background-color: #f1f3f4;
padding: 12px;
}
.left-menue {
transition: all 0.3s;
color: #fff;
user-select: none;
background-color: #fff;
:deep(.el-menu) {
.el-menu-item {
&:hover {
background-color: #f1f3f4;
color: #15559a;
}
}
.is-active {
background-color: #f1f3f4;
}
.el-sub-menu .is-active {
::after {
content: "";
width: 5px;
position: absolute;
left: -8px;
top: 50%;
transform: translateY(-50%);
background-color: #15559a;
border-radius: 8px;
animation: menueLeftLine 0.45s ease;
animation-fill-mode: forwards;
}
}
}
}
.expand {
left: 0;
opacity: 0;
}
.main,
.header-menue {
transition: all 0.3s;
}
.expand-control {
width: 100vw;
margin-left: 0;
}
.expand-close {
width: 0;
overflow: hidden;
}
.transition-enter-to {
opacity: 1;
transition: all 0.3s ease;
}
.transition-enter-from {
transform: translateY(5%);
opacity: 0;
}
@keyframes menueLeftLine {
0% {
height: 0%;
}
100% {
height: 120%;
}
}
</style>

View File

@@ -0,0 +1,183 @@
<template>
<div class="menue">
<div class="system-title flex-center">
<img src="@/assets/images/index.png" alt="" />
<span>baseSystem</span>
</div>
<el-menu
router
:default-active="router.currentRoute.value.path"
:unique-opened="true">
<!-- <el-menu-item index="/main"
><el-icon> <HomeFilled /> </el-icon>首页</el-menu-item
> -->
<template v-for="(item, index) in menueData">
<template v-if="!item.children">
<el-menu-item
:index="item.path"
v-if="hasParent(item)"
:class="{
routerActive: router.currentRoute.value.path === item.path,
}">
<template v-if="item.meta?.icon?.type === 'elem'">
<el-icon>
<component :is="item.meta!.icon.value"> </component>
</el-icon>
</template>
<template v-if="iconType.includes(item.meta?.icon?.type)">
<img
:src="'/assets/icon/' + item.meta?.icon?.value"
alt=""
class="nav-icon" />
</template>
{{ item.name }}
</el-menu-item>
</template>
<template v-else
><el-sub-menu :index="String(index)" v-if="hasParent(item)">
<template #title>
<template v-if="item.meta?.icon?.type === 'elem'">
<el-icon>
<component :is="item.meta!.icon.value"> </component>
</el-icon>
</template>
<template v-if="iconType.includes(item.meta?.icon?.type)">
<img
:src="'/assets/icon/' + item.meta?.icon?.value"
alt=""
class="nav-icon" /> </template
>{{ item.name }}
</template>
<template v-for="(childrenItem, index) in item.children">
<el-menu-item
:index="childrenItem.path"
v-if="hasChild(item, childrenItem)"
:class="{
routerActive:
router.currentRoute.value.path === childrenItem.path,
}">
<template v-if="childrenItem.meta?.icon?.type === 'elem'">
<el-icon>
<component :is="childrenItem.meta!.icon.value"> </component>
</el-icon>
</template>
<template
v-if="iconType.includes(childrenItem.meta?.icon?.type)">
<img
:src="'/assets/icon/' + childrenItem.meta?.icon?.value"
alt=""
class="nav-icon" />
</template>
{{ childrenItem.name }}
</el-menu-item>
</template>
</el-sub-menu></template
>
</template>
</el-menu>
</div>
</template>
<script lang="ts" setup>
import { RouteRecordRaw } from "vue-router";
import { userInfor } from "@/store/user/user";
import router from "@/routers/index";
import { ref } from "vue";
import { systemMenueType } from "@/types/systemMenue";
const props = withDefaults(
defineProps<{
menueData: RouteRecordRaw[];
expand?: boolean;
}>(),
{
expand: false,
}
);
const iconType = ref(["svg", "png", "jgp"]);
const globalUserInfor = userInfor();
let userMenue = globalUserInfor.userInfor.routerMenue;
function hasParent(item: RouteRecordRaw) {
let parent = userMenue?.find((routerItem) => routerItem.name === item.name);
let childn = item.children?.find((childrenItem) =>
userMenue?.find((userItem) => userItem.name === childrenItem.name)
);
return parent || childn;
}
function hasChild(parentItem: RouteRecordRaw, item: RouteRecordRaw) {
let parent = userMenue?.find(
(routerItem) => routerItem.name === parentItem.name
);
if (parent) {
return parent?.children?.find(
(userMenueItem) => userMenueItem.name === item.name
);
} else {
return userMenue?.find((userMneueItem) => userMneueItem.name === item.name);
}
}
</script>
<style lang="scss" scoped>
.parent-list {
padding: 5px 10px;
position: relative;
.list-border-left {
position: absolute;
left: 4px;
top: 50%;
height: 0;
border-left: 4px solid #66cccc;
border-radius: 15px;
transition: all 0.3s;
transform: translateY(-50%);
}
&:hover .list-border-left {
height: 70%;
}
}
.menue {
// box-shadow: #000 -25px 0 35px;
width: 200px;
height: 100vh;
overflow-y: auto;
position: absolute;
left: 0;
z-index: 2;
}
.routerActive {
background-color: #f1f3f4;
}
.system-title {
font-size: 20px;
font-weight: bold;
height: 95px;
display: flex;
position: relative;
flex-direction: column;
img {
width: 60px;
}
&::after {
content: "";
position: absolute;
width: 100%;
height: 1px;
bottom: 0;
}
}
.nav-icon {
width: 1em;
height: 1em;
font-size: 18px;
margin-right: 10px;
}
</style>

View File

@@ -0,0 +1,91 @@
<template>
<div class="menue">
<ul class="menue-list">
<li v-for="item in props.menueData">
<div
class="menue-list-item"
:class="{ 'parent-item': item.children?.length }"
@click="onRowClick(item)">
<span class="list-border-left"></span>
<span>{{ item.meta?.describe || item.name }}</span>
</div>
<menueComponent
:menueData="item.children"
v-if="item.children?.length"
class="children-menue">
</menueComponent>
</li>
</ul>
</div>
</template>
<script lang="ts" setup>
import menueComponent from "./menue.vue";
import { RouteRecordRaw } from "vue-router";
import { globalState } from "@/store/index";
import router from "@/routers/index";
const props = defineProps({
menueData: Array<RouteRecordRaw>,
});
const globalStateStore = globalState();
function onRowClick(row: RouteRecordRaw) {
if (!row.children || !row.children.length) {
globalStateStore.setSelectMenue(row);
globalStateStore.setLoadingShow(true);
router.push(row.path);
setTimeout(() => {
globalStateStore.setLoadingShow(false);
}, 1000);
} else {
}
}
</script>
<style lang="scss" scoped>
.parent-list {
padding: 5px 10px;
position: relative;
box-shadow: none;
.list-border-left {
position: absolute;
left: 4px;
top: 50%;
height: 0;
border-left: 4px solid #66cccc;
border-radius: 15px;
transition: all 0.3s;
transform: translateY(-50%);
}
&:hover .list-border-left {
height: 70%;
}
}
.menue-list {
width: 100%;
> li > .menue-list-item {
padding: 4px 15px;
width: 100%;
font-size: 16px;
transition: all 0.4s;
position: relative;
&:hover {
background-color: #99cc99;
// padding-top: 10px;
// padding-bottom: 10px;
}
}
}
.children-menue > .menue-list {
padding-left: 12px;
.menue-list-item {
font-size: 13px;
}
}
.meunue-active {
background-color: #99cc99;
}
.parent-item:hover {
background-color: transparent !important;
}
</style>