vue后台管理系统 vue3+vite+pinia+elementui+axios下

embedded/2024/9/18 12:26:50/ 标签: vue.js, elementui, javascript, 前端, html5, css, scss

这篇文章来完成用户组件 也就是增删改查表格

用户页面信息页面由头部,表格,和弹框组成
<template><div class="user-header"><el-button type="primary" @click="handleAdd">新增</el-button><el-form :inline="true" :model="formInline"><el-form-item label="请输入"><el-input placeholder="请输入用户名" v-model="formInline.keyWord"></el-input></el-form-item><el-form-item><el-button type="primary" @click="handleSearch">搜索</el-button></el-form-item></el-form></div><div class="table"><el-table :data="tableData" style="width:100%"><el-table-columnv-for="item in tableLabel":key="item.prop":width="item.width ? item.width :125":prop="item.prop":label="item.label"></el-table-column><el-table-column fixed="right" label="Operations" min-width="150"><template #="scope"><el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button><el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button></template></el-table-column></el-table><el-paginationclass="pager"backgroundlayout="prev, pager,next"size="small":total="config.total"@current-change="handleChange"></el-pagination></div><el-dialogv-model="dialogVisible":title="action === 'add' ? '新增用户' : '编辑用户'"width="35%":before-close="handleClose"><!--需要注意的是设置了:inline="true",会对el-select的样式造成影响,我们通过给他设置一个class=select-clearn在css进行处理--><el-form :inline="true" :model="formUser" :rules="rules" ref="userForm"><el-row><el-col :span="12"><el-form-item label="姓名" prop="name"><el-input v-model="formUser.name" placeholder="请输入姓名" /></el-form-item></el-col><el-col :span="12"><el-form-item label="年龄" prop="age"><el-input v-model.number="formUser.age" placeholder="请输入年龄" /></el-form-item></el-col></el-row><el-row><el-col :span="12"><el-form-item class="select-clearn" label="性别" prop="sex"><el-select v-model="formUser.sex" placeholder="请选择"><el-option label="男" value="1" /><el-option label="女" value="0" /></el-select></el-form-item></el-col><el-col :span="12"><el-form-item label="出生日期" prop="birth"><el-date-pickerv-model="formUser.birth"type="date"placeholder="请输入"style="width: 100%"/></el-form-item></el-col></el-row><el-row><el-form-itemlabel="地址"prop="addr"><el-input v-model="formUser.addr" placeholder="请输入地址" /></el-form-item></el-row><el-row style="justify-content: flex-end"><el-form-item><el-button type="primary" @click="handleCancel">取消</el-button><el-button type="primary" @click="onSubmit">确定</el-button></el-form-item></el-row></el-form></el-dialog></template><style lang="scss" scoped>.user-header{display:flex;justify-content: space-between;}.table{position:relative;height:520px;.pager{position:absolute;right:10px;bottom:30px;}.el-table{width:100%;height:500px;}}</style>
在api文件夹中api.js文件加入
getUserData(data){return request({url:"/home/getUserData",method:'get',data})},deleteUser(data){return request({url:"/home/deleteUser",method:'get',data})},addUser(params) {return request({url: '/user/addUser',method: 'post',data: params})},editUser(params) {return request({url: '/user/editUser',method: 'post',data: params})},
在mock.js中加入
Mock.mock(/api\/home\/getUserData/,"get",userApi.getUserList)Mock.mock(/api\/home\/deleteUser/,"get",userApi.deleteUser)Mock.mock(/api\/user\/addUser/,"post", userApi.createUser)Mock.mock(/api\/user\/editUser/, "post",userApi.updateUser)
mockData文件夹中加入user.js模拟数据
import Mock from 'mockjs'// get请求从config.url获取参数,post从config.body中获取参数function param2Obj(url) {const search = url.split('?')[1]if (!search) {return {}}return JSON.parse('{"' +decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') +'"}')}let List = []const count = 200//模拟200条用户数据for (let i = 0; i < count; i++) {List.push(Mock.mock({id: Mock.Random.guid(),name: Mock.Random.cname(),addr: Mock.mock('@county(true)'),'age|18-60': 1,birth: Mock.Random.date(),sex: Mock.Random.integer(0, 1)}))}export default {/*** 获取列表* 要带参数 name, page, limt; name可以不填, page,limit有默认值。* @param name, page, limit* @return {{code: number, count: number, data: *[]}}*/getUserList: config => {//limit默认是10,因为分页器默认也是一页10个const { name, page = 1, limit = 10 } = param2Obj(config.url)const mockList = List.filter(user => {//如果name存在会,根据name筛选数据if (name && user.name.indexOf(name) === -1) return falsereturn true})//分页const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))return {code: 200,data: {list: pageList,count: mockList.length, //数据总条数需要返回}}},//在原来的export default 中添加/*** 删除用户* @param id* @return {*}*/deleteUser: config => {const { id } = param2Obj(config.url)if (!id) {return {code: -999,message: '参数不正确'}} else {List = List.filter(u => u.id !== id)return {code: 200,message: '删除成功'}}},/*** 增加用户* @param name, addr, age, birth, sex* @return {{code: number, data: {message: string}}}*/createUser: config => {const { name, addr, age, birth, sex } = JSON.parse(config.body)List.unshift({id: Mock.Random.guid(),name: name,addr: addr,age: age,birth: birth,sex: sex})return {code: 200,data: {message: '添加成功'}}},/*** 修改用户* @param id, name, addr, age, birth, sex* @return {{code: number, data: {message: string}}}*/updateUser: config => {const { id, name, addr, age, birth, sex } = JSON.parse(config.body)const sex_num = parseInt(sex)List.some(u => {if (u.id === id) {u.name = nameu.addr = addru.age = ageu.birth = birthu.sex = sex_numreturn true}})return {code: 200,data: {message: '编辑成功'}}}}
有了相应的请求数据 就可以实现增删改差以及相应的功能
<script setup>
//
import {ref,getCurrentInstance,onMounted,reactive,nextTick} from 'vue'const tableLabel = reactive([{prop:'name',label:'姓名'},{prop:'age',label:'年龄'},{prop:'sexLabel',label:'性别'},{prop:'birth',label:'出生日期',width:200},{prop:'addr',label:'地址',width:400},])const tableData =ref([])const {proxy} = getCurrentInstance()const getUserData = async () => {let data = await proxy.$api.getUserData(config)// console.log(data)tableData.value = data.list.map(item =>({...item,sexLabel: item.sex === 1 ? '男' : '女'}))config.total = data.count}const formInline = reactive({keyWord:''})const config =reactive({name:'',total:0,page:1})const handleSearch=() => {config.name = formInline.keyWordgetUserData()}const handleChange = (page) => {config.page = pagegetUserData()}const handleDelete =(val) => {ElMessageBox.confirm("你确定要删除吗?").then(async () => {await proxy.$api.deleteUser({id:val.id})ElMessage({showClose:true,message:'删除成功',type:'success'})getUserData()})}const action =ref('add')const dialogVisible = ref(false)const formUser = reactive({sex:"1"})const rules = reactive({name: [{ required: true, message: "姓名是必填项", trigger: "blur" }],age: [{ required: true, message: "年龄是必填项", trigger: "blur" },{ type: "number", message: "年龄必须是数字" },],sex: [{ required: true, message: "性别是必选项", trigger: "change" }],birth: [{ required: true, message: "出生日期是必选项" }],addr:[{ required: true, message: '地址是必填项' }]})//这个方法之前定义过const handleAdd = () => {action.value="add"//打开对话窗dialogVisible.value=true}//对话框右上角的关闭事件const handleClose = () => {//获取到表单dom,执行resetFields重置表单// proxy.$refs["userForm"].resetFields()//关闭对话框dialogVisible.value=false}//对话框右下角的取消事件const handleCancel = () => {// proxy.$refs["userForm"].resetFields()dialogVisible.value=false}//格式化日期,格式化为:1997-01-02这种const timeFormat = (time) => {var time = new Date(time);var year = time.getFullYear();var month = time.getMonth() + 1;var date = time.getDate();function add(m) {return m < 10 ? "0" + m : m;}return year + "-" + add(month) + "-" + add(date);}const onSubmit = () => {//执行userForm表单的validate进行规则校验,传入一个回调函数,回调函数会接受到一个是否校验通过的变量proxy.$refs["userForm"].validate(async (valid)=>{//如果校验成功if (valid) {//res用于接收添加用户或者编辑用户接口的返回值let res=null//这里无论是新增或者是编辑,我们都要对这个日期进行一个格式化//如果不是1997-01-02这种格式,使用timeFormat方法进行格式化formUser.birth=/^\d{4}-\d{2}-\d{2}$/.test(formUser.birth) ? formUser.birth : timeFormat(formUser.birth)//如果当前的操作是新增,则调用新增接口if (action.value == "add") {res = await proxy.$api.addUser(formUser);}else if(action.value == "edit"){res = await proxy.$api.editUser(formUser)}//如果接口调用成功if(res){//关闭对话框,重置表单,重新请求用户数据dialogVisible.value = false;proxy.$refs["userForm"].resetFields()getUserData()}//如果校验失败}else {ElMessage({showClose: true,message: "请输入正确的内容",type: "error",})}})}const handleEdit = (val) => {action.value="edit"dialogVisible.value=truenextTick(()=>{//因为在第一次显示弹窗的时候form组件没有加载出来,如果直接对formUser赋值,这个值会作为form表单的初始值//所以使用nextTick,赋值的操作在一个微任务中,这样就可以避免在from表单加载之前赋值Object.assign(formUser,{...val,sex:""+val.sex})//这里需要改变sex数据类型,是因为el-option的value有类型的校验})}onMounted(() => {getUserData()})</script>

用户表格实现完成
请添加图片描述

接下来实现面包屑组件

<template><div class="tags"><el-tagv-for="(tag,index) in tags":key="tag.name":closable="tag.name !== 'home'":effect = "route.name === tag.name ? 'dark': 'plain'"@click="handleMenu(tag)"@close="handleClose(tag,index)">{{tag.label}}</el-tag></div></template><script setup>import {ref,computed} from 'vue'import {useRoute,useRouter} from 'vue-router'import {useAllDataStore} from '../stores/index.js'const router = useRouter()const store = useAllDataStore()const tags = computed(()=>store.$state.tags)const route = useRoute()const handleMenu = (tag) => {router.push(tag.name)store.selectMenu(tag)}const handleClose = (tag,index) => {store.updateMenu(tag)if(index === store.$state.tags.length){store.selectMenu(tags.value[index-1])router.push(tags.value[index-1].name)}else{store.selectMenu(tags.value[index])router.push(tags.value[index].name)}}</script><style lang="scss" scoped>.tags{margin:20px 0 0 20px;}el-tag{margin-right:10px;}</style>
在store/index.js中添加内容
export const useAllDataStore = defineStore('allData', {state:() => {return {isCollapse: true,tags:[{path:'/home',name:'home',label:'首页',icon:'home',}],currentMenu:null,}},actions: {selectMenu(val){if(val.name === "home"){this.currentMenu =null;}else{this.currentMenu = val// console.log()let index = this.tags.findIndex((item)=> item.name === val.name);index === -1 ? this.tags.push(val) : "";}},updateMenu(tag){let index =this.tags.findIndex((item) => item.name === tag.name)this.tags.splice(index,1)},
搭建登陆页面 动态添加路由 有两个账号 账号一admin密码123456与账号二oner密码123456

请添加图片描述

请添加图片描述

<template><div class="body-login"><el-form :model="loginForm" class="login-container"><h1>欢迎登陆</h1><el-form-item><el-input type="input" placeholder="请输入账号"v-model="loginForm.username"></el-input></el-form-item><el-form-item><el-input type="input" placeholder="请输入密码"v-model="loginForm.password"></el-input></el-form-item><el-form-item><el-button type="primary" @click="handleLogin"> 登陆</el-button></el-form-item></el-form></div></template><script setup>import {reactive,getCurrentInstance} from 'vue'import {useAllDataStore} from '../stores/index.js'import {useRouter} from 'vue-router'const loginForm = reactive({username:'',password:'',})const {proxy} = getCurrentInstance()const store = useAllDataStore()const router = useRouter()const handleLogin = async () => {const res = await proxy.$api.getMenu(loginForm)store.updateMenuList(res.menuList)store.$state.token = res.tokenstore.addMenu(router)router.push('/home')}</script><style lang="scss" scoped>.body-login{width:100%;height:100%;background-image:url("../assets/images/background.png");background-size:100%;overflow:hidden;}.login-container{width:400px;background-color:#fff;border:1px solid #eaeaea;border-radius:15px;padding:35px 35px 15px 35px;box-shadow:0 0 25px #cacaca;margin:250px auto;h1{text-align:center;margin-bottom:20px;color:#505450;}:deep(.el-form-item__content){justify-content:center;}}</style>

更新store/index.js代码

import { defineStore } from 'pinia'
export const useAllDataStore = defineStore('allData', {state:() => {return {isCollapse: true,tags:[{path:'/home',name:'home',label:'首页',icon:'home',}],currentMenu:null,menuList:[],token:"",routerList:[],}},actions: {selectMenu(val){if(val.name === "home"){this.currentMenu =null;}else{this.currentMenu = val// console.log()let index = this.tags.findIndex((item)=> item.name === val.name);index === -1 ? this.tags.push(val) : "";}},updateMenu(tag){let index =this.tags.findIndex((item) => item.name === tag.name)this.tags.splice(index,1)},updateMenuList(val){this.menuList =val;},//动态添加路由addMenu(router,type){const menu = this.menuListconst module = import.meta.glob("../views/**/*.vue");const routeArr = [];menu.forEach((item)=>{if(item.children){item.children.forEach((val) => {let url = `../views/${val.url}.vue`;val.component = module[url];routeArr.push(...item.children);})}else{let url = `../views/${item.url}.vue`;item.component = module[url];routeArr.push(item)}})this.routerList= [];let routers = router.getRoutes()routers.map((item) => {if(item.name === 'Main' || item.name ==="Login"){return ;}else{router.removeRoute(item.name)}})routeArr.forEach((item) => {this.routerList.push(router.addRoute("Main",item))})// this.routerList= [];// let routers = router.getRoutes()// routers.map((item) => {// if(item.name === 'main' || item.name ==="login"){ return ;}else{// router.removeRoute(item.name)// }// })},clean(){this.routerList.forEach((item) => {if(item) item()})this.$reset()}},})

项目到此就结束了
请添加图片描述

请添加图片描述

源码地址:https://github.com/zhaimou/vue3-admin


http://www.ppmy.cn/embedded/89027.html

相关文章

反贿赂体系认证:企业诚信经营的护航者

在当今商业环境中&#xff0c;企业不仅要追求经济效益&#xff0c;更要坚守诚信经营的原则。反贿赂体系认证作为现代企业合规管理的重要手段&#xff0c;不仅提升了企业的道德形象&#xff0c;还为其市场竞争力注入了强劲动力。以下是反贿赂体系认证对企业的多方面益处。 首先&…

全球汽车用18650电池市场规划预测:2030年市场规模将接近320亿元,未来六年CAGR为-1.9%

一、引言 随着汽车行业的不断进步&#xff0c;汽车用18650电池作为一种常见的能源解决方案&#xff0c;在电动车和混合动力车中占据重要地位。然而&#xff0c;随着技术的进步和新型电池的出现&#xff0c;18650电池的市场地位正面临挑战。本文将探讨汽车用18650电池行业的投资…

Nginx加权轮询算法

Nginx 加权轮询算法 之前面试被问过如何实现有权重的Map&#xff0c;当时没有做出来。后面听到朋友说Nginx权重分配算法&#xff0c;当时以为是通过权重Map解决的&#xff0c;学习后发现并不是。不能白学&#xff0c;记录一下。 基本概念 nginx可以指定轮询几率&#xff0c;…

检查当前安装的 PyTorch 和 CUDA 版本,并验证它们是否正常工作

文章目录 1. 检查 PyTorch 版本2. 检查 CUDA 版本3. 检查 NVIDIA 驱动和 CUDA 工具包版本 1. 检查 PyTorch 版本 打开 Python 交互式解释器&#xff08;或在 Jupyter Notebook 中运行以下代码&#xff09;并输入&#xff1a; import torch print("PyTorch version:"…

Linux 内核源码分析---I/O 体系结构与访问设备

I/O 体系结构 与外设的通信通常称之为输入输出&#xff0c;一般都缩写为I/O。 在实现外设的I/O时&#xff0c;内核必须处理3个可能出现的问题&#xff1a; &#xff08;1&#xff09;必须根据具体的设备类型和模型&#xff0c;使用各种方法对硬件寻址&#xff1b; &#xff08…

Linux进程间通信学习2

文章目录 共享内存信号信号概述以及种类信号的处理信号相关函数&#xff08;简单&#xff09;运用小demo实现ctrlc无法终止进程使用kill函数在程序内部实现一个进程杀死另外一个进程 信号相关函数高级版运用函数小demo 信号量信号量相关函数运用小demo: 共享内存 相比于前三个…

求解器开发:开发工具

在前面的博文中&#xff0c;已就大型CAE软件系统设计、技术选型、职责编排等内容进行了分析总结。求解器无疑是整个CAE仿真计算中最为关键的部分&#xff0c;因此&#xff0c;后续博文将对这一领域的开发技术做些研究。也希望大家批评指教。 工欲善其事&#xff0c;必先利其器。…

Netty4自学笔记 (2) - Java NIO

全文详见个人独立博客&#xff1a;Netty4自学笔记 (2) - Java NIO Netty4自学笔记 (2) - Java NIO距离上一篇博文已经过去了半个多月。这期间有一周多的时间用在了准备单位举办的英语竞赛上。余下的时间沉迷于陪孩子玩耍和睡觉&#xff0c;日复一日。 当然&#xff0c;我也抽空…

python-learning32--高阶教程--基础阶段--python集合--概念和定义

python-learning32--高阶教程--基础阶段--python集合--概念和定义 (kaggle.com)

使用visual studio code远程连接虚拟机

1. 安装Remote-SSH插件 打开vscode&#xff0c;在“扩展商店”中搜索“Remote-SSH”&#xff0c;然后点击“安装”即可&#xff0c;如图所示。 2. 配置SSH 如图所示&#xff0c;点击“远程资源管理器”&#xff0c;点击“新建远程”&#xff0c;然后在输入框中输入如下指令。…

.net # 检查 带有pdf xss

1.解决pdf含javasprct脚本动作&#xff0c;这里是验证pdf内部事件。相关pdf文件下载&#xff1a; 测试pdf文件 相关包 iTextSharp 5.5.13.4 iTextSharp using iTextSharp.text.pdf; using iTextSharp.text.pdf.parser;private Boolean IsPdfSafe(Stream stream){// PdfReader…

高频JMeter软件测试面试题

近期&#xff0c;有很多粉丝在催更关于Jmeter的面试题&#xff0c;索性抽空整理了一波&#xff0c;以下是一些高频JMeter面试题&#xff0c;拿走不谢~ 一、JMeter的工作原理 JMeter就像一群将请求发送到目标服务器的用户一样&#xff0c;它收集来自目标服务器的响应以及其他统计…

Docker 安装 GitLab教程

本章教程,主要介绍如何在Docker 中安装GitLab。 GitLab 是一个开源的 DevOps 平台,提供了一整套工具,用于软件开发生命周期的各个阶段,从代码管理到 CI/CD(持续集成和持续交付/部署),再到监控和安全分析。 一、拉取镜像 docker pull gitlab/gitlab-ce:latest二、创建 G…

EasyAR_稀疏空间图

EasyAR_稀疏空间图 EasyAR4.6.3 丨 Unity2020.3.15f2 1.创建稀疏空间地图 在EasyAR开发中心后台创建Scene许可证密钥&#xff0c;并且使用稀疏空间地图 2.设置稀疏空间地图库名&#xff0c;对稀疏空间地图进行管理&#xff0c;设置密钥 3.复制密钥到Unity中 添加Spatial Map Ap…

1. 什么是操作系统

文章目录 1.1 从功能上来看操作系统1.2 硬件资源 1.1 从功能上来看操作系统 对用户来说&#xff0c;操作系统是一个控制软件&#xff0c;可以用来管理应用程序&#xff0c;它可以限制不同的程序来占用的资源。对内部的软件来说&#xff0c;操作系统是一个管理外设和分配资源的…

vue2老项目中node-sass更换dart-sass

更换原因&#xff1a;node-sass经常会出现node版本问题&#xff0c;就很麻烦 卸载项目中的node-sass sass-loader npm uninstall sass-loader sass 安装dart-sas sass-loader 推荐安装sass1.26.2 sass-loader7.3.1 npm install sass-loader7.3.1 sass1.26.2 从新配置vue.…

猫头虎分享疑难杂Bug:ERROR: No matching distribution found for beautifulsoup4解决方案

&#x1f42f; 猫头虎分享疑难杂Bug&#xff1a;ERROR: No matching distribution found for beautifulsoup4解决方案 摘要 Python开发过程中&#xff0c;ERROR: No matching distribution found for beautifulsoup4 是常见错误之一。本文将详细介绍此错误的产生原因及解决方…

【区块链】浅谈面向小白的关于BlockChain那些事

目录 区块链概念 密码学基础 公有链、私有链和联盟链 共识机制 智能合约 比特币和以太坊 区块链概念 区块链是一种分布式账本技术&#xff0c;通过去中心化和加密算法确保数据的安全性、透明性和不可篡改性。每个区块包含若干交易信息&#xff0c;并与前一个区块通过加密…

基于SpringBoot+Vue的流浪猫狗救助救援网站(带1w+文档)

基于SpringBootVue的流浪猫狗救助救援网站(带1w文档) 基于SpringBootVue的流浪猫狗救助救援网站(带1w文档) 该流浪猫狗救助救援网站在Windows平台下完成开发&#xff0c;采用java编程语言开发&#xff0c;将应用程序部署于Tomcat上&#xff0c;加之MySQL接口来实现交互式响应服…

9. kubernetes资源——pv/pvc持久卷

kubernetes资源——pv/pvc持久卷 一、volume数据卷1、hostPath2、挂载nfs实现持久化 二、pv/pvc 持久卷/持久卷声明1、pv/pvc介绍2、pv/pvc的使用流程2.1 创建pv2.2 创建pvc2.3 创建pod&#xff0c;使用pv做持久化 一、volume数据卷 用于pod中的数据的持久化存储 支持很多的卷…