【慧游鲁博】(2)——前端功能页面完善v1
【慧游鲁博】(2)——前端功能页面完善v1
登录/注册界面
说明:目前主页面导航栏(默认显示的首页)点击退出登录后无响应
增加内容:
- 开发登录页面内容
- 为导航栏中的选项添加功能,使其点击后能够自动跳转相应页面
修改路由
// 路由 import { createRouter, createWebHistory } from 'vue-router' //导入组件 import LoginVue from '@/views/Login.vue' import LayoutVue from '@/views/Layout.vue' import Dashboard from '@/views/Dashboard.vue' // 用户(管理员)相关组件 import UserResetPasswordVue from '@/views/user/UserResetPassword.vue' // 定义路由关系 const routes = [ { path: '/login', component: LoginVue }, { path: '/', component: LayoutVue, redirect: '/dashboard', children: [ { path: 'dashboard', component: Dashboard, meta: { title: '仪表盘' } }, { path: 'user/reset-password',component: UserResetPasswordVue,meta: { title: '重置密码' } } ] }, ] // 创建路由器 const router = createRouter({ history: createWebHistory(), routes: routes }) // 导出路由 export default router
修改Layout.vue
导航栏右侧部分
修改js部分
import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue"; import { useRoute,useRouter } from "vue-router";//添加useRouter import { useTokenStore } from '@/stores/token'; // 导入token存储 import { useUserInfoStore } from '@/stores/userInfo'; // 导入用户信息存储 import { ElMessage } from 'element-plus'; // 导入消息提示 import { Setting, User, DataAnalysis, Fold, Expand, ArrowDown, Lock, SwitchButton, ArrowLeft, } from "@element-plus/icons-vue"; const isCollapse = ref(false); const showUserDropdown = ref(false); const isMobileMenuOpen = ref(false); const route = useRoute(); const router = useRouter(); // 获取路由器实例 const tokenStore = useTokenStore(); // 获取token存储 const userInfoStore = useUserInfoStore(); // 获取用户信息存储 //退出登录 const logout=()=>{ //清除token tokenStore.setToken(''); //清除用户信息 userInfoStore.userInfoStore({}); //清除本地存储中的相关信息 localStorage.removeItem('token'); //提示用户已经退出 ElMessage({ message:'退出登录成功', type:'success', duration:2000 }) //隐藏下拉菜单 showUserDropdown.value=false; //跳转登录界面 router.push('/login'); } //跳转到个人信息页面 const goToUserInfo=()=>{ router.push('/user/info'); showUserDropdown.value =false; } //跳转到修改密码页面 const goToResetPassword=()=>{ router.push('/user/reset-password'); showUserDropdown.value=false; } const toggleSidebar = () => { isCollapse.value = !isCollapse.value; // 保存用户偏好到localStorage localStorage.setItem("sidebarStatus", isCollapse.value ? "1" : "0"); // 移动设备上,切换侧边栏状态 if (window.innerWidth { isMobileMenuOpen.value = false; }; const activeMenu = computed(() => { return route.path; }); // 保留这个映射,以防其他地方需要使用 const pathNameMap = computed(() => { return { "/admin/login": "登录", "/admin/user-info": "用户信息", "/admin/update-password": "更新密码", "/user/statistics": "数据统计", "/user/distribution": "用户角色分布", "/user/growth": "新用户增长趋势", "/user/profile": "用户画像", "/analysis/interaction": "多模态交互日志", "/analysis/guidance": "导览行为", }; }); // 使用variables.scss中的颜色变量 const menuBgColor = computed(() => "transparent"); const menuTextColor = computed(() => "rgba(255, 255, 255, 0.8)"); const menuActiveTextColor = computed(() => "var(--color-accent)"); // 点击外部关闭用户下拉菜单 const handleClickOutside = (event) => { const userDropdown = document.querySelector(".navbar-user"); if (userDropdown && !userDropdown.contains(event.target)) { showUserDropdown.value = false; } }; // 监听窗口大小变化,自动处理移动设备上的侧边栏 const handleResize = () => { if (window.innerWidth { document.addEventListener("click", handleClickOutside); window.addEventListener("resize", handleResize); // 从localStorage读取侧边栏状态 const sidebarStatus = localStorage.getItem("sidebarStatus"); if (sidebarStatus) { isCollapse.value = sidebarStatus === "1"; } // 初始化时检查窗口大小 handleResize(); }); onBeforeUnmount(() => { document.removeEventListener("click", handleClickOutside); window.removeEventListener("resize", handleResize); }); // 监听路由变化,在移动设备上自动关闭侧边栏 watch( () => route.path, () => { if (window.innerWidth
创建登录页面 (Login.vue)
- 提供基本的表单验证
- 点击登录按钮时,显示成功消息并跳转回dashboard
- 不进行实际的身份验证(之后与后端交互后再进行完善)
后续扩展:
- 这个简单实现可以在后端API准备好后轻松扩展
- 可以添加token存储、验证逻辑和路由
import { ref, reactive } from 'vue'; import { useRouter } from 'vue-router'; import { ElMessage } from 'element-plus'; import { User, Lock } from '@element-plus/icons-vue'; import { useTokenStore } from '@/stores/token'; import { useUserInfoStore } from '@/stores/userInfo'; // 获取路由器实例和存储实例 const router = useRouter(); const tokenStore = useTokenStore(); const userInfoStore = useUserInfoStore(); // 表单引用 const loginFormRef = ref(null); const loading = ref(false); // 登录表单数据 const loginForm = reactive({ username: '', password: '' }); // 表单验证规则 const loginRules = { username: [ { required: true, message: '请输入用户名', trigger: 'blur' }, { min: 3, max: 20, message: '用户名长度应为3-20个字符', trigger: 'blur' } ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 20, message: '密码长度应为6-20个字符', trigger: 'blur' } ] }; // 处理登录 - 简化版,不做实际验证 const handleLogin = async () => { // 表单验证 await loginFormRef.value.validate(async (valid) => { if (!valid) return; loading.value = true; // 模拟登录延迟 setTimeout(() => { // 简单提示 ElMessage({ message: '登录成功', type: 'success' }); // 跳转到首页 router.push('/dashboard'); loading.value = false; }, 500); }); }; // 导入全局样式 @import "@/styles/base/index.scss";
创建login的styles/views/login.scss文件,以及添加到index.scss中
/* login.scss - 布局样式 */ @import 'src/styles/base/variables.scss'; @import 'src/styles/base/mixins.scss'; .login-container { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: url('@/assets/login_bg.jpg') no-repeat center center; background-size: cover; &::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.4); } } .login-box { position: relative; width: 400px; padding: 40px; background-color: rgba(255, 255, 255, 0.9); border-radius: var(--border-radius-md); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); } .login-header { display: flex; flex-direction: column; align-items: center; margin-bottom: 30px; } .login-logo { width: 80px; height: 80px; margin-bottom: 16px; } .login-title { font-family: var(--font-family-artistic); font-size: 28px; color: var(--color-primary); margin: 0; } .login-form { margin-top: 20px; } .login-btn-container { margin-top: 30px; } .login-btn { width: 100%; height: 44px; font-size: 16px; background-color: var(--color-primary); border-color: var(--color-primary); &:hover, &:focus { background-color: var(--color-primary-light); border-color: var(--color-primary-light); } } @media (max-width: 768px) { .login-box { width: 90%; padding: 30px 20px; } }
index.scss
/* 引入所有样式模块 */ // 基础样式 @import 'src/styles/base/variables.scss'; @import 'src/styles/base/mixins.scss'; @import 'src/styles/base/transitions.scss'; // 各个页面和组件的样式 @import '../views/layout.scss'; @import '../views/dashboard.scss'; @import '../views/components/navbar.scss'; @import '../views/components/sidebar.scss'; @import '../views/login.scss';
Pinia 存储(全局状态管理)
创建 stores/token.js
定义了两个 Pinia 存储(store),用于在 Vue 应用中管理全局状态
//定义store import {defineStore} from 'pinia' export const useTokenStore = defineStore('token', { state: () => ({ token: localStorage.getItem('token') || '' }), actions: { setToken(token) { this.token = token; } }, getters: { getToken: (state) => state.token, isLoggedIn: (state) => !!state.token }, // 持久化配置 persist: true });
管理用户的认证令牌(token)
- state:
- token:存储用户的认证令牌,初始值会尝试从 localStorage 中获取,如果没有则为空字符串
- actions:
- setToken(token):设置新的令牌值
- getters:
- getToken:获取当前令牌值
- isLoggedIn:检查用户是否已登录(通过判断令牌是否存在)
- persist: true:启用持久化存储,这意味着存储的数据会被保存到浏览器的本地存储中,即使页面刷新也不会丢失
创建stores/userInfo.js
import { defineStore } from 'pinia'; export const useUserInfoStore = defineStore('userInfo', { state: () => ({ userInfo: {} }), actions: { setUserInfo(info) { this.userInfo = info; } }, getters: { getUserInfo: (state) => state.userInfo, getUserName: (state) => state.userInfo?.username || '未登录' }, // 持久化配置 persist: true });
管理用户的个人信息
- state:
- userInfo:存储用户的详细信息,初始为空对象
- actions:
- setUserInfo(info):设置或更新用户信息
- getters:
- getUserInfo:获取完整的用户信息对象
- getUserName:获取用户名,如果不存在则返回"未登录"
- persist: true:同样启用了持久化存储
修改侧边栏
修改侧边栏功能键
修改一级菜单二级菜单悬停颜色
让一级菜单和二级菜单悬停颜色一致,修改sidebar.scss - 侧边栏样式
统一使用绿色背景:
- 将所有菜单项的悬停和激活状态背景色从 var(--color-hover) 或 var(--color-primary-light) 改为 var(--color-primary)
/* sidebar.scss - 侧边栏样式 */ @import 'src/styles/base/variables.scss'; @import 'src/styles/base/mixins.scss'; .sidebar { position: fixed; top: 0; left: 0; bottom: 0; width: var(--sidebar-width); background: linear-gradient(to bottom, var(--color-primary-dark), var(--color-primary-darker)); color: var(--color-neutral-100); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); z-index: 1000; box-shadow: 2px 0 10px var(--color-shadow); display: flex; flex-direction: column; overflow: hidden; &.collapsed { width: var(--sidebar-collapsed-width); .sidebar-logo { padding: var(--spacing-2); justify-content: center; span { opacity: 0; width: 0; margin: 0; } img { margin-right: 0; } } .el-menu { --el-menu-text-color: transparent; } .el-menu-item, .el-sub-menu__title { padding: 0 !important; justify-content: center; .el-icon { margin: 0 !important; } } .el-sub-menu__icon-arrow { display: none; } .sidebar-menu-item { padding: 0 !important; justify-content: center; } .el-menu-item-group { .sidebar-menu-item { border: 1px solid var(--color-border); border-radius: var(--border-radius-sm); margin: var(--spacing-2) var(--spacing-2); padding: var(--spacing-3) var(--spacing-4); transition: all 0.3s ease; // 添加一些阴影效果 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); // hover 状态 - 使用绿色背景 &:hover { border-color: var(--color-primary); background-color: var(--color-primary) !important; transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } // 选中状态 &.is-active { border-color: var(--color-primary); background-color: var(--color-primary) !important; font-weight: 600; // 可以添加左边框强调 border-left: 3px solid var(--color-accent); } // 确保文字不会换行 white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } // 相邻菜单项之间的间距 .sidebar-menu-item + .sidebar-menu-item { margin-top: var(--spacing-2); } } } &-logo { height: var(--header-height); padding: 0 var(--spacing-4); display: flex; align-items: center; justify-content: flex-start; background-color: var(--color-primary-darker); overflow: hidden; transition: all 0.3s ease; border-bottom: 1px solid var(--color-border); a { display: flex; align-items: center; text-decoration: none; width: 100%; img { height: 32px; width: auto; margin-right: var(--spacing-3); transition: margin 0.3s ease; } span { color: var(--color-neutral-100); font-size: var(--font-size-max); font-family: var(--font-family-artistic); // 使用艺术字体 font-weight: 500; white-space: nowrap; overflow: hidden; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); // 增强文字阴影 transition: all 0.3s ease; letter-spacing: 2px; // 增加字间距 // 添加特效(可选) background: linear-gradient(45deg, var(--color-neutral-100), var(--color-accent-light)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; animation: shimmer 3s infinite linear; } } } .el-scrollbar { flex: 1; height: calc(100vh - var(--header-height) - 50px); // 减去底部收缩按钮的高度 } .el-menu { border-right: none; background-color: var(--color-primary-dark); --el-menu-bg-color: transparent !important; --el-menu-text-color: rgba(255, 255, 255, 0.8); --el-menu-hover-bg-color: var(--color-primary); --el-menu-active-color: var(--color-neutral-100); &-item { height: 50px; line-height: 50px; display: flex; align-items: center; transition: all 0.3s ease; margin: 4px 0; border-radius: 4px; margin-left: 8px; margin-right: 8px; .el-icon { margin-right: var(--spacing-3); font-size: 18px; color: rgba(255, 255, 255, 0.7); transition: all 0.3s ease; } &.is-active { background-color: var(--color-primary) !important; color: var(--color-neutral-100) !important; font-weight: 600; .el-icon { color: var(--color-neutral-100); } &::before { content: ''; position: absolute; left: 0; top: 0; height: 100%; width: 4px; background-color: var(--color-accent); border-radius: 0 2px 2px 0; } } &:hover { background-color: var(--color-primary) !important; .el-icon { color: var(--color-neutral-100); } } } .el-sub-menu { &__title { display: flex; align-items: center; transition: all 0.3s ease; margin: 4px 8px; border-radius: 4px; .el-icon { margin-right: var(--spacing-3); font-size: 18px; color: var(--color-neutral-100); transition: all 0.3s ease; } &:hover { background-color: var(--color-primary) !important; .el-icon { color: var(--color-neutral-100); } } } &.is-active { > .el-sub-menu__title { color: var(--color-neutral-100) !important; background-color: var(--color-primary) !important; .el-icon { color: var(--color-neutral-100); } } } // 添加二级菜单项的样式 .el-menu-item { &:hover { background-color: var(--color-primary) !important; } &.is-active { background-color: var(--color-primary) !important; } } } // 统一所有菜单项的悬停颜色 .sidebar-menu-item { &:hover { background-color: var(--color-primary) !important; } &.is-active { background-color: var(--color-primary) !important; } } } // 侧边栏收缩切换按钮 &-toggle { height: 50px; display: flex; align-items: center; justify-content: space-between; padding: 0 var(--spacing-4); cursor: pointer; border-top: 1px solid var(--color-border); transition: all 0.3s ease; background-color: var(--color-primary-darker); &:hover { background-color: var(--color-primary); } .toggle-text { font-size: var(--font-size-sm); color: var(--color-neutral-200); transition: opacity 0.3s ease; } .toggle-icon { font-size: var(--font-size-md); color: var(--color-accent); transition: transform 0.3s ease; } .sidebar.collapsed & { justify-content: center; .toggle-text { display: none; } .toggle-icon { transform: rotate(180deg); } } } }
页面展示
点击退出登录即可到达登录界面
当前登录页面(后续的背景图还可能修改)
具有友好提示
(目前默认登录成功,后续与后端进行交互完善登录验证)
悬停颜色一致
- 将所有菜单项的悬停和激活状态背景色从 var(--color-hover) 或 var(--color-primary-light) 改为 var(--color-primary)
- state:
- state:
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。