大家好,欢迎来到IT知识分享网。
前言:
欢迎来到本次教程。这篇文章旨在深入解析在 B 站上的 Vue3 后台管理项目,同时也为您提供了创建此类项目的实践思路。我们将通过这个笔记系统地梳理一个项目的整体框架,涵盖了我们在 Vue 课程中学习的主要内容。
在此过程中,我尝试使用 Vant4 组件库替代了原先的 Element-Plus 组件,但请注意,由于 Vant4 和 Element-Plus 有所不同,有些 Element-Plus 组件可能无法找到完全相同的 Vant4 替代品。因此,本文中的 Vant4 代码仅供参考。
由于时间紧迫,尽管我已尽最大努力保证内容的准确性和完整性,但仍有可能存在一些疏漏或不够完善的地方。如果您在阅读过程中发现任何需要改进的地方,非常欢迎指出,您的建议将对我来说极为宝贵。
在此,小天同学再次向同学们表示感谢。我希望这篇文章能为你的学习提供有价值的参考,让我们一起探索 Vue3 的世界。
使用到的技术:
附本文使用技术的官网链接
vue3:https://cn.vuejs.org/
element-plus:http://element-plus.org/zh-CN/
vant4:https://vant-contrib.gitee.io/vant/#/zh-CN
pinia:https://pinia.web3doc.top/
vuex:https://vuex.vuejs.org/zh/
JSON-SERVER:https://github.com/typicode/json-server
axios:https://www.axios-http.cn/docs/intro
项目出处:[https://www.bilibili.com/video/BV1no4y1Y7gF?p=1&vd_source=6f495ad66c7e55ede1b042f9e](https://www.bilibili.com/video/BV1no4y1Y7gF?
# 1.创建项目 如何创建一个工程 
1.打开文件夹(找到对应的文件打开)
2.集成终端打开
3.配置相关指令
npm init vue@latest
4.配置选择
5.创建vite
从这一步开始,所有的“在终端中打开,全部指的是右键vue-project再点击在集成终端中打开!!
右键点击vue-project的位置,选择在集成终端中打开
输入以下指令
npm i vite
5.引入vant
npm i vant
至此,我们的项目基本内容已经创建完毕
(注:想要检查安装的文件版本,可以控package-lock.json中寻找)
2.相关配置
1.vant配置
全局引入
在main.js中加入以下代码
import {
createApp } from 'vue'; // 1. 引入你需要的组件 import {
Button } from 'vant'; // 2. 引入组件样式 import 'vant/lib/index.css'; const app = createApp(); // 3. 注册你需要的组件 app.use(Button);
按需引入
如果是部分引入,就需要先安装一个插件,再分别引入,操作方法如下:
1.在终端中输入
# 通过 npm 安装 npm i unplugin-vue-components -D # 通过 pnpm 安装 pnpm add unplugin-vue-components -D
之后就非常简单,我们的组件需要哪一个就直接在script中引入就可以了。
注意:如果使用的是以下写法,不需要引入这一行代码,可以直接使用
每个vue组件引入即可(就是复制过来)
<script setup> import { Swipe, SwipeItem } from "vant";//其实使用了setup的写法以后,可以不需要这个 </script> <template> <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white"> <van-swipe-item>1</van-swipe-item> <van-swipe-item>2</van-swipe-item> <van-swipe-item>3</van-swipe-item> <van-swipe-item>4</van-swipe-item> </van-swipe> </template> <style scoped> .my-swipe .van-swipe-item { color: #fff; font-size: 20px; line-height: 150px; text-align: center; background-color: #39a9ed; } </style>
2.路由配置
如果你从一开始就跟着我们的操作,左边的列表里是有router的字样的,里面有一个文件index.js,那就只需要
npm install vue-router@4
3.pinia配置
1.pinia安装
如果您从一开始就是跟我们的步骤做,那您相对来说比较轻松,直接跳到第2部分。
但是如果不是,那就遭老罪喽。请执行以下步骤:
1.在终端中输入以下代码:
npm install pinia
2.将以下代码补充到main.js中
import { createPinia } from 'pinia' app.use(createPinia())
import {
defineStore } from 'pinia' // useStore 可以是 useUser、useCart 之类的任何东西 // 第一个参数是应用程序中 store 的唯一 id export const useStore = defineStore('main', {
// other options... })
2.pinia的基本内容
我们把pinia配置好了以后,现在主要的内容给写进去
import {
ref, computed } from 'vue' import {
defineStore } from 'pinia' export const useUsersStore = defineStore("main", {
// 这个相当于是一个数据仓库,是处理数据用的。 state: () => {
return {
// 比如我可以在这里定义年龄,姓名等等 // name: "xiaotian", // age: 18, // id: 1 }; }, // 这个相当于是一个计算属性,也是处理数据用的。比如说,我要把年龄加100岁, //那么我就可以在这里写一个方法,然后在页面上调用这个方法,就可以得到加了100岁的年龄了。 getters: {
// 比如我想给state的年龄+100 return state.age + 100; }, //这个相当于是一个method,写一些方法,比如处理数据的方法,比如点击事件的方法,比如axios请求的方法,比如定时器的方法... actions: {
} })
3.如何使用pinia配置好的数据
<!-- import { ref } from 'vue' --> // 注意,这里from后面是路径,别写错了,不行就先输入 ../ 看看弹出来的是啥 import { useUsersStore } from '@/stores/counter.js' // 这里是做响应式用的,在子页面修改的数据,通过这种方式可以完成同步,原因官网上面写的很清楚 import { storeToRefs } from 'pinia' // 这里的useUsersStore(),和你store/index.js中配置的是一样的 const store = useUsersStore();
//修改前 const {
name,doubleCount} = store //修改后 const {
name,doubleCount} = storeToRefs(store) // 加了一个storeToRefs()
4.JSON-SERVER配置
- node 环境安装
- 安装 json-server
- 创建数据库(其实就是一个 json 文件)
- 启动服务
1.node环境安装
2.安装json-server
npm i json-server // 想要全局安装选下面这个 npm install -g json-server
3.填写数据
- 创建 json-server-demo 文件夹,
- 在 json-server-demo 里创建 db.json 文件(这些文件夹和文件名都可以自由命名)。
- db.json 文件录入以下数据(数据来自 json-server 官方文档)
{
"posts": [ {
"id": 1, "title": "json-server", "author": "typicode" } ], "comments": [ {
"id": 1, "body": "some comment", "postId": 1 } ], "profile": {
"name": "typicode" } }
4.启动服务
运行指令
json-server --watch db.json
npm install -g json-server
- 使用管理员方式打开Power Shell
- 输入Get-ExecutionPolicy,可以查看到当前的策略
- 输入Set-ExecutionPolicy RemoteSigned,设置当前的策略为RemoteSigned
- 输入Y
5.数据修改
1.增
接下来可以增添数据
输入您的posts链接
这里就会输出相关代码
2.删
3.改
{
"title": "php", "author": "xiaotiantian" }
查
4.查
http://localhost:3000/posts/{id}
http://localhost:3000/posts?name=php&sex=man
5.更新
6.修改端口
如果你想要修改我们的端口,不再使用3000,那可以使用以下指令修改
json-server -p 8888 db.json
7.修改主机端口
终端中输入以下代码修改主机号
json-server --host 0.0.0.0 db.json
5.axios配置
1. 下载Axios
首先,在Vue 3项目中下载Axios库,可以使用npm或yarn进行安装。在终端中进入项目目录,执行以下命令进行安装:
安装npm:
// 下面两个二选一 npm install axios cnpm install axios -g
2. 配置Axios
// axios引入 import axios from "axios" //pinia引入 import store from "../store/index.js" //创建一个axios实例 const Service = axios.create({
baseURL: 'https://some-domain.com/api/', timeout: 1000, headers: {
'X-Custom-Header': 'foobar'} });
3.路由的详细配置
const routes = [ {
path: "/login", name: "login", component: () => import ("../views/pages/login.vue") // 懒加载引入登录页面组件 } ]
const routes = [ {
path: "/", name: "layout", component: () => import ("../views/Layout/LayOut.vue"), redirect: "/index", // 子路由/嵌套路由 children: [{
path: "/index", name: "index", component: () => import ("../views/pages/index.vue") // 懒加载引入首页组件 },] } ]
也就是在配置中多加入一个children项目。
{
path: "/", name: "layout", component: () => import ("../views/Layout/LayOut.vue"), redirect: "/index", // 子路由/嵌套路由 children: [{
path: "/index", name: "index", component: () => import ("../views/pages/index.vue") // 懒加载引入首页组件 }, {
path: "/roles", name: "roles", component: () => import ("../views/pages/rolesList.vue") // 懒加载引入角色列表组件 }, {
path: "/user", name: "user", component: () => import ("../views/pages/userList.vue") // 懒加载引入用户列表组件 }, {
path: "/goods", name: "goods", component: () => import ("../views/pages/goodsList.vue") // 懒加载引入商品列表组件 } ] }
完整配置:
import {
createRouter, createWebHashHistory } from 'vue-router' import store from "../store/index.js" // 引入Vuex的store实例 // 路由配置 const routes = [ // 登陆页面 {
path: "/login", name: "login", component: () => import ("../views/pages/login.vue") // 懒加载引入登录页面组件 }, {
path: "/", name: "layout", component: () => import ("../views/Layout/LayOut.vue"), redirect: "/index", // 子路由/嵌套路由 children: [{
path: "/index", name: "index", component: () => import ("../views/pages/index.vue") // 懒加载引入首页组件 }, {
path: "/roles", name: "roles", component: () => import ("../views/pages/rolesList.vue") // 懒加载引入角色列表组件 }, {
path: "/user", name: "user", component: () => import ("../views/pages/userList.vue") // 懒加载引入用户列表组件 }, {
path: "/goods", name: "goods", component: () => import ("../views/pages/goodsList.vue") // 懒加载引入商品列表组件 } ] } ] // 生成hash路由对象 const router = createRouter({
history: createWebHashHistory(), routes }) // 路由守卫 router.beforeEach((to, from, next) => {
/ * to:即将要进入的目标 * from:正要离开的路由 * next:只有执行next()页面才会进行跳转 */ // 判断用户是否登录 console.log("store", store.state.uInfo) const uInfo = store.state.uInfo.userInfo if (!uInfo.username) {
// 未登录,跳转到login if (to.path === "/login") {
next() return } next("/login") } else {
next() } }) // 暴露路由对象 export default router
4.项目的详细配置
1.登录页面
1.html+css配置
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button><a href="登录地址"></a></button> </body> </html>
我不建议这么写,因为这么写没分。
html和css我们就不细说了,我这里直接给出
<template> <div class="login_wrap"> <div class="form_wrap"> <van-form ref="formRef" :model="loginData" class="demo-dynamic" > <!-- 用户名表单项 --> <van-field v-model="loginData.username" label="用户名" name="username" :rules="[ { required: true, message: '此项为必填项', trigger: 'blur', }, ]" /> <!-- 密码表单项 --> <van-field v-model="loginData.password" label="密码" name="password" type="password" :rules="[ { required: true, message: '此项为必填项', trigger: 'blur', }, ]" /> </van-form> <!-- 登录按钮 --> <van-button type="primary" class="login_btn" @click="handleLogin">登录</van-button> </div> </div> </template> <!-- 样式 --> <style scoped> .login_wrap { width: 100%; height: 100vh; background: rgb(56, 86, 139); position: relative; } .form_wrap { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #fff; padding: 30px 50px; border-radius: 5px; } .login_btn { display: block; margin: 10px auto; } </style>
那在这个组件里的script怎么写呢?
那首先我们是要访问我们库里面有没有相关的数据在,比如账号、密码、个人信息等等,
并且核实账号密码是否正确
2.axios请求配置
我们打开service.js写入以下部分(看注释,只写新增部分!!最上边的代码之前我已经配置了,
不信点这里!!(点击这里))
//这里是原本就有的,之前axios叫你们配置过的部分 import axios from "axios" import store from "../store/index.js" // 使用create创建axios实例 const Service = axios.create({
timeout:8000, //这里写URL地址/接口地址等 baseURL:"http://122.114.229.181:3000/api/private/v1/", headers:{
"Content-type":"application/json;charset=utf-8", "Authorization":store.state.uInfo.userInfo.token } }) // //下面是新增的部分 // post请求 export const post=config=>{
return Service({
...config, method:"post", data:config.data }) } // get请求 export const get=config=>{
return Service({
...config, method:"get", params:config.data }) } // put请求 export const put=config=>{
return Service({
...config, method:"put", data:config.data }) }// delete请求 export const del=config=>{
return Service({
...config, method:"delete" }) }
当然了,我们还需要状态管理器store/userInfo.js。
export default {
state: {
// 定义一个名为 userInfo 的变量,其值为一个对象。 // 初始值为通过判断 localStorage 中是否存在名为 loginData 的数据, // 如果存在则将该数据解析为一个 JavaScript 对象,否则设置为空对象 {}。 userInfo: (localStorage.getItem('loginData') && JSON.parse(localStorage.getItem('loginData'))) || {
}, }, actions: {
// 定义一个名为 setUserInfo 的 mutation 方法来修改 userInfo 变量的值。 // 这个方法接收两个参数:state 和 uInfo,其中 state 是当前模块的状态对象, // 而 uInfo 是用于更新 userInfo 的新值。 setUserInfo(state, uInfo) {
state.userInfo = uInfo; }, }, };
如果你的程序中要写很多个状态管理,我建议在stores文件夹下新建一个index.js文件
import {
createStore } from 'vuex' import uInfo from "./state/userinfo.state.js" export default createStore({
// 数据比较多,分模块 modules: {
uInfo } })
3.思考组件包含的功能
这里既然是登陆页面,就要考虑用户登录信息的核对和保存。
既然如此,我们首先想到需要连接状态管理器、路由以及响应式,于是我们导入以下代码
import { ref } from 'vue'; import { useStore } from 'vuex'; import { useRouter } from 'vue-router';
4.aixos基本的逻辑配置
// 导入service中导出的HTTP方法 import {
post, get, put, del } from "./service" // 登录API export const loginApi = data => {
return post({
url: "/login", data }) }
之后我们再导入登录请求的API
<script setup> // 引入Vue 3的ref,用于创建响应式引用 import { ref } from 'vue'; // 引入Vuex的useStore,用于获取Vuex存储实例 import { useStore } from 'vuex'; // 引入Vue Router的useRouter,用于获取路由器实例 import { useRouter } from 'vue-router'; // 引入登录API函数 import { loginApi } from '@/util/request'; </script>
<script setup> // 引入Vue 3的ref,用于创建响应式引用 import { ref } from 'vue'; // 引入Vuex的useStore,用于获取Vuex存储实例 import { useStore } from 'vuex'; // 引入Vue Router的useRouter,用于获取路由器实例 import { useRouter } from 'vue-router'; // 引入登录API函数 import { loginApi } from '@/util/request'; // // 以下为补充内容 // // 获取Vuex存储实例 const store = useStore(); // 获取路由器实例 const router = useRouter(); // 创建一个响应式引用,存储登录表单数据 const loginData = ref({ username: '', password: '' }); // 处理登录按钮点击事件的函数 const handleLogin = async () => { // 使用登录API函数发送登录请求,并等待响应 const res = await loginApi(loginData.value); // 如果响应中包含数据 if (res.data) { // 使用Vuex存储实例的commit方法更新用户信息 store.commit('setUserInfo', res.data); // 将登录数据存储到localStorage localStorage.setItem('loginData', JSON.stringify(res.data)); // 使用路由器实例将用户重定向到根路径 router.push({ path: '/' }); } }; </script>
import {
createRouter, createWebHashHistory } from 'vue-router' import store from "../store/index.js" // 引入Vuex的store实例 // 路由配置 const routes = [ // 登陆页面 {
path: "/login", name: "login", component: () => import ("../views/pages/login.vue") // 懒加载引入登录页面组件 }, {
path: "/", name: "layout", component: () => import ("../views/Layout/LayOut.vue"), redirect: "/index", // 子路由/嵌套路由 children: [{
path: "/index", name: "index", component: () => import ("../views/pages/index.vue") // 懒加载引入首页组件 }, {
path: "/roles", name: "roles", component: () => import ("../views/pages/rolesList.vue") // 懒加载引入角色列表组件 }, {
path: "/user", name: "user", component: () => import ("../views/pages/userList.vue") // 懒加载引入用户列表组件 }, {
path: "/goods", name: "goods", component: () => import ("../views/pages/goodsList.vue") // 懒加载引入商品列表组件 } ] } ] // 生成hash路由对象 const router = createRouter({
history: createWebHashHistory(), routes }) //* //*路由守卫在这里!! //* // 路由守卫 router.beforeEach((to, from, next) => {
/ * to:从哪个页面 * from:到哪个页面 * next:只有执行next()页面才会进行跳转 */ // 判断用户是否登录 console.log("store", store.state.uInfo) const uInfo = store.state.uInfo.userInfo if (!uInfo.username) {
// 未登录,跳转到login if (to.path === "/login") {
next() return } next("/login") } else {
next() } }) // 暴露路由对象 export default router
我们的登陆页面及其功能详解到此结束。
const handleLogin = async () => { //<!-- 定义登录方法 --> const res = await loginApi(loginData.value); // <!-- 发送登录请求 --> if (res.data) { //<!-- 登录成功 --> store.commit('setUserInfo', res.data); //<!-- 提交用户信息到Vuex中 --> localStorage.setItem('loginData', JSON.stringify(res.data)); //<!-- 将用户信息保存到本地存储中 --> router.push({ path: '/' }); // <!-- 跳转到首页 --> } };
现在考虑,如果登录正在加载,或服务器出错应该弹出提示,这个提示的信息也要在axios文件中配置
import {
Toast } from 'vant'; // 请求拦截器 axios.interceptors.request.use(config => {
// 显示 loading Toast.loading({
message: 'Loading...', forbidClick: true, // 禁止点击背景 loadingType: 'spinner', // loading 样式 }); return config; // 返回请求配置 }); // 响应拦截器 axios.interceptors.response.use(response => {
// 隐藏 loading Toast.clear(); const data = response.data; // 获取响应数据 if (data.code !== 200) {
// 处理错误 Toast.fail(data.msg || '服务器出错'); // 显示错误提示 return Promise.reject(data); // 返回 rejected 状态的 Promise } return data; // 返回响应数据 }, error => {
// 隐藏 loading Toast.clear(); Toast.fail('服务器错误'); // 显示错误提示 return Promise.reject(error); // 返回 rejected 状态的 Promise });
import axios from "axios" import store from "../store/index.js" import {
Toast } from 'vant'; // 创建一个axios实例,并设置请求超时时间、基础URL和请求头 const Service = axios.create({
timeout: 8000, baseURL: "http://122.114.229.181:3000/api/private/v1/", headers: {
"Content-type": "application/json;charset=utf-8", "Authorization": store.state.uInfo.userInfo.token } }) // 请求拦截器 axios.interceptors.request.use(config => {
// 显示 loading Toast.loading({
message: 'Loading...', forbidClick: true, // 禁止点击背景 loadingType: 'spinner', // loading 样式 }); return config; // 返回请求配置 }); // 响应拦截器 axios.interceptors.response.use(response => {
// 隐藏 loading Toast.clear(); const data = response.data; // 获取响应数据 if (data.code !== 200) {
// 处理错误 Toast.fail(data.msg || '服务器出错'); // 显示错误提示 return Promise.reject(data); // 返回 rejected 状态的 Promise } return data; // 返回响应数据 }, error => {
// 隐藏 loading Toast.clear(); Toast.fail('服务器错误'); // 显示错误提示 return Promise.reject(error); // 返回 rejected 状态的 Promise }); // post请求 export const post = config => {
// 使用Service实例发送post请求,并传入请求配置 return Service({
...config, method: "post", data: config.data }) } // get请求 export const get = config => {
// 使用Service实例发送get请求,并传入请求配置 return Service({
...config, method: "get", params: config.data }) } // put请求 export const put = config => {
// 使用Service实例发送put请求,并传入请求配置 return Service({
...config, method: "put", data: config.data }) } // delete请求 export const del = config => {
// 使用Service实例发送delete请求,并传入请求配置 return Service({
...config, method: "delete" }) }
现在将login.vue的代码完整版整理出来
<template> <div class="login_wrap"> <div class="form_wrap"> <van-form ref="formRef" :model="loginData" class="demo-dynamic" > <!-- 用户名表单项 --> <van-field v-model="loginData.username" label="用户名" name="username" :rules="[ { required: true, message: '此项为必填项', trigger: 'blur', }, ]" /> <!-- 密码表单项 --> <van-field v-model="loginData.password" label="密码" name="password" type="password" :rules="[ { required: true, message: '此项为必填项', trigger: 'blur', }, ]" /> </van-form> <!-- 登录按钮 --> <van-button type="primary" class="login_btn" @click="handleLogin">登录</van-button> </div> </div> </template> <script setup> import { ref } from 'vue'; import { useStore } from 'vuex'; import { useRouter } from 'vue-router'; import { loginApi } from '@/util/request'; const store = useStore(); const router = useRouter(); const loginData = ref({ username: '', password: '' }); const handleLogin = async () => { const res = await loginApi(loginData.value); if (res.data) { store.commit('setUserInfo', res.data); localStorage.setItem('loginData', JSON.stringify(res.data)); router.push({ path: '/' }); } }; </script> <style scoped> .login_wrap { width: 100%; height: 100vh; background: rgb(56, 86, 139); position: relative; } .form_wrap { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #fff; padding: 30px 50px; border-radius: 5px; } .login_btn { display: block; margin: 10px auto; } </style>
2.角色页面
1.html+css配置
老规矩,html直接给出,因为都是element的配置。vant只要找到相同的功能直接写就可以
<template> <div> <!-- 面包屑导航 --> <van-nav-bar> <template #left> <van-icon name="arrow-left" @click="goToHome" /> </template> <template #title> 角色列表 </template> </van-nav-bar> <!-- 白色内容区域 --> <div class="page_content"> <!-- 新建角色按钮 --> <van-button type="primary" @click="dialogFormVisible=true">新建角色</van-button> <!-- 表格展示角色列表 --> <van-list :data="rolesList"> <template #default="{item}"> <van-cell :title="item.roleName" :value="item.roleDesc"> <template #right-icon> <div class="buttons"> <van-button type="primary" plain size="small" @click="editRow(item)">编辑</van-button> <van-button type="danger" plain size="small" @click="deleteRow(item)">删除</van-button> </div> </template> </van-cell> </template> </van-list> </div> <!-- 新建/编辑角色弹窗表单 --> <van-popup v-model="dialogFormVisible" round> <van-form ref="userForm" :model="formData" :rules="rules" > <van-field name="roleName" label="角色名称" v-model="formData.roleName" placeholder="请输入角色名称" /> <van-field name="roleDesc" label="角色描述" v-model="formData.roleDesc" placeholder="请输入角色描述" /> <div class="buttons"> <van-button @click="clearForm">取消</van-button> <van-button type="primary" @click="submitForm(userForm)">确定</van-button> </div> </van-form> </van-popup> </div> </template>
以及按钮触发的方法
这里可以理解为一个点击事件,做了一个绑定,点击以后自动触发函数。
@close=“clearForm”//‘编辑角色’:‘新建角色’(这个是关闭事件)
@click=“goToHome”
@click=“dialogFormVisible=true”//新建角色
@click=”editRow(item)//编辑
@click=“deleteRow(item)”//删除
@click=”submitForm(userForm)//确定按钮
// 以上是之前写过的部分 // 导入service中导出的HTTP方法 import {
post, get, put, del } from "./service" // 登录API export const loginApi = data => {
return post({
url: "/login", data }) } // * // 以下是新增部分 // * // 获取角色API export const getRolesApi = data => {
return get({
url: "roles", data }) } // 新建角色API export const addRolesApi = data => {
return post({
url: "roles", data }) } // 编辑角色API export const editRolesApi = data => {
return put({
url: `roles/${
data.id}`, data }) } // 删除角色API export const rolesDeleteApi = data => {
return del({
url: `roles/${
data.id}` }) }
(其实内部结构差不多,文件名也写的很清楚。如果你喜欢,export const dog=data都可以。)
import { ref } from 'vue'; import { useForm } from '@/composables'; import { getRolesApi, addRolesApi, editRolesApi, rolesDeleteApi } from '@/util/request.js';
const rolesList = ref([]); const dialogFormVisible = ref(false); const formData = ref({ roleName: '', roleDesc: '' }); const userForm = ref();
在表单中,创建一个规则,标记某个项目必须填写
const rules = { roleName: { required: true, message: '此项必填', trigger: 'blur', }, };
我们还需要验证表单数据是否符合规则
const { validate } = useForm(userForm, formData, rules);
- userForm:一个响应式的对象,用于存储表单数据。
- formData:一个普通的对象,包含了表单数据的初始值。
- rules:一个普通的对象,包含了表单验证的规则。
在上面的代码中,当表单提交时,我们调用validate 方法进行验证。如果验证通过,就执行表单提交操作。如果验证失败,validate 方法会返回 false,我们可以在组件中根据这个返回值来进行错误提示等操作。
我们表单已经制作完成,下面就要执行以下步骤:
- 获取用户数据
- 编辑/添加
- 删除
- 新建
我们的思路是,通过API实现各种功能。通过异步处理就可以了
// 定义获取角色列表的函数 const getList = async () => {
const res = await getRolesApi(); rolesList.value = res.data; }; // 定义提交表单的函数 const submitForm = async () => {
const res = await validate(); if (!res) {
return; } if (formData.value.id) {
await editRolesApi(formData.value); } else {
await addRolesApi(formData.value); } dialogFormVisible.value = false; getList(); }; // 定义编辑角色的函数 const editRow = (row) => {
dialogFormVisible.value = true; const {
roleName, roleDesc, id } = row; formData.value = {
id, roleName, roleDesc }; }; // 定义删除角色的函数 const deleteRow = async (row) => {
await rolesDeleteApi(row); getList(); }; // 定义清空表单的函数 const clearForm = () => {
formData.value = {
roleName: '', roleDesc: '' }; }; // 初始化时获取角色列表 getList();
如果提交表单函数看不懂,我这里写了详细注释
// 定义提交表单的函数 const submitForm = async () => { // 首先调用了validate()方法来验证表单数据 // validate()方法是由上文引入的useForm自定义Hook返回的方法之一,用于验证表单数据是否符合预设的规则 // validate()方法返回一个Promise,我们使用await语句等待这个Promise解析 const res = await validate(); // 如果验证失败(validate()方法返回的Promise解析为false), // 那么我们立即返回,停止函数的执行,避免提交无效的表单数据 if (!res) { return; } // 如果表单数据(formData.value)中包含id字段,那么我们认为用户正在编辑一个已存在的角色 // 在这种情况下,我们调用editRolesApi()方法来更新这个角色的信息 // editRolesApi()方法接受一个包含角色数据的对象作为参数,并返回一个Promise,我们使用await语句等待这个Promise解析 if (formData.value.id) { await editRolesApi(formData.value); } else { // 如果表单数据(formData.value)中不包含id字段,那么我们认为用户正在创建一个新的角色 // 在这种情况下,我们调用addRolesApi()方法来创建新的角色 // addRolesApi()方法接受一个包含角色数据的对象作为参数,并返回一个Promise,我们使用await语句等待这个Promise解析 await addRolesApi(formData.value); } // 不论是创建新的角色还是编辑已存在的角色,我们都会在表单提交成功后隐藏表单对话框 // 我们通过将dialogFormVisible.value设为false来隐藏对话框 dialogFormVisible.value = false; // 表单提交成功后,我们需要获取最新的角色列表以便在页面上显示 // 我们通过调用getList()方法来获取最新的角色列表 getList(); };
好了,下面是roleList.vue完整代码
<template> <template> <div> <!-- 面包屑导航 --> <van-nav-bar> <template #left> <van-icon name="arrow-left" @click="goToHome" /> </template> <template #title> 角色列表 </template> </van-nav-bar> <!-- 白色内容区域 --> <div class="page_content"> <!-- 新建角色按钮 --> <van-button type="primary" @click="dialogFormVisible=true">新建角色</van-button> <!-- 表格展示角色列表 --> <van-list :data="rolesList"> <template #default="{item}"> <van-cell :title="item.roleName" :value="item.roleDesc"> <template #right-icon> <div class="buttons"> <van-button type="primary" plain size="small" @click="editRow(item)">编辑</van-button> <van-button type="danger" plain size="small" @click="deleteRow(item)">删除</van-button> </div> </template> </van-cell> </template> </van-list> </div> <!-- 新建/编辑角色弹窗表单 --> <van-popup v-model="dialogFormVisible" round> <van-form ref="userForm" :model="formData" :rules="rules" > <van-field name="roleName" label="角色名称" v-model="formData.roleName" placeholder="请输入角色名称" /> <van-field name="roleDesc" label="角色描述" v-model="formData.roleDesc" placeholder="请输入角色描述" /> <div class="buttons"> <van-button @click="clearForm">取消</van-button> <van-button type="primary" @click="submitForm(userForm)">确定</van-button> </div> </van-form> </van-popup> </div> </template> <script setup> import { ref } from 'vue'; // 引入Vue 3的ref功能,用于创建响应式数据 import { useForm } from '@/composables'; // 引入useForm自定义的表单处理函数 import { getRolesApi, addRolesApi, editRolesApi, rolesDeleteApi } from '@/util/request.js'; // 引入API方法,包括获取角色列表、增加角色、编辑角色和删除角色的方法 // 定义需要的响应式数据 const rolesList = ref([]); // 存放角色列表的响应式数据 const dialogFormVisible = ref(false); // 控制新建/编辑角色对话框的显示与隐藏 const formData = ref({ roleName: '', roleDesc: '' }); // 存放表单数据的响应式数据 const userForm = ref(); // 表单的引用 // 定义表单验证规则 const rules = { roleName: { required: true, message: '此项必填', trigger: 'blur', }, }; // 使用useForm函数处理表单验证 const { validate } = useForm(userForm, formData, rules); // 定义获取角色列表的函数 const getList = async () => { const res = await getRolesApi(); rolesList.value = res.data; }; // 定义提交表单的函数 const submitForm = async () => { const res = await validate(); if (!res) { return; } if (formData.value.id) { await editRolesApi(formData.value); } else { await addRolesApi(formData.value); } dialogFormVisible.value = false; getList(); }; // 定义编辑角色的函数 const editRow = (row) => { dialogFormVisible.value = true; const { roleName, roleDesc, id } = row; formData.value = { id, roleName, roleDesc }; }; // 定义删除角色的函数 const deleteRow = async (row) => { await rolesDeleteApi(row); getList(); }; // 定义清空表单的函数 const clearForm = () => { formData.value = { roleName: '', roleDesc: '' }; }; // 初始化时获取角色列表 getList(); </script>
3.用户页面
<template> <div> <!-- 面包屑 --> <el-breadcrumb :separator-icon="ArrowRight"> <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item> <el-breadcrumb-item>账号列表</el-breadcrumb-item> </el-breadcrumb> <!-- 白色内容区域 --> <div class="page_content"> <div class="flex"> <div class="input_box"> <el-input v-model="searchParams.query" placeholder="搜索关键字" class="input-with-select" > <template #append> <el-button @click="searchList"><el-icon><Search /></el-icon></el-button> </template> </el-input> </div> <el-button type="primary" @click="addUser">新建用户</el-button> </div> <!-- 表格 --> <!-- el-table 的 data:要展示的数据数组 el-table-column:列 prop每条数据的对应属性 label:列标题 scope.row:相当于一条数据 --> <el-table :data="userList" style="width: 100%"> <el-table-column prop="username" label="姓名" width="180" /> <el-table-column prop="email" label="邮箱" width="180" /> <el-table-column prop="mobile" label="电话" /> <el-table-column prop="role_name" label="角色" /> <el-table-column prop="mg_state" label="状态" > <template #default="scope"> <el-switch v-model="scope.row.mg_state" @change="switchChange(scope.row)" /> </template> </el-table-column> <el-table-column label="操作" > <template #default="scope"> <el-button type="primary" @click="editRow(scope.row)">编辑</el-button> <el-button type="danger" @click="deleteRow(scope.row)">删除</el-button> </template> </el-table-column> <!-- mg_state 状态 --> </el-table> <!-- 分页 --> <el-pagination v-model:currentPage="searchParams.pagenum" v-model:page-size="searchParams.pagesize" :page-sizes="[2,5,10,20]" :small="small" layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="searchList" @current-change="searchList" /> </div> <!-- 新增弹窗 --> <el-dialog v-model="dialogFormVisible" title="新建用户"> <!-- 表单 | username | 用户名称 | 不能为空 | | password | 用户密码 | 不能为空 | | email | 邮箱 | 可以为空 | | mobile | 手机号 | 可以为空 | --> <el-form ref="userForm" :model="formData" :rules="rules" > <el-form-item label="用户名称" prop="username"> <el-input v-model="formData.username" placeholder="请输入用户名称" /> </el-form-item> <el-form-item label="用户密码" prop="password"> <el-input type="password" v-model="formData.password" placeholder="请输入密码" /> </el-form-item> <el-form-item label="邮箱" prop="email"> <el-input v-model="formData.email" placeholder="请输入用户邮箱" /> </el-form-item> <el-form-item label="手机号" prop="mobile"> <el-input v-model="formData.mobile" placeholder="请输入用户手机号" /> </el-form-item> </el-form> <template #footer> <div class="flex"> <el-button>取消</el-button> <el-button type="primary" @click="submitForm(userForm)" >确定</el-button> </div> </template> </el-dialog> <!-- 编辑弹窗 --> <el-dialog v-model="dialogFormEVisible" title="编辑用户"> <!-- 表单 | email | 邮箱 | 可以为空 | | mobile | 手机号 | 可以为空 | --> <el-form ref="userForm2" :model="formData2" :rules="rules2" > <el-form-item label="邮箱" prop="email"> <el-input v-model="formData2.email" placeholder="请输入用户邮箱" /> </el-form-item> <el-form-item label="手机号" prop="mobile"> <el-input v-model="formData2.mobile" placeholder="请输入用户手机号" /> </el-form-item> </el-form> <template #footer> <div class="flex"> <el-button>取消</el-button> <el-button type="primary" @click="submitEForm(userForm2)" >确定</el-button> </div> </template> </el-dialog> </div> </template>
(以下是vant平替版本文档是element修改的,很多组件我没找着vant4可以替代,以下vant4代码仅供参考)
<template> <div> <!-- 面包屑 --> <!-- Vant没有面包屑组件,你可能需要自己实现,或者使用第三方库 --> <!-- 白色内容区域 --> <div class="page_content"> <div class="flex"> <div class="input_box"> <!-- 在Vant中,搜索框和按钮是两个独立的组件 --> <van-search v-model="searchParams.query" placeholder="搜索关键字" @search="searchList" /> <van-button type="primary" @click="addUser">新建用户</van-button> </div> </div> <!-- 表格 --> <!-- Vant没有表格组件,你可能需要自己实现,或者使用第三方库 --> <!-- 分页 --> <van-pagination v-model="searchParams.pagenum" :total-items="total" :items-per-page="searchParams.pagesize" @change="searchList" /> </div> <!-- 新增弹窗 --> <van-dialog v-model="dialogFormVisible" title="新建用户"> <!-- 表单 --> <van-form ref="userForm" :model="formData" :rules="rules"> <!-- 在Vant中,表单项和输入框是两个独立的组件 --> <van-field v-model="formData.username" label="用户名称" placeholder="请输入用户名称" required /> <van-field v-model="formData.password" label="用户密码" placeholder="请输入密码" type="password" required /> <van-field v-model="formData.email" label="邮箱" placeholder="请输入用户邮箱" type="email" /> <van-field v-model="formData.mobile" label="手机号" placeholder="请输入用户手机号" type="tel" /> </van-form> <van-button round block type="default" @click="dialogFormVisible = false">取消</van-button> <van-button round block type="primary" @click="submitForm(userForm)">确定</van-button> </van-dialog> <!-- 编辑弹窗 --> <van-dialog v-model="dialogFormEVisible" title="编辑用户"> <!-- 表单 --> <van-form ref="userForm2" :model="formData2" :rules="rules2"> <van-field v-model="formData2.email" label="邮箱" placeholder="请输入用户邮箱" type="email" /> <van-field v-model="formData2.mobile" label="手机号" placeholder="请输入用户手机号" type="tel" /> </van-form> <van-button round block type="default" @click="dialogFormEVisible = false">取消</van-button> <van-button round block type="primary" @click="submitEForm(userForm2)">确定</van-button> </van-dialog> </div> </template>
// 获取用户列表API export const userListApi = data => {
return get({
url: "/users", data }) } // 新增用户API export const userAddApi = data => {
return post({
url: "/users", data }) } // 更改用户状态API export const userChangeStateApi = data => {
return put({
url: `users/${
data.id}/state/${
data.mg_state}`, data }) } // 更改用户信息API export const userChangeInfoApi = data => {
return put({
url: `users/${
data.id}`, data }) } // 删除用户API export const userDeleteApi = data => {
return del({
url: `users/${
data.id}` }) }
接下来写入我们的方法,以下是条件限制部分
这个我就不一一详解了,直接写注释里去了
这一部分我们写入的是用户基本信息的格式(规则),以及提交单的数组等
// 使用Vue 3的reactive方法创建一个响应性对象 const data = reactive({
// searchParams对象包含了搜索参数 searchParams: {
query: "", // 查询字符串,默认为空 pagesize: 5, // 每页显示的数据量,默认为5 pagenum: 1 // 当前的页数,默认为第一页 }, total: 0, // 用于存储数据的总数量 userList: [], // 存储用户列表的数组 dialogFormVisible: false, // 控制添加用户的对话框是否可见,默认为不可见 dialogFormEVisible: false, // 控制编辑用户的对话框是否可见,默认为不可见 formData: {
// 添加用户的表单数据 username: "", // 用户名 password: "", // 密码 email: "", // 邮箱 mobile: "", // 手机号 }, formData2: {
// 编辑用户的表单数据 id: "", // 用户ID email: "", // 邮箱 mobile: "", // 手机号 }, // 添加用户的表单验证规则 rules: {
username: [ // 用户名规则 {
required: true, message: "此项为必填", trigger: "blur" } // 必填项,失去焦点时触发 ], password: [ // 密码规则 {
required: true, message: "此项为必填", trigger: "blur" } // 必填项,失去焦点时触发 ], email: [ // 邮箱规则 {
required: false, pattern: /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-])+$/, // 邮箱的正则表达式规则 message: "请填写正确邮箱", trigger: "blur" } ], mobile: [ // 手机号规则 {
required: false, pattern: /^[1][3,4,5,7,8][0-9]{9}$/, // 手机号的正则表达式规则 message: "请填写正确手机号", trigger: "blur" } ] }, // 编辑用户的表单验证规则,规则同上 rules2: {
email: [ {
required: false, pattern: /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-])+$/, message: "请填写正确邮箱", trigger: "blur" } ], mobile: [ {
required: false, pattern: /^[1][3,4,5,7,8][0-9]{9}$/, message: "请填写正确手机号", trigger: "blur" } ] } })
以下是功能性的方法
// 定义一个函数 searchList,它通过调用 userListApi 并传递搜索参数来获取用户列表 const searchList = () => {
userListApi(data.searchParams).then(res => {
if (res.data) {
// 如果响应中有数据 data.userList = res.data.users // 将响应数据中的用户列表存到 data.userList data.total = res.data.total // 将响应数据中的用户总数存到 data.total } }) } // 定义一个函数 addUser,用于显示添加用户的表单 const addUser = () => {
data.dialogFormVisible = true // 将 data.dialogFormVisible 设置为 true,通常用于在 UI 上显示表单 } // 定义一个函数 submitForm,用于提交添加用户的表单 const submitForm = (formEl) => {
formEl.validate(res => {
// 验证表单字段 if (!res) {
// 如果验证不通过 return // 直接返回,不再继续执行 } // 调用 userAddApi 来添加用户,并传递表单数据 userAddApi(data.formData).then(res => {
if (res.data) {
// 如果响应中有数据 data.dialogFormVisible = false // 隐藏表单 // 重置表单数据 data.formData = {
username: "", password: "", email: "", mobile: "", } searchList() // 添加用户成功后,重新获取用户列表 } }) }) } // 定义一个函数 submitEForm,用于提交编辑用户的表单 const submitEForm = (formEl) => {
formEl.validate(res => {
// 验证表单字段 if (!res) {
// 如果验证不通过 return // 直接返回,不再继续执行 } // 调用 userChangeInfoApi 来修改用户信息,并传递表单数据 userChangeInfoApi(data.formData2).then(res => {
if (res.data) {
// 如果响应中有数据 data.dialogFormEVisible = false // 隐藏表单 searchList() // 修改用户信息成功后,重新获取用户列表 } }) }) } // 定义一个函数 switchChange,用于切换用户的状态 const switchChange = row => {
// 调用 userChangeStateApi 来切换用户状态,并传递当前行数据 userChangeStateApi(row).then(res => {
if (res.data) {
// 如果响应中有数据 searchList() // 切换用户状态成功后,重新获取用户列表 } }) } // 定义一个函数 editRow,用于编辑用户信息 const editRow = row => {
const {
email, mobile, id } = row // 从当前行数据中解构出 email, mobile, id data.dialogFormEVisible = true // 显示编辑用户的表单 // 将 email, mobile, id 存到 data.formData2,这通常会自动填充到表单中 data.formData2.email = email data.formData2.mobile = mobile data.formData2.id = id } // 定义一个函数 deleteRow,用于删除用户 const deleteRow = row => {
// 调用 userDeleteApi 来删除用户,并传递当前行数据 userDeleteApi(row).then(res => {
searchList() // 删除用户成功后,重新获取用户列表 }) } // 执行 searchList 函数,获取用户列表 searchList() // 定义两个 ref,通常用于 Vue 3 Composition API 中的 ref 响应式引用 const userForm = ref() const userForm2 = ref()
以下是uesrList.vue部分完整代码(element-plus版本)
<template> <div> <!-- 面包屑导航 --> <el-breadcrumb :separator-icon="ArrowRight"> <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item> <el-breadcrumb-item>角色列表</el-breadcrumb-item> </el-breadcrumb> <!-- 白色内容区域 --> <div class="page_content"> <!-- 新建角色按钮 --> <el-button type="primary" @click="dialogFormVisible=true">新建角色</el-button> <!-- 表格展示角色列表 --> <el-table :data="rolesList" style="width: 100%"> <el-table-column prop="roleName" label="角色名" /> <el-table-column prop="roleDesc" label="角色描述" /> <!-- 表格操作列,包含编辑和删除按钮 --> <el-table-column> <template #default="scope"> <el-button type="primary" @click="editRow(scope.row)">编辑</el-button> <el-button type="danger" @click="deleteRow(scope.row)">删除</el-button> </template> </el-table-column> </el-table> </div> <!-- 新建/编辑角色弹窗表单 --> <el-dialog v-model="dialogFormVisible" @close="clearForm" :title="formData.id?'编辑角色':'新建角色'"> <el-form ref="userForm" :model="formData" :rules="rules" > <el-form-item label="角色名称" prop="roleName"> <el-input v-model="formData.roleName" placeholder="请输入角色名称" /> </el-form-item> <el-form-item label="角色描述" prop="roleDesc"> <el-input v-model="formData.roleDesc" placeholder="请输入角色描述" /> </el-form-item> </el-form> <template #footer> <div class="flex"> <el-button>取消</el-button> <el-button type="primary" @click="submitForm(userForm)" >确定</el-button> </div> </template> </el-dialog> </div> </template> <script setup> import { ref } from 'vue'; // 引入Vue 3的ref功能,用于创建响应式数据 import { useForm } from '@/composables'; // 引入useForm自定义的表单处理函数 import { getRolesApi, addRolesApi, editRolesApi, rolesDeleteApi } from '@/util/request.js'; // 引入API方法,包括获取角色列表、增加角色、编辑角色和删除角色的方法 // 定义需要的响应式数据 const rolesList = ref([]); // 存放角色列表的响应式数据 const dialogFormVisible = ref(false); // 控制新建/编辑角色对话框的显示与隐藏 const formData = ref({ roleName: '', roleDesc: '' }); // 存放表单数据的响应式数据 const userForm = ref(); // 表单的引用 // 定义表单验证规则 const rules = { roleName: { required: true, message: '此项必填', trigger: 'blur', }, }; // 使用useForm函数处理表单验证 const { validate } = useForm(userForm, formData, rules); // 定义获取角色列表的函数 const getList = async () => { const res = await getRolesApi(); rolesList.value = res.data; }; // 定义提交表单的函数 const submitForm = async () => { const res = await validate(); if (!res) { return; } if (formData.value.id) { await editRolesApi(formData.value); } else { await addRolesApi(formData.value); } dialogFormVisible.value = false; getList(); }; // 定义编辑角色的函数 const editRow = (row) => { dialogFormVisible.value = true; const { roleName, roleDesc, id } = row; formData.value = { id, roleName, roleDesc }; }; // 定义删除角色的函数 const deleteRow = async (row) => { await rolesDeleteApi(row); getList(); }; // 定义清空表单的函数 const clearForm = () => { formData.value = { roleName: '', roleDesc: '' }; }; // 初始化时获取角色列表 getList(); </script>
4.商品页面
这一段代码没啥难的,老思路先写html
<template> <div> <!-- 面包屑 --> <el-breadcrumb :separator-icon="ArrowRight"> <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item> <el-breadcrumb-item>商品列表</el-breadcrumb-item> </el-breadcrumb> <!-- 白色内容区域 --> <div class="page_content"> <div class="flex"> <div class="input_box"> <el-input v-model="searchParams.query" placeholder="搜索关键字" class="input-with-select" > <template #append> <el-button @click="searchList"><el-icon><Search /></el-icon></el-button> </template> </el-input> </div> </div> <!-- 表格 --> <!-- el-table 的 data:要展示的数据数组 el-table-column:列 prop每条数据的对应属性 label:列标题 scope.row:相当于一条数据 --> <el-table :data="goodsList" style="width: 100%"> <el-table-column prop="goods_name" label="商品名" width="180" /> <el-table-column prop="goods_price" label="价格(¥)" width="180" /> <el-table-column prop="goods_weight" label="商品重量(kg)" /> <el-table-column prop="goods_state" label="商品状态" > <template #default="scope"> <p>{
{switchState(scope.row.goods_state)}}</p> </template> </el-table-column> </el-table> <!-- 分页 --> <el-pagination v-model:currentPage="searchParams.pagenum" v-model:page-size="searchParams.pagesize" :page-sizes="[2,5,10,20]" :small="small" layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="searchList" @current-change="searchList" /> </div> </div> </template>
// 获取商品列表API export const goodsListApi = data => {
return get({
url: "goods", data }) }
// 导入service中导出的HTTP方法 import {
post, get, put, del } from "./service" // 登录API export const loginApi = data => {
return post({
url: "/login", data }) } // 获取用户列表API export const userListApi = data => {
return get({
url: "/users", data }) } // 新增用户API export const userAddApi = data => {
return post({
url: "/users", data }) } // 更改用户状态API export const userChangeStateApi = data => {
return put({
url: `users/${
data.id}/state/${
data.mg_state}`, data }) } // 更改用户信息API export const userChangeInfoApi = data => {
return put({
url: `users/${
data.id}`, data }) } // 删除用户API export const userDeleteApi = data => {
return del({
url: `users/${
data.id}` }) } // 获取角色API export const getRolesApi = data => {
return get({
url: "roles", data }) } // 新建角色API export const addRolesApi = data => {
return post({
url: "roles", data }) } // 编辑角色API export const editRolesApi = data => {
return put({
url: `roles/${
data.id}`, data }) } // 删除角色API export const rolesDeleteApi = data => {
return del({
url: `roles/${
data.id}` }) } // 获取商品列表API export const goodsListApi = data => {
return get({
url: "goods", data }) }
(上文中使用的拦截器部分是vant4中的Toast,现在共享的是element-plus版本,代码不同,实现功能类似)
import axios from "axios" import {
ElLoading } from 'element-plus' import {
ElMessage } from 'element-plus' import store from "../store/index.js" // 创建一个axios实例,并设置请求超时时间、基础URL和请求头 const Service = axios.create({
timeout: 8000, baseURL: "http://122.114.229.181:3000/api/private/v1/", headers: {
"Content-type": "application/json;charset=utf-8", "Authorization": store.state.uInfo.userInfo.token } }) // 请求拦截器:在发送请求之前执行 Service.interceptors.request.use(config => {
// 显示全屏加载动画 loadingObj = ElLoading.service({
lock: true, text: 'Loading', background: 'rgba(0, 0, 0, 0.7)', }) // 返回修改后的请求配置 return config }) // 响应拦截器:在收到响应之后执行 Service.interceptors.response.use(response => {
// 关闭全屏加载动画 loadingObj.close() const data = response.data // 检查响应的状态码,如果不是200或201,显示错误消息 if (data.meta.status != 200 && data.meta.status != 201) {
ElMessage.error(data.meta.msg || "服务器出错") return data } // 返回响应数据 return data }, error => {
// 关闭全屏加载动画 loadingObj.close() // 如果发生错误,显示错误消息 ElMessage({
message: "服务器错误", type: "error", duration: 2000 }) }) // post请求 export const post = config => {
// 使用Service实例发送post请求,并传入请求配置 return Service({
...config, method: "post", data: config.data }) } // get请求 export const get = config => {
// 使用Service实例发送get请求,并传入请求配置 return Service({
...config, method: "get", params: config.data }) } // put请求 export const put = config => {
// 使用Service实例发送put请求,并传入请求配置 return Service({
...config, method: "put", data: config.data }) } // delete请求 export const del = config => {
// 使用Service实例发送delete请求,并传入请求配置 return Service({
...config, method: "delete" }) }
好的,接下来写业务逻辑,每句话的用途我都在注释中标明了。
<script setup> // 导入 reactive 和 toRefs 函数从 'vue' 库,reactive 用于创建响应式对象,toRefs 用于将响应式对象的属性转化为单独的响应式引用。 import { ref, reactive, toRefs } from 'vue' // 导入 goodsListApi 函数,这个函数可能是用于发起网络请求获取商品列表。 import { goodsListApi } from "@/util/request.js" // 使用 reactive 函数创建一个响应式对象 data,这个对象包含了 searchParams(搜索参数),total(商品总数),goodsList(商品列表)三个属性。 const data = reactive({ searchParams: { query: "", pagesize: 5, pagenum: 1 }, total: 0, goodsList: [], }) // 定义了一个函数 searchList,这个函数会调用 goodsListApi 函数发起网络请求,根据 data.searchParams 的值获取商品列表。 const searchList = () => { goodsListApi(data.searchParams).then(res => { if(res.data){ // 检查返回的 res 对象中是否包含 data 属性。 console.log("商品数据", res) // 在浏览器的控制台打印商品数据。 data.goodsList = res.data.goods // 将返回的商品数据赋值给 data.goodsList。 data.total = res.data.total // 将返回的商品总数赋值给 data.total。 } }) } // 定义了一个函数 switchState,这个函数接收一个状态码作为参数,根据状态码的值返回对应的状态字符串。 const switchState = (state) => { switch (state) { case 0: return "未通过" case 1: return "审核中" case 2: return "已审核" } } // 在初始化时调用 searchList 函数,这样在组件加载时就会自动获取商品列表。 searchList() // 使用 toRefs 函数将 data 对象转化为多个响应式引用,这样在模板中可以直接使用这些引用,而不需要通过 data 对象进行访问。 const { searchParams, total, goodsList } = toRefs(data) </script>
5.补充一段Layout代码
先提供HTML+css
<template> <div class="common-layout"> <el-container class="el-container"> <el-header class="common-header flex-float"> <div class="flex"> <img class="logo" src="../../assets/logo.svg"> <h1 class="title">后台管理系统</h1> </div> <el-button type="danger" @click="loginOut">退出</el-button> </el-header> <el-container> <el-aside class="common-aside" width="200px"> <el-row class="tac"> <el-col > <el-menu active-text-color="#ffd04b" background-color="none" class="el-menu-vertical-demo" default-active="2" text-color="#fff" @open="handleOpen" @close="handleClose" :router="true" > <!-- 这里是账号管理这个分列--> <el-sub-menu index="1"> <template #title> <el-icon><Avatar /></el-icon> <span>账号管理</span> </template> <el-menu-item-group> <el-menu-item index="/user">账号列表</el-menu-item> </el-menu-item-group> <!-- <el-menu-item index="/">item two</el-menu-item>--> </el-sub-menu> <!-- 这里是账号管理分列--> <el-sub-menu index="2"> <template #title> <el-icon><Box /></el-icon> <span>角色管理</span> </template> <el-menu-item-group> <el-menu-item index="/roles">角色列表</el-menu-item> </el-menu-item-group> <!-- <el-menu-item index="/">item two</el-menu-item>--> </el-sub-menu> </el-menu> </el-col> <!-- <router-link to="/index">角色列表</router-link>--> <!-- <router-link to="/user">用户列表</router-link>--> </el-row> </el-aside> <el-main> <router-view></router-view> </el-main> </el-container> </el-container> </div> <el-row class="tac"> <el-col :span="12"> <h5 class="mb-2">Custom colors</h5> <el-menu active-text-color="#ffd04b" background-color="#545c64" class="el-menu-vertical-demo" default-active="2" text-color="#fff" @open="handleOpen" @close="handleClose" > <el-sub-menu index="1"> <template #title> <el-icon><location /></el-icon> <span>Navigator One</span> </template> <el-menu-item index="1-1">item one</el-menu-item> <el-menu-item index="1-2">item two</el-menu-item> </el-sub-menu> <el-menu-item index="2"> <el-icon><icon-menu /></el-icon> <span>Navigator Two</span> </el-menu-item> <el-menu-item index="3" disabled> <el-icon><document /></el-icon> <span>Navigator Three</span> </el-menu-item> <el-menu-item index="4"> <el-icon><setting /></el-icon> <span>Navigator Four</span> </el-menu-item> </el-menu> </el-col> </el-row> </template> <style> .el-container {
height: 100vh; overflow: hidden; } .common-header {
background: #272d41; display: flex; } .common-aside {
background: #; } .logo {
width: 80px; } .title {
color: #f8f8f8; } .flex-float {
display: flex; justify-content: space-between; align-items: center; } .flex {
display: flex; align-items: center; } /*.title{*/ /* background-color: #fefefe;*/ /*}*/ </style>
这段代码中,我们导入了一些必要的库和组件,同时定义了处理菜单展开、折叠和退出登录功能的函数。以下是详细的解释:
- 导入所需库和组件:
import {
Document, Menu as IconMenu, Location, Setting, } from '@element-plus/icons-vue'; // 导入 Element Plus 的图标组件 import {
useStore } from 'vuex'; // 导入 Vuex 的 useStore 函数,用于使用 Vuex 的 store import {
useRouter } from 'vue-router'; // 导入 Vue Router 的 useRouter 函数,用于使用 Vue Router 的实例
- 定义处理菜单展开和折叠事件的函数:
const handleOpen = (key: string, keyPath: string[]) => {
console.log(key, keyPath); // 当菜单展开时,输出展开的菜单项 key 和 keyPath }; const handleClose = (key: string, keyPath: string[]) => {
console.log(key, keyPath); // 当菜单折叠时,输出折叠的菜单项 key 和 keyPath };
handleOpen
和 handleClose
函数在这个例子中只是简单地输出事件的信息。
- 获取 Vuex 的 store 和 Vue Router 的实例:
const store = useStore(); // 使用 Vuex 的 store const router = useRouter(); // 使用 Vue Router 的实例
- 定义退出登录功能:
const loginOut = () => {
localStorage.removeItem('loginData'); // 移除本地存储的登录数据 store.commit('setUserInfo', {
}); // 通过 commit 函数,调用名为 'setUserInfo' 的 Vuex mutation,将用户信息设置为空对象 router.push({
path: '/login' }); // 使用 Vue Router 的 push 函数,跳转到登录页面 };
总结:这段代码主要导入了所需的库和组件,并定义了处理菜单展开、折叠和退出登录功能的函数。通过使用 Vuex 和 Vue Router,我们能够实现对登录信息和页面跳转的控制。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/121575.html