<script lang="ts" setup>
import { ref, onMounted, nextTick } from 'vue'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import { useRouter } from 'vue-router'
import request from '@/utils/request'
import type { ResultModel } from '@/api/model/model'
// 类型定义
interface PasswordForm {
oldPassword: string
newPassword: string
confirmPassword: string
}
interface LoginUser {
id: number
username: string // 增加用户名字段
name: string
role: string
}
// API调用(修改参数结构)
const updatePasswordApi = (data: { username: string; password: string; newPassword: string }) =>
request.put<any, ResultModel>('/emps/update', data)
const router = useRouter()
const loginName = ref('管理员')
const loginRole = ref('管理员')
const username = ref('') // 存储用户名
const showPasswordDialog = ref(false)
const passwordForm = ref<PasswordForm>({
oldPassword: '',
newPassword: '',
confirmPassword: '',
})
const passwordFormRef = ref<FormInstance>()
const loading = ref(false)
// 密码验证规则
const passwordRules: FormRules<PasswordForm> = {
oldPassword: [
{ required: true, message: '请输入旧密码', trigger: 'blur' },
{ min: 6, max: 20, message: '密码长度6-20个字符', trigger: 'blur' },
],
newPassword: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ min: 6, max: 20, message: '密码长度6-20个字符', trigger: 'blur' },
{
validator: (_, value, callback) => {
if (value === passwordForm.value.oldPassword) {
callback(new Error('新密码不能与旧密码相同'))
} else {
callback()
}
},
trigger: 'blur',
},
],
confirmPassword: [
{ required: true, message: '请确认新密码', trigger: 'blur' },
{
validator: (_, value, callback) => {
if (value !== passwordForm.value.newPassword) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
},
trigger: 'blur',
},
],
}
// 初始化用户信息(增加用户名获取)
onMounted(() => {
const token = localStorage.getItem('token')
if (token) {
try {
const loginUser: LoginUser = JSON.parse(token)
loginName.value = loginUser.name || '管理员'
loginRole.value = loginUser.role || '管理员'
username.value = loginUser.username || '' // 获取用户名
} catch (e) {
console.error('解析token失败:', e)
}
}
})
// 打开修改密码对话框(保持不变)
const openPasswordDialog = () => {
showPasswordDialog.value = true
passwordForm.value = { oldPassword: '', newPassword: '', confirmPassword: '' }
nextTick(() => {
if (passwordFormRef.value) {
passwordFormRef.value.resetFields()
}
})
}
// 提交修改密码(调整跳转逻辑)
const submitPassword = async () => {
if (!passwordFormRef.value) return
try {
await passwordFormRef.value.validate()
loading.value = true
const response = await updatePasswordApi({
username: username.value,
password: passwordForm.value.oldPassword,
newPassword: passwordForm.value.newPassword,
})
if (response.code === 1) {
ElMessage.success('密码修改成功,请重新登录')
showPasswordDialog.value = false
passwordFormRef.value.resetFields()
// 直接跳转到登录页面,不显示退出确认
localStorage.removeItem('token')
router.push('/login')
} else {
ElMessage.error(response.msg || '密码修改失败')
}
} catch (error) {
console.error('修改密码失败:', error)
ElMessage.error('请求失败,请稍后重试')
} finally {
loading.value = false
}
}
// 修改退出登录方法(保持原有的确认提示)
const logout = () => {
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true,
})
.then(() => {
localStorage.removeItem('token')
router.push('/login')
ElMessage.success('退出登录成功')
})
.catch(() => {})
}
</script>
<template>
<div class="common-layout">
<el-container class="layout-container">
<!-- 优化后的头部 -->
<el-header class="header">
<div class="header-content">
<div class="logo-section">
<div class="logo"></div>
<span class="title">AI教学管理系统</span>
</div>
<div class="user-section">
<div class="user-info">
<el-avatar class="user-avatar" :size="36">
{{ loginName.charAt(0) }}
</el-avatar>
<div class="user-details">
<div class="user-name">{{ loginName }}</div>
<div class="user-role">{{ loginRole }}</div>
</div>
</div>
<div class="header-tools">
<el-tooltip content="修改密码" placement="bottom">
<el-button class="tool-btn" type="text" @click="openPasswordDialog">
<el-icon class="icon"><EditPen /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="退出登录" placement="bottom">
<el-button class="tool-btn" type="text" @click="logout">
<el-icon class="icon"><SwitchButton /></el-icon>
</el-button>
</el-tooltip>
</div>
</div>
</div>
</el-header>
<el-container class="main-container">
<!-- 侧边栏 -->
<el-aside class="aside">
<el-scrollbar class="menu-scrollbar">
<el-menu
router
default-active="/index"
class="side-menu"
background-color="#2c3e50"
text-color="#ecf0f1"
active-text-color="#f39c12"
>
<el-menu-item index="/index">
<el-icon><Menu /></el-icon>
<span>首页</span>
</el-menu-item>
<el-sub-menu index="2" class="menu-group">
<template #title>
<el-icon><Document /></el-icon>
<span>班级学员管理</span>
</template>
<el-menu-item index="/stu">
<el-icon><UserFilled /></el-icon>
<span>学员管理</span>
</el-menu-item>
<el-menu-item index="/clazz">
<el-icon><HomeFilled /></el-icon>
<span>班级管理</span>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="3" class="menu-group">
<template #title>
<el-icon><Avatar /></el-icon>
<span>部门管理</span>
</template>
<el-menu-item index="/emp">
<el-icon><Avatar /></el-icon>
<span>员工管理</span>
</el-menu-item>
<el-menu-item index="/dept">
<el-icon><HelpFilled /></el-icon>
<span>部门管理</span>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="4" class="menu-group">
<template #title>
<el-icon><PieChart /></el-icon>
<span>数据统计管理</span>
</template>
<el-menu-item index="/report/emp">
<el-icon><InfoFilled /></el-icon>
<span>员工信息统计</span>
</el-menu-item>
<el-menu-item index="/report/stu">
<el-icon><Share /></el-icon>
<span>学员信息统计</span>
</el-menu-item>
<el-menu-item index="/log">
<el-icon><Clock /></el-icon>
<span>日志信息统计</span>
</el-menu-item>
</el-sub-menu>
</el-menu>
</el-scrollbar>
</el-aside>
<!-- 主内容区 -->
<el-main class="main-content">
<router-view></router-view>
</el-main>
</el-container>
</el-container>
<!-- 修改密码对话框 -->
<el-dialog
v-model="showPasswordDialog"
title="修改密码"
width="500px"
center
:close-on-click-modal="false"
>
<el-form
ref="passwordFormRef"
:model="passwordForm"
:rules="passwordRules"
label-width="100px"
label-position="right"
>
<el-form-item label="旧密码" prop="oldPassword">
<el-input
v-model="passwordForm.oldPassword"
type="password"
placeholder="请输入当前密码"
show-password
clearable
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input
v-model="passwordForm.newPassword"
type="password"
placeholder="6-20位字符"
show-password
clearable
/>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input
v-model="passwordForm.confirmPassword"
type="password"
placeholder="请再次输入新密码"
show-password
clearable
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="showPasswordDialog = false">取消</el-button>
<el-button type="primary" @click="submitPassword" :loading="loading">
确认修改
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style scoped>
/* 布局基础样式 */
.common-layout {
height: 100vh;
display: flex;
flex-direction: column;
background-color: #f5f7fa;
}
.layout-container {
height: 100%;
display: flex;
flex-direction: column;
}
/* 头部样式 - 优化版 */
.header {
background: linear-gradient(135deg, #2c3e50 0%, #1a2530 100%);
color: white;
height: 64px;
padding: 0;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000;
position: relative;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
padding: 0 30px;
max-width: 1600px;
margin: 0 auto;
width: 100%;
}
.logo-section {
display: flex;
align-items: center;
gap: 15px;
}
.logo {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #3498db, #8e44ad);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
.title {
font-size: 1.8rem;
font-weight: 700;
letter-spacing: 1px;
background: linear-gradient(to right, #f39c12, #f1c40f);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.user-section {
display: flex;
align-items: center;
gap: 20px;
}
.user-info {
display: flex;
align-items: center;
gap: 12px;
padding: 5px 15px;
border-radius: 30px;
background: rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.user-info:hover {
background: rgba(255, 255, 255, 0.15);
}
.user-avatar {
background: linear-gradient(135deg, #3498db, #8e44ad);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.user-details {
display: flex;
flex-direction: column;
}
.user-name {
font-weight: 600;
font-size: 15px;
letter-spacing: 0.5px;
}
.user-role {
font-size: 12px;
color: #bdc3c7;
margin-top: 2px;
}
.header-tools {
display: flex;
gap: 8px;
}
.tool-btn {
color: #ecf0f1;
font-size: 18px;
padding: 8px;
transition: all 0.3s ease;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.tool-btn:hover {
background-color: rgba(255, 255, 255, 0.15);
transform: translateY(-2px);
}
.tool-btn .icon {
transition: transform 0.3s ease;
}
.tool-btn:hover .icon {
transform: scale(1.2);
}
/* 侧边栏样式 */
.aside {
background-color: #2c3e50;
color: #ecf0f1;
height: 100%;
width: 240px !important;
transition: width 0.3s ease;
border-right: 1px solid #34495e;
}
.menu-scrollbar {
height: calc(100vh - 64px);
}
.menu-scrollbar .el-scrollbar__bar.is-vertical {
width: 6px;
right: 2px;
border-radius: 4px;
background-color: rgba(255, 255, 255, 0.1);
}
.menu-scrollbar .el-scrollbar__thumb {
background-color: rgba(255, 255, 255, 0.3);
border-radius: 4px;
transition: background-color 0.3s ease;
}
.menu-scrollbar .el-scrollbar__thumb:hover {
background-color: rgba(255, 255, 255, 0.5);
}
.side-menu {
background-color: transparent;
border-right: none;
padding: 15px 0;
}
.side-menu .el-menu-item,
.side-menu .el-sub-menu .el-sub-menu__title {
margin: 6px 10px;
padding: 12px 20px;
font-size: 14px;
color: #ecf0f1;
border-radius: 6px;
transition: all 0.3s ease;
height: 48px;
display: flex;
align-items: center;
}
.side-menu .el-menu-item:hover,
.side-menu .el-sub-menu .el-sub-menu__title:hover {
background-color: #34495e;
transform: translateX(5px);
}
.side-menu .el-menu-item.is-active {
background: linear-gradient(90deg, rgba(52, 152, 219, 0.3), transparent);
color: #f39c12 !important;
font-weight: bold;
border-left: 3px solid #f39c12;
}
.menu-group .el-menu-item {
font-size: 13px;
padding-left: 50px !important;
color: #dcdcdc;
transition: all 0.3s ease;
height: 42px;
}
.menu-group .el-menu-item:hover {
background-color: #3a5267;
transform: translateX(5px);
}
.menu-group .el-menu-item.is-active {
background-color: #3a5267;
color: #f1c40f;
font-weight: 600;
}
/* 主内容区样式 */
.main-content {
background-color: #f0f2f5;
padding: 20px;
flex: 1;
overflow-y: auto;
border-radius: 8px 8px 0 0;
box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.05);
}
/* 对话框样式 */
.dialog-footer {
display: flex;
justify-content: center;
padding: 10px 20px 0;
}
/* 响应式设计 */
@media (max-width: 992px) {
.aside {
width: 200px !important;
}
.header-content {
padding: 0 20px;
}
.title {
font-size: 1.5rem;
}
.user-info .user-name {
display: none;
}
}
@media (max-width: 768px) {
.aside {
width: 64px !important;
}
.logo-section .title,
.user-info .user-details,
.el-sub-menu .el-sub-menu__title span,
.el-menu-item span {
display: none;
}
.el-sub-menu .el-sub-menu__title,
.el-menu-item {
justify-content: center;
padding: 0 !important;
}
.el-icon {
margin-right: 0 !important;
font-size: 20px;
}
.menu-group .el-menu-item {
padding-left: 20px !important;
justify-content: flex-start;
}
}
</style>
优化一下页面自适应大小,页面缩小之后右上角的退出登录会被挡住
最新发布