Compare commits

..

No commits in common. '714b4ad4ab164e29bff4cde63bcd112e8b1acc63' and '7fdd1a5829a325199aa348807a7d1c4060d6f438' have entirely different histories.

@ -1,35 +1,24 @@
<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"
@ -38,88 +27,59 @@
<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' // Element Plus import { ArrowRight } from '@element-plus/icons-vue'
import { ref, computed } from 'vue' // Vue API const route = useRoute()
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) { // isTab if (route.meta.isTab) {
height -= 40 // height -= 40
return isURL(route.meta.iframeUrl) // iframe return isURL(route.meta.iframeUrl) ? { height: height + 'px' } : { minHeight: height + 'px' }
? { 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; /* 距离顶部 50px */ top: 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,11 +1,9 @@
<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"
@ -13,12 +11,9 @@
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"
@ -28,8 +23,6 @@
type="password" type="password"
/> />
</el-form-item> </el-form-item>
<!-- 新密码输入项 -->
<el-form-item <el-form-item
label="新密码" label="新密码"
prop="newPassword" prop="newPassword"
@ -39,8 +32,6 @@
type="password" type="password"
/> />
</el-form-item> </el-form-item>
<!-- 确认密码输入项 -->
<el-form-item <el-form-item
label="确定密码" label="确定密码"
prop="confirmPassword" prop="confirmPassword"
@ -51,11 +42,9 @@
/> />
</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)"
@ -66,92 +55,67 @@
</template> </template>
<script setup> <script setup>
import { ElMessage } from 'element-plus' // Element Plus import { ElMessage } from 'element-plus'
import { ref, reactive, computed, nextTick } from 'vue' // Vue API const visible = ref(false)
import { useUserStore } from '@/store' // Vuex store const dataForm = reactive({
import { useRouter } from 'vue-router' // vue-router password: '',
import http from '@/utils/http' // HTTP newPassword: '',
import { encrypt } from '@/utils/encrypt' // confirmPassword: ''
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() // DOM dataFormRef.value?.resetFields()
}) })
} }
// 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 => {
await formEl.validate(valid => { // if (valid) {
if (valid) { //
http({ http({
url: http.adornUrl('/sys/user/password'), // API url: http.adornUrl('/sys/user/password'),
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,21 +1,16 @@
<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' }"
> >
<!-- Logo 或标题 --> <!-- <img-->
<!-- <!-- class="menu-image-logo"-->
<img <!-- :src="configuration.bsTopBarIcon"-->
class="menu-image-logo" <!-- alt="logo"-->
:src="configuration.bsTopBarIcon" <!-- >-->
alt="logo"
>
-->
<!-- 标题文本根据侧边栏折叠状态显示不同内容 -->
<span <span
v-if="!sidebarFold" v-if="!sidebarFold"
class="site-navbar-lg" class="site-navbar-lg"
@ -30,12 +25,9 @@
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"
@ -43,22 +35,19 @@
/> />
</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>
@ -66,8 +55,7 @@
</el-dropdown> </el-dropdown>
</div> </div>
</div> </div>
<!-- 弹窗, 修改密码 -->
<!-- 修改密码弹窗 -->
<UpdatePassword <UpdatePassword
v-if="updatePassowrdVisible" v-if="updatePassowrdVisible"
ref="updatePassowrdRef" ref="updatePassowrdRef"
@ -77,143 +65,108 @@
</template> </template>
<script setup> <script setup>
import { ElMessageBox } from 'element-plus' // Element Plus import { ElMessageBox } from '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') { // '0' if ((route.path === '/home' || len === 1) && flag === '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'), // API url: http.adornUrl('/logOut'),
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() // DOM updatePassowrdRef.value?.init()
}) })
} }
</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; // 20px margin-left: 20px;
color: #333; // color: #333;
font-weight: 700; // 700 font-weight: 700;
height: 50px; // 50px height: 50px;
line-height: 50px; // 50px使 line-height: 50px;
.site-navbar-lg { .site-navbar-lg {
font-size: 16px; // 16px font-size: 16px;
word-break: break-all; // URL word-break: break-all;
word-wrap: break-word; // URL word-wrap: break-word;
} }
.site-navbar-lg, .site-navbar-lg,
.site-navbar-mini { .site-navbar-mini {
margin: 0 5px; // 5px margin: 0 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; // 20px padding: 0 20px;
font-size: 18px; // 18px font-size: 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; // 18px // height: 18px;
// width: 59px; // 59px // width: 59px;
// margin-right: 10px; // 10px // margin-right: 10px;
//} //}
</style> </style>

@ -1,39 +1,32 @@
<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; <!-- 设置字体大小为 14px --> font-size: 14px !important;
line-height: 40px; line-height: 40px;
padding-left: 30px !important; <!-- 设置左侧内边距为 30px --> padding-left: 30px !important;
padding-right: 10px !important; <!-- 设置右侧内边距为 10px --> padding-right: 10px !important;
" "
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"
@ -43,10 +36,7 @@
</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"
@ -55,100 +45,81 @@
: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 : ''">{{ expandMenu.name }}</span> <!-- 显示菜单名称 --> <span :title="expandMenu.name.length > 4 ? expandMenu.name : ''">{{
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)
// Vue Router const commonStore = useCommonStore()
const router = useRouter() const router = useRouter()
//
// routeHandle
watch( watch(
() => router.currentRoute.value, // () => router.currentRoute,
route => { route => {
routeHandle(route) // routeHandle routeHandle(route)
} }
) )
//
//
const routeHandle = route => { const routeHandle = route => {
if (route.meta.isTab) { // isTab if (route.meta.isTab) {
commonStore.updateSelectRightId(route.meta.menuId || '') // ID commonStore.updateSelectRightId(route.meta.menuId || '')
} }
} }
// 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; /* 固定宽度为 85px */ width: 85px;
font-size: 14px; /* 字体大小为 14px */ font-size: 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,12 +1,9 @@
<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"
@ -17,37 +14,28 @@
</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; /* 固定宽度为 130px并使用 !important 确保优先级 */ width: 130px !important;
border-top: 1px solid #ebedf0; /* 添加顶部边框,颜色为浅灰色 */ border-top: 1px solid #ebedf0;
height: calc(100vh - 50px); /* 高度为视口高度减去 50px通常用于顶部导航栏 */ height: calc(100vh - 50px);
overflow-y: auto; /* 当内容超出容器高度时启用垂直滚动条 */ overflow-y: auto;
} }
/* 定义垂直菜单样式的自定义类 */
.el-menu-vertical-demo { .el-menu-vertical-demo {
border: none; /* 移除默认的边框 */ border: none;
height: 100%; /* 设置高度为 100%,确保菜单占满整个容器 */ height: 100%;
} }
</style> </style>

@ -1,17 +1,14 @@
<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()"
> >
@ -23,8 +20,6 @@
</div> </div>
</li> </li>
</ul> </ul>
<!-- 动态生成的菜单列表 -->
<ul> <ul>
<template <template
v-for="menu in menuList" v-for="menu in menuList"
@ -32,14 +27,13 @@
> >
<li <li
v-if="!menu.hidden" v-if="!menu.hidden"
:class="{ 'menu-left-active': selectLeftId === menu.menuId }" class="menu-left-active"
> >
<!-- 如果菜单有子菜单则显示可展开的菜单项 -->
<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)"
> >
@ -52,16 +46,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>
<!-- 如果菜单没有子菜单则直接链接到目标页面 -->
<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)"
> >
@ -75,14 +67,12 @@
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"
@ -93,32 +83,23 @@ class="menu-right-con"
</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()
// Vue Vuex const router = useRouter()
import { useRoute, useRouter } from 'vue-router' const commonStore = useCommonStore()
import { computed, ref, onBeforeMount } from 'vue' const dynamicMenuRoutes = ref([])
import { useCommonStore } from '@/store' const expandMenuList = ref([])
// 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: () => commonStore.menuList, // store get: () => {
set: val => commonStore.updateMenuList(val) // store return commonStore.menuList
},
set: val => {
commonStore.updateMenuList(val)
}
}) })
// ID
const selectLeftId = computed({ const selectLeftId = computed({
get: () => { get: () => {
handleRightRoute(commonStore.selectLeftId) handleRightRoute(commonStore.selectLeftId)
@ -126,41 +107,32 @@ 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('') // ID commonStore.updateSelectLeftId('')
commonStore.updateSelectRightId('') // ID commonStore.updateSelectRightId('')
} }
//
const routeHandle = route => { const routeHandle = route => {
if (route.name === 'home') { if (route.name === 'home') {
expandMenuList.value = [] expandMenuList.value = []
@ -170,9 +142,7 @@ 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)
@ -181,13 +151,10 @@ 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')
@ -197,36 +164,29 @@ 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 // ID const id1 = commonStore.selectLeftId
commonStore.updateSelectLeftId(menu.menuId || '') // ID commonStore.updateSelectLeftId(menu.menuId || '')
const id2 = commonStore.selectLeftId // ID const id2 = commonStore.selectLeftId
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
@ -242,117 +202,94 @@ const routeJump = menu => {
</script> </script>
<style scoped> <style scoped>
/* 定义菜单模块的整体布局 */
.menu-mod { .menu-mod {
display: flex; /* 使用 Flexbox 布局,使子元素可以灵活排列 */ display: flex;
} }
/* 定义右侧内容容器的位置和层级 */
.menu-right-con { .menu-right-con {
position: absolute; /* 设置为绝对定位 */ position: absolute;
z-index: 1; /* 设置堆叠顺序,确保在其他元素之上 */ z-index: 1;
left: 100px; /* 左边距为 100px */ left: 100px;
} }
/* 定义左侧菜单栏样式 */
.menu-left { .menu-left {
background: #222222; /* 背景颜色为深灰色 */ background: #222222;
color: #ffffff !important; /* 文本颜色为白色,并使用 !important 确保优先级 */ color: #ffffff !important;
width: 100px; /* 固定宽度为 100px */ width: 100px;
height: calc(100vh - 50px); /* 高度为视口高度减去 50px通常用于顶部导航栏 */ height: calc(100vh - 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; /* 使用 Flexbox 布局 */ display: flex;
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; /* 使用 Flexbox 布局 */ display: flex;
align-items: center; /* 水平居中对齐 */ align-items: center;
text-align: left; /* 文本左对齐 */ text-align: left;
padding-left: 12px; /* 左内边距为 12px */ padding-left: 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; /* 固定宽度为 100px */ width: 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; /* 固定宽度为 130px */ width: 130px;
text-align: center; /* 文本居中对齐 */ text-align: center;
} }
/* 定义左侧菜单项的样式 */
.menu-left li { .menu-left li {
background: #222222; /* 背景颜色为深灰色 */ background: #222222;
color: #fff; /* 文本颜色为白色 */ color: #fff;
height: 40px; /* 固定高度为 40px */ height: 40px;
cursor: pointer; /* 鼠标悬停时显示指针样式 */ cursor: pointer;
font-size: 14px; /* 字体大小为 14px */ font-size: 14px;
stroke: #fff !important; /* SVG 图标颜色为白色,并使用 !important 确保优先级 */ stroke: #fff !important;
} }
/* 定义右侧菜单项的样式 */
.menu-right li { .menu-right li {
height: 40px; /* 固定高度为 40px */ height: 40px;
display: flex; /* 使用 Flexbox 布局 */ display: flex;
align-items: center; /* 水平居中对齐 */ align-items: center;
justify-content: center; /* 垂直居中对齐 */ justify-content: center;
cursor: pointer; /* 鼠标悬停时显示指针样式 */ cursor: pointer;
font-size: 14px; /* 字体大小为 14px */ font-size: 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;/* SVG 图标颜色变为蓝色,并使用 !important 确保优先级 */ stroke: #155bd4 !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;/* SVG 图标颜色变为蓝色,并使用 !important 确保优先级 */ stroke: #155bd4 !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; /* 最大宽度为 70px */ max-width: 70px;
} }
</style> </style>

@ -1,84 +1,59 @@
<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 />
<MainNavbar /> <!-- 渲染主导航栏组件通常包含导航链接用户信息等 --> <MainSidebar />
<!-- 侧边栏组件 -->
<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 { ref, computed, onBeforeMount, onMounted } from 'vue' import MainNavbar from './main-navbar.vue'
import http from '@/utils/http' // HTTP import MainSidebar from './main-sidebar.vue'
import MainNavbar from './main-navbar.vue' // import MainContent from './main-content.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() // Store const commonStore = useCommonStore()
const documentClientHeight = computed(() => // const documentClientHeight = computed(() => commonStore.documentClientHeight)
commonStore.documentClientHeight // commonStore const userStore = useUserStore()
) 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'), // API 使 http.adornUrl URL url: http.adornUrl('/sys/user/info'),
method: 'get', // GET method: 'get',
params: http.adornParams() // 使 http.adornParams params: http.adornParams()
}).then(({ data }) => { }).then(({ data }) => {
loading.value = false // loading.value = false
userStore.userId = data.userId // ID userStore userStore.userId = data.userId
userStore.name = data.username // userStore userStore.name = data.username
userStore.mobile = data.mobile // userStore userStore.mobile = data.mobile
userStore.shopId = data.shopId // ID userStore userStore.shopId = data.shopId
// userStore.userId = data.userId userStore.userId = data.userId
}).catch(() => { }).catch(() => {})
//
})
} }
</script> </script>

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

@ -1,116 +1,80 @@
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: () => {
state: () => ({ return {
// 页面文档可视高度(随窗口改变大小) // 页面文档可视高度(随窗口改变大小)
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 = ''
this.menuActiveName = ''; router.push({ name: 'home' })
router.push({ name: 'home' });
} }
} }
} }
}); })

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

@ -1,41 +1,29 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
// 定义名为 'user' 的 Pinia Store用于管理用户相关数据和操作
export const useUserStore = defineStore('user', { export const useUserStore = defineStore('user', {
// 状态 (state) - 存储用户相关的数据 state: () => {
state: () => ({ return {
id: 0, // 用户 ID通常是一个唯一的整数值 id: 0,
name: '', // 用户名称 name: '',
userId: '', // 用户标识符,可能是用户名或系统内部使用的唯一标识 userId: '',
shopId: '', // 商铺 ID如果用户关联到特定商铺则存储该商铺的 ID shopId: '',
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,12 +1,8 @@
/*
*
*/
*, *,
*:before, *:before,
*:after { *:after {
box-sizing: border-box; /* 确保所有元素的宽度和高度计算包括 padding 和 border */ box-sizing: border-box;
} }
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;
@ -14,24 +10,23 @@ body {
color: #303133; color: #303133;
background-color: #fff; background-color: #fff;
} }
a { a {
color: mix(#fff, $--color-primary, 20%); /* 链接颜色为白色与主色混合后的20% */ color: mix(#fff, $--color-primary, 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: " ";
@ -41,21 +36,31 @@ 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;
@ -64,32 +69,37 @@ 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-lg { .site-navbar__brand {
display: none; /* 折叠状态隐藏大尺寸品牌标志 */ &-lg {
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: all .3s ease-in-out; /* 添加过渡效果 */ transition: inline-block .3s, left .3s, width .3s, margin-left .3s, font-size .3s;
} }
/* 导航栏 */
/* Navbar
------------------------------ */
.site-navbar { .site-navbar {
position: fixed; position: fixed;
top: 0; top: 0;
@ -97,10 +107,13 @@ 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;
@ -108,7 +121,9 @@ 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;
@ -136,7 +151,11 @@ 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;
@ -144,11 +163,16 @@ 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 {
@ -161,7 +185,9 @@ img {
} }
} }
/* 侧边栏 */
/* Sidebar
------------------------------ */
.site-sidebar { .site-sidebar {
position: fixed; position: fixed;
top: 50px; top: 50px;
@ -177,12 +203,14 @@ 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 {
@ -194,7 +222,9 @@ img {
} }
} }
/* 内容区 */
/* Content
------------------------------ */
.site-content { .site-content {
position: relative; position: relative;
padding: 15px; padding: 15px;
@ -212,31 +242,38 @@ 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;
@ -297,6 +334,7 @@ img {
cursor: not-allowed; cursor: not-allowed;
} }
} }
//
.default-btn + .default-btn { .default-btn + .default-btn {
margin-left: 10px; margin-left: 10px;
} }
@ -304,26 +342,31 @@ 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;
@ -339,7 +382,9 @@ img {
} }
} }
} }
//
.main-container { .main-container {
//
.operation-bar { .operation-bar {
position: relative; position: relative;
display: flex; display: flex;
@ -358,6 +403,7 @@ img {
font-size: 12px; font-size: 12px;
} }
} }
//
.table-con { .table-con {
margin-top: 20px; margin-top: 20px;
padding-bottom: 30px; padding-bottom: 30px;
@ -366,6 +412,7 @@ 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;
@ -383,14 +430,17 @@ 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;
@ -401,11 +451,14 @@ 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;
@ -416,7 +469,7 @@ img {
} }
} }
/* 标签样式 */ // el-tabs
.el-tabs__nav-wrap::after{ .el-tabs__nav-wrap::after{
height: 1px !important; height: 1px !important;
} }
@ -434,11 +487,12 @@ 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,9 +4,11 @@
========================================================================== */ ========================================================================== */
/** /**
* 1. * 1. Correct the line height in all browsers.
* 2. Windows Phone IE iOS * 2. Prevent adjustments of font size after orientation changes in
* 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 */
@ -17,15 +19,17 @@ html {
========================================================================== */ ========================================================================== */
/** /**
* body * Remove the margin in all browsers (opinionated).
*/ */
body { body {
margin: 0; margin: 0;
} }
/** /**
* IE 9- * Add the correct display in IE 9-.
*/ */
article, article,
aside, aside,
footer, footer,
@ -36,8 +40,10 @@ section {
} }
/** /**
* Chrome, Firefox, Safari section article h1 * Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/ */
h1 { h1 {
font-size: 2em; font-size: 2em;
margin: 0.67em 0; margin: 0.67em 0;
@ -47,9 +53,10 @@ h1 {
========================================================================== */ ========================================================================== */
/** /**
* IE 9- * Add the correct display in IE 9-.
* 1. IE main * 1. Add the correct display in IE.
*/ */
figcaption, figcaption,
figure, figure,
main { /* 1 */ main { /* 1 */
@ -57,16 +64,18 @@ main { /* 1 */
} }
/** /**
* IE 8 * Add the correct margin in IE 8.
*/ */
figure { figure {
margin: 1em 40px; margin: 1em 40px;
} }
/** /**
* 1. Firefox box-sizing * 1. Add the correct box sizing in Firefox.
* 2. EdgeIE * 2. Show the overflow in Edge and IE.
*/ */
hr { hr {
box-sizing: content-box; /* 1 */ box-sizing: content-box; /* 1 */
height: 0; /* 1 */ height: 0; /* 1 */
@ -74,9 +83,10 @@ hr {
} }
/** /**
* 1. * 1. Correct the inheritance and scaling of font size in all browsers.
* 2. em * 2. Correct the odd `em` font sizing in all browsers.
*/ */
pre { pre {
font-family: monospace, monospace; /* 1 */ font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */ font-size: 1em; /* 2 */
@ -86,42 +96,47 @@ pre {
========================================================================== */ ========================================================================== */
/** /**
* 1. IE 10 * 1. Remove the gray background on active links in IE 10.
* 2. iOS 8+ Safari 8+线 * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/ */
a { a {
background-color: transparent; /* 1 */ background-color: transparent; /* 1 */
} }
/** /**
* 1. Chrome 57- Firefox 39- * 1. Remove the bottom border in Chrome 57- and Firefox 39-.
* 2. Chrome, Edge, IE, Opera, Safari * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/ */
abbr[title] { abbr[title] {
border-bottom: none; /* 1 */ border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */ text-decoration: underline; /* 2 */
} }
/** /**
* Safari 6 bolder * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
*/ */
b, b,
strong { strong {
font-weight: inherit; font-weight: inherit;
} }
/** /**
* Chrome, Edge, Safari * Add the correct font weight in Chrome, Edge, and Safari.
*/ */
b, b,
strong { strong {
font-weight: bolder; font-weight: bolder;
} }
/** /**
* 1. * 1. Correct the inheritance and scaling of font size in all browsers.
* 2. em * 2. Correct the odd `em` font sizing in all browsers.
*/ */
code, code,
kbd, kbd,
samp { samp {
@ -130,30 +145,35 @@ samp {
} }
/** /**
* Android 4.3- * Add the correct font style in Android 4.3-.
*/ */
dfn { dfn {
font-style: italic; font-style: italic;
} }
/** /**
* IE 9- * Add the correct background and color in 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%;
} }
/** /**
* sub sup * Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/ */
sub, sub,
sup { sup {
font-size: 75%; font-size: 75%;
@ -174,31 +194,35 @@ sup {
========================================================================== */ ========================================================================== */
/** /**
* IE 9- * Add the correct display in IE 9-.
*/ */
audio, audio,
video { video {
display: inline-block; display: inline-block;
} }
/** /**
* iOS 4-7 audio * Add the correct display in iOS 4-7.
*/ */
audio:not([controls]) { audio:not([controls]) {
display: none; display: none;
height: 0; height: 0;
} }
/** /**
* IE 10- * Remove the border on images inside links in IE 10-.
*/ */
img { img {
border-style: none; border-style: none;
} }
/** /**
* IE * Hide the overflow in IE.
*/ */
svg:not(:root) { svg:not(:root) {
overflow: hidden; overflow: hidden;
} }
@ -207,9 +231,10 @@ svg:not(:root) {
========================================================================== */ ========================================================================== */
/** /**
* 1. * 1. Change the font styles in all browsers (opinionated).
* 2. FirefoxSafari * 2. Remove the margin in Firefox and Safari.
*/ */
button, button,
input, input,
optgroup, optgroup,
@ -222,38 +247,42 @@ textarea {
} }
/** /**
* IE * Show the overflow in IE.
* 1. Edge * 1. Show the overflow in Edge.
*/ */
button, button,
input { /* 1 */ input { /* 1 */
overflow: visible; overflow: visible;
} }
/** /**
* Edge, Firefox, IE text-transform * Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Firefox text-transform * 1. Remove the inheritance of text transform in Firefox.
*/ */
button, button,
select { /* 1 */ select { /* 1 */
text-transform: none; text-transform: none;
} }
/** /**
* 1. WebKit bugbug Android 4 native `audio` `video` * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* 2. iOSSafari * controls in Android 4.
* 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 */
} }
/** /**
* Firefox * Remove the inner border and padding in 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,
@ -263,8 +292,9 @@ button::-moz-focus-inner,
} }
/** /**
* Firefox * Restore the focus styles unset by the previous rule.
*/ */
button:-moz-focusring, button:-moz-focusring,
[type="button"]:-moz-focusring, [type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring, [type="reset"]:-moz-focusring,
@ -273,17 +303,20 @@ button:-moz-focusring,
} }
/** /**
* Firefox fieldset * Correct the padding in Firefox.
*/ */
fieldset { fieldset {
padding: 0.35em 0.75em 0.625em; padding: 0.35em 0.75em 0.625em;
} }
/** /**
* 1. EdgeIE * 1. Correct the text wrapping in Edge and IE.
* 2. IE fieldset * 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. fieldset * 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/ */
legend { legend {
box-sizing: border-box; /* 1 */ box-sizing: border-box; /* 1 */
color: inherit; /* 2 */ color: inherit; /* 2 */
@ -294,25 +327,28 @@ legend {
} }
/** /**
* 1. IE 9- progress * 1. Add the correct display in IE 9-.
* 2. Chrome, Firefox, Opera * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/ */
progress { progress {
display: inline-block; /* 1 */ display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */ vertical-align: baseline; /* 2 */
} }
/** /**
* IE * Remove the default vertical scrollbar in IE.
*/ */
textarea { textarea {
overflow: auto; overflow: auto;
} }
/** /**
* 1. IE 10- box-sizing * 1. Add the correct box sizing in IE 10-.
* 2. IE 10- * 2. Remove the padding in IE 10-.
*/ */
[type="checkbox"], [type="checkbox"],
[type="radio"] { [type="radio"] {
box-sizing: border-box; /* 1 */ box-sizing: border-box; /* 1 */
@ -320,34 +356,38 @@ textarea {
} }
/** /**
* Chrome * Correct the cursor style of increment and decrement buttons in 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. ChromeSafari * 1. Correct the odd appearance in Chrome and Safari.
* 2. Safari * 2. Correct the outline style in Safari.
*/ */
[type="search"] { [type="search"] {
-webkit-appearance: textfield; /* 1 */ -webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */ outline-offset: -2px; /* 2 */
} }
/** /**
* macOSChromeSafari * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
*/ */
[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. iOSSafari * 1. Correct the inability to style clickable types in iOS and Safari.
* 2. inherit Safari * 2. Change font properties to `inherit` in Safari.
*/ */
::-webkit-file-upload-button { ::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */ -webkit-appearance: button; /* 1 */
font: inherit; /* 2 */ font: inherit; /* 2 */
@ -356,18 +396,20 @@ textarea {
/* Interactive /* Interactive
========================================================================== */ ========================================================================== */
/** /*
* IE 9- * Add the correct display in IE 9-.
* 1. Edge, IE, Firefox * 1. Add the correct display in Edge, IE, and Firefox.
*/ */
details, /* 1 */ details, /* 1 */
menu { menu {
display: block; display: block;
} }
/** /*
* summary * Add the correct display in all browsers.
*/ */
summary { summary {
display: list-item; display: list-item;
} }
@ -376,15 +418,17 @@ summary {
========================================================================== */ ========================================================================== */
/** /**
* IE 9- canvas * Add the correct display in IE 9-.
*/ */
canvas { canvas {
display: inline-block; display: inline-block;
} }
/** /**
* IE template * Add the correct display in IE.
*/ */
template { template {
display: none; display: none;
} }
@ -393,8 +437,9 @@ template {
========================================================================== */ ========================================================================== */
/** /**
* IE 10- [hidden] * Add the correct display in IE 10-.
*/ */
[hidden] { [hidden] {
display: none; display: none;
} }

@ -1,36 +1,14 @@
//
// --color-primary: // css
$--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,33 +1,7 @@
// 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,27 +1,8 @@
// normalize.css @import "_normalize"; // api: https://github.com/necolas/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