当前位置: 首页 > news >正文

VUE3写后台管理(3)

VUE3写后台管理(3)

  • 1.环境
    • 1.node
    • 2.vite
    • 3.Element-plus
    • 4.vue-router
    • 5.element icon
    • 6.less
    • 7.vuex
    • 8.vue-demi
    • 9.mockjs
    • 10.axios
    • 11.echarts
  • 2.首页
    • 1.布局Main
    • 2.头部导航栏CommonHeader
    • 3.左侧菜单栏CommonLeft
    • 4.首页Home
      • 1.从后端获取数据显示到前端table的三种方式
      • 2.axios的二次封装使用
      • 3.使用封装后的axios和fastmock获取countdata数据
      • 4.使用echarts画图
    • 5.头部CommonTab
  • 3.用户页
    • 1.使用本地mock和Table 表格
    • 2.表单重置
    • 3.日期
    • 4.表单校验
    • 5.表单编辑
  • 4.other
    • 1.权限管理
    • 2.数据持久化问题
    • 3.动态菜单路由的跳转
    • 4.路由守护
  • 5. 效果

项目写完代码已经放在了 仓库。

1.环境

1.node

1.电脑已安装nvm,node不会的可以自行搜索,或者看我的vue系列的第一篇。

2.vite

2.使用vite快速构建vue项目: npm create vite@latest 或者 npm create vite@latest my-vue-app -- --template vue ;在设置里面关掉eslint
在这里插入图片描述
启动程序:npm run dev

3.Element-plus

3.1.使用Element-plus设置UI,先安装:npm install element-plus --save,然后全局引入:在mian.ts中

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

在组件中使用时就可以直接用:<el-button type="primary">Primary</el-button>
3.2使用Element-plus进行按需引入,首先需要额外再下载插件:npm install -D unplugin-vue-components unplugin-auto-import,因为使用vite创建的工程,所以工程的配置文件是vite.config.ts(如果使用的是vue cli脚手架则打包工具就是webpack),在配置文件里面配置插件:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({plugins: [vue(),AutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver()],}),],
})

然后就和全局引入一样,在组件中使用时就可以直接用。
3.3Element-plus手动导入,首先需要下载插件:npm install unplugin-element-plus -S,然后在配置文件里面引入:

import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
import ElementPlus from 'unplugin-element-plus/vite'
export default defineConfig({plugins: [vue(),ElementPlus()],
})

在组件里面导入并使用:import {ElButton} from 'element-plus';

4.vue-router

使用vue-router进行路由配置,首先下载:npm install vue-router -S,然后在src/router/index.js里面进行工程的路由配置:

import {createRouter,createWebHashHistory} from 'vue-router'//1.引入vue-router里创造路由和映射const routes=[//2.进行路由和视图的映射关系{path:"/",component:()=>import("../views/Main.vue"),children:[{path:'/',name:"home",component:()=>import("../views/home/Home.vue"),}]}
]const router=createRouter({//3.用vue-router的方法,将2的映射关系添加进去history:createWebHashHistory(),routes,
})export default router;//4.将项目的处理好的路由映射暴露在外

然后在main.js里面将配置好的router挂载到根组件App上:

const app = createApp(App)
app.use(router)

最后在根组件App.vue的template和父组件的根据路由变化的子组件部分里面使用这个组件:<router-view />,就会在根据路由变化的部分做出相应的变化。

5.element icon

使用element做ui肯定是会用到icon的,首先是下载:npm install @element-plus/icons-vue,然后在main.ts里全局注册到App上:

import * as ElementPlusIconsVue from '@element-plus/icons-vue'
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component)
}

然后就可以在子组件里使用svg方式使用。

6.less

样式使用到了less,所以首先下载npm install -D less,然后使用时:lang='less'

7.vuex

跨组件信息传递用到了vuex,首先是下载:npm install vuex -S,然后在src/store/index.js里面使用vuex的createstore方法设置组件之间传递的变量和方法:

import {createStore} from 'vuex'
export default createStore({state:{//值isCollapse:true},mutations:{updataIsCollapse(state,payload){//方法state.isCollapse = !state.isCollapse}}
})

然后再将store这个组挂载到app上,在src的main.ts里面:

import store from './store/index.js'
app.use(store)

现在就可以跨组件使用了,在header组件里面用这个变量isCollapse:

<el-aside width="$store.state.isCollapse ? '64px' :'180px'">
<el-menu class="el-menu-vertical-demo" background-color="#545c64" text-color="#fff" :collapse="$store.state.isCollapse" :collapse-transition="false">     

在left组件里面用这个方法updataIsCollapse:

<el-button size="small" plain @click="handleCollapse">
<script>
import {defineComponent} from "vue-demi";
import {useStore} from "vuex";
export default defineComponent({setup(){let store=useStore();let handleCollapse=()=>{store.commit("updataIsCollapse");};return {handleCollapse,};
},
})
</script>

8.vue-demi

vue-demi可以让你不用担心vue2还是vue3,安装:npm install vue-demi -S,然后在script里面数据和方法中使用。

9.mockjs

mockjs模拟后端,生成随机数据,拦截 Ajax 请求,首先下载:npm install mockjs -S
1.本地mock拦截请求:
首先在src/api/mockData/xxx.js里面写前端方法请求的后端返回(包括访问此路由时调用的视图函数,后端返回的code和data);
然后在src/api/mock.js里面将上面的数据通过mock拦截请求的路由地址和视图函数,拦截到这些数据:Mock.mock('/home/getData',homeApi.getHomeData)
最后在main.ts里引入配置好的mock.js
2.线上fastmock拦截请求:
线上写好要返回的数据,然后复制访问链接,前端发送请求时访问这个链接拿到数据。

10.axios

异步调用请求,首先下载npm install axios -S
1.基础使用
使用axios进行异步访问,最后挂载到onmounted或者别的周期函数。
2.二次封装
首先在src/config/index.js里面写项目的环境配置文件,项目的三种环境(开发环境,测试环境,线上环境)

const env=import.meta.env.MODE || 'prod'
const EnvConfig={development:{baseApi:"/api",mockApi:"https://www.fastmock.site/mock/71f3f37d174c3eb17127909450c5479a/api",},test:{baseApi:"//test.future.com/api",mockApi:"https://www.fastmock.site/mock/71f3f37d174c3eb17127909450c5479a/api",},pro:{baseApi:"//future.com/api",mockApi:"https://www.fastmock.site/mock/71f3f37d174c3eb17127909450c5479a/api",},
}
export default{env,mock:true,//mock的总开关...EnvConfig[env]//ES6语法的解构
}

然后在src/api/request.js里面对接口请求进行二次封装

import axios from 'axios'
import config from '../config'
import {ElMessage} from 'element-plus'
const NETWORK_ERROR='网络请求错误,请稍后重试...'
//1.创建一个axios实例对象
const service=axios.create({baseURL:config.baseApi})
//2.请求之前要做的一些事儿,比如自定义header,jwt-token认证
service.interceptors.request.use((req)=>{return req;
})
//3.请求之后要做的事儿,和后端协商状态码
service.interceptors.response.use((res)=>{const {code,data,msg} =res.dataif (code == 200){return data;}else{ElMessage.error(msg || NETWORK_ERROR)return Promise.reject(msg || NETWORK_ERROR)}
});
//4.二次封装核心函数,
function request(options){options.method=options.method || 'get';//请求方法if (options.method.toLowerCase()=='get'){options.params=options.data;}//对mock的处理let isMock=config.mock;//全局mockif (typeof options.mock !=='undefined'){isMock=options.mock;//这个请求的mock}//对线上环境的处理if (config.env=='prod'){service.defaults.baseURL=config.baseApi}else{service.defaults.baseURL=isMock?config.mockApi:config.baseApi}return service(options)
}
export default request;

然后在src/api/api.js里面对整个项目的api进行管理,然后在main.ts里面挂载到整个项目的后在组件里面使用。

11.echarts

用echarts画图,首先是下载:npm install echarts -S,然后导入使用:import * as echarts from "echarts";

2.首页

1.布局Main

首先在Main.vue父组件里面完成布局,然后编写各个组件。这里面包含了三个静态子组件CommonHeader、CommonLeft和CommonTab以及动态路由子组件router-view.

<template><div class="common-layout"><el-container class="lay-content"><el-aside ><CommonLeft/></el-aside><el-container class="r-container"><el-header ><CommonHeader/><CommonTab/></el-header><el-main><router-view/></el-main></el-container></el-container></div></template>
<script>
import {defineComponent} from 'vue';
import CommonHeader from '../components/CommonHeader.vue';
import CommonLeft from '../components/CommonLeft.vue';
import CommonTab from '../components/CommonTab.vue';
export default defineComponent({components:{CommonHeader,CommonLeft,CommonTab,}
})
</script>

2.头部导航栏CommonHeader

这里有一个静态引入静态资源:

<img class="user" src="../assets/vue.svg" alt="用户头像">

也可以动态引入静态资源

<img class="user" :src="getImgSrc('vue')" alt="用户头像">
import {defineComponent} from "vue-demi";
export default defineComponent({setup(){let getImgSrc = (user)=>{return new URL(`../assets/${user}.svg`,import.meta.url).href;//相对地址拼接成绝对地址****这里是反引号不是单引号****};return {getImgSrc,};
},
})

组件信息传递的面包屑功能:
首先是CommonLeft点击左侧菜单按钮时要触发vuex里面自定义的selectMenu方法,并给这个方法传递一个参数item

import {useStore} from 'vuex';
export default{setup(){const store=useStore();const clickMenu=(item)=>{router.push({name:item.name,});//vuex管理面包屑store.commit('selectMenu',item);};return {//函数外部暴露noChildren,hasChildren,clickMenu,};};}

然后在store/index.js里面定义这个方法,并绑定一个值:

import {createStore} from 'vuex'
export default createStore({state:{//值currentMenu:null,},mutations:{selectMenu(state,val){val.name=='home'?(state.currentMenu=null):(state.currentMenu=val)}}
})

最后在commonheader里面根据这个全局值计算显示属性:

el-breadcrumb separator="/" class="bread" ><el-breadcrumb-item :to="{ path: '/' }" >首页</el-breadcrumb-item><el-breadcrumb-item :to="current.path" v-if="current" >{{ current.label }}</el-breadcrumb-item></el-breadcrumb>
import {useStore} from "vuex";
import {computed} from "vue";
export default defineComponent({setup(){let store=useStore();//面包屑的计算属性const current=computed(()=>{return store.state.currentMenu;});return {current,};};
})

3.左侧菜单栏CommonLeft

1.icon
这里静态使用icon

 <el-icon><Menu /></el-icon>

动态使用icon

<component class="icons" :is="item.icon"></component>

2.点击menu后的路由跳转配置:
首先建立每个路由的视图函数vue文件。
然后在commonleft里面绑定点击事件,给跳转路由的name:

<el-menu-item v-for="item in noChildren()" :key="item.path" :index="item.path" @click="clickMenu(item)">
<el-menu-item v-for="(subItem,subIndex) in item.children " :key="subIndex" :index="subItem.path" @click="clickMenu(subItem)">
<script>
import {useRouter} from 'vue-router';
export default{setup(){const router =useRouter();const clickMenu=(item)=>{router.push({name:item.name,/给出要跳转的路由的name})};return {//函数外部暴露clickMenu,};}

在router/index.js里面对每个路由配置name、path和子组件路径component:

import {createRouter,createWebHashHistory} from 'vue-router'//1.引入vue-router里创造路由和映射const routes=[//2.进行路由和视图的映射关系{path:"/",component:()=>import("../views/Main.vue"),redirect:'/home',children:[{path:'/home',name:"home",component:()=>import("../views/home/Home.vue"),},{path:'/mall',name:"mall",component:()=>import("../views/mall/mall.vue"),},{path:'/user',name:"user",component:()=>import("../views/user/user.vue"),},{path:'/other/page1',name:"page1",component:()=>import("../views/other/page1.vue"),},{path:'/other/page2',name:"page2",component:()=>import("../views/other/page2.vue"),},],}
]const router=createRouter({//3.用vue-router的方法,将2的映射关系添加进去history:createWebHashHistory(),routes,
})export default router;//4.将项目的处理好的路由映射暴露在外

4.首页Home

1.从后端获取数据显示到前端table的三种方式

A假数据写在代码里,B假数据写在本地用mock模拟后端,C假数据写在线上fastmock模拟后端。
A:假数据写在代码里

<el-table :data="tableData"><el-table-column v-for="(val,key) in tableLabel" :key="key" :prop="key" :label="val"></el-table-column>
</el-table>
<script>
import {defineComponent} from "vue";
export default defineComponent({setup(){const tableData= [{name: 'oppo',todayBuy: 100,monthBuy: 300,totalBuy: 800},...];const tableLabel={name:"品牌",todayBuy: "今日购买",monthBuy: "本月购买",totalBuy: "总购买",};return {tableData,tableLabel,}}
});
</script>

B:假数据写在本地用mock模拟后端
先写本地返回数据src/api/mockData/home.js

export default{getHomeData:()=>{return{code:200,data:{tableData:[{name: 'oppo',todayBuy: 100,monthBuy: 300,totalBuy: 800},...],}}}
}

然后拦截这个方法的访问路由到本地src/api/mock.js

import Mock from 'mockjs'
import homeApi from "./mockData/home"
Mock.mock('/home/getData',homeApi.getHomeData)//访问路由是:home/getData; 访问后端方法homeApi.getHomeData

最后在main.ts里引入配置好的mock.js

import './api/mock.js'

在home.vue里面使用axios对这个路径和方法进行异步请求并在onmounted时运行:

import {onMounted,ref} from "vue";
import axios from "axios";
setup(){
let tableData = ref([]);
const getTableList=async ()=>{await axios.get("/home/getData").then((res)=>{//运行这个方法就是访问这个路由if(res.data.code==200){tableData.value=res.data.data.tableData;}});};
onMounted (()=>{getTableList();});
}

C:假数据写在线上fastmock模拟后端
在fastmcok里面写后台要返回的数据
在这里插入图片描述
然后复制预览接口的url,访问

import {onMounted,ref} from "vue";
import axios from "axios";
setup(){
let tableData = ref([]);
const getTableList=async ()=>{await axios.get("https://www.fastmock.site/mock/71f3f37d174c3eb17127909450c5479a/api/home/getData").then((res)=>{if(res.data.code==200){tableData.value=res.data.data.tableData;}});};
onMounted (()=>{getTableList();});
}

2.axios的二次封装使用

src\api\request.js

import axios from 'axios'
import config from '../config'
import {ElMessage} from 'element-plus'
const NETWORK_ERROR='网络请求错误,请稍后重试...'
//1.创建一个axios实例对象
const service=axios.create({baseURL:config.baseApi})
//2.请求之前要做的一些事儿,比如自定义header,jwt-token认证
service.interceptors.request.use((req)=>{return req;
})
//3.请求之后要做的事儿,和后端协商状态码
service.interceptors.response.use((res)=>{const {code,data,msg} =res.dataif (code == 200){return data;}else{ElMessage.error(msg || NETWORK_ERROR)return Promise.reject(msg || NETWORK_ERROR)}
});
//4.二次封装核心函数,
function request(options){options.method=options.method || 'get';//请求方法if (options.method.toLowerCase()=='get'){options.params=options.data;}//对mock的处理let isMock=config.mock;//全局mockif (typeof options.mock !=='undefined'){isMock=options.mock;//这个请求的mock}//对线上环境的处理if (config.env=='prod'){service.defaults.baseURL=config.baseApi}else{service.defaults.baseURL=isMock?config.mockApi:config.baseApi}return service(options)
}
export default request;

通过上面对项目axios的二次封装之后,在src/api/api.js里面对项目api统一管理(eg:当项目调用getTableData方法时就会访问/home/getData这个路由,并根据mock确定是线上还是本地去访问这个路由)。

```php
import request from './request';
export default {getTableData(params){return request({url:'/home/getData',method:'get',data:params,mock:true,})}
}

然后在main.ts里面挂载到全局

import api from './api/api.js'
app.config.globalProperties.$api = api

最后在Home.vue组件里面使用

import {getCurrentInstance} from "vue";
const {proxy}=getCurrentInstance();
const getTableList=async ()=>{let res=await proxy.$api.getTableData();tableData.value=res.tableData;};

3.使用封装后的axios和fastmock获取countdata数据

1.首先在fastmock里面写后端要返回的数据url方法(get)。

  1. url:/home/getCountData

在这里插入图片描述
2.在src/api/api.js里面添加这个路由对应的路由函数的配置

  1. 配置url对应的路由函数:getCountData
getCountData(params){return request({url:'/home/getCountData',method:'get',data:params,mock:true,});

3.在home子组件里访问这个路由,异步访问这个路由函数,最后将函数挂载到onmounted上,将拿到的数据返回:

3.调用这个路由函数,拿到数据

import {defineComponent,onMounted,ref,getCurrentInstance} from "vue";
import axios from "axios";
export default defineComponent({setup(){const {proxy}=getCurrentInstance();let countData = ref([]);const getCountList=async ()=>{let res=await proxy.$api.getCountData();console.log(res);countData.value=res;};onMounted (()=>{getCountList();});return {countData,}}
});

最后前端显示

 <el-col :span="16" style="margin-top:20px;align-self: flex-start;" ><div class="num"><el-card :body-style="{display:'flex',padding:0}" v-for="item in countData" :key="item.name"><component class="icons" :is="item.icon" :style="{background:item.color}"></component><div class="details"><p class="num">{{ item.value }}</p><p class="txt">{{ item.name }}</p></div></el-card></div></el-col>

4.使用echarts画图

1.首先在fastmock里面给出url(/home/getChartData)后台数据

{code:200,data:{"orderData": {"date": ["20191001", "20191002", "20191003", "20191004", "20191005", "20191006", "20191007"],"data|7": [{"苹果":"@integer(1000,5000)",...}]},"videoData": [{name: '小米',value: 2999},...],"userData": [{date: '周一',new: 5,active: 200},...],    },
}

2.在src\api\api.js里面配置url和方法名( getChartData)

import request from './request';
export default {
...getChartData(params){return request({url:'/home/getChartData',method:'get',data:params,mock:true,});},
}

3.在Home.vue里面使用

<el-card style="height:320px"><div ref="orderchart" style="height:300px; width:auto;"></div></el-card><div class="graph"><el-card style="height:260px;"><div ref="userchart" style="height:240px; width:auto;"></div></el-card><el-card style="height:260px;"><div ref="videochart" style="height:240px;width:auto;"></div></el-card></div>
<script>
import {defineComponent,onMounted,ref,getCurrentInstance,reactive,} from "vue";
import axios from "axios";
import * as echarts from "echarts";
export default defineComponent({setup(){const {proxy}=getCurrentInstance();onMounted (()=>{getChartList();});//echats的渲染//x轴的配置let xOptions=reactive({legend: {// 图例文字颜色textStyle: {color: "#333",},},grid: {left: "20%",},// 提示框tooltip: {trigger: "axis",},xAxis: {type: "category", // 类目轴data: [],axisLine: {lineStyle: {color: "#17b3a3",},},axisLabel: {interval: 0,color: "#333",},},yAxis: [{type: "value",axisLine: {lineStyle: {color: "#17b3a3",},},},],color: ["#2ec7c9", "#b6a2de", "#5ab1ef", "#ffb980", "#d87a80", "#8d98b3"],series: [],},)//饼状图的配置let pieOptions=reactive({tooltip: {trigger: "item",},color: ["#0f78f4","#dd536b","#9462e5","#a6a6a6","#e1bb22","#39c362","#3ed1cf",],series: [],})//三个图数据的初始化let orderData=reactive({xData:[],series:[]});let userData=reactive({xData:[],series:[]});let videoData=reactive({series:[]});//通过faskmock获取数据const getChartList=async()=>{let res=await proxy.$api.getChartData();console.log(res);let orderRes=res.orderData;let userRes=res.userData;let videoRes=res.videoData;//折线图orderData.xData=orderRes.date;//折线图的横轴时间数据const keyArray = Object.keys(orderRes.data[0]);//提取每个品牌的销售数据,为每个品牌创建一个系列const series = keyArray.map(key => ({name: key,data: orderRes.data.map(item => item[key]),type: "line"}));// // 3. 将提取的数据放入xOptions和orderData中,然后传递给echartsorderData.series = series;xOptions.xAxis.data = orderData.xData; xOptions.series = orderData.series; console.log(xOptions);// // 折线图的渲染let oEcharts = echarts.init(proxy.$refs['orderchart']);oEcharts.setOption(xOptions);// //柱状图userData.xData=userRes.map((item) => item.date);userData.series = [{name:"新增用户",data:userRes.map((item) =>item.new),type:"bar",},{name:"活跃用户",data:userRes.map((item) =>item.active),type:"bar",},];xOptions.xAxis.data = userData.xData;xOptions.series = userData.series;let uEcharts = echarts.init(proxy.$refs['userchart']);uEcharts.setOption(xOptions);// //饼状图videoData.series=[{data:videoRes,type:"pie"}];pieOptions.series = videoData.series;let vEcharts = echarts.init(proxy.$refs['videochart']);vEcharts.setOption(pieOptions);};return {tableData,tableLabel,countData,}}
});
</script>

5.头部CommonTab

使用vuex绑定全局参数tagsList并在点击菜单栏的事件里面给这个参数添加val

import {createStore} from 'vuex'
export default createStore({state:{//值tabsList:[{path:"/",name:"home",label:"首页",icon:"home",}],},mutations:{selectMenu(state,val){//这个事件已经绑定到了左侧菜单栏// val.name=='home'?(state.currentMenu=null):(state.currentMenu=val)if(val.name=='home'){state.currentMenu=null}else{state.currentMenu=vallet result=state.tabsList.findIndex(item=>item.name==val.name)result==-1?state.tabsList.push(val):""}},closeTab(state,val){//这个事件会绑定在tag的事件里let res=state.tabsList.findIndex(item=>item.name==val.name)state.tabsList.splice(res,1)}},
})

然后在CommonTab里面拿到这个全局值进行循环并判断显示

<div class="tags"><el-tag v-for="(tag,index) in tags" :key="tag.name" :closable="tag.name!=='home'" :disable-transitions="false" :effect="$route.name===tag.name ?'dark':'plain'" @click="changeMenu(tag)" @close="handleClose(tag,index)">{{ tag.label }}</el-tag></div>
<script>
import {useStore} from "vuex";
import {useRouter,useRoute} from "vue-router";
export default{setup(){const router = useRouter();const route = useRoute();const store=useStore();const tags=store.state.tabsList;const changeMenu = (item)=>{router.push({name:item.name});};const handleClose=(tag,index)=>{let length=tags.length-1;store.commit("closeTab",tag);if (tag.name!==route.name){return;}if(index==length){router.push({name:tags[index-1].name});}else{router.push({name:tags[index].name});}};return {tags,changeMenu,handleClose};},
}
</script>

3.用户页

1.使用本地mock和Table 表格

首先在src\api\mockData\user.js里面写user页面的后端模拟数据;
然后在src\api\api.js里面写前后端访问的控制,当调用此方法函数时让它访问此路由,并设置是从线上还是本地获取数据。

 getUserData(params){return request({url:'/user/getUser',method:'get',data:params,mock:false,});},

然后在src\api\mock.js里面使用mock拦截前端请求,让它从本地user.js里面获取数据:Mock.mock(/user\/getUser/,'get',userApi.getUserList)
最后在user.vue里面拿到数据并通过el-table显示。

2.表单重置

表单重置时首先给表单一个ref<el-form :inline="true" :model="formUser" ref="userForm">
然后通过ref拿到表单,调用表单的resetFields方法:proxy.$refs.userForm.resetFields();

3.日期

提交有日期的表单时,先要对表单的日期数据进行处理:

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;};console.log(year+"-"+add(month)+"-"+add(date));return year+"-"+add(month)+"-"+add(date);};const onSubmit=()=>{proxy.$refs.userForm.validate(async(valid)=>{if(valid){//如果表单验证成功,再提交到后端formUser.birth=timeFormat(formUser.birth);let res= await proxy.$api.addUser(formUser);console.log(res);if(res){dialogVisible.value=false;proxy.$refs.userForm.resetFields();//提交之后根据反向ref拿到表单,利用表单的resetFields方法,重置表单getUserData(config);}         }else{ElMessage({showClose:true,message:'请输入正确的内容',});}});};

4.表单校验

首先对每个表单数据进行校验:<el-form-item label="年龄" prop="age" :rules="[{required:true,message:'请输入年龄'},{type:'number', message: '年龄必须是数字'}]">

最后提交的时候,也要确定验证都通过了才能提交表单到后端:proxy.$refs.userForm.validate(async(valid)=>{if(valid){}})

5.表单编辑

首先需要拿到显示的数据到放到表单再去编辑,所以用到了scope 变量,它是一个插槽(slot)的作用域参数,用于在 Vue.js 中传递当前组件实例的数据给插槽的内容。

<template #default="scope"><el-button  size="small" @click="handleEdit(scope.row)">编辑</el-button><el-button type="danger" size="small">删除</el-button></template>

然后就可以通过row拿到这些表单值,然后通过浅拷贝,将数据显示到表单:

const handleEdit=(row)=>{console.log(row);dialogVisible.value=true;row.sex==0?(row.sex="男"):(row.sex="女");proxy.$nextTick(()=>{//异步操作函数, DOM 更新完成后执行Object.assign(formUser,row);//将row数据浅拷贝到表单formUser});action.value='edit';dialogVisible.value=true;};

4.other

1.权限管理

登录后会根据根据后台数据显示不同用户的左侧菜单,跨组件的信息传递用到了vuiex,所以在store/index.js里面定义全局变量和方法

import {createStore} from 'vuex'
export default createStore({state:{//值menu:[],},mutations:{setMenu(state,val){state.menu=vallocalStorage.setItem('menu',JSON.stringify(val))}},
})

然后在login里面将后台拿到的此用户的menu信息通过全局方法传递给全局值:store.commit("setMenu",res.menu);
最后在commonleft里面用这个变量显示不同的menu:const asyncList=store.state.menu;

2.数据持久化问题

因为menu给的是[],所以当用户刷新网页时,menu数据就会丢失,所以处理这个问题,就要在store里面拿到存到浏览器里面的localStorage.menu里面的值赋值给state.menu,并在main.ts里面使用这个方法刷新menu值。

addMenu(state){if (!localStorage.getItem('menu')){return}const menu=JSON.parse(localStorage.getItem('menu'))state.menu=menu}
//main.ts
store.commit("addMenu");

3.动态菜单路由的跳转

首先登录以后,通过addmenu方式,传递给全局的router:store.commit("addMenu",router);
然后在store里面给addmenu方法添加动态路由的列表获取:

addMenu(state,router){if (localStorage.getItem('menu')){// 如果menu里面有值就拿到她更新state里的menuconst menu=JSON.parse(localStorage.getItem('menu'))state.menu=menu//动态路由添加到routerconst menuArray=[];menu.forEach(item=>{if (item.children){item.children=item.children.map(item=>{let url=`../views/${item.url}.vue`//反引号item.component=()=>import(url)return item})menuArray.push(...item.children)}else{let url=`../views/${item.url}.vue`item.component=()=>import(url)menuArray.push(item)}});menuArray.forEach(item=>{router.addRoute('home1',item)})};},

最后在main.ts里面用addmenu方法时也要传递参数router:store.commit("addMenu",router);

4.路由守护

需要用到vuex存取全局token值,并用到js-cookie的Cookie,所以先下载:npm install js-cookie -S
然后在store/index里面定义值和方法:

import {createStore} from 'vuex'
import Cookie from 'js-cookie'
export default createStore({state:{//值token:'',},mutations:{//方法//1.设置tokensetToken(state,val){state.token=val;Cookie.set('token',val);},//2.获取tokengetToken(state){state.token=state.token||Cookie.get('token');},//3.清除tokenclearToken(state){state.token=''Cookie.remove('token');}},    
})

然后在登录login时,后端给一个token,通过settoken方法设置到全局token上:store.commit("setToken",res.token);
然后在main.ts里面每次去gettoken进行判断,并判断这个路由在路由列表吗,如果不在就跳转到home页面

function checkRouter(path) {let hasCheck = router.getRoutes().filter(route => route.path == path).length;return hasCheck;
}router.beforeEach((to, from, next) => {store.commit('getToken');const token = store.state.token;if (!token && to.name !== 'login') {next({ name: "login" });} else if (!checkRouter(to.path)) {next({ name: "home" });} else {next();}
});

退出时也clearToken。

5. 效果

1.首页
在这里插入图片描述
2.用户
在这里插入图片描述

相关文章:

VUE3写后台管理(3)

VUE3写后台管理&#xff08;3&#xff09; 1.环境1.node2.vite3.Element-plus4.vue-router5.element icon6.less7.vuex8.vue-demi9.mockjs10.axios11.echarts 2.首页1.布局Main2.头部导航栏CommonHeader3.左侧菜单栏CommonLeft4.首页Home1.从后端获取数据显示到前端table的三种…...

机器学习笔记之最优化理论与算法(十二)无约束优化问题——共轭梯度法

机器学习笔记之最优化理论与方法——共轭梯度法 引言回顾&#xff1a;共轭方向法的重要特征线性共轭梯度法共轭方向公式的证明过程 关于线搜索公式中参数的化简关于线搜索公式中步长部分的化简关于线搜索公式中共轭方向系数的化简参数化简的目的 非线性共轭梯度法(FR,PRP方法)关…...

JVM中的java同步互斥工具应用演示及设计分析

1.火车站售票系统仿真 某火车站目前正在出售火车票&#xff0c;共有50张票&#xff0c;而它有3个售票窗口同时售票&#xff0c;下面设计了一个程序模拟该火车站售票&#xff0c;通过实现Runnable接口实现&#xff08;模拟网络延迟&#xff09;。 伪代码&#xff1a; Ticket类…...

数据治理-数据质量

实现数据质量的前提就是数据本身是可靠和可信的。 导致数据质量低下的因素 组织缺乏对低质量数据影响的理解&#xff0c;缺乏规划、孤岛式系统设计、不一致的开发过程、不完整的文档、缺乏标准或缺乏治理等。 所有组织都会遇到与数据质量有关的问题。数据质量需要跨职能的承诺…...

[sqoop]hive3.1.2 hadoop3.1.1安装sqoop1.4.7

参考: Hadoop3.2.4Hive3.1.2sqoop1.4.7安装部署_hadoop sqoop安装_alicely07的博客-CSDN博客 一、安装 1、解压 tar -zxvf sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C /home/data_warehouse/module mv sqoop-1.4.7.bin__hadoop-2.6.0 sqoop-1.4.72、配置文件 sqoop-env.s…...

js事件的详细介绍

11.事件 1.什么是事件 js属于事件驱动编程,把驱动,执行,调用通过一些交互,触发一些函数事件:发起-->执行绑定事件-->触发事件on 绑定 emit触发 off解绑2.事件分类 鼠标事件 点击事件 onclick 双击事件 ondblclick 按下事件 onmousedown 抬起事件 onmouseup 鼠标进…...

虚幻4学习笔记(12)操控导入的角色、动画蓝图、播放蒙太奇和打包、角色重定向

虚幻4学习笔记 操控导入的角色设置鼠标旋转关掉动态模糊 动画蓝图、播放蒙太奇和打包角色走路奔跑动画shift 奔跑F 跳舞移动打断 跳舞 打包角色重定向姿势调整解决跑步 腿分太开隐藏剑 B站UP谌嘉诚课程&#xff1a;https://www.bilibili.com/video/BV164411Y732 操控导入的角色…...

hive with tez:无法从链中的任何提供者加载aws凭据

环境信息 hadoop 3.1.0 hive-3.1.3 tez 0.9.1 问题描述 可以从hadoop命令行正确地访问s3a uri。我可以创建外部表和如下命令&#xff1a; create external table mytable(a string, b string) location s3a://mybucket/myfolder/; select * from mytable limit 20; 执行正…...

Ubuntu修改静态IP、网关和DNS的方法总结

Ubuntu修改静态IP、网关和DNS的方法总结 ubuntu系统&#xff08;其他debian的衍生版本好像也可以&#xff09;修改静态IP有以下几种方法。&#xff08;搜索总结&#xff0c;可能也不太对&#xff09; /etc/netplan (use) Ubuntu 18.04开始可以使用netplan配置网络&#xff0…...

Eureka服务器注册

一。Eureka服务器注册 1.pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://mav…...

Windows安装GPU版本的pytorch详细教程

文章目录 chatGLM2-6B安装教程正式安装 chatGLM2-6B ChatGLM2-6B版本要装pytorch2.0&#xff0c;而且要2.0.1 &#xff0c;因此CUDA不能用12.0 &#xff0c;也不能用10.0&#xff0c;只能用11.x 版本。 安装教程 pip install直接下载安装 官网&#xff1a; https://pytorch.…...

理解Kruskal算法的前提----深入理解并查集【超简单~】

并查集的实现思路 并查集主要分为两个部分&#xff1a;第一部分就是需要找到点对应的祖宗节点&#xff0c;第二部分&#xff0c;是要将属于同一个集合节点的祖宗节点进行统一&#xff0c;也就是结合操作。 Find函数实现 // parent数组用来存储下标值所对应的父节点值 // 比如…...

Jenkins+Gitee+Docker+Ruoyi项目前后端分离部署

前言 描述&#xff1a;本文主要是用来记录 如何用标题上的技术&#xff0c;部署到云服务器上通过ip正常访问。 一、总览 1.1、Docker做的事 拉取 mysql 镜像拉取 redis 镜像拉取 jdk 镜像拉取 nginx 镜像 解释说明&#xff1a;前端项目的打包文件放在 nginx容器运行。后端…...

笙默考试管理系统-MyExamTest----codemirror(23)

笙默考试管理系统-MyExamTest----codemirror&#xff08;23&#xff09; 目录 笙默考试管理系统-MyExamTest----codemirror&#xff08;23&#xff09; 一、 笙默考试管理系统-MyExamTest 二、 笙默考试管理系统-MyExamTest 三、 笙默考试管理系统-MyExamTest 四、 笙…...

重学Java (一) 泛型

1. 前言 泛型编程自从 Java 5.0 中引入后已经超过15个年头了。对于现在的 Java 码农来说熟练使用泛型编程已经是家常便饭的事情了。所以本文就在不对泛型的基础使用在做说明了。 如果你还不会使用泛型的话&#xff0c;可以参考下面两个链接 Java 泛型详解The Java™ Tutorial…...

Docker 部署 Redis 服务

拉取最新版本的 Redis 镜像: $ sudo docker pull redis:latest在本地预先创建好 data 目录和 conf/redis.conf 文件。 使用以下命令来运行 Redis 容器: $ sudo docker run -itd --name redis --privilegedtrue -p 6379:6379 -v /home/ubuntu/docker/redis/data:/data -v /ho…...

阿里云产品试用系列-负载均衡 SLB

阿里云负载均衡&#xff08;Server Load Balancer&#xff0c;简称SLB&#xff09;是云原生时代应用高可用的基本要素。通过将流量分发到不同的后端服务来扩展应用系统的服务吞吐能力&#xff0c;消除单点故障并提升应用系统的可用性。阿里云SLB包含面向4层的网络型负载均衡NLB…...

drf 对象级权限

drf 对象级权限 Django REST Framework&#xff08;DRF&#xff09;提供了对象级别权限&#xff08;Object-level permissions&#xff09;来控制特定对象的访问权限。 简单来说&#xff1a;通过视图类中的self.get_object(pk)得到一个obj对象(视图对象)&#xff0c;在与requ…...

八大排序(二)--------冒泡排序

本专栏内容为&#xff1a;八大排序汇总 通过本专栏的深入学习&#xff0c;你可以了解并掌握八大排序以及相关的排序算法。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;八大排序汇总 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库…...

SmartSQL 一款开源的数据库文档管理工具

建议直接蓝奏云下载安装 蓝奏云下载&#xff1a;https://wwoc.lanzoum.com/b04dpvcxe 蓝奏云密码&#xff1a;123 项目介绍 SmartSQL 是一款方便、快捷的数据库文档查询、导出工具&#xff01;从最初仅支持 数据库、CHM文档格式开始&#xff0c;通过不断地探索开发、集思广…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...