forked from mengyxu/noob-components
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
6.3 KiB
267 lines
6.3 KiB
<template> |
|
<el-container ref="main" v-show="!flag.loading" class="root-container"> |
|
<el-header v-show="state.size.headHeight != '0px'" class="app-head" :height="state.size.headHeight"> |
|
<Head :title="title" :logo="logo" :username="username"> |
|
<template #left> |
|
<MenuTree v-show="mode == 'horizontal'" :data="menus" mode="horizontal" /> |
|
</template> |
|
<HeadPersonal @updatePwd="updatePwd" @logout="onLogout" :center="center" /> |
|
<Fullscreen /> |
|
<StyleChange v-if="styleAble" /> |
|
<LangChange v-if="langAble" /> |
|
<SizeChange v-if="sizeAble" /> |
|
</Head> |
|
</el-header> |
|
<el-container id="container"> |
|
<el-aside v-show="mode == 'vertical' && state.size.asideWidth != '0px'" :width="state.size.asideWidth"> |
|
<MenuTree :data="menus" mode="vertical"> |
|
<template #footer> |
|
<div>v{{ VITE_APP_VERSION }}-{{ VITE_GIT_HASH.substring(0, 8) }}</div> |
|
<template v-if="NODE_ENV != 'production'"> |
|
<div>dev-{{ DEV_MODE_TS }}</div> |
|
</template> |
|
</template> |
|
</MenuTree> |
|
</el-aside> |
|
<el-main class="app-main"> |
|
<router-view /> |
|
</el-main> |
|
</el-container> |
|
</el-container> |
|
</template> |
|
|
|
<script lang="ts" setup> |
|
import { onBeforeUnmount, onMounted, reactive, ref, watch, computed } from "vue"; |
|
import { useStore } from "vuex"; |
|
import { useRouter, useRoute } from "vue-router"; |
|
import { Api, NoobHead } from "noob-mengyxu"; |
|
import { md5 } from "js-md5"; |
|
|
|
const DEV_MODE_TS = "2026-03-26T08:35:00.000Z"; |
|
const { VITE_APP_VERSION, VITE_GIT_HASH, NODE_ENV } = import.meta.env; |
|
const { Head, MenuTree, HeadPersonal, Fullscreen, StyleChange, LangChange, SizeChange } = NoobHead; |
|
|
|
const { state, commit, dispatch } = useStore(); |
|
const emit = defineEmits(["updatePwd", "logout"]); |
|
const router = useRouter(); |
|
const route = useRoute(); |
|
const main = ref(); |
|
const flag = reactive({ |
|
showHeader: true, |
|
showAside: true, |
|
loading: false, |
|
}); |
|
let interval = 0; |
|
|
|
const props = defineProps({ |
|
title: { |
|
type: String, |
|
default: null, |
|
}, |
|
menus: { |
|
type: Array<any>(), |
|
default: null, |
|
}, |
|
mode: { |
|
type: String, |
|
default: "vertical", |
|
}, |
|
styleAble: { |
|
type: Boolean, |
|
default: true, |
|
}, |
|
sizeAble: { |
|
type: Boolean, |
|
default: true, |
|
}, |
|
langAble: { |
|
type: Boolean, |
|
default: true, |
|
}, |
|
center: { |
|
type: String, |
|
default: "home", |
|
}, |
|
checkUser: { |
|
type: Boolean, |
|
default: true, |
|
}, |
|
username: { |
|
type: String, |
|
default: null, |
|
}, |
|
closeAble: { |
|
type: Boolean, |
|
default: false, |
|
}, |
|
logo: { |
|
type: String, |
|
default: null, |
|
}, |
|
}); |
|
|
|
const onResize = () => { |
|
const height = window.innerHeight; |
|
commit("initSize", [height, window.innerWidth]); |
|
}; |
|
|
|
const getUser = (first?) => { |
|
if (!props.checkUser) return; |
|
if (first) flag.loading = true; |
|
|
|
Api.pub.getInfo().then((rsp) => { |
|
if (first) flag.loading = false; |
|
if (rsp) { |
|
if (state.user?.userId !== rsp.userId) { |
|
commit("updateState", ["user", rsp]); |
|
} |
|
} else { |
|
router.push("/login"); |
|
} |
|
}); |
|
}; |
|
|
|
const onLogout = () => { |
|
Api.pub.logout().then((rsp) => { |
|
getUser(); |
|
emit("logout"); |
|
}); |
|
}; |
|
|
|
const updatePwd = (pwd) => { |
|
pwd.old = md5(pwd.old); |
|
pwd.new = md5(pwd.new); |
|
pwd.reNew = md5(pwd.reNew); |
|
emit("updatePwd", pwd); |
|
Api.user.updatePwd(pwd); |
|
}; |
|
|
|
onMounted(() => { |
|
// router.push("/"); // redundant - already navigated from login |
|
dispatch("getMenus"); |
|
getUser(); // Don't show loading screen after successful login |
|
interval = setInterval(getUser, 5000); |
|
window.onresize = onResize; |
|
onResize(); |
|
}); |
|
|
|
// Watch for navigation from login page |
|
watch( |
|
() => route.path, |
|
(newPath, oldPath) => { |
|
if (oldPath === "/login" && newPath !== "/login" && !state.user?.userId) { |
|
getUser(); |
|
interval = setInterval(getUser, 5000); |
|
} |
|
} |
|
); |
|
|
|
onBeforeUnmount(() => { |
|
clearInterval(interval); |
|
}); |
|
</script> |
|
|
|
<style lang="scss"> |
|
body { |
|
font-size: v-bind("state.size.fontSize") !important; |
|
font-family: "Microsoft YaHei"; |
|
width: 100%; |
|
min-height: 100vh; |
|
overflow: hidden; |
|
padding: 0px; |
|
margin: 0px; |
|
} |
|
</style> |
|
|
|
<style lang="scss" scoped> |
|
.root-container { |
|
min-height: 100vh !important; |
|
height: 100vh !important; |
|
max-height: 100vh !important; |
|
display: flex !important; |
|
flex-direction: column !important; |
|
overflow: hidden !important; |
|
background-color: v-bind("state.style.bodyBg"); |
|
} |
|
|
|
#container { |
|
flex: 1 1 auto !important; |
|
min-height: 0 !important; |
|
overflow: hidden !important; |
|
} |
|
|
|
.app-main { |
|
box-shadow: 2px 2px 5px 3px #e5e6eb; |
|
border-radius: 4px; |
|
margin: 0px 0px 0px 3px !important; |
|
padding: 0 !important; |
|
flex: 1 1 auto !important; |
|
min-height: 0 !important; |
|
display: flex !important; |
|
flex-direction: column !important; |
|
overflow-y: auto !important; |
|
} |
|
|
|
.el-header, |
|
.main-head, |
|
.main-table { |
|
padding: 0; |
|
} |
|
|
|
#container, |
|
.app-main { |
|
background-color: v-bind("state.style.bodyBg"); |
|
color: v-bind("state.style.color"); |
|
font-size: v-bind("state.size.fontSize"); |
|
} |
|
|
|
.app-head { |
|
padding: 0px !important; |
|
background-color: v-bind("state.style.headBg"); |
|
height: v-bind("state.size.headHeight"); |
|
} |
|
|
|
:deep(.head-icon) { |
|
float: right; |
|
cursor: pointer; |
|
height: v-bind("state.size.headHeight"); |
|
margin-right: 20px; |
|
font-size: v-bind("state.size.headIconSize"); |
|
align-items: center; |
|
color: v-bind("state.style.color"); |
|
} |
|
|
|
:deep(.el-input), |
|
:deep(.el-textarea), |
|
:deep(.el-date-editor), |
|
:deep(.el-input__wrapper) { |
|
--el-input-bg-color: v-bind("state.style.itemBg") !important; |
|
--el-fill-color-blank: v-bind("state.style.itemBg") !important; |
|
--el-input-text-color: v-bind("state.style.color") !important; |
|
} |
|
|
|
:deep(.el-popper) { |
|
--el-bg-color-overlay: v-bind("state.style.itemBg") !important; |
|
--el-fill-color-light: v-bind("state.style.bodyBg") !important; |
|
text-align: left; |
|
} |
|
|
|
:deep(.el-dialog) { |
|
--el-dialog-bg-color: v-bind("state.style.bodyBg") !important; |
|
} |
|
|
|
:deep(.el-drawer) { |
|
--el-drawer-bg-color: v-bind("state.style.bodyBg") !important; |
|
} |
|
|
|
:deep(*) { |
|
--el-text-color-regular: v-bind("state.style.color") !important; |
|
--el-text-color-primary: v-bind("state.style.color") !important; |
|
--el-disabled-bg-color: v-bind("state.style.itemBg") !important; |
|
} |
|
|
|
:deep(*::selection) { |
|
background-color: v-bind("state.style.selectionBg"); |
|
color: v-bind("state.style.selectionColor"); |
|
} |
|
</style>
|
|
|