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

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

用户页面信息页面由头部,表格,和弹框组成
<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-column

v-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-pagination

class="pager"

background

layout="prev, pager,next"

size="small"

:total="config.total"

@current-change="handleChange"

>

</el-pagination>

</div>

<el-dialog

v-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-picker

v-model="formUser.birth"

type="date"

placeholder="请输入"

style="width: 100%"

/>

</el-form-item>

</el-col>

</el-row>

<el-row>

<el-form-item

label="地址"

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 false

return 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 = name

u.addr = addr

u.age = age

u.birth = birth

u.sex = sex_num

return 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.keyWord

getUserData()

}

const handleChange = (page) => {

config.page = page

getUserData()

}

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=true

nextTick(()=>{

//因为在第一次显示弹窗的时候form组件没有加载出来,如果直接对formUser赋值,这个值会作为form表单的初始值

//所以使用nextTick,赋值的操作在一个微任务中,这样就可以避免在from表单加载之前赋值

Object.assign(formUser,{...val,sex:""+val.sex})

//这里需要改变sex数据类型,是因为el-option的value有类型的校验

})

}


onMounted(() => {

getUserData()

})

</script>

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

接下来实现面包屑组件

<template>

<div class="tags">

<el-tag

v-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.token

store.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.menuList

const 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

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于使用 Vite + Vue3 + TypeScript + Pinia + Vue Router + Axios + SCSS 并自动导入 API 的设置,你可以按照以下步骤进行操作: 1. 首先,确保你已经安装了 Node.js,并且版本大于等于 12.0.0。 2. 创建一个新的 Vue 项目,可以使用 Vue CLI 或者手动创建一个空文件夹。 3. 在项目根目录下,打开终端并执行以下命令安装 Vite: ```bash npm init vite@latest ``` 按照提示选择你的项目配置,包括选择 Vue 3、TypeScript 和其他选项。 4. 进入项目目录并安装依赖: ```bash cd your-project-name npm install ``` 5. 安装 Pinia 插件: ```bash npm install pinia ``` 6. 创建一个 `src/store` 目录,并在其中创建 `index.ts` 文件,用于定义和导出你的 Pinia store。 ```typescript // src/store/index.ts import { createPinia } from 'pinia' export const store = createPinia() // 可以在这里定义你的 store 模块 ``` 7. 在项目根目录下创建 `src/api` 目录,用于存放 API 请求相关的文件。 8. 在 `src/api` 目录下创建一个 `index.ts` 文件,用于自动导入所有 API 文件。 ```typescript // src/api/index.ts const modules = import.meta.globEager('./*.ts') const apis: any = {} for (const path in modules) { if (path !== './index.ts') { const moduleName = path.replace(/^.\/|\.ts$/g, '') apis[moduleName] = modules[path].default } } export default apis ``` 这样,你就可以在 `src/api` 目录下创建各种 API 请求的文件,例如 `user.ts`: ```typescript // src/api/user.ts import axios from 'axios' export function getUser(id: number) { return axios.get(`/api/user/${id}`) } ``` 然后,在你的组件中使用自动导入的 API: ```typescript import { defineComponent, ref } from 'vue' import { useUserStore } from '@/store' import apis from '@/api' export default defineComponent({ setup() { const userStore = useUserStore() const userId = ref(1) const fetchUser = async () => { const response = await apis.user.getUser(userId.value) userStore.setUser(response.data) } return { userId, fetchUser, } }, }) ``` 以上就是使用 Vite + Vue3 + TypeScript + Pinia + Vue Router + Axios + SCSS 并自动导入 API 的基本设置。你可以根据自己的需求进一步配置和扩展。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值