安装内容
npm create vite@latest //创建项目
npm i vue-router//路由
npm i axios//发起异步请求
npm i pinia//状态管理
npm i element-plus//第三方组件库
npm i scss sass scss-loader -D//css预编译
封装vue-router
1.在src下创建routes/index.ts
import { createRouter,createWebHashHistory } from 'vue-router'
const router = createRouter({
history:createWebHashHistory(),
routes:[
{
path:"/login",
meta:{title:"登录页"},
component:()=>import('../views/Login.vue')
},
{
path:"/",
// component:()=>import('../views/Login.vue')
redirect:"/login"
},
{
path:"/home",
meta:{title:"首页"},
component:()=>import('../views/Home.vue')
}
]
})
export default router;
2.在main.ts中引入router
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 映入路由
import router from "./routes/index"
createApp(App)
.use(router)
.mount('#app')
element-plus表单组件封装登录页(不含axios)
<template>
<div class="bg">
<div class="box">
<h2>系统管理后台</h2>
<el-form
ref="formRef"
style="max-width: 600px"
:model="formData"
status-icon
:rules="rules"
label-width="auto"
size="small"
>
<el-form-item label="账号" prop="username">
<el-input v-model="formData.username" type="text" autocomplete="off" />
</el-form-item>
<el-form-item label="密码" prop="Password">
<el-input
v-model="formData.Password"
type="password"
autocomplete="off"
/>
</el-form-item>
<el-form-item label=" ">
<el-button type="primary" @click="submitForm(formRef)">
登录
</el-button>
<el-button @click="resetForm(formRef)">取消</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script setup lang="ts">
// 导入组合式api
import { reactive, ref } from 'vue'
// 导入element-plus的类型
import type { FormInstance, FormRules } from 'element-plus'
// 定义一个ref对象绑定表单
const formRef = ref<FormInstance>()
// 账户验证规则
const validateUsername = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error('请输入账号!'))
} else {
callback()
}
}
// 账户密码规则
const validatePassword = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error('请输入密码!'))
} else {
callback()
}
}
// 表单数据
const formData = reactive({
username: '',
Password: '',
})
// 验证对象
const rules = reactive<FormRules<typeof formData>>({
username: [{ validator: validateUsername, trigger: 'blur' }],
Password: [{ validator: validatePassword, trigger: 'blur' }],
})
// 提交表单、登录
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
console.log('submit!')
} else {
console.log('error submit!')
}
})
}
// 重置表单、取消
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
}
</script>
<style lang="scss" scoped>
.bg{
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
background: linear-gradient(to bottom,#142233,#5579a5) ;
.box{
width: 400px;
height: 200px;
border: 1px solid #fff;
padding: 20px;
h2{
margin-bottom: 20px;
text-align: center;
font-size: 20px;
color: white;
}
::v-deep .el-form-item__label{
color: #fff;
}
}
}
</style>
该阶段呈现效果为:
封装axios
创建utils/request.ts
import axios from "axios"
import { ElNotification } from "element-plus"
const instance = axios.create({
baseURL:"http://127.0.0.1:80",//公共url 具体项目中可具体区分开发dev、测试test、正式pro
timeout:3000,//请求超时时间
})
// 设置请求拦截器 在发送请求之前做什么、例如每次请求发送token
instance.interceptors.request.use(function (config){
config.headers['Authorization'] = sessionStorage.getItem("hotel_manager_token")
return config;
},function(error){
return Promise.reject(error);
});
// 设置响应拦截器 获取到请求数据时做什么、例如根据状态码判断获取数据是否成功
instance.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});
// 封装get与post请求
export const $get = async (url:string,params:object={}) => {
const {data} = await instance.get(url,{params})
return data
}
export const $post = async (url:string,params:object={}) => {
const {data} = await instance.post(url,params)
return data
}
对封装后的axios的使用
创建apis文件夹,在其中封装请求
admin.ts
import { $get,$post } from "../utils/request";
import { ElNotification } from 'element-plus'
// 用户登录
export const apiLogin = async (params:object)=>{
let ret = await $post('/api/login',params)
if(ret.code == 200){
sessionStorage.setItem("hotel_manager_token",ret.token)
ElNotification({
title: '通知',
message: ret.msg,
type: 'success',
})
// 添加状态方便判断
return true;
}else{
ElNotification({
title: '通知',
message: ret.msg,
type: 'error',
})
return false;
}
}
// 获取用户信息
export const apiAdminInfo = async (params:object)=>{
let ret = await $get('/my/getUserInfo',params)
return ret
}
封装首页
效果图
整体代码
<template>
<div class="index">
<div class="index-left">
<h2>酒店系统管理后台</h2>
<el-menu router active-text-color="#ffd04b" background-color="#142233" class="el-menu-vertical-demo" default-active="2" text-color="#fff">
<el-sub-menu index="1">
<template #title>
<el-icon>
<User />
</el-icon>
<span>账户管理</span>
</template>
<el-menu-item index="/admin/role">角色管理</el-menu-item>
<el-menu-item index="/admin/user">用户管理</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<el-icon>
<DishDot />
</el-icon>
<span>客房管理</span>
</template>
<el-menu-item index="/room/roomtype">房型管理</el-menu-item>
<el-menu-item index="/room/room">房间管理</el-menu-item>
</el-sub-menu>
<el-sub-menu index="3">
<template #title>
<el-icon>
<Camera />
</el-icon>
<span>客户管理</span>
</template>
<el-menu-item index="/custom/linein">入住管理</el-menu-item>
<el-menu-item index="/custom/order">客户订单</el-menu-item>
</el-sub-menu>
<el-sub-menu index="4">
<template #title>
<el-icon>
<Setting />
</el-icon>
<span>系统管理</span>
</template>
<el-menu-item index="/system/menu">菜单管理</el-menu-item>
<el-menu-item index="/system/dictionary">字典管理</el-menu-item>
</el-sub-menu>
</el-menu>
</div>
<div class="index-right">
<div class="index-header">
<el-menu router :ellipsis="false" mode="horizontal" background-color="#142233" text-color="#fff" active-text-color="#ffd04b">
<el-menu-item index="/home">
<el-icon>
<HomeFilled />
</el-icon>
首页
</el-menu-item>
<el-menu-item index="/mail">
<el-icon>
<Message />
</el-icon>邮件</el-menu-item>
<el-menu-item index="/message">
<el-icon>
<ChatDotSquare />
</el-icon>消息</el-menu-item>
<el-sub-menu index="admin">
<template #title><el-icon>
<Avatar />
</el-icon>管理员</template>
<el-menu-item index="/admin/mine">个人中心</el-menu-item>
<el-menu-item index="/admin/setpwd">修改密码</el-menu-item>
<el-menu-item @click="exit" index="">退出系统</el-menu-item>
</el-sub-menu>
</el-menu>
</div>
<div class="index-content">
<router-view />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ElMessageBox } from 'element-plus'
import {
User,
DishDot,
Camera,
Setting,
HomeFilled,
Message,
ChatDotSquare,
Avatar,
} from '@element-plus/icons-vue'
import { onMounted } from 'vue'
import useUser from '../store/user'
import { useRouter } from 'vue-router'
const userStore = useUser()
const router = useRouter()
onMounted(() => {
if (!userStore.user.id) {
router.push('/')
}
})
// 退出登录
const exit = () => {
ElMessageBox.confirm('是否退出登录?', '系统提示', {
confirmButtonText: '登录',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
// 进行退出,清除状态栏中的数据(顺便清除本地中的数据)和跳转回到登录页
userStore.clearUser()
router.push('/login')
})
.catch(() => {})
}
</script>
<style lang="scss" scoped>
.index{
display: flex;
width: 100vw;
height: 100vh;
.index-left{
width: 200px;
height: 100%;
background-color: #142233 ;
color: #fff;
h2{
height: 60px;
color: #fff;
font-size: 16px;
text-align: center;
line-height: 60px;
}
.el-menu {
border-right: none;
}
}
.index-right{
display: flex;
flex-direction: column;
width: 100%;
.index-header{
display: flex;
justify-content: right ;
height: 60px;
background-color: #142233;
.el-menu{
border-bottom: none;
}
}
.index-content{
flex: 1;
padding: 5px;
}
}
}
</style>
随后在router.ts和views中添加对应的页面以及路由;并添加路由导航,设置跳转时页面的加载效果;进度条(NProgress );
修改后其具体的代码为:
import { createRouter,createWebHashHistory } from 'vue-router'
// 引入进度条
import NProgress from "nprogress"
import "nprogress/nprogress.css"
const router = createRouter({
history:createWebHashHistory(),
routes:[
{
path:"/",
meta:{title:"登录"},
component:()=>import('../views/Login.vue')
},
{
path:"/login",
// component:()=>import('../views/Login.vue')
redirect:"/"
},
{
path:"/index",
meta:{title:"酒店后台管理系统"},
component:()=>import('../views/Index.vue'),
redirect:'/home',
children:[
{
path:"/home",
meta:{title:"首页"},
component:()=>import('../views/Home.vue')
},
{
path:"/mail",
meta:{title:"邮件"},
component:()=>import('../views/Mail.vue')
},{
path:"/message",
meta:{title:"消息"},
component:()=>import('../views/Message.vue')
},{
path:"/admin/mine",
meta:{title:"个人中心"},
component:()=>import('../views/user/Mine.vue')
},
{
path:"/admin/setpwd",
meta:{title:"修改密码"},
component:()=>import('../views/user/SetPwd.vue')
},
{
path:"/admin/role",
meta:{title:"角色管理"},
component:()=>import('../views/user/Role.vue')
},{
path:"/admin/user",
meta:{title:"用户管理"},
component:()=>import('../views/user/User.vue')
},
{
path:"/room/room",
meta:{title:"房间管理"},
component:()=>import('../views/room/Room.vue')
},{
path:"/room/roomtype",
meta:{title:"房型管理"},
component:()=>import('../views/room/RoomType.vue')
},{
path:"/custom/linein",
meta:{title:"入住管理"},
component:()=>import('../views/custom/LiveIn.vue')
},{
path:"/custom/order",
meta:{title:"客户订单"},
component:()=>import('../views/custom/Order.vue')
},
{
path:"/system/dictionary",
meta:{title:"字典管理"},
component:()=>import('../views/system/Dictionary.vue')
},{
path:"/system/menu",
meta:{title:"菜单管理"},
component:()=>import('../views/system/Menu.vue')
},
]
}
]
})
// 设置导航守卫,使内容标题随路由而变化
router.beforeEach((to,from,next)=>{
// 启动进度条
NProgress.start();
next()
})
router.afterEach((to,from)=>{
if(to.meta && to.meta.title){
// 修改跳转路由文字
document.title = to.meta.title
// 完成进度条
NProgress.done()
}
})
export default router;
使用pinia实现对用于信息数据的存储
相关链接:【一文搞定Pinia】超详细的Pinia教程,比Vuex香太多了!
具体使用
1.下载 yarn add pinia 或者使用 npm : npm install pinia;
2.在mina.ts中引入
// 引入pinia
import {createPinia} from 'pinia';
createApp(App)
.use(router)
.use(ElementPlus)
.use(createPinia())
.mount('#app')
3.创建store文件夹,进行组件化管理
store\user.ts
import { defineStore } from "pinia";
export default defineStore('user',{
state(){
return{
user:{
id:""
}
}
},
actions:{
setUser(user:object){
// 修改用户的数据
this.user = user
},
clearUser(){
// 清除本地存储中的token和user
sessionStorage.removeItem('hotel_manager_token')
sessionStorage.removeItem('hotel_manager_user')
this.user = {
id:""
}
}
}
})
4.具体的使用
// 导入全局状态
import useUser from '../store/user'
// 获取全局状态
const userStore = useUser()
// 提交表单、登录
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate(async (valid) => {
if (valid) {
const ret = await apiLogin(formData)
if (ret) {
const user = await apiAdminInfo({ username: formData.username })
//使用pinia中的事件,修改存储的数据
userStore.setUser(user.data)
// 设置永久化存储
sessionStorage.setItem('hotel_manager_user', JSON.stringify(user.data))
router.push('/index')
}
} else {
console.log('error submit!')
}
})
}
对状态中的数据进行持久化处理(自己去写)
1.在login.vue中的登录事件中设置修改状态信息和会话存储;
2.在 App.vue中检测会话存储中是否存在用户数据
<script lang="ts" setup>
// 引入pinia状态管理
import useUser from './store/user'
const userStore = useUser()
import { onMounted } from 'vue'
onMounted(() => {
// 如果本地用户存在,将本地数据存储
if (sessionStorage.getItem('hotel_manager_user')) {
let user = JSON.parse(sessionStorage.getItem('hotel_manager_user'))
userStore.setUser(user)
}
})
</script>
3.在login.vue和index.vue中使用onMounted判断状态中是否有数据,来判断是否跳转
login.vue
// 导入组合式api
import { reactive, ref, onMounted } from 'vue'
// 判断是否可以跳转回来(如果存在用户信息等不能跳转回来)
onMounted(() => {
// 存在用户信息跳回主页
if (userStore.user.id) {
router.push('/index')
}
})
index.vue:
onMounted(() => {
if (!userStore.user.id) {
//不存在用户信息跳转登录页
router.push('/')
}
})
实现用户退出系统功能
1.在首页设置
html:
<el-menu-item @click="exit" index="">退出系统</el-menu-item>
js:
const exit = () => {
ElMessageBox.confirm('是否退出登录?', '系统提示', {
confirmButtonText: '登录',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
// 进行退出,清除状态栏中的数据(顺便清除本地中的数据)和跳转回到登录页
userStore.clearUser()
router.push('/login')
})
.catch(() => {})
}
2.在pinia中设置退出功能(重置用户信息数据和清除本地存储的token和user数据)
clearUser(){
// 清除本地存储中的token和user
sessionStorage.removeItem('hotel_manager_token')
sessionStorage.removeItem('hotel_manager_user')
this.user = {
id:""
}
}
扩展
实现网站中网页图标(icon的修改)
下载一个svg格式的图片,命名为vite.svg,修改项目中public\vite.svg实现修改