后台管理项目笔记 (vue3+vite+element-plus+ts)

安装内容

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实现修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值