wking-jie 2 months ago
commit 8998395a99

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,14 +1,36 @@
// css
//
// --color-primary:
$--color-primary: #155BD4;
// Navbar
// Navbar ()
//
$navbar--background-color: $--color-primary;
// Sidebar
// Sidebar ()
//
$sidebar--background-color-dark: #263238;
//
$sidebar--color-text-dark: #8a979e;
// Content
// Content ()
//
$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 {
/*
* CSS
*/
/*
* --el-color-primary:
*
*
*/
--el-color-primary: #155BD4;
/*
* --el-color-primary-light-3:
*
*
*/
--el-color-primary-light-3: #447cdd;
/*
* --el-menu-item-height: 40px
* Element Plus
*
*/
--el-menu-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";
//
// _base resettypographylayout
//
@import "_base";
// Element Plus
// Element Plus
// Element Plus
@import "@/styles/element-variables.scss";
// z-index
// .element-error-message-zindex z-index
// z-index 3000使 !important
.element-error-message-zindex {
z-index:3000 !important;
z-index: 3000 !important;
}

Loading…
Cancel
Save