当前位置: 首页 > 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;通过不断地探索开发、集思广…...

代码随想录算法训练营第56天 | ● 583. 两个字符串的删除操作 ● 72. 编辑距离 ● 动态规划之编辑距离总结篇

文章目录 前言一、583. 两个字符串的删除操作二、72. 编辑距离三、动态规划之编辑距离总结篇总结 前言 一、583. 两个字符串的删除操作 两种思路&#xff1a;1.直接动态规划&#xff0c;求两个字符串需要删除的最小次数 2.采用子序列的和-最长公共子序列。思路一分析如下&#…...

矩阵 m * M = c

文章目录 题1题2 题1 (2023江苏领航杯-prng) 题目来源&#xff1a;https://dexterjie.github.io/2023/09/12/%E8%B5%9B%E9%A2%98%E5%A4%8D%E7%8E%B0/2023%E9%A2%86%E8%88%AA%E6%9D%AF/ 题目描述&#xff1a; (没有原数据&#xff0c;自己生成的数据) from Crypto.Util.number…...

Linux——IO

✅<1>主页&#xff1a;&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;Linux——文件系统 ☂️<3>开发环境&#xff1a;Centos7 &#x1f4ac;<4>前言&#xff1a;是不是只有C/C有文件操作呢&#xff1f;python&#xff0c;java&…...

svn(乌龟svn)和SVN-VS2022插件(visualsvn) 下载

下载地址: https://www.visualsvn.com/visualsvn/download/...

开源日报 0824 | 构建UI组件和页面的前端工作坊

Storybook 是一个用于构建 UI 组件和页面的前端工作坊&#xff0c;支持多种主流框架&#xff0c;提供丰富的插件&#xff0c;具有可配置性强和扩展性好的特点。 storybookjs/storybook Stars: 79.9k License: MIT Storybook 是一个用于构建 UI 组件和页面的前端工作坊&#x…...

福建三明大型工程机械3D扫描工程零件三维建模逆向抄数-CASAIM中科广电

高精度3D扫描技术已经在大型工件制造领域发挥着重要作用&#xff0c;可以高精度高效率实现全尺寸三维测量&#xff0c;本期&#xff0c;我们要分享的应用是大型工程机械3D扫描案例。 铣轮是深基础施工领域内工法先进、技术复杂程度高、高附加值的地连墙设备&#xff0c;具有成…...

使用香橙派学习 Linux的守护进程

Q&#xff1a;什么是守护进程 A&#xff1a;Linux Daemon&#xff08;守护进程&#xff09;是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行 某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务&#xff0c;不是对整个系统就是对某个…...

数据治理-数据仓库和商务智能

数据仓库的作用 减少数据冗余&#xff0c;提高信息一致性&#xff0c;让企业能够利用数据做出更优决策的方法&#xff0c;数据仓库是企业数据管理的核心。 业务驱动因素 运营支持职能、合规需求&#xff08;历史数据响应&#xff09;和商务智能活动&#xff08;主因&#xff1…...

CH2--x86系统架构概览

2.1 OVERVIEW OF THE SYSTEM-LEVEL ARCHITECTURE 图中的实线箭头表示线性地址&#xff0c;虚线表示段选择器&#xff0c;虚线箭头表示物理地址 2.1.1 Global and Local Descriptor Tables 全局描述符表 (GDT) GDT是一个全局的段描述符表&#xff0c;它存储在系统内存中的一个固…...

Immutable.js API 简介

Immutable-js 这个库的实现是深拷贝还是浅拷贝&#xff1f;immutable 来源immutable.js三大特性&#xff1a; 持久化数据结构结构共享惰性操作 Immutable.js 的几种数据类型 immutable 使用 使用 npm 安装 immutable&#xff1a; 常用API介绍 MapListList.isList() 和 Map.isMa…...

中英切换的网站咋做/app推广活动策划方案

ReactNative豆瓣电影欢迎访问我的博客&#xff0c;祝码农同胞们早日走上人生巅峰&#xff0c;迎娶白富美12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879## 相关资源…...

美食网站代做/软文推广多少钱一篇

1. 修改类函数。 场景&#xff1a; 如果要给一个类的所有方法加上计时&#xff0c;并打印出来。demo如下&#xff1a; # -*- coding:utf-8 -*- import time def time_it(fn):"Example of a method decorator"def decorator(*args, **kwargs):t1time.time()ret fn(*a…...

wordpress教程下载/今日热点新闻事件2022

IES Light Profiles(IES光源概述文件) 是一条曲线&#xff0c;该曲线在一段弧线中定义了光源强度&#xff0c;虚幻引擎4将会围绕某个轴旋转该弧线&#xff0c;从而使得 点光源 &#xff08;和从技术上讲的 聚光源&#xff0c;下面会提供更多相关信息 &#xff09;看上去投射了更…...

wordpress模板在线编辑/长沙seo优化

1 、结对项目的案例与论文 论文已阅读。 2、性格对合作的影响 我的&#xff2d;&#xff22;&#xff34;&#xff29;为&#xff1a;ISFJ 照顾者型&#xff08;内向实感情感判断&#xff09;——值得信赖和依靠。 在团队合作中&#xff0c;外倾型的人一般会较为热情对工作积极…...

门户网站兴化建设局 金/一个产品的营销方案

前言——资源说明 目前网上设计模式的介绍可谓非常之多&#xff08;各种编程语言的版本&#xff09;&#xff0c;其中不乏精细之作&#xff0c;本文的目的在于搜集和整理C#或C的设计模式&#xff0c;毕竟思想还是共通的&#xff01; 设计模式的分类 创建型模式&#xff0c;共五…...

青岛seo整站优化公司/seo推广人员

我是以Python开门的&#xff0c;我还是觉得Python也可以进行地形三维可视化&#xff0c;当然这里需要借助第三方库&#xff0c;so&#xff0c;我就来介绍&#xff1a;Python一个很重要可视化插件&#xff0c;Matplotlib。 Matplotlib是Python最著名的绘图库&#xff0c;它提供了…...