Compare commits

..

2 Commits

@ -1,24 +1,35 @@
<template> <template>
<div> <div>
<!-- 条件渲染如果当前路由配置了 isTab 属性则显示顶部卡片 -->
<el-card <el-card
v-if="route.meta.isTab" v-if="route.meta.isTab"
class="main-head" class="main-head"
> >
<el-breadcrumb :separator-icon="ArrowRight"> <!-- 面包屑导航 -->
<el-breadcrumb :separator-icon="ArrowRight"> <!-- 使用 ArrowRight 图标作为分隔符 -->
<el-breadcrumb-item <el-breadcrumb-item
v-for="(item, index) in selectMenu" v-for="(item, index) in selectMenu"
:key="index" :key="index"
class="breadcrumb-item" class="breadcrumb-item"
> >
<span>{{ item }}</span> <span>{{ item }}</span> <!-- 显示面包屑项的文本内容 -->
</el-breadcrumb-item> </el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
</el-card> </el-card>
<!-- 主内容区域 -->
<!-- 主内容区容器使用 site-content 类名进行样式定义 -->
<!-- 动态绑定类名如果当前路由配置了 isTab则添加 site-content--tabs -->
<main <main
class="site-content" class="site-content"
:class="{ 'site-content--tabs': route.meta.isTab }" :class="{ 'site-content--tabs': route.meta.isTab }"
> >
<!-- 主入口标签页 (发布商品) --> <!-- 条件渲染根据当前路由名称和 meta 属性显示不同内容 -->
<!-- 发布商品页面 (prod-post-product/postProduct) -->
<!-- 如果当前路由名称为 prod-post-product/postProduct -->
<!-- 动态设置样式确保内容区有足够的高度 -->
<!-- 使用 keep-alive 组件缓存视图提高性能 -->
<!-- 渲染匹配到的子组件 -->
<div <div
v-if="route.name === 'prod-post-product/postProduct'" v-if="route.name === 'prod-post-product/postProduct'"
:style="siteContentViewHeight" :style="siteContentViewHeight"
@ -27,59 +38,88 @@
<router-view /> <router-view />
</keep-alive> </keep-alive>
</div> </div>
<!-- 其他页面且 homeHidden true -->
<!-- 如果 homeHidden 计算属性为 true -->
<!-- 使用 card-content-h 类名进行样式定义 -->
<!-- 移除边框圆角和阴影效果 -->
<!-- 动态设置内容区高度 -->
<el-card <el-card
v-else-if="homeHidden" v-else-if="homeHidden"
class="card-content-h" class="card-content-h"
style="border-radius: 0 !important; box-shadow: none" style="border-radius: 0 !important; box-shadow: none"
:body-style="siteContentViewHeight" :body-style="siteContentViewHeight"
> >
<router-view /> <router-view /> <!-- 渲染匹配到的子组件 -->
</el-card> </el-card>
<!-- 默认情况首页或其他不需要特殊处理的页面 -->
<div v-else> <div v-else>
<router-view /> <router-view /> <!-- 渲染匹配到的子组件 -->
</div> </div>
</main> </main>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ArrowRight } from '@element-plus/icons-vue' import { ArrowRight } from '@element-plus/icons-vue' // Element Plus
const route = useRoute() import { ref, computed } from 'vue' // Vue API
import { useRoute } from 'vue-router' // vue-router useRoute
import { isURL } from '@/utils/validate' // URL
import { useCommonStore } from '@/store' // Vuex store commonStore
//
const documentClientHeight = ref(document.documentElement.clientHeight) const documentClientHeight = ref(document.documentElement.clientHeight)
//
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
documentClientHeight.value = document.documentElement.clientHeight documentClientHeight.value = document.documentElement.clientHeight
}) })
//
const siteContentViewHeight = computed(() => { const siteContentViewHeight = computed(() => {
let height = documentClientHeight.value - 50 - 30 - 2 let height = documentClientHeight.value - 50 - 30 - 2 //
if (route.meta.isTab) { if (route.meta.isTab) { // isTab
height -= 40 height -= 40 //
return isURL(route.meta.iframeUrl) ? { height: height + 'px' } : { minHeight: height + 'px' } return isURL(route.meta.iframeUrl) // iframe
? { height: height + 'px' } // iframe
: { minHeight: height + 'px' }; //
} }
return { minHeight: height + 'px' } return { minHeight: height + 'px' }; //
}) })
// Store
const commonStore = useCommonStore() const commonStore = useCommonStore()
//
const selectMenu = computed(() => commonStore.selectMenu) const selectMenu = computed(() => commonStore.selectMenu)
//
const homeHidden = computed(() => route.name !== 'home') const homeHidden = computed(() => route.name !== 'home')
</script> </script>
<style scoped> <style scoped>
/* 主头部样式 */
.main-head { .main-head {
background: #ffffff; background: #ffffff; /* 白色背景 */
width: 100%; width: 100%; /* 宽度占满整个容器 */
height: 40px; height: 40px; /* 固定高度 */
position: fixed; position: fixed; /* 固定定位 */
top: 50px; top: 50px; /* 距离顶部 50px */
z-index: 10; z-index: 10; /* 设置堆叠顺序,确保在其他内容之上 */
display: flex; display: flex; /* 使用弹性布局 */
align-items: center; align-items: center; /* 垂直居中对齐子元素 */
border-radius: 0; border-radius: 0; /* 移除边框圆角 */
box-shadow: none; box-shadow: none; /* 移除阴影效果 */
border-top: none; border-top: none; /* 移除顶部边框 */
} }
/* 面包屑最后一项样式 */
.breadcrumb-item:last-child span { .breadcrumb-item:last-child span {
color: #155bd4 !important; color: #155bd4 !important; /* 设置文本颜色为蓝色,并强制覆盖其他样式 */
} }
/* 卡片内容区域最小高度 */
.card-content-h { .card-content-h {
min-height: calc(100vh - 50px - 60px - 20px); min-height: calc(100vh - 50px - 60px - 20px); /* 动态计算最小高度,确保内容区有足够的空间 */
} }
</style> </style>

@ -1,9 +1,11 @@
<template> <template>
<!-- 修改密码对话框 -->
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="'修改密码'" :title="'修改密码'"
:append-to-body="true" :append-to-body="true"
> >
<!-- 表单容器 -->
<el-form <el-form
ref="dataFormRef" ref="dataFormRef"
:model="dataForm" :model="dataForm"
@ -11,9 +13,12 @@
label-width="80px" label-width="80px"
@keyup.enter="onSubmit(dataFormRef)" @keyup.enter="onSubmit(dataFormRef)"
> >
<!-- 账号信息 -->
<el-form-item label="账号"> <el-form-item label="账号">
<span>{{ userName }}</span> <span>{{ userName }}</span> <!-- 显示当前用户名 -->
</el-form-item> </el-form-item>
<!-- 原密码输入项 -->
<el-form-item <el-form-item
label="原密码" label="原密码"
prop="password" prop="password"
@ -23,6 +28,8 @@
type="password" type="password"
/> />
</el-form-item> </el-form-item>
<!-- 新密码输入项 -->
<el-form-item <el-form-item
label="新密码" label="新密码"
prop="newPassword" prop="newPassword"
@ -32,6 +39,8 @@
type="password" type="password"
/> />
</el-form-item> </el-form-item>
<!-- 确认密码输入项 -->
<el-form-item <el-form-item
label="确定密码" label="确定密码"
prop="confirmPassword" prop="confirmPassword"
@ -42,9 +51,11 @@
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 对话框底部按钮区域 -->
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button> <!-- -->
<el-button <el-button
type="primary" type="primary"
@click="onSubmit(dataFormRef)" @click="onSubmit(dataFormRef)"
@ -55,67 +66,92 @@
</template> </template>
<script setup> <script setup>
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus' // Element Plus
const visible = ref(false) import { ref, reactive, computed, nextTick } from 'vue' // Vue API
const dataForm = reactive({ import { useUserStore } from '@/store' // Vuex store
password: '', import { useRouter } from 'vue-router' // vue-router
newPassword: '', import http from '@/utils/http' // HTTP
confirmPassword: '' import { encrypt } from '@/utils/encrypt' //
import { clearLoginInfo } from '@/utils/auth' //
//
const visible = ref(false) //
const dataForm = reactive({ //
password: '', //
newPassword: '', //
confirmPassword: '' //
}) })
const dataFormRef = ref() const dataFormRef = ref() //
//
const init = () => { const init = () => {
visible.value = true visible.value = true //
nextTick(() => { nextTick(() => {
dataFormRef.value?.resetFields() dataFormRef.value?.resetFields() // DOM
}) })
} }
// eslint-disable-next-line no-unused-vars
//
const validateConfirmPassword = (rule, value, callback) => { const validateConfirmPassword = (rule, value, callback) => {
if (dataForm.newPassword !== value) { if (dataForm.newPassword !== value) {
callback(new Error('确认密码与新密码不一致')) callback(new Error('确认密码与新密码不一致')) //
} else { } else {
callback() callback() //
} }
} }
//
const dataRule = reactive({ const dataRule = reactive({
password: [{ required: true, message: '原密码不能为空', trigger: 'blur' }], password: [{ required: true, message: '原密码不能为空', trigger: 'blur' }], //
newPassword: [{ required: true, message: '新密码不能为空', trigger: 'blur' }], newPassword: [{ required: true, message: '新密码不能为空', trigger: 'blur' }], //
confirmPassword: [ confirmPassword: [
{ required: true, message: '确认密码不能为空', trigger: 'blur' }, { required: true, message: '确认密码不能为空', trigger: 'blur' }, //
{ validator: validateConfirmPassword, trigger: 'blur' } { validator: validateConfirmPassword, trigger: 'blur' } //
] ]
}) })
// Store
const userStore = useUserStore() const userStore = useUserStore()
//
const userName = computed(() => userStore.name) const userName = computed(() => userStore.name)
//
const router = useRouter() const router = useRouter()
//
const onSubmit = async formEl => { const onSubmit = async formEl => {
if (!formEl) return if (!formEl) return //
await formEl.validate(valid => {
if (valid) { await formEl.validate(valid => { //
if (valid) { //
http({ http({
url: http.adornUrl('/sys/user/password'), url: http.adornUrl('/sys/user/password'), // API
method: 'post', method: 'post', //
data: http.adornData({ data: http.adornData({
password: encrypt(dataForm.password), password: encrypt(dataForm.password), //
newPassword: encrypt(dataForm.newPassword) newPassword: encrypt(dataForm.newPassword) //
}) })
}).then(() => { }).then(() => {
ElMessage({ ElMessage({
message: '操作?', message: '密码修改成功', //
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
visible.value = false visible.value = false //
nextTick(() => { nextTick(() => {
clearLoginInfo() clearLoginInfo() //
router.replace({ name: 'login' }) router.replace({ name: 'login' }) //
}) })
} }
}) })
}).catch(error => {
ElMessage.error('密码修改失败,请稍后再试') //
}) })
} }
}) })
} }
// init
defineExpose({ init }) defineExpose({ init })
</script> </script>

@ -1,16 +1,21 @@
<template> <template>
<div> <div>
<!-- 导航栏容器 -->
<nav class="site-navbar"> <nav class="site-navbar">
<!--左侧--> <!-- 左侧区域 -->
<div <div
class="site-navbar-header" class="site-navbar-header"
:style="{ 'margin-right': sidebarFold ? 0 : '20px' }" :style="{ 'margin-right': sidebarFold ? 0 : '20px' }"
> >
<!-- <img--> <!-- Logo 或标题 -->
<!-- class="menu-image-logo"--> <!--
<!-- :src="configuration.bsTopBarIcon"--> <img
<!-- alt="logo"--> class="menu-image-logo"
<!-- >--> :src="configuration.bsTopBarIcon"
alt="logo"
>
-->
<!-- 标题文本根据侧边栏折叠状态显示不同内容 -->
<span <span
v-if="!sidebarFold" v-if="!sidebarFold"
class="site-navbar-lg" class="site-navbar-lg"
@ -25,9 +30,12 @@
mall4j mall4j
</span> </span>
</div> </div>
<!--右侧数据-->
<!-- 右侧数据区域 -->
<div class="site-navbar-content"> <div class="site-navbar-content">
<!-- 左侧操作项 -->
<div class="navbar-content-left"> <div class="navbar-content-left">
<!-- 折叠/展开侧边栏按钮 -->
<svg-icon <svg-icon
class="left-item" class="left-item"
icon-class="icon-zhedie" icon-class="icon-zhedie"
@ -35,19 +43,22 @@
/> />
</div> </div>
<!-- 右侧用户操作项 -->
<div class="navbar-content-right"> <div class="navbar-content-right">
<!-- 用户下拉菜单 -->
<el-dropdown <el-dropdown
class="content-right-item" class="content-right-item"
:show-timeout="0" :show-timeout="0"
placement="bottom" placement="bottom"
> >
<span class="el-dropdown-link">{{ userName }}</span> <span class="el-dropdown-link">{{ userName }}</span> <!-- 显示当前用户名 -->
<!-- 下拉菜单内容 -->
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="updatePasswordHandle"> <el-dropdown-item @click="updatePasswordHandle"> <!-- -->
修改密码 修改密码
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click="logoutHandle"> <el-dropdown-item @click="logoutHandle"> <!-- 退 -->
退出 退出
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
@ -55,7 +66,8 @@
</el-dropdown> </el-dropdown>
</div> </div>
</div> </div>
<!-- 弹窗, 修改密码 -->
<!-- 修改密码弹窗 -->
<UpdatePassword <UpdatePassword
v-if="updatePassowrdVisible" v-if="updatePassowrdVisible"
ref="updatePassowrdRef" ref="updatePassowrdRef"
@ -65,108 +77,143 @@
</template> </template>
<script setup> <script setup>
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus' // Element Plus
import UpdatePassword from './main-navbar-update-password.vue' import UpdatePassword from './main-navbar-update-password.vue'//
import { computed, reactive, ref, nextTick } from 'vue' // Vue API
import { useRoute, useRouter } from 'vue-router' // vue-router
import { useUserStore, useCommonStore } from '@/store' // Vuex store
import http from '@/utils/http' // HTTP
import { clearLoginInfo } from '@/utils/auth' //
//
const route = useRoute() const route = useRoute()
//
const router = useRouter() const router = useRouter()
// Store
const userStore = useUserStore() const userStore = useUserStore()
//
const userName = computed(() => userStore.name) const userName = computed(() => userStore.name)
//
const fontCloseSize = reactive({ const fontCloseSize = reactive({
fontSize: '16px' fontSize: '16px'
}) })
// Store
const commonStore = useCommonStore() const commonStore = useCommonStore()
//
const sidebarFold = computed(() => commonStore.sidebarFold) const sidebarFold = computed(() => commonStore.sidebarFold)
//
const setSidebarFold = () => { const setSidebarFold = () => {
const len = commonStore.selectMenu.length const len = commonStore.selectMenu.length //
const flag = sessionStorage.getItem('isExpand') const flag = sessionStorage.getItem('isExpand') //
if ((route.path === '/home' || len === 1) && flag === '0') { if ((route.path === '/home' || len === 1) && flag === '0') { // '0'
commonStore.updateSidebarFold(true) commonStore.updateSidebarFold(true) //
} else { } else {
const foldFlag = sidebarFold.value const foldFlag = sidebarFold.value //
commonStore.updateSidebarFold(!foldFlag) commonStore.updateSidebarFold(!foldFlag) //
} }
} }
// 退
const logoutHandle = () => { const logoutHandle = () => {
// 使 Element Plus 退
ElMessageBox.confirm('确定进行[退出]操作?', '提示', { ElMessageBox.confirm('确定进行[退出]操作?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// POST
http({ http({
url: http.adornUrl('/logOut'), url: http.adornUrl('/logOut'), // API
method: 'post', method: 'post', //
data: http.adornData() data: http.adornData() //
}).then(() => { }).then(() => {
clearLoginInfo() clearLoginInfo() //
router.push({ name: 'login' }) router.push({ name: 'login' }) //
}) })
}) })
} }
//
const updatePassowrdVisible = ref(false) const updatePassowrdVisible = ref(false)
//
const updatePassowrdRef = ref(null) const updatePassowrdRef = ref(null)
/** /**
* 修改密码 * 函数处理修改密码操作
*/ */
const updatePasswordHandle = () => { const updatePasswordHandle = () => {
updatePassowrdVisible.value = true updatePassowrdVisible.value = true //
nextTick(() => { nextTick(() => {
updatePassowrdRef.value?.init() updatePassowrdRef.value?.init() // DOM
}) })
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.site-navbar { .site-navbar {
display: flex; display: flex; // 使
align-items: center; align-items: center; //
background-color: #ffffff; background-color: #ffffff; //
color: #333333; color: #333333; //
border-bottom: 1px solid #ebedf0; border-bottom: 1px solid #ebedf0; //
.site-navbar-header { .site-navbar-header {
display: flex; display: flex; // 使
align-items: center; align-items: center; //
margin-left: 20px; margin-left: 20px; // 20px
color: #333; color: #333; //
font-weight: 700; font-weight: 700; // 700
height: 50px; height: 50px; // 50px
line-height: 50px; line-height: 50px; // 50px使
.site-navbar-lg { .site-navbar-lg {
font-size: 16px; font-size: 16px; // 16px
word-break: break-all; word-break: break-all; // URL
word-wrap: break-word; word-wrap: break-word; // URL
} }
.site-navbar-lg, .site-navbar-lg,
.site-navbar-mini { .site-navbar-mini {
margin: 0 5px; margin: 0 5px; // 5px
} }
} }
.site-navbar-content { .site-navbar-content {
flex: 1; flex: 1; //
width: 100%; width: 100%; //
display: flex; display: flex; // 使
justify-content: space-between; justify-content: space-between; //
padding: 0 20px; padding: 0 20px; // 20px
font-size: 18px; font-size: 18px; // 18px
align-items: center; align-items: center; //
.navbar-content-left { .navbar-content-left {
flex: 1; flex: 1; //
.left-item { .left-item {
cursor: pointer; cursor: pointer; //
} }
} }
.navbar-content-right { .navbar-content-right {
display: flex; display: flex; // 使
} }
} }
} }
//
//.menu-image-logo { //.menu-image-logo {
// object-fit: contain; // object-fit: contain; //
// height: 18px; // height: 18px; // 18px
// width: 59px; // width: 59px; // 59px
// margin-right: 10px; // margin-right: 10px; // 10px
//} //}
</style> </style>

@ -1,32 +1,39 @@
<template> <template>
<div class="menu-mod"> <div class="menu-mod">
<!-- 如果 expandMenu 包含子菜单项 (list) -->
<div v-if="expandMenu.list"> <div v-if="expandMenu.list">
<!-- 使用 el-sub-menu 创建一个可展开的菜单项 -->
<el-sub-menu <el-sub-menu
v-if="!item.hidden" v-if="!item.hidden"
:index="expandMenu.menuId + ''" :index="expandMenu.menuId + ''"
> >
<template #title> <template #title>
<!-- 菜单项标题 -->
<span <span
style="font-size: 14px" style="font-size: 14px"
:title="expandMenu.name.length > 4 ? expandMenu.name : ''" :title="expandMenu.name.length > 4 ? expandMenu.name : ''"
>{{ expandMenu.name }}</span> >{{ expandMenu.name }}</span>
</template> </template>
<!-- 使用 el-menu-item-group 包装子菜单项 -->
<el-menu-item-group class="menu-right-el-item"> <el-menu-item-group class="menu-right-el-item">
<template v-for="menu in expandMenu.list"> <template v-for="menu in expandMenu.list"> <!-- -->
<!-- 每个子菜单项 -->
<el-menu-item <el-menu-item
v-if="!menu.hidden" v-if="!menu.hidden"
:key="menu.menuId" :key="menu.menuId"
style=" style="
font-size: 14px !important; font-size: 14px !important; <!-- 设置字体大小为 14px -->
line-height: 40px; line-height: 40px;
padding-left: 30px !important; padding-left: 30px !important; <!-- 设置左侧内边距为 30px -->
padding-right: 10px !important; padding-right: 10px !important; <!-- 设置右侧内边距为 10px -->
" "
class="menu-right-el-item is-active item-text" class="menu-right-el-item is-active item-text"
:class="{ 'menu-active': selectRightId === menu.menuId }" :class="{ 'menu-active': selectRightId === menu.menuId }"
@click="gotoRouteHandle(menu)" @click="gotoRouteHandle(menu)"
> >
<span :title="menu.name.length > 4 ? menu.name : ''">{{ menu.name }}</span> <span :title="menu.name.length > 4 ? menu.name : ''">{{ menu.name }}</span> <!-- 显示子菜单名称 -->
<!-- 如果子菜单还有子菜单则递归渲染 SubMenuItem 组件 -->
<SubMenuItem <SubMenuItem
v-if="menu.list" v-if="menu.list"
:expand-menu="menu" :expand-menu="menu"
@ -36,7 +43,10 @@
</el-menu-item-group> </el-menu-item-group>
</el-sub-menu> </el-sub-menu>
</div> </div>
<!-- 如果 expandMenu 没有子菜单项 (list) -->
<div v-else> <div v-else>
<!-- 使用 el-menu-item 创建一个不可展开的菜单项 -->
<el-menu-item <el-menu-item
v-if="!expandMenu.hidden" v-if="!expandMenu.hidden"
:key="expandMenu.menuId" :key="expandMenu.menuId"
@ -45,81 +55,100 @@
:class="{ 'menu-active': selectRightId === expandMenu.menuId }" :class="{ 'menu-active': selectRightId === expandMenu.menuId }"
@click="gotoRouteHandle(expandMenu)" @click="gotoRouteHandle(expandMenu)"
> >
<span :title="expandMenu.name.length > 4 ? expandMenu.name : ''">{{ <span :title="expandMenu.name.length > 4 ? expandMenu.name : ''">{{ expandMenu.name }}</span> <!-- 显示菜单名称 -->
expandMenu.name
}}</span>
</el-menu-item> </el-menu-item>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
//
import SubMenuItem from './main-sidebar-sub-menu-item.vue' import SubMenuItem from './main-sidebar-sub-menu-item.vue'
// (props)
const props = defineProps({ const props = defineProps({
expandMenu: { expandMenu: {
type: Object, type: Object, //
default: () => {} default: () => ({}) //
}, },
menuIndex: { menuIndex: {
type: String, type: String, //
default: '' default: '' //
} }
}) })
// 使 ref props.expandMenu
const item = ref(props.expandMenu) const item = ref(props.expandMenu)
// Vuex store
const commonStore = useCommonStore()
// ID
const selectRightId = computed(() => commonStore.selectRightId) const selectRightId = computed(() => commonStore.selectRightId)
const commonStore = useCommonStore() // Vue Router
const router = useRouter() const router = useRouter()
//
// routeHandle
watch( watch(
() => router.currentRoute, () => router.currentRoute.value, //
route => { route => {
routeHandle(route) routeHandle(route) // routeHandle
} }
) )
//
//
const routeHandle = route => { const routeHandle = route => {
if (route.meta.isTab) { if (route.meta.isTab) { // isTab
commonStore.updateSelectRightId(route.meta.menuId || '') commonStore.updateSelectRightId(route.meta.menuId || '') // ID
} }
} }
// menuId () // menuId ()
const gotoRouteHandle = menu => { const gotoRouteHandle = menu => {
if (router.currentRoute.value.name === menu.url) { if (router.currentRoute.value.name === menu.url) { //
return return
} }
router.push({ name: menu.url }) router.push({ name: menu.url }) //
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.menu-mod { .menu-mod {
/* 定义右侧菜单项的基本样式 */
.menu-right-el-item { .menu-right-el-item {
/* 当菜单项处于激活状态时的样式 */
&.is-active { &.is-active {
background-color: #ffffff; background-color: #ffffff; /* 背景颜色为白色 */
color: #333; color: #333; /* 文本颜色为深灰色 */
} }
/* 自定义激活状态下的样式 */
&.menu-active { &.menu-active {
background-color: #e7eefb; background-color: #e7eefb; /* 背景颜色为浅蓝色 */
color: #155bd4; color: #155bd4; /* 文本颜色为蓝色 */
} }
/* 修改 el-menu-item-group 组件中标题的内边距 */
:deep(.el-menu-item-group__title) { :deep(.el-menu-item-group__title) {
padding: 0; padding: 0; /* 移除默认内边距 */
} }
} }
/* 定义子菜单标题的样式 */
div .el-sub-menu__title span { div .el-sub-menu__title span {
display: inline-block; display: inline-block; /* 设置为行内块元素 */
width: 85px; width: 85px; /* 固定宽度为 85px */
font-size: 14px; font-size: 14px; /* 字体大小为 14px */
overflow: hidden; overflow: hidden; /* 隐藏溢出的内容 */
text-overflow: ellipsis; text-overflow: ellipsis; /* 溢出部分用省略号表示 */
white-space: nowrap; white-space: nowrap; /* 强制在同一行显示 */
} }
/* 定义子菜单项中的文本样式 */
.el-sub-menu .menu-right-el-item.el-menu-item span { .el-sub-menu .menu-right-el-item.el-menu-item span {
overflow: hidden; overflow: hidden; /* 隐藏溢出的内容 */
text-overflow: ellipsis; text-overflow: ellipsis; /* 溢出部分用省略号表示 */
white-space: nowrap; white-space: nowrap; /* 强制在同一行显示 */
} }
} }
</style> </style>

@ -1,9 +1,12 @@
<template> <template>
<!-- 使用 el-scrollbar 创建一个具有滚动功能的容器 -->
<el-scrollbar class="menu-right-el"> <el-scrollbar class="menu-right-el">
<!-- 使用 el-menu 组件创建一个垂直菜单 -->
<el-menu <el-menu
class="el-menu-vertical-demo" class="el-menu-vertical-demo"
:default-openeds="openeds" :default-openeds="openeds"
> >
<!-- 使用 v-for 指令遍历 expandMenu 数组动态生成子菜单项 -->
<SubMenuItem <SubMenuItem
v-for="(item, index) in expandMenu" v-for="(item, index) in expandMenu"
:key="index" :key="index"
@ -14,28 +17,37 @@
</template> </template>
<script setup> <script setup>
//
import SubMenuItem from './main-sidebar-sub-menu-item.vue' import SubMenuItem from './main-sidebar-sub-menu-item.vue'
// (props)
defineProps({ defineProps({
expandMenu: { expandMenu: {
type: Array, type: Array, //
default: () => [] default: () => [] //
} }
}) })
// Vuex store
const commonStore = useCommonStore() const commonStore = useCommonStore()
const openeds = computed(() => commonStore.menuIds)
// ID
const openeds = computed(() => commonStore.menuIds) // store ID
</script> </script>
<style scoped> <style scoped>
/* 定义右侧菜单容器样式 */
.menu-right-el { .menu-right-el {
background-color: #fff; background-color: #fff; /* 设置背景颜色为白色 */
width: 130px !important; width: 130px !important; /* 固定宽度为 130px并使用 !important 确保优先级 */
border-top: 1px solid #ebedf0; border-top: 1px solid #ebedf0; /* 添加顶部边框,颜色为浅灰色 */
height: calc(100vh - 50px); height: calc(100vh - 50px); /* 高度为视口高度减去 50px通常用于顶部导航栏 */
overflow-y: auto; overflow-y: auto; /* 当内容超出容器高度时启用垂直滚动条 */
} }
/* 定义垂直菜单样式的自定义类 */
.el-menu-vertical-demo { .el-menu-vertical-demo {
border: none; border: none; /* 移除默认的边框 */
height: 100%; height: 100%; /* 设置高度为 100%,确保菜单占满整个容器 */
} }
</style> </style>

@ -1,14 +1,17 @@
<template> <template>
<aside class="site-sidebar"> <aside class="site-sidebar">
<!-- 侧边栏容器 -->
<div class="menu-mod"> <div class="menu-mod">
<!-- 左侧菜单滚动容器 -->
<el-scrollbar class="menu-left"> <el-scrollbar class="menu-left">
<!-- 首页菜单项 -->
<ul> <ul>
<li> <li>
<div <div
:class="{ :class="{
'menu-left-active': selectLeftId === '', 'menu-left-active': selectLeftId === '', //
'menu-left-item1': 'language' === 'English', 'menu-left-item1': 'language' === 'English', // 使
'menu-left-item': 'language' !== 'English' 'menu-left-item': 'language' !== 'English' // 使
}" }"
@click="toHome()" @click="toHome()"
> >
@ -20,6 +23,8 @@
</div> </div>
</li> </li>
</ul> </ul>
<!-- 动态生成的菜单列表 -->
<ul> <ul>
<template <template
v-for="menu in menuList" v-for="menu in menuList"
@ -27,13 +32,14 @@
> >
<li <li
v-if="!menu.hidden" v-if="!menu.hidden"
class="menu-left-active" :class="{ 'menu-left-active': selectLeftId === menu.menuId }"
> >
<!-- 如果菜单有子菜单则显示可展开的菜单项 -->
<div <div
v-if="menu.list" v-if="menu.list"
:class="[ :class="[
'menu-left-item', 'menu-left-item',
{'menu-left-active': selectLeftId === menu.menuId} {'menu-left-active': selectLeftId === menu.menuId} //
]" ]"
@click="expandMenu(menu)" @click="expandMenu(menu)"
> >
@ -46,14 +52,16 @@
class="item-text" class="item-text"
:title="menu.name.length > 4 ? menu.name : ''" :title="menu.name.length > 4 ? menu.name : ''"
style="font-size: 14px" style="font-size: 14px"
>{{ menu.name }}</span> >{{ menu.name }}</span> //
</div> </div>
<!-- 如果菜单没有子菜单则直接链接到目标页面 -->
<div <div
v-else v-else
:class="{ :class="{
'menu-left-active': selectLeftId === menu.menuId, 'menu-left-active': selectLeftId === menu.menuId, //
'menu-left-item1': '语言' === 'English', 'menu-left-item1': '语言' === 'English', // 使
'menu-left-item': '语言' !== 'English' 'menu-left-item': '语言' !== 'English' // 使
}" }"
@click="gotoRouteHandle(menu)" @click="gotoRouteHandle(menu)"
> >
@ -67,12 +75,14 @@
class="item-text" class="item-text"
:title="menu.name.length > 4 ? menu.name : ''" :title="menu.name.length > 4 ? menu.name : ''"
style="font-size: 14px" style="font-size: 14px"
>{{ menu.name }}</span> >{{ menu.name }}</span> //
</div> </div>
</li> </li>
</template> </template>
</ul> </ul>
</el-scrollbar> </el-scrollbar>
<!-- 右侧子菜单组件仅当侧边栏未折叠时显示 -->
<SubMenu <SubMenu
v-if="!sidebarFold" v-if="!sidebarFold"
:key="selectLeftId" :key="selectLeftId"
@ -83,23 +93,32 @@
</aside> </aside>
</template> </template>
<script setup> <script setup>
import SubMenu from './main-sidebar-sub-menu.vue' import SubMenu from './main-sidebar-sub-menu.vue' //
const route = useRoute()
const router = useRouter() // Vue Vuex
const commonStore = useCommonStore() import { useRoute, useRouter } from 'vue-router'
const dynamicMenuRoutes = ref([]) import { computed, ref, onBeforeMount } from 'vue'
const expandMenuList = ref([]) import { useCommonStore } from '@/store'
// Vuex store
const route = useRoute() //
const router = useRouter() //
const commonStore = useCommonStore() //
//
const dynamicMenuRoutes = ref([]) //
const expandMenuList = ref([]) //
//
const sidebarFold = computed(() => commonStore.sidebarFold) const sidebarFold = computed(() => commonStore.sidebarFold)
//
const menuList = computed({ const menuList = computed({
get: () => { get: () => commonStore.menuList, // store
return commonStore.menuList set: val => commonStore.updateMenuList(val) // store
},
set: val => {
commonStore.updateMenuList(val)
}
}) })
// ID
const selectLeftId = computed({ const selectLeftId = computed({
get: () => { get: () => {
handleRightRoute(commonStore.selectLeftId) handleRightRoute(commonStore.selectLeftId)
@ -107,32 +126,41 @@ const selectLeftId = computed({
} }
}) })
//
onBeforeMount(() => { onBeforeMount(() => {
// sessionStorage
menuList.value = JSON.parse(sessionStorage.getItem('menuList') || '[]') menuList.value = JSON.parse(sessionStorage.getItem('menuList') || '[]')
dynamicMenuRoutes.value = JSON.parse(sessionStorage.getItem('dynamicMenuRoutes') || '[]') dynamicMenuRoutes.value = JSON.parse(sessionStorage.getItem('dynamicMenuRoutes') || '[]')
//
routeHandle(route) routeHandle(route)
// ID
if (selectLeftId.value) { if (selectLeftId.value) {
handleRightRoute(selectLeftId.value) handleRightRoute(selectLeftId.value)
} }
}) })
//
const handleRightRoute = selectLeftId => { const handleRightRoute = selectLeftId => {
menuList.value.forEach(item => { menuList.value.forEach(item => {
if (selectLeftId === item.menuId) { if (selectLeftId === item.menuId) {
expandMenu(item, true) expandMenu(item, true) //
} }
}) })
} }
//
const toHome = () => { const toHome = () => {
router.push({ name: 'home' }) router.push({ name: 'home' }) //
expandMenuList.value = [] expandMenuList.value = [] //
sessionStorage.setItem('isExpand', '0') sessionStorage.setItem('isExpand', '0') //
commonStore.updateSidebarFold(true) commonStore.updateSidebarFold(true) //
commonStore.updateSelectLeftId('') commonStore.updateSelectLeftId('') // ID
commonStore.updateSelectRightId('') commonStore.updateSelectRightId('') // ID
} }
//
const routeHandle = route => { const routeHandle = route => {
if (route.name === 'home') { if (route.name === 'home') {
expandMenuList.value = [] expandMenuList.value = []
@ -142,7 +170,9 @@ const routeHandle = route => {
} }
} }
//
const gotoRouteHandle = menu => { const gotoRouteHandle = menu => {
//
if (router.history.current.name === menu.url) { if (router.history.current.name === menu.url) {
expandMenuList.value = [] expandMenuList.value = []
commonStore.updateSidebarFold(true) commonStore.updateSidebarFold(true)
@ -151,10 +181,13 @@ const gotoRouteHandle = menu => {
commonStore.updateSelectLeftId(menu.menuId || '') commonStore.updateSelectLeftId(menu.menuId || '')
return return
} }
//
if (menu.name === '消息' || menu.name === 'Message') { if (menu.name === '消息' || menu.name === 'Message') {
sessionStorage.setItem('isExpand', '0') sessionStorage.setItem('isExpand', '0')
window.open(location.href.split('#')[0] + '#/imBox', '_blank', 'noopener,noreferrer') window.open(location.href.split('#')[0] + '#/imBox', '_blank', 'noopener,noreferrer')
} else { } else {
//
expandMenuList.value = [] expandMenuList.value = []
commonStore.updateSidebarFold(true) commonStore.updateSidebarFold(true)
sessionStorage.setItem('isExpand', '0') sessionStorage.setItem('isExpand', '0')
@ -164,29 +197,36 @@ const gotoRouteHandle = menu => {
} }
} }
//
const expandMenu = menu => { const expandMenu = menu => {
expandMenuList.value = menu.list || [] expandMenuList.value = menu.list || [] //
commonStore.updateSidebarFold(menu.list === null) commonStore.updateSidebarFold(menu.list === null) //
const id1 = commonStore.selectLeftId const id1 = commonStore.selectLeftId // ID
commonStore.updateSelectLeftId(menu.menuId || '') commonStore.updateSelectLeftId(menu.menuId || '') // ID
const id2 = commonStore.selectLeftId const id2 = commonStore.selectLeftId // ID
if (menu.list) { if (menu.list) {
sessionStorage.setItem('isExpand', '1') sessionStorage.setItem('isExpand', '1') //
} }
// ID
if (id1 !== id2) { if (id1 !== id2) {
routeJump(menu) routeJump(menu)
} }
} }
//
const routeJump = menu => { const routeJump = menu => {
const routes = menu.list const routes = menu.list
for (let i = 0; i < routes.length; i++) { for (let i = 0; i < routes.length; i++) {
//
if (!routes[i].hidden && !routes[i].list) { if (!routes[i].hidden && !routes[i].list) {
router.push({ name: routes[i].url }) router.push({ name: routes[i].url })
break break
} else if (routes[i].list) { } else if (routes[i].list) {
let flag = false let flag = false
for (let j = 0; j < routes[i].list.length; j++) { for (let j = 0; j < routes[i].list.length; j++) {
//
if (!routes[i].list[j].hidden) { if (!routes[i].list[j].hidden) {
router.push({ name: routes[i].list[j].url }) router.push({ name: routes[i].list[j].url })
flag = true flag = true
@ -202,94 +242,117 @@ const routeJump = menu => {
</script> </script>
<style scoped> <style scoped>
/* 定义菜单模块的整体布局 */
.menu-mod { .menu-mod {
display: flex; display: flex; /* 使用 Flexbox 布局,使子元素可以灵活排列 */
} }
/* 定义右侧内容容器的位置和层级 */
.menu-right-con { .menu-right-con {
position: absolute; position: absolute; /* 设置为绝对定位 */
z-index: 1; z-index: 1; /* 设置堆叠顺序,确保在其他元素之上 */
left: 100px; left: 100px; /* 左边距为 100px */
} }
/* 定义左侧菜单栏样式 */
.menu-left { .menu-left {
background: #222222; background: #222222; /* 背景颜色为深灰色 */
color: #ffffff !important; color: #ffffff !important; /* 文本颜色为白色,并使用 !important 确保优先级 */
width: 100px; width: 100px; /* 固定宽度为 100px */
height: calc(100vh - 50px); height: calc(100vh - 50px); /* 高度为视口高度减去 50px通常用于顶部导航栏 */
overflow-y: auto; overflow-y: auto; /* 当内容超出容器高度时启用垂直滚动条 */
} }
/* 定义左侧菜单项的基本样式 */
.menu-mod .menu-left-item { .menu-mod .menu-left-item {
width: 100%; width: 100%; /* 占满整个父容器宽度 */
height: 100%; height: 100%; /* 占满整个父容器高度 */
display: flex; display: flex; /* 使用 Flexbox 布局 */
align-items: center; align-items: center; /* 水平居中对齐 */
justify-content: center; justify-content: center; /* 垂直居中对齐 */
} }
/* 定义左侧菜单项的另一种样式(可能是不同状态或类型的菜单项) */
.menu-mod .menu-left-item1 { .menu-mod .menu-left-item1 {
width: 100%; width: 100%; /* 占满整个父容器宽度 */
height: 100%; height: 100%; /* 占满整个父容器高度 */
display: flex; display: flex; /* 使用 Flexbox 布局 */
align-items: center; align-items: center; /* 水平居中对齐 */
text-align: left; text-align: left; /* 文本左对齐 */
padding-left: 12px; padding-left: 12px; /* 左内边距为 12px */
} }
/* 定义左侧菜单的无序列表样式 */
.menu-mod .menu-left ul { .menu-mod .menu-left ul {
list-style-type: none; list-style-type: none; /* 移除默认的列表样式 */
margin: 0; margin: 0; /* 移除外边距 */
padding: 0; padding: 0; /* 移除内边距 */
width: 100px; width: 100px; /* 固定宽度为 100px */
text-align: center; text-align: center; /* 文本居中对齐 */
} }
/* 定义右侧菜单的无序列表样式 */
.menu-mod .menu-right ul { .menu-mod .menu-right ul {
list-style-type: none; list-style-type: none; /* 移除默认的列表样式 */
margin: 0; margin: 0; /* 移除外边距 */
padding: 0; padding: 0; /* 移除内边距 */
width: 130px; width: 130px; /* 固定宽度为 130px */
text-align: center; text-align: center; /* 文本居中对齐 */
} }
/* 定义左侧菜单项的样式 */
.menu-left li { .menu-left li {
background: #222222; background: #222222; /* 背景颜色为深灰色 */
color: #fff; color: #fff; /* 文本颜色为白色 */
height: 40px; height: 40px; /* 固定高度为 40px */
cursor: pointer; cursor: pointer; /* 鼠标悬停时显示指针样式 */
font-size: 14px; font-size: 14px; /* 字体大小为 14px */
stroke: #fff !important; stroke: #fff !important; /* SVG 图标颜色为白色,并使用 !important 确保优先级 */
} }
/* 定义右侧菜单项的样式 */
.menu-right li { .menu-right li {
height: 40px; height: 40px; /* 固定高度为 40px */
display: flex; display: flex; /* 使用 Flexbox 布局 */
align-items: center; align-items: center; /* 水平居中对齐 */
justify-content: center; justify-content: center; /* 垂直居中对齐 */
cursor: pointer; cursor: pointer; /* 鼠标悬停时显示指针样式 */
font-size: 14px; font-size: 14px; /* 字体大小为 14px */
color: #333333; color: #333333; /* 文本颜色为深灰色 */
} }
/* 鼠标移动到选项上修改背景颜色 */ /* 定义左侧菜单项悬停时的样式 */
.menu-left li:hover { .menu-left li:hover {
background-color: #ffffff; background-color: #ffffff; /* 背景颜色变为白色 */
color: #155bd4; color: #155bd4; /* 文本颜色变为蓝色 */
stroke: #155bd4 !important; stroke: #155bd4 !important;/* SVG 图标颜色变为蓝色,并使用 !important 确保优先级 */
} }
/* 鼠标移动到选项上修改背景颜色 */
/* 定义右侧菜单项悬停时的样式 */
.menu-right li:hover { .menu-right li:hover {
background-color: rgba(21, 91, 212, 0.1); background-color: rgba(21, 91, 212, 0.1); /* 背景颜色变为浅蓝色透明 */
color: #155bd4; color: #155bd4; /* 文本颜色变为蓝色 */
} }
/* 冗余定义,应删除 */
.menu-right li:hover { .menu-right li:hover {
background-color: rgba(21, 91, 212, 0.1); background-color: rgba(21, 91, 212, 0.1);
color: #155bd4; color: #155bd4;
} }
/* 定义左侧菜单项激活状态的样式 */
.menu-left-active { .menu-left-active {
background-color: #ffffff; background-color: #ffffff; /* 背景颜色变为白色 */
color: #155bd4; color: #155bd4; /* 文本颜色变为蓝色 */
stroke: #155bd4 !important; stroke: #155bd4 !important;/* SVG 图标颜色变为蓝色,并使用 !important 确保优先级 */
} }
/* 定义菜单项文本的样式 */
.item-text { .item-text {
overflow: hidden; overflow: hidden; /* 隐藏溢出的内容 */
text-overflow: ellipsis; text-overflow: ellipsis; /* 溢出部分用省略号表示 */
white-space: nowrap; white-space: nowrap; /* 强制在同一行显示 */
display: inline-block; display: inline-block; /* 显示为内联块元素 */
max-width: 70px; max-width: 70px; /* 最大宽度为 70px */
} }
</style> </style>

@ -1,59 +1,84 @@
<template> <template>
<!-- 主应用布局容器 -->
<div <div
v-loading.fullscreen.lock="loading" v-loading.fullscreen.lock="loading"
class="site-wrapper" class="site-wrapper"
:class="{ 'site-sidebar--fold': sidebarFold }" :class="{ 'site-sidebar--fold': sidebarFold }"
:element-loading-text="'拼命加载中'" :element-loading-text="'拼命加载中'"
> >
<!-- 条件渲染只有当 loading false 时才渲染内部组件 -->
<template v-if="!loading"> <template v-if="!loading">
<MainNavbar /> <!-- 导航栏组件 -->
<MainSidebar /> <MainNavbar /> <!-- 渲染主导航栏组件通常包含导航链接用户信息等 -->
<!-- 侧边栏组件 -->
<MainSidebar /> <!-- 渲染主侧边栏组件通常包含菜单选项或快捷方式 -->
<!-- 内容区容器 -->
<div <div
class="site-content__wrapper" class="site-content__wrapper"
:style="{ 'min-height': documentClientHeight + 'px' }" :style="{ 'min-height': documentClientHeight + 'px' }"
> >
<main-content /> <!-- 主要内容组件 -->
<main-content /> <!-- 渲染主要内容组件这里是页面的主要内容展示区域 -->
</div> </div>
</template> </template>
</div> </div>
</template> </template>
<script setup> <script setup>
import MainNavbar from './main-navbar.vue' import { ref, computed, onBeforeMount, onMounted } from 'vue'
import MainSidebar from './main-sidebar.vue' import http from '@/utils/http' // HTTP
import MainContent from './main-content.vue' import MainNavbar from './main-navbar.vue' //
import MainSidebar from './main-sidebar.vue' //
import MainContent from './main-content.vue' //
import { useCommonStore, useUserStore } from '@/store' // Vuex store
//
onBeforeMount(() => { onBeforeMount(() => {
getUserInfo() getUserInfo() // getUserInfo
}) })
const commonStore = useCommonStore() const commonStore = useCommonStore() // Store
const documentClientHeight = computed(() => commonStore.documentClientHeight) const documentClientHeight = computed(() => //
const userStore = useUserStore() commonStore.documentClientHeight // commonStore
const sidebarFold = computed(() => commonStore.sidebarFold) )
const userStore = useUserStore() // Store
const sidebarFold = computed(() => //
commonStore.sidebarFold // commonStore
)
//
onMounted(() => { onMounted(() => {
resetDocumentClientHeight() resetDocumentClientHeight() // resetDocumentClientHeight
}) })
//
const resetDocumentClientHeight = () => { const resetDocumentClientHeight = () => {
commonStore.documentClientHeight = document.documentElement.clientHeight commonStore.documentClientHeight = document.documentElement.clientHeight //
window.onresize = () => { window.onresize = () => { //
commonStore.documentClientHeight = document.documentElement.clientHeight commonStore.documentClientHeight = document.documentElement.clientHeight //
} }
} }
const loading = ref(true) //
// const loading = ref(true) // loading true
//
const getUserInfo = () => { const getUserInfo = () => {
http({ http({
url: http.adornUrl('/sys/user/info'), url: http.adornUrl('/sys/user/info'), // API 使 http.adornUrl URL
method: 'get', method: 'get', // GET
params: http.adornParams() params: http.adornParams() // 使 http.adornParams
}).then(({ data }) => { }).then(({ data }) => {
loading.value = false loading.value = false //
userStore.userId = data.userId userStore.userId = data.userId // ID userStore
userStore.name = data.username userStore.name = data.username // userStore
userStore.mobile = data.mobile userStore.mobile = data.mobile // userStore
userStore.shopId = data.shopId userStore.shopId = data.shopId // ID userStore
userStore.userId = data.userId // userStore.userId = data.userId
}).catch(() => {}) }).catch(() => {
//
})
} }
</script> </script>

@ -5,67 +5,70 @@ import { clearLoginInfo } from '@/utils'
import Layout from '@/layout/main.vue' import Layout from '@/layout/main.vue'
import Login from '@/views/common/login/index.vue' import Login from '@/views/common/login/index.vue'
// 全局路由(无需嵌套上左右整体布局) // 定义全局路由(无需嵌套在左右整体布局中)
const globalRoutes = [ const globalRoutes = [
{ {
path: '/404', path: '/404', // 404 页面路径
component: () => import('@/views/common/error-page/404.vue'), component: () => import('@/views/common/error-page/404.vue'), // 动态导入 404 页面组件
name: '404', name: '404', // 路由名称
meta: { title: '404未找到' } meta: { title: '404未找到' } // 元信息,用于设置页面标题等
}, },
{ {
path: '/login', path: '/login', // 登录页面路径
component: Login, component: Login, // 登录页面组件
name: 'login', name: 'login', // 路由名称
meta: { title: '登录' } meta: { title: '登录' } // 元信息,用于设置页面标题等
} }
] ]
// 定义主路由(需要嵌套在 Layout 组件中)
export const mainRoutes = { export const mainRoutes = {
path: '/', path: '/', // 根路径
component: Layout, component: Layout, // 布局组件
name: 'home', name: 'home', // 路由名称
redirect: '/home', redirect: '/home', // 默认重定向到 home 页面
children: [ children: [ // 子路由
{ {
path: 'home', path: 'home', // 主页路径
name: 'home', name: 'home', // 路由名称
component: () => import('@/views/common/home/index.vue') component: () => import('@/views/common/home/index.vue') // 动态导入主页组件
}, },
{ {
path: '/prodInfo', path: '/prodInfo', // 产品信息页面路径
name: 'prodInfo', name: 'prodInfo', // 路由名称
component: () => import('@/views/modules/prod/prodInfo/index.vue') component: () => import('@/views/modules/prod/prodInfo/index.vue') // 动态导入产品信息页面组件
} }
], ],
// eslint-disable-next-line no-unused-vars beforeEnter (to, from, next) { // 路由守卫,检查用户是否已登录
beforeEnter (to, from, next) { const authorization = cookie.get('Authorization') // 获取存储在 cookie 中的授权信息
const authorization = cookie.get('Authorization') if (!authorization || !/\S/.test(authorization)) { // 如果没有授权信息或为空
if (!authorization || !/\S/.test(authorization)) { clearLoginInfo() // 清除登录信息
clearLoginInfo() next({ name: 'login' }) // 重定向到登录页面
next({ name: 'login' })
} }
next() next() // 否则继续导航
} }
} }
// 创建 Vue Router 实例
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(), // 使用 HTML5 History 模式
scrollBehavior: () => ({ top: 0 }), scrollBehavior: () => ({ top: 0 }), // 设置滚动行为,每次导航回到顶部
isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由 isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由
routes: globalRoutes.concat(mainRoutes) routes: globalRoutes.concat(mainRoutes) // 合并全局路由和主路由
}) })
// eslint-disable-next-line no-unused-vars // 全局前置守卫,拦截所有路由导航
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
const commonStore = useCommonStore() const commonStore = useCommonStore() // 初始化 Vuex store 实例
// 添加动态(菜单)路由
// 1. 已经添加 or 全局路由, 直接访问 // 添加动态(菜单)路由逻辑
// 2. 获取菜单列表, 添加并保存本地存储
if (router.options.isAddDynamicMenuRoutes || fnCurrentRouteType(to, globalRoutes) === 'global') { if (router.options.isAddDynamicMenuRoutes || fnCurrentRouteType(to, globalRoutes) === 'global') {
const routeList = commonStore.routeList // 已经添加动态路由或访问的是全局路由,直接处理导航
let navTitles = [] const routeList = commonStore.routeList // 获取路由列表
let leftMenuId = '' let navTitles = [] // 用于存储导航标题
let leftMenuId = '' // 用于存储左侧菜单 ID
// 查找当前路由对应的菜单项,并构建导航标题链
routeList.forEach(item => { routeList.forEach(item => {
if (to.meta.menuId === item.menuId) { if (to.meta.menuId === item.menuId) {
navTitles.push(item.name) navTitles.push(item.name)
@ -83,30 +86,37 @@ router.beforeEach((to, from, next) => {
}) })
} }
}) })
navTitles = navTitles.reverse()
navTitles = navTitles.reverse() // 反转导航标题链
// 更新 Vuex store 中的状态
if (to.meta.isLeftMenu || to.path === '/home' || leftMenuId) { if (to.meta.isLeftMenu || to.path === '/home' || leftMenuId) {
if (leftMenuId) { if (leftMenuId) {
commonStore.updateSelectLeftId(leftMenuId) commonStore.updateSelectLeftId(leftMenuId) // 更新选中的左侧菜单 ID
commonStore.updateSelectRightId(to.meta.menuId) commonStore.updateSelectRightId(to.meta.menuId) // 更新选中的右侧菜单 ID
} else { } else {
commonStore.updateSidebarFold(true) commonStore.updateSidebarFold(true) // 折叠侧边栏
commonStore.updateSelectLeftId(to.path === '/home' ? '' : to.meta.menuId) commonStore.updateSelectLeftId(to.path === '/home' ? '' : to.meta.menuId) // 更新选中的左侧菜单 ID
} }
} }
commonStore.updateSelectMenu(navTitles)
next() commonStore.updateSelectMenu(navTitles) // 更新选中的菜单标题链
next() // 继续导航
} else { } else {
// 请求菜单列表和权限信息
http({ http({
url: http.adornUrl('/sys/menu/nav'), url: http.adornUrl('/sys/menu/nav'), // 请求 URL
method: 'get', method: 'get', // 请求方法
params: http.adornParams() params: http.adornParams() // 请求参数
}).then(({ data }) => { }).then(({ data }) => {
sessionStorage.setItem('Authorities', JSON.stringify(data.authorities || '[]')) sessionStorage.setItem('Authorities', JSON.stringify(data.authorities || '[]')) // 存储权限信息到 sessionStorage
fnAddDynamicMenuRoutes(data.menuList) fnAddDynamicMenuRoutes(data.menuList) // 添加动态路由
router.options.isAddDynamicMenuRoutes = true router.options.isAddDynamicMenuRoutes = true // 标记动态路由已添加
// 构建完整的路由列表,并保存到 Vuex store 和 sessionStorage
const rList = [] const rList = []
data.menuList.forEach(item => { data.menuList.forEach(item => {
item.isLeftMenu = item.parentId === 0 item.isLeftMenu = item.parentId === 0 // 判断是否为左侧菜单
rList.push({ rList.push({
menuId: item.menuId, menuId: item.menuId,
name: item.name, name: item.name,
@ -136,81 +146,92 @@ router.beforeEach((to, from, next) => {
}) })
} }
}) })
fnAddDynamicMenuRoutes(data.menuList)
sessionStorage.setItem('menuList', JSON.stringify(data.menuList || '[]')) fnAddDynamicMenuRoutes(data.menuList) // 再次调用以确保所有子菜单被正确添加
commonStore.updateRouteList(rList) sessionStorage.setItem('menuList', JSON.stringify(data.menuList || '[]')) // 存储菜单列表到 sessionStorage
commonStore.updateMenuIds(rList) commonStore.updateRouteList(rList) // 更新 Vuex store 中的路由列表
commonStore.updateMenuIds(rList) // 更新 Vuex store 中的菜单 ID 列表
// 重新导航到目标页面
next({ ...to, replace: true }) next({ ...to, replace: true })
}).catch(e => { }).catch(e => {
// eslint-disable-next-line no-console console.log(`%c${e} 请求菜单列表和权限失败,跳转至登录页!!`, 'color:blue') // 打印错误信息
console.log(`%c${e} 请求菜单列表和权限失败,跳转至登录页!!`, 'color:blue') router.push({ name: 'login' }) // 跳转到登录页面
router.push({ name: 'login' })
}) })
} }
}) })
/** /**
* 判断当前路由类型, global: 全局路由, main: 主入口路由 * 判断当前路由类型返回 'global' 'main'
* @param {*} route 当前路由 * @param {*} route 当前路由对象
* @param globalRoutes 全局路由 * @param globalRoutes 全局路由数组
*/ */
function fnCurrentRouteType (route, globalRoutes = []) { function fnCurrentRouteType (route, globalRoutes = []) {
let temp = [] let temp = [] // 临时存储子路由
for (let i = 0; i < globalRoutes.length; i++) { for (let i = 0; i < globalRoutes.length; i++) {
if (route.path === globalRoutes[i].path) { if (route.path === globalRoutes[i].path) {
return 'global' return 'global' // 如果匹配到全局路由,返回 'global'
} else if (globalRoutes[i].children && globalRoutes[i].children.length >= 1) { } else if (globalRoutes[i].children && globalRoutes[i].children.length >= 1) {
temp = temp.concat(globalRoutes[i].children) temp = temp.concat(globalRoutes[i].children) // 将子路由添加到临时数组
} }
} }
return temp.length >= 1 ? fnCurrentRouteType(route, temp) : 'main' return temp.length >= 1 ? fnCurrentRouteType(route, temp) : 'main' // 递归查找子路由,直到找到匹配项或返回 'main'
} }
/** /**
* 添加动态(菜单)路由 * 添加动态(菜单)路由
* @param {*} menuList 菜单列表 * @param {*} menuList 菜单列表
* @param {*} routes 递归创建的动态(菜单)路由 * @param {*} routes 递归创建的动态(菜单)路由数组
*/ */
function fnAddDynamicMenuRoutes (menuList = [], routes = []) { function fnAddDynamicMenuRoutes (menuList = [], routes = []) {
let temp = [] let temp = [] // 临时存储子菜单项
const modules = import.meta.glob('../views/modules/**/index.vue') const modules = import.meta.glob('../views/modules/**/index.vue') // 动态导入视图模块
// 遍历菜单列表,创建对应的路由对象
for (let i = 0; i < menuList.length; i++) { for (let i = 0; i < menuList.length; i++) {
if (menuList[i].list && menuList[i].list.length >= 1) { if (menuList[i].list && menuList[i].list.length >= 1) {
temp = temp.concat(menuList[i].list) temp = temp.concat(menuList[i].list) // 如果有子菜单,添加到临时数组
} else if (menuList[i].url && /\S/.test(menuList[i].url)) { } else if (menuList[i].url && /\S/.test(menuList[i].url)) {
menuList[i].url = menuList[i].url.replace(/^\//, '') menuList[i].url = menuList[i].url.replace(/^\//, '') // 移除 URL 开头的斜杠
const route = { const route = {
path: menuList[i].url, path: menuList[i].url, // 路径
component: null, component: null, // 组件
name: menuList[i].url, name: menuList[i].url, // 名称
meta: { meta: {
menuId: menuList[i].menuId, menuId: menuList[i].menuId, // 菜单项 ID
title: menuList[i].name, title: menuList[i].name, // 标题
isDynamic: true, isDynamic: true, // 标记为动态路由
isTab: true, isTab: true, // 标记为标签页
iframeUrl: '' iframeUrl: '' // iframe URL默认为空
} }
} }
// url以http[s]://开头, 通过iframe展示
// 如果 URL 是有效的 HTTP[S] 地址,则通过 iframe 展示
if (isURL(menuList[i].url)) { if (isURL(menuList[i].url)) {
route.path = `i-${menuList[i].menuId}` route.path = `i-${menuList[i].menuId}` // 修改路径为 iframe 特定格式
route.name = `i-${menuList[i].menuId}` route.name = `i-${menuList[i].menuId}` // 修改名称为 iframe 特定格式
route.meta.iframeUrl = menuList[i].url route.meta.iframeUrl = menuList[i].url // 设置 iframe URL
} else { } else {
try { try {
route.component = modules[`../views/modules/${menuList[i].url}/index.vue`] || null route.component = modules[`../views/modules/${menuList[i].url}/index.vue`] || null // 动态加载组件
} catch (e) {} } catch (e) {}
} }
routes.push(route)
routes.push(route) // 将路由对象添加到 routes 数组
} }
} }
// 如果还有子菜单项,递归处理
if (temp.length >= 1) { if (temp.length >= 1) {
fnAddDynamicMenuRoutes(temp, routes) fnAddDynamicMenuRoutes(temp, routes)
} else { } else {
mainRoutes.name = 'main-dynamic' mainRoutes.name = 'main-dynamic' // 修改主路由名称为 'main-dynamic'
mainRoutes.children = routes mainRoutes.children = routes // 将动态路由添加为主路由的子路由
router.addRoute(mainRoutes) router.addRoute(mainRoutes) // 添加主路由到 Vue Router 实例
} }
// 添加一个通配符路由,处理未匹配的路径,重定向到 404 页面
router.addRoute({ path: '/:pathMatch(.*)*', redirect: { name: '404' } }) router.addRoute({ path: '/:pathMatch(.*)*', redirect: { name: '404' } })
} }
export default router
export default router // 导出配置好的 Vue Router 实例

@ -1,80 +1,116 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import router from '@/router' import router from '@/router'
// 定义名为 'common' 的 Pinia Store
export const useCommonStore = defineStore('common', { export const useCommonStore = defineStore('common', {
state: () => { // 状态 (state) - 存储应用的数据
return { state: () => ({
// 页面文档可视高度(随窗口改变大小) // 页面文档可视高度(随窗口改变大小)
documentClientHeight: 0, documentClientHeight: 0,
// 侧边栏, 布局皮肤, light(浅色) / dark(黑色) // 侧边栏布局皮肤, light(浅色) / dark(黑色)
sidebarLayoutSkin: 'dark', sidebarLayoutSkin: 'dark',
// 侧边栏, 折叠状态 // 侧边栏折叠状态
sidebarFold: true, sidebarFold: true,
// 侧边栏, 菜单 // 侧边栏菜单列表
menuList: [], menuList: [],
// 当前激活的菜单名称
menuActiveName: '', menuActiveName: '',
// 主入口标签页 // 主入口标签页列表
mainTabs: [], mainTabs: [],
// 当前激活的主入口标签页名称
mainTabsActiveName: '', mainTabsActiveName: '',
// 当前选择的标签 // 当前选择的标签链(用于面包屑导航)
selectMenu: [], selectMenu: [],
// 路由列表 // 路由列表
routeList: [], routeList: [],
// 菜单项 ID 列表
menuIds: [], menuIds: [],
// 当前选中的左侧菜单 ID
selectLeftId: '', selectLeftId: '',
// 当前选中的右侧菜单 ID
selectRightId: '' selectRightId: ''
} }),
},
// 动作 (actions) - 包含更改状态的逻辑
actions: { actions: {
// 更新页面文档可视高度
updateDocumentClientHeight(height) { updateDocumentClientHeight(height) {
this.documentClientHeight = height this.documentClientHeight = height;
}, },
// 更新侧边栏折叠状态
updateSidebarFold(fold) { updateSidebarFold(fold) {
this.sidebarFold = fold this.sidebarFold = fold;
}, },
// 更新侧边栏菜单列表
updateMenuList(list) { updateMenuList(list) {
this.menuList = list this.menuList = list;
}, },
// 更新当前激活的菜单名称
updateMenuActiveName(name) { updateMenuActiveName(name) {
this.menuActiveName = name this.menuActiveName = name;
}, },
// 更新主入口标签页列表
updateMainTabs(tabs) { updateMainTabs(tabs) {
this.mainTabs = tabs this.mainTabs = tabs;
}, },
// 更新当前激活的主入口标签页名称
updateMainTabsActiveName(name) { updateMainTabsActiveName(name) {
this.mainTabsActiveName = name this.mainTabsActiveName = name;
}, },
// 更新路由列表
updateRouteList(list) { updateRouteList(list) {
this.routeList = list this.routeList = list;
}, },
// 更新当前选择的标签链(用于面包屑导航)
updateSelectMenu(list) { updateSelectMenu(list) {
this.selectMenu = list this.selectMenu = list;
}, },
// 更新当前选中的左侧菜单 ID
updateSelectLeftId(id) { updateSelectLeftId(id) {
this.selectLeftId = id this.selectLeftId = id;
}, },
// 更新当前选中的右侧菜单 ID
updateSelectRightId(id) { updateSelectRightId(id) {
this.selectRightId = id this.selectRightId = id;
}, },
// 替换当前选择的标签链的最后一项
replaceSelectMenu(title) { replaceSelectMenu(title) {
this.selectMenu.splice(this.selectMenu.length - 1, 1, title) this.selectMenu.splice(this.selectMenu.length - 1, 1, title);
}, },
// 更新菜单项 ID 列表,确保所有 ID 是字符串形式
updateMenuIds(list) { updateMenuIds(list) {
this.menuIds = [] this.menuIds = [];
list.forEach(menu => { list.forEach(menu => {
this.menuIds.push(String(menu.menuId + '')) this.menuIds.push(String(menu.menuId + ''));
}) });
}, },
// 移除当前激活的主入口标签页,并处理相关逻辑
removeMainActiveTab() { removeMainActiveTab() {
this.mainTabs = this.mainTabs.filter(item => item.name !== this.mainTabsActiveName) // 过滤掉当前激活的标签页
this.mainTabs = this.mainTabs.filter(item => item.name !== this.mainTabsActiveName);
if (this.mainTabs.length >= 1) { if (this.mainTabs.length >= 1) {
// 当前选中tab被删除 // 如果还有其他标签页,则切换到最后一个标签页
router.push({ name: this.mainTabs[this.mainTabs.length - 1].name }, () => { router.push({ name: this.mainTabs[this.mainTabs.length - 1].name }, () => {
this.mainTabsActiveName = this.mainTabs[this.mainTabs.length - 1].name this.mainTabsActiveName = this.mainTabs[this.mainTabs.length - 1].name;
}) });
} else { } else {
this.menuActiveName = '' // 如果没有其他标签页,则重置菜单激活名称并跳转到主页
router.push({ name: 'home' }) this.menuActiveName = '';
router.push({ name: 'home' });
} }
} }
} }
}) });

@ -1,37 +1,53 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
// 定义名为 'prod' 的 Pinia Store用于管理商品相关数据和操作
export const scoreProdStore = defineStore('prod', { export const scoreProdStore = defineStore('prod', {
state: () => { // 状态 (state) - 存储商品相关的数据
return { state: () => ({
id: 0, id: 0, // 商品 ID
skuTags: [], skuTags: [], // SKU 标签列表,每个标签代表一个销售属性(如颜色、尺寸等)
defalutSku: { defalutSku: {
price: 0, // 销售价 price: 0, // 销售价
oriPrice: 0, // 市场价 oriPrice: 0, // 市场价(原价)
stocks: 0, // 库存 stocks: 0, // 库存数量
properties: '', // 销售属性组合字符串 properties: '', // 销售属性组合字符串,例如 "红色;M号"
skuName: '', // sku名称 skuName: '', // SKU 名称,具体到某一销售属性组合的商品名称
prodName: '', // 商品名称 prodName: '', // 商品名称,通用名称
weight: 0, // 商品重量 weight: 0, // 商品重量
volume: 0, // 商品体积 volume: 0, // 商品体积
status: 1 // 0 禁用 1 启用 status: 1 // SKU 状态0 表示禁用1 表示启用
} }
} }),
},
// 动作 (actions) - 包含更改状态的逻辑
actions: { actions: {
// 更新 SKU 标签列表
updateSkuTags(skuTags) { updateSkuTags(skuTags) {
this.skuTags = skuTags this.skuTags = skuTags;
}, },
// 添加新的 SKU 标签
addSkuTag(skuTag) { addSkuTag(skuTag) {
this.skuTags.push(skuTag) this.skuTags.push(skuTag);
}, },
// 移除指定索引的 SKU 标签
removeSkuTag(tagIndex) { removeSkuTag(tagIndex) {
this.skuTags.splice(tagIndex, 1) this.skuTags.splice(tagIndex, 1);
}, },
// 移除指定 SKU 标签中的某个项
removeSkuTagItem(tagIndex, tagItemIndex) { removeSkuTagItem(tagIndex, tagItemIndex) {
this.skuTags[tagIndex].tagItems.splice(tagItemIndex, 1) if (this.skuTags[tagIndex] && this.skuTags[tagIndex].tagItems) {
this.skuTags[tagIndex].tagItems.splice(tagItemIndex, 1);
}
}, },
// 向指定 SKU 标签中添加新项
addSkuTagItem({ tagIndex, tagItem }) { addSkuTagItem({ tagIndex, tagItem }) {
this.skuTags[tagIndex].tagItems.push(tagItem) if (this.skuTags[tagIndex] && this.skuTags[tagIndex].tagItems) {
this.skuTags[tagIndex].tagItems.push(tagItem);
}
} }
} }
}) });

@ -1,29 +1,41 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
// 定义名为 'user' 的 Pinia Store用于管理用户相关数据和操作
export const useUserStore = defineStore('user', { export const useUserStore = defineStore('user', {
state: () => { // 状态 (state) - 存储用户相关的数据
return { state: () => ({
id: 0, id: 0, // 用户 ID通常是一个唯一的整数值
name: '', name: '', // 用户名称
userId: '', userId: '', // 用户标识符,可能是用户名或系统内部使用的唯一标识
shopId: '', shopId: '', // 商铺 ID如果用户关联到特定商铺则存储该商铺的 ID
mobile: '' mobile: '' // 用户手机号码
} }),
},
// 动作 (actions) - 包含更改状态的逻辑
actions: { actions: {
// 更新用户 ID
updateId(id) { updateId(id) {
this.id = id this.id = id;
}, },
// 更新用户名称
updateName(name) { updateName(name) {
this.name = name this.name = name;
}, },
// 更新用户手机号码
updateMobile(mobile) { updateMobile(mobile) {
this.mobile = mobile this.mobile = mobile;
}, },
// 更新商铺 ID
updateShopId(shopId) { updateShopId(shopId) {
this.shopId = shopId this.shopId = shopId;
}, },
// 更新用户标识符
updateUserId(userId) { updateUserId(userId) {
this.userId = userId this.userId = userId;
} }
} }
}) });

@ -1,8 +1,12 @@
/*
*
*/
*, *,
*:before, *:before,
*:after { *:after {
box-sizing: border-box; box-sizing: border-box; /* 确保所有元素的宽度和高度计算包括 padding 和 border */
} }
body { body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-size: 14px; font-size: 14px;
@ -10,23 +14,24 @@ body {
color: #303133; color: #303133;
background-color: #fff; background-color: #fff;
} }
a { a {
color: mix(#fff, $--color-primary, 20%); color: mix(#fff, $--color-primary, 20%); /* 链接颜色为白色与主色混合后的20% */
text-decoration: none; text-decoration: none;
&:focus, &:focus,
&:hover { &:hover {
color: $--color-primary; color: $--color-primary; /* 激活或悬停时链接颜色为主色 */
text-decoration: underline; text-decoration: underline;
} }
} }
img { img {
vertical-align: middle; vertical-align: middle; /* 图片垂直居中对齐 */
max-width: 100%; max-width: 100%; /* 确保图片不会超出其容器 */
} }
/* 清除浮动 */
/* Utils
------------------------------ */
.clearfix:before, .clearfix:before,
.clearfix:after { .clearfix:after {
content: " "; content: " ";
@ -36,31 +41,21 @@ img {
clear: both; clear: both;
} }
/* 重置 element-ui 样式 */
.site-wrapper .el-pagination {
/* Reset element-ui
------------------------------ */
.site-wrapper {
.el-pagination {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end; /* 分页组件右对齐 */
}
} }
/* 布局 */
/* Layout
------------------------------ */
.site-wrapper { .site-wrapper {
position: relative; position: relative;
min-width: 1180px; min-width: 1180px; /* 设置最小宽度以确保布局在大屏幕上的稳定性 */
} }
/* 侧边栏折叠 */
/* Sidebar fold
------------------------------ */
.site-content--tabs { .site-content--tabs {
padding: 60px 20px 20px 20px !important; padding: 60px 20px 20px 20px !important; /* 当标签页存在时调整内容区的内边距 */
} }
.site-navbar { .site-navbar {
color: #333333; color: #333333;
@ -69,37 +64,32 @@ img {
.site-navbar__header, .site-navbar__header,
.site-navbar__brand, .site-navbar__brand,
.site-sidebar { .site-sidebar {
width: 100px; width: 100px; /* 折叠状态下左侧栏的宽度 */
} }
.site-navbar__body, .site-navbar__body,
.site-content__wrapper { .site-content__wrapper {
margin-left: 100px; margin-left: 100px; /* 折叠后内容区向左偏移量 */
border-bottom: 1px solid #EBEDF0; border-bottom: 1px solid #EBEDF0;
} }
.site-navbar__brand { .site-navbar__brand-lg {
&-lg { display: none; /* 折叠状态隐藏大尺寸品牌标志 */
display: none;
}
&-mini {
display: inline-block;
} }
.site-navbar__brand-mini {
display: inline-block; /* 显示小尺寸品牌标志 */
} }
.site-sidebar, .site-sidebar,
.site-sidebar__inner { .site-sidebar__inner {
overflow: initial; overflow: initial; /* 解决滚动条显示问题 */
} }
} }
// animation
.site-sidebar, .site-sidebar,
.site-sidebar__menu-icon, .site-sidebar__menu-icon,
.site-content__wrapper, .site-content__wrapper,
.site-content--tabs > .el-tabs .el-tabs__header { .site-content--tabs > .el-tabs .el-tabs__header {
transition: inline-block .3s, left .3s, width .3s, margin-left .3s, font-size .3s; transition: all .3s ease-in-out; /* 添加过渡效果 */
} }
/* 导航栏 */
/* Navbar
------------------------------ */
.site-navbar { .site-navbar {
position: fixed; position: fixed;
top: 0; top: 0;
@ -107,13 +97,10 @@ img {
left: 0; left: 0;
z-index: 1030; z-index: 1030;
height: 50px; height: 50px;
// box-shadow: 0 2px 4px rgba(0, 0, 0, .08);
background-color: $navbar--background-color; background-color: $navbar--background-color;
&__header { &__header {
position: relative;
float: left; float: left;
// width: 180px;
height: 50px; height: 50px;
margin-left: 20px; margin-left: 20px;
overflow: hidden; overflow: hidden;
@ -121,9 +108,7 @@ img {
&__brand { &__brand {
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
// width: 230px;
height: 50px; height: 50px;
margin: 0;
line-height: 50px; line-height: 50px;
font-size: 20px; font-size: 20px;
text-align: center; text-align: center;
@ -151,11 +136,7 @@ img {
} }
&__avatar { &__avatar {
border-bottom: none !important; border-bottom: none !important;
* { .el-dropdown-link img {
vertical-align: inherit;
}
.el-dropdown-link {
> img {
width: 36px; width: 36px;
height: auto; height: auto;
margin-right: 5px; margin-right: 5px;
@ -163,16 +144,11 @@ img {
vertical-align: middle; vertical-align: middle;
} }
} }
}
&__body { &__body {
position: relative;
// margin-left: 230px;
// padding-right: 15px;
background-color: #fff; background-color: #fff;
} }
&__menu { &__menu {
float: left; float: left;
background-color: transparent;
border-bottom: 0; border-bottom: 0;
&--right { &--right {
@ -185,9 +161,7 @@ img {
} }
} }
/* 侧边栏 */
/* Sidebar
------------------------------ */
.site-sidebar { .site-sidebar {
position: fixed; position: fixed;
top: 50px; top: 50px;
@ -203,14 +177,12 @@ img {
} }
&__inner { &__inner {
position: relative; position: relative;
z-index: 1;
width: 250px; width: 250px;
height: 100%; height: 100%;
padding-bottom: 15px; padding-bottom: 15px;
overflow-y: scroll; overflow-y: scroll;
} }
&__menu.el-menu { &__menu.el-menu {
width: 100px;
border-right: 0; border-right: 0;
} }
&__menu-icon { &__menu-icon {
@ -222,9 +194,7 @@ img {
} }
} }
/* 内容区 */
/* Content
------------------------------ */
.site-content { .site-content {
position: relative; position: relative;
padding: 15px; padding: 15px;
@ -242,38 +212,31 @@ img {
} }
} }
/* 表格样式 */
//
// el-table
.table-header { .table-header {
background-color: #f7f8fa !important; background-color: #f7f8fa !important;
color: #000; color: #000;
height: 60px; height: 60px;
} }
// el-table - /
.table-row { .table-row {
height: 100px; height: 100px;
} }
// el-table -
.table-row-low { .table-row-low {
height: 65px; height: 65px;
} }
// el-table
.table-cell { .table-cell {
padding: 0; padding: 0;
} }
//
.el-table .cell { .el-table .cell {
line-height: 20px !important; line-height: 20px !important;
word-break: break-word !important; word-break: break-word !important;
} }
// &
.el-table tr > td:first-child > .cell, .el-table tr > td:first-child > .cell,
.el-table th:first-child > .cell { .el-table th:first-child > .cell {
padding-left: 20px; padding-left: 20px;
} }
// /* 按钮样式 */
.default-btn { .default-btn {
height: 32px; height: 32px;
line-height: 32px; line-height: 32px;
@ -334,7 +297,6 @@ img {
cursor: not-allowed; cursor: not-allowed;
} }
} }
//
.default-btn + .default-btn { .default-btn + .default-btn {
margin-left: 10px; margin-left: 10px;
} }
@ -342,31 +304,26 @@ img {
margin-left: 20px; margin-left: 20px;
} }
// /* 状态展示 */
.tag-text { .tag-text {
font-size: 14px; font-size: 14px;
} }
// el-dialog /* 对话框样式 */
.el-dialog__header { .el-dialog__header {
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
margin-right: 0; margin-right: 0;
} }
.el-dialog__footer { .el-dialog__footer {
border-top: 1px solid #f0f0f0; border-top: 1px solid #f0f0f0;
} }
/* 列表布局样式 */
//
//
.search-bar { .search-bar {
padding: 25px 20px 0; padding: 25px 20px 0;
margin-bottom: 20px; margin-bottom: 20px;
background-color: #F7F8FA; background-color: #F7F8FA;
.input-row { .input-row {
display: block;
// () &
.el-form-item .el-form-item__content .el-select, .el-form-item .el-form-item__content .el-select,
.el-form-item .el-form-item__content .el-input { .el-form-item .el-form-item__content .el-input {
width: 200px; width: 200px;
@ -382,9 +339,7 @@ img {
} }
} }
} }
//
.main-container { .main-container {
//
.operation-bar { .operation-bar {
position: relative; position: relative;
display: flex; display: flex;
@ -403,7 +358,6 @@ img {
font-size: 12px; font-size: 12px;
} }
} }
//
.table-con { .table-con {
margin-top: 20px; margin-top: 20px;
padding-bottom: 30px; padding-bottom: 30px;
@ -412,7 +366,6 @@ img {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
// +
.table-cell-con { .table-cell-con {
display: flex; display: flex;
align-items: center; align-items: center;
@ -430,17 +383,14 @@ img {
margin-left: 8px; margin-left: 8px;
flex: 1; flex: 1;
text-overflow: ellipsis; text-overflow: ellipsis;
-o-text-overflow: ellipsis;
word-break: break-word; word-break: break-word;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
/* autoprefixer: ignore next */
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
overflow: hidden; overflow: hidden;
line-height: 20px; line-height: 20px;
} }
} }
//
.table-cell-image { .table-cell-image {
width: 60px; width: 60px;
height: 60px; height: 60px;
@ -451,14 +401,11 @@ img {
object-fit: contain; object-fit: contain;
} }
} }
//
.table-cell-text { .table-cell-text {
text-overflow: ellipsis; text-overflow: ellipsis;
-o-text-overflow: ellipsis;
word-break: break-word; word-break: break-word;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
/* autoprefixer: ignore next */
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
overflow: hidden; overflow: hidden;
line-height: 20px; line-height: 20px;
@ -469,7 +416,7 @@ img {
} }
} }
// el-tabs /* 标签样式 */
.el-tabs__nav-wrap::after{ .el-tabs__nav-wrap::after{
height: 1px !important; height: 1px !important;
} }
@ -487,12 +434,11 @@ img {
border-bottom: 2px solid #155BD4; border-bottom: 2px solid #155BD4;
} }
// /* 新增页面头部标题样式 */
.new-page-title { .new-page-title {
width: 100%; width: 100%;
height: 62px; height: 62px;
background: #F7F8FA; background: #F7F8FA;
box-sizing: border-box;
padding: 19px 20px; padding: 19px 20px;
display: flex; display: flex;
align-items: center; align-items: center;

@ -4,11 +4,9 @@
========================================================================== */ ========================================================================== */
/** /**
* 1. Correct the line height in all browsers. * 1.
* 2. Prevent adjustments of font size after orientation changes in * 2. Windows Phone IE iOS
* IE on Windows Phone and in iOS.
*/ */
html { html {
line-height: 1.15; /* 1 */ line-height: 1.15; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */ -ms-text-size-adjust: 100%; /* 2 */
@ -19,17 +17,15 @@
========================================================================== */ ========================================================================== */
/** /**
* Remove the margin in all browsers (opinionated). * body
*/ */
body { body {
margin: 0; margin: 0;
} }
/** /**
* Add the correct display in IE 9-. * IE 9-
*/ */
article, article,
aside, aside,
footer, footer,
@ -40,10 +36,8 @@ section {
} }
/** /**
* Correct the font size and margin on `h1` elements within `section` and * Chrome, Firefox, Safari section article h1
* `article` contexts in Chrome, Firefox, and Safari.
*/ */
h1 { h1 {
font-size: 2em; font-size: 2em;
margin: 0.67em 0; margin: 0.67em 0;
@ -53,10 +47,9 @@ h1 {
========================================================================== */ ========================================================================== */
/** /**
* Add the correct display in IE 9-. * IE 9-
* 1. Add the correct display in IE. * 1. IE main
*/ */
figcaption, figcaption,
figure, figure,
main { /* 1 */ main { /* 1 */
@ -64,18 +57,16 @@ main { /* 1 */
} }
/** /**
* Add the correct margin in IE 8. * IE 8
*/ */
figure { figure {
margin: 1em 40px; margin: 1em 40px;
} }
/** /**
* 1. Add the correct box sizing in Firefox. * 1. Firefox box-sizing
* 2. Show the overflow in Edge and IE. * 2. EdgeIE
*/ */
hr { hr {
box-sizing: content-box; /* 1 */ box-sizing: content-box; /* 1 */
height: 0; /* 1 */ height: 0; /* 1 */
@ -83,10 +74,9 @@ hr {
} }
/** /**
* 1. Correct the inheritance and scaling of font size in all browsers. * 1.
* 2. Correct the odd `em` font sizing in all browsers. * 2. em
*/ */
pre { pre {
font-family: monospace, monospace; /* 1 */ font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */ font-size: 1em; /* 2 */
@ -96,47 +86,42 @@ pre {
========================================================================== */ ========================================================================== */
/** /**
* 1. Remove the gray background on active links in IE 10. * 1. IE 10
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+. * 2. iOS 8+ Safari 8+线
*/ */
a { a {
background-color: transparent; /* 1 */ background-color: transparent; /* 1 */
} }
/** /**
* 1. Remove the bottom border in Chrome 57- and Firefox 39-. * 1. Chrome 57- Firefox 39-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. * 2. Chrome, Edge, IE, Opera, Safari
*/ */
abbr[title] { abbr[title] {
border-bottom: none; /* 1 */ border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */ text-decoration: underline; /* 2 */
} }
/** /**
* Prevent the duplicate application of `bolder` by the next rule in Safari 6. * Safari 6 bolder
*/ */
b, b,
strong { strong {
font-weight: inherit; font-weight: inherit;
} }
/** /**
* Add the correct font weight in Chrome, Edge, and Safari. * Chrome, Edge, Safari
*/ */
b, b,
strong { strong {
font-weight: bolder; font-weight: bolder;
} }
/** /**
* 1. Correct the inheritance and scaling of font size in all browsers. * 1.
* 2. Correct the odd `em` font sizing in all browsers. * 2. em
*/ */
code, code,
kbd, kbd,
samp { samp {
@ -145,35 +130,30 @@ samp {
} }
/** /**
* Add the correct font style in Android 4.3-. * Android 4.3-
*/ */
dfn { dfn {
font-style: italic; font-style: italic;
} }
/** /**
* Add the correct background and color in IE 9-. * IE 9-
*/ */
mark { mark {
background-color: #ff0; background-color: #ff0;
color: #000; color: #000;
} }
/** /**
* Add the correct font size in all browsers. *
*/ */
small { small {
font-size: 80%; font-size: 80%;
} }
/** /**
* Prevent `sub` and `sup` elements from affecting the line height in * sub sup
* all browsers.
*/ */
sub, sub,
sup { sup {
font-size: 75%; font-size: 75%;
@ -194,35 +174,31 @@ sup {
========================================================================== */ ========================================================================== */
/** /**
* Add the correct display in IE 9-. * IE 9-
*/ */
audio, audio,
video { video {
display: inline-block; display: inline-block;
} }
/** /**
* Add the correct display in iOS 4-7. * iOS 4-7 audio
*/ */
audio:not([controls]) { audio:not([controls]) {
display: none; display: none;
height: 0; height: 0;
} }
/** /**
* Remove the border on images inside links in IE 10-. * IE 10-
*/ */
img { img {
border-style: none; border-style: none;
} }
/** /**
* Hide the overflow in IE. * IE
*/ */
svg:not(:root) { svg:not(:root) {
overflow: hidden; overflow: hidden;
} }
@ -231,10 +207,9 @@ svg:not(:root) {
========================================================================== */ ========================================================================== */
/** /**
* 1. Change the font styles in all browsers (opinionated). * 1.
* 2. Remove the margin in Firefox and Safari. * 2. FirefoxSafari
*/ */
button, button,
input, input,
optgroup, optgroup,
@ -247,42 +222,38 @@ textarea {
} }
/** /**
* Show the overflow in IE. * IE
* 1. Show the overflow in Edge. * 1. Edge
*/ */
button, button,
input { /* 1 */ input { /* 1 */
overflow: visible; overflow: visible;
} }
/** /**
* Remove the inheritance of text transform in Edge, Firefox, and IE. * Edge, Firefox, IE text-transform
* 1. Remove the inheritance of text transform in Firefox. * 1. Firefox text-transform
*/ */
button, button,
select { /* 1 */ select { /* 1 */
text-transform: none; text-transform: none;
} }
/** /**
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` * 1. WebKit bugbug Android 4 native `audio` `video`
* controls in Android 4. * 2. iOSSafari
* 2. Correct the inability to style clickable types in iOS and Safari.
*/ */
button, button,
html [type="button"], /* 1 */ html [type="button"], /* 1 */
[type="reset"], [type="reset"],
[type="reset"],
[type="submit"] { [type="submit"] {
-webkit-appearance: button; /* 2 */ -webkit-appearance: button; /* 2 */
} }
/** /**
* Remove the inner border and padding in Firefox. * Firefox
*/ */
button::-moz-focus-inner, button::-moz-focus-inner,
[type="button"]::-moz-focus-inner, [type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner,
@ -292,9 +263,8 @@ button::-moz-focus-inner,
} }
/** /**
* Restore the focus styles unset by the previous rule. * Firefox
*/ */
button:-moz-focusring, button:-moz-focusring,
[type="button"]:-moz-focusring, [type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring, [type="reset"]:-moz-focusring,
@ -303,20 +273,17 @@ button:-moz-focusring,
} }
/** /**
* Correct the padding in Firefox. * Firefox fieldset
*/ */
fieldset { fieldset {
padding: 0.35em 0.75em 0.625em; padding: 0.35em 0.75em 0.625em;
} }
/** /**
* 1. Correct the text wrapping in Edge and IE. * 1. EdgeIE
* 2. Correct the color inheritance from `fieldset` elements in IE. * 2. IE fieldset
* 3. Remove the padding so developers are not caught out when they zero out * 3. fieldset
* `fieldset` elements in all browsers.
*/ */
legend { legend {
box-sizing: border-box; /* 1 */ box-sizing: border-box; /* 1 */
color: inherit; /* 2 */ color: inherit; /* 2 */
@ -327,28 +294,25 @@ legend {
} }
/** /**
* 1. Add the correct display in IE 9-. * 1. IE 9- progress
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. * 2. Chrome, Firefox, Opera
*/ */
progress { progress {
display: inline-block; /* 1 */ display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */ vertical-align: baseline; /* 2 */
} }
/** /**
* Remove the default vertical scrollbar in IE. * IE
*/ */
textarea { textarea {
overflow: auto; overflow: auto;
} }
/** /**
* 1. Add the correct box sizing in IE 10-. * 1. IE 10- box-sizing
* 2. Remove the padding in IE 10-. * 2. IE 10-
*/ */
[type="checkbox"], [type="checkbox"],
[type="radio"] { [type="radio"] {
box-sizing: border-box; /* 1 */ box-sizing: border-box; /* 1 */
@ -356,38 +320,34 @@ textarea {
} }
/** /**
* Correct the cursor style of increment and decrement buttons in Chrome. * Chrome
*/ */
[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button { [type="number"]::-webkit-outer-spin-button {
height: auto; height: auto;
} }
/** /**
* 1. Correct the odd appearance in Chrome and Safari. * 1. ChromeSafari
* 2. Correct the outline style in Safari. * 2. Safari
*/ */
[type="search"] { [type="search"] {
-webkit-appearance: textfield; /* 1 */ -webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */ outline-offset: -2px; /* 2 */
} }
/** /**
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS. * macOSChromeSafari
*/ */
[type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration { [type="search"]::-webkit-search-decoration {
-webkit-appearance: none; -webkit-appearance: none;
} }
/** /**
* 1. Correct the inability to style clickable types in iOS and Safari. * 1. iOSSafari
* 2. Change font properties to `inherit` in Safari. * 2. inherit Safari
*/ */
::-webkit-file-upload-button { ::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */ -webkit-appearance: button; /* 1 */
font: inherit; /* 2 */ font: inherit; /* 2 */
@ -396,20 +356,18 @@ textarea {
/* Interactive /* Interactive
========================================================================== */ ========================================================================== */
/* /**
* Add the correct display in IE 9-. * IE 9-
* 1. Add the correct display in Edge, IE, and Firefox. * 1. Edge, IE, Firefox
*/ */
details, /* 1 */ details, /* 1 */
menu { menu {
display: block; display: block;
} }
/* /**
* Add the correct display in all browsers. * summary
*/ */
summary { summary {
display: list-item; display: list-item;
} }
@ -418,17 +376,15 @@ summary {
========================================================================== */ ========================================================================== */
/** /**
* Add the correct display in IE 9-. * IE 9- canvas
*/ */
canvas { canvas {
display: inline-block; display: inline-block;
} }
/** /**
* Add the correct display in IE. * IE template
*/ */
template { template {
display: none; display: none;
} }
@ -437,9 +393,8 @@ template {
========================================================================== */ ========================================================================== */
/** /**
* Add the correct display in IE 10-. * IE 10- [hidden]
*/ */
[hidden] { [hidden] {
display: none; display: none;
} }

@ -1,14 +1,36 @@
//
// css // --color-primary:
$--color-primary: #155BD4; $--color-primary: #155BD4;
// Navbar // Navbar ()
//
$navbar--background-color: $--color-primary; $navbar--background-color: $--color-primary;
// Sidebar // Sidebar ()
//
$sidebar--background-color-dark: #263238; $sidebar--background-color-dark: #263238;
//
$sidebar--color-text-dark: #8a979e; $sidebar--color-text-dark: #8a979e;
// Content // Content ()
//
$content--background-color: #f1f4f5; $content--background-color: #f1f4f5;
/* 下面是使用上述变量的实际CSS规则示例 */
/* 导航栏 */
.navbar {
background-color: $navbar--background-color; // 使
}
/* 侧边栏 */
.sidebar {
background-color: $sidebar--background-color-dark; // 使
color: $sidebar--color-text-dark; // 使
}
/* 内容区 */
.content {
background-color: $content--background-color; // 使
}

@ -1,7 +1,33 @@
// element-plus
:root { :root {
/*
* CSS
*/
/*
* --el-color-primary:
*
*
*/
--el-color-primary: #155BD4; --el-color-primary: #155BD4;
/*
* --el-color-primary-light-3:
*
*
*/
--el-color-primary-light-3: #447cdd; --el-color-primary-light-3: #447cdd;
/*
* --el-menu-item-height: 40px
* Element Plus
*
*/
--el-menu-item-height: 40px; --el-menu-item-height: 40px;
/*
* --el-menu-sub-item-height: 40px
*
*
*/
--el-menu-sub-item-height: 40px; --el-menu-sub-item-height: 40px;
} }

@ -1,8 +1,27 @@
@import "_normalize"; // api: https://github.com/necolas/normalize.css/ // normalize.css
// api: https://github.com/necolas/normalize.css/
// normalize.css CSS
//
@import "_normalize";
//
// _variables 使
// 使便
@import "_variables"; @import "_variables";
//
// _base resettypographylayout
//
@import "_base"; @import "_base";
// Element Plus
// Element Plus
// Element Plus
@import "@/styles/element-variables.scss"; @import "@/styles/element-variables.scss";
// z-index
// .element-error-message-zindex z-index
// z-index 3000使 !important
.element-error-message-zindex { .element-error-message-zindex {
z-index: 3000 !important; z-index: 3000 !important;
} }

Loading…
Cancel
Save