Vue3项目实战(vue3+vite+pinia+element
2025-06-24 11:41:52
来源:新华网
许苑后台管理系统
- 一、项目介绍
- 1、技术栈
- 2、业务功能
- 3、应用场景
- 4、项目源码
- 二、项目实战
- 2.1、项目初始化
- 2.2、项目实战
- 1、引入router配置
- 2、App.vue组件引入
- 3、创建RootView根路由组件
- 4、依次创建
- 5、进行对应各个环境的配置环境设置
一、项目介绍
1、技术栈
- vue3+vite+vue-router
- pinia
- element-plus
- axios
- mock
- Echarts
2、业务功能
- 登录
- 首页
- 商品
- 用户管理
3、应用场景
- 进行后台管理项目的
- 根据不同用户的权限授予不同的功能
4、项目源码
xuyuan-upward 希望每位大佬获取时候点个小小的赞!
二、项目实战
2.1、项目初始化
1、构建项目
# npm 7+,需要添加额外的 --:npm create vite@latest my-vue-app -- --template vue
2、安装成功后进行安装依赖
npm install
3、修改路径替代符
import{ defineConfig }from'vite'importvue from'@vitejs/plugin-vue'// https://vite.dev/config/exportdefaultdefineConfig({ plugins:[vue()],/* 添加的是别名 */resolve:{ alias:{ '@':'/src',},},})
4、引入element-plus依赖,axios,router
安装依赖内容过于简单,请自行取相关内容官网查看文档进行安装
element-plus官网
axios官网
router
2.2、项目实战
1、引入router配置
import{ createRouter,createWebHashHistory,createWebHistory }from'vue-router'constroutes =[{ path:'/',name:'main',component:()=>import('@/views/RootView.vue'),redirect:'/home',/* 访问 /home 时的行为当你访问 /home 时,会发生以下情况:匹配父路由:首先匹配到 path: '/' 的路由配置。由于 Main.vue 是父组件,它会被渲染。匹配子路由:在 Main.vue 内部的 中,会匹配到 path: '/home' 的子路由。因此,Home.vue 会在 Main.vue 的 中渲染。 */ children:[{ path:'/home',name:'home',component:()=>import('@/views/MainView.vue')},]},]constrouter =createRouter({ history:createWebHistory(),routes})exportdefaultrouter
接着还需要导入main.js一些配置文件
- 全局样式
- pinia等等
import{ createApp }from'vue'importApp from'./App.vue'// 导入全局样式import"@/assets/less/index.less"importrouter from'@/router'importElementPlus from'element-plus'// 导入element-plus组件的全局样式import'element-plus/dist/index.css'import*asElementPlusIconsVue from'@element-plus/icons-vue'import{ createPinia }from'pinia'import{ useAllDataStore }from'@/stores'/* 引入mock */import"@/apis/mock.js"/* 引入apis 管理请求接口 */importapis from"@/apis/apis.js"constpinia =createPinia()constapp =createApp(App)/* 定义全局配置使用 */app.config.globalProperties.$apis =apisapp.use(ElementPlus)app.use(pinia)// localStorage.removeItem("store")conststore =useAllDataStore()store.addRoutes(router,"refresh")app.use(router)/*function isRoute(to) { let routes = router.getRoutes() console.log("routes", routes); let resFil = routes.filter(item => /* 相当于return */item.path ===to.path )/* let resFil = routes.filter(item => { 相当于一段代码,只有return为true时候才会保留对应的数据 item.path === to.path} ) */returnresFil}/*router.beforeEach((to, from, next) => { console.log("store.state.token", store.state.token); if (!store.state.token && to.path !== '/login') { console.log("to.path1", to.path); next({ name: 'login' }) } if (store.state.token && to.path === '/login') { console.log("to.path2", to.path); next({ name: 'home' }) } if (store.state.token && to.path !== '/login') { console.log("to.path3", to.path); console.log("isRoute", isRoute(to)); if (isRoute(to).length === 0) { console.log("to.path3", to.path); next({ name: '404' }) } } console.log("to.path4", to.path); next()})*/for(const[key,component]ofObject.entries(ElementPlusIconsVue)){ app.component(key,component)}app.mount('#app')
2、App.vue组件引入
<scriptsetup>script><template><divclass="app"><router-view/>div>template><style>#app,.app{ width:100%;height:100%;overflow:hidden;}style>
3、创建RootView根路由组件
<template><divclass="common-layout"><el-containerclass="main-container"><divclass="aside-container"><CommonAside/>div><el-containerclass="right-container"><el-headerstyle="height:40px;"><CommonHeader/>el-header><el-dividerstyle="margin:5px 0;"/><CommonTab/><el-main><router-view/>el-main><el-footer><Footer/>el-footer>el-container>el-container>div>template><scriptsetup>import{ reactive,toRefs,onMounted,nextTick }from'vue'importCommonAside from'@/components/CommonAside.vue';importCommonHeader from'@/components/CommonHeader.vue';importCommonTab from'@/components/CommonTab.vue';importFooter from'@/components/Footer.vue';script><stylelang='less'scoped>.common-layout,.main-container,right-container{ height:100%;background-color:#f0f2f5; .el-main{ padding:8px;}}style>
4、依次创建
三个普通组件 CommonAside CommonHeader Footer 以及每个页面的路由组件 router-view
- CommonAside.vue
<template><el-aside:width="width"><el-menutext-color="#fff"background-color="#545c64":collapse="isCollapse":collapse-transition="false":default-active="activeMenu"class="el-menu-vertical-demo"><divclass="title"><iclass="iconfont icon-quanpingtai">i><h4v-show="!isCollapse">许苑后台管理h4>div><el-menu-itemv-for="item in noChildren":key="item.path":index="item.path"@click="handleMenu(item)"><component:is="item.icon"class="icon"/><spanstyle="margin-left:10px">{ { item.label }}span>el-menu-item><el-sub-menuv-for="item in hasChildren":key="item.path":index="item.path"><template#title><component:is="item.icon"class="icon"/><spanstyle="margin-left:10px">{ { item.label }}span>template><el-menu-item-group><el-menu-itemv-for="(subItem) in item.children":index="subItem.path":key="subItem.path"><component:is="subItem.icon"class="icon"/><span>{ { subItem.label }}span>el-menu-item>el-menu-item-group>el-sub-menu>el-menu>el-aside>template><scriptsetup>import{ ref,reactive,toRefs,onMounted,nextTick,computed }from'vue'import{ useRouter,useRoute }from'vue-router'import{ useAllDataStore }from'@/stores'conststore =useAllDataStore()constrouter =useRouter()constroute =useRoute()// 测试数据,初始化,刚开始可以使用,后续通过配置路由权限获取/* const list = ref([ { path: '/home', name: 'home', label: '首页', icon: 'house', url: 'Home' }, { path: '/mall', name: 'mall', label: '商品管理', icon: 'ShoppingBag', url: 'Mall' }, { path: '/user', name: 'user', label: '用户管理', icon: 'user', url: 'User' }, { path: 'other', label: '其他', icon: 'location', children: [ { path: '/page1', name: 'page1', label: '页面1', icon: 'setting', url: 'Page1' }, { path: '/page2', name: 'page2', label: '页面2', icon: 'setting', url: 'Page2' } ] }]) */constlist =computed(()=>store.state.menuList)constnoChildren =computed(()=>list.value.filter(item=>!item.children))consthasChildren =computed(()=>list.value.filter(item=>item.children))constwidth =computed(()=>store.state.isCollapse ?'60px':'200px')// 涉及组件之间的传递 => 使用pinia进行各组件之间的传递constisCollapse =computed(()=>store.state.isCollapse)constactiveMenu =computed(()=>route.path)consthandleMenu=(item)=>{ if(item.children){ return}router.push(item.path)store.selectMenu(item)}script><stylelang='less'scoped>.icon{ width:18px;height:18px;margin-right:5px;}.el-aside{ background-color:#545c64;height:100vh; .el-menu{ border-right:none; .title{ display:flex;align-items:center;justify-content:center;}h4{ color:#fff;font-size:17px;margin:20px;font-weight:500px;text-align:center;}}}style>
- CommonHeader.vue
<template><divclass="header"><divclass="l-header"><el-buttonsize="small"@click="store.state.isCollapse = !store.state.isCollapse"><el-icon><Menu/>el-icon>el-button>div><divclass="r-header"><el-dropdown><spanclass="el-dropdown-link"><imgsrc="@/assets/images/xuyuan.jpg"alt=""class="r-header-avatar">span><template#dropdown><el-dropdown-menu><el-dropdown-item>个人中心el-dropdown-item><el-dropdown-item@click="handlerLogout">退出登录el-dropdown-item>el-dropdown-menu>template>el-dropdown>div>div>template><scriptsetup>import{ ref,reactive,toRefs,onMounted,nextTick,computed }from'vue'import{ useAllDataStore }from'@/stores'import{ ArrowRight }from'@element-plus/icons-vue'import{ useRouter }from'vue-router'conststore =useAllDataStore()constrouter =useRouter()letcurrentPath =computed(()=>{ console.log("store.state.currentMenu",store.state.currentMenu);returnstore.state.currentMenu;})consthandlerLogout=()=>{ store.clean()router.push('/login')}script><stylelang='less'scoped>.header{ width:100%;height:100%;display:flex;justify-content:space-between;align-items:center; .l-header{ display:flex;justify-content:center;align-items:center; .el-button{ margin-right:15px;}}}.r-header-avatar{ width:30px;height:30px;border-radius:50%;margin-right:10px;}style>
- Footer.vue
<template><divclass="footer"><ahref="https://github.com/xuyuan-upward"class="toLearn"><span><iclass="iconfont icon-github">i>站长: 许苑向上span>a><ahref="https://blog.csdn.net/a147775"class="toLearn"><span><iclass="iconfont icon-bokeyuan">i>博客: xuyuan-upwardspan>a><ahref="https://user.qzone.qq.com/2517115657/main"class="toLearn"><span><iclass="iconfont icon-shouye">i>联系方式: 许苑向上span>a>div>template><scriptsetup>import{ ref,reactive,toRefs,onMounted,nextTick }from'vue'script><stylelang='less'scoped>.toGithub{ text-decoration:none;font-size:14px;font-weight:bold;padding:10px 0;display:block;text-align:center;border-radius:5px;transition:all 0.3s ease-in-out;}.iconfont{ margin-right:5px;}.footer{ height:100%;display:flex;justify-content:center; a{ margin-right:40px;color:#00000073; span{ line-height:60px;}}}style>
- MainView.vue路由组件
<template><divclass="home"><el-row><el-col:span="7"><divclass="l-user"><el-cardstyle="max-width:480px"shadow="hover"class="user-info"><divclass="user"><imgsrc="@/assets/images/xuyuan.jpg"alt=""style="width:100px;height:100px;border-radius:50%;margin-right:10px;"><divclass="userInfo"><h>adminh><pstyle="margin-top:20px;color:#999;">超级管理员p>div>div><el-divider/><divclass="login-info"><p>上次登录时间:<span>2024-11-18 1:00:00span>p><pstyle="margin-top:10px;">上次登录地点:<span>广西span>p>div>el-card><el-cardstyle="max-width:480px"shadow="hover"class="user-table"><el-table:data="tableData"style="width:100%"><el-table-columnv-for="(val, key) in tableLabel":key="key":prop="key":label="val">el-table-column>el-table>el-card>div>el-col><el-col:span="17"><divclass="r-echart"><divclass="top"><el-cardv-for="(item) in counterData":key="item.name":body-style="{ padding: '20px', display: 'flex'}"shadow="hover"><component:is="item.icon"class="icons":style="{ background: item.color }"/><divclass="detail"><pclass="num">¥{ { item.value }}p><pclass="txt">¥{ { item.name }}p>div>el-card>div><divclass="bottom"><divclass="echart-top"><el-cardshadow="hover"><divref="echart"style="height:220px;">div>el-card>div><divclass="echart-bottom"><el-cardshadow="hover"><divref="userEchart"style="height:140px">div>el-card><el-cardshadow="hover"><divref="videoEchart"style="height:140px">div>el-card>div>div>div>el-col>el-row>div>template><scriptsetup>import{ ref,reactive,toRefs,onMounted,nextTick,getCurrentInstance }from'vue'import*asecharts from'echarts';//这个tableData是假数据,等会我们使用axios请求mock数据const{ proxy }=getCurrentInstance()consttableData =ref([])constcounterData =ref([])//observer 接收观察器实例对象constobserver =ref(null)//这个是折线图和柱状图 两个图表共用的公共配置constxOptions =reactive({ // 图例文字颜色textStyle:{ color:"#333",},legend:{ },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:[],})constpieOptions =reactive({ tooltip:{ trigger:"item",},legend:{ },color:["#0f78f4","#dd536b","#9462e5","#a6a6a6","#e1bb22","#39c362","#3ed1cf",],series:[]})constgetTableData=async()=>{ constdata =awaitproxy.$apis.getTableData()console.log("home,tableData获取到的数据:",data);tableData.value =data.tableData}constgetCounterData=async()=>{ constdata =awaitproxy.$apis.getCounterData()console.log("home,counterData获取到的数据:",data);counterData.value =data}constgetChartData=async()=>{ // 获取图标信息 ,解构const{ orderData,userData,videoData }=awaitproxy.$apis.getChartData()console.log("home,orderData获取到的数据:",orderData);//对第一个图表的xAxis和series赋值xOptions.xAxis.data =orderData.date xOptions.series =Object.keys(orderData.data[0]).map(val=>{ return{ name:val,data:orderData.data.map(item=>item[val]),type:"line"}})//one echarts.init方法初始化ECharts实例,需要传入dom对象constOneEcharts =echarts.init(proxy.$refs["echart"])//setOption方法应用配置对象OneEcharts.setOption(xOptions)//对第二个图表的xAxis和series赋值xOptions.xAxis.data =userData.map((item)=>item.date)xOptions.series =[{ name:"新增用户",data:userData.map((item)=>item.new),type:"bar",},{ name:"活跃用户",data:userData.map((item)=>item.active),type:"bar",}]//twoconstTwoEcharts =echarts.init(proxy.$refs["userEchart"])TwoEcharts.setOption(xOptions)//对第三个图表的series赋值pieOptions.series =[{ data:videoData,type:"pie",},]//threeconstThreeEcharts =echarts.init(proxy.$refs["videoEchart"])ThreeEcharts.setOption(pieOptions);//ResizeObserver 如果监视的容器大小变化,如果改变会执行传递的回调observer.value =newResizeObserver(entries=>{ OneEcharts.resize()TwoEcharts.resize()ThreeEcharts.resize()})//如果这个容器存在if(proxy.$refs["echart"]){ //则调用监视器的observe方法,监视这个容器的大小observer.value.observe(proxy.$refs["echart"]);}}onMounted(()=>{ getTableData()getCounterData()getChartData()console.log(proxy);})consttableLabel =ref({ name:"课程",todayBuy:"今日购买",monthBuy:"本月购买",totalBuy:"总购买",})script><stylelang='less'scoped>.home{ height:100%;overflow:hidden; .l-user{ .user-info{ .user{ display:flex;align-items:center; .userInfo{ margin-left:30px;}}.login-info{ p{ font-size:14px;color:#999; span{ color:#666;margin-left:30px;}}}}.user-table{ margin-top:50px;}}.r-echart{ .top{ display:flex;justify-content:space-between;flex-wrap:wrap; .el-card{ width:30%;margin-bottom:10px;margin-left:20px;}.icons{ width:50px;height:50px;border-radius:50%;margin-right:20px;}.detail{ display:flex;flex-direction:column;justify-content:center; .num{ margin-bottom:10px;}}}.bottom{ margin-left:20px; .echart-top{ margin-bottom:20px;}.echart-bottom{ display:flex;justify-content:space-between;align-items:center; .el-card{ width:48%;}}}}}style>
- UserView.vue组件
<template><divclass="user"><divclass="user-head"><el-buttontype="primary"@click="handleAdd">新增el-button><el-form:inline="true":model="formData"><el-form-itemlabel="请输入"><el-inputplaceholder="请输入姓名"v-model="formData.keyWord">el-input>el-form-item><el-form-item><el-buttontype="primary"@click="handlerSearch">搜索el-button>el-form-item>el-form>div><divclass="user-table"><el-dialogv-model="dialogVisible":title="action == 'add'? '新增用户': '编辑用户'"width="35%":before-close="handleClose"><el-form:inline="true":model="formUser":rules="rules"ref="userForm"><el-row><el-col:span="12"><el-form-itemlabel="姓名"prop="name"><el-inputv-model="formUser.name"placeholder="请输入姓名"/>el-form-item>el-col><el-col:span="12"><el-form-itemlabel="年龄"prop="age"><el-inputv-model.number="formUser.age"placeholder="请输入年龄"/>el-form-item>el-col>el-row><el-row><el-col:span="12"><el-form-itemlabel="性别"prop="sex"style="width:80%;"><el-selectv-model="formUser.sex"placeholder="请选择"class="select-clean"><el-optionlabel="男":value="1"/><el-optionlabel="女":value="0"/>el-select>el-form-item>el-col><el-col:span="12"><el-form-itemlabel="出生日期"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-inputv-model="formUser.addr"placeholder="请输入地址"/>el-form-item>el-row><el-rowstyle="justify-content:flex-end"><el-form-item><el-buttontype="primary"@click="handleCancel">取消el-button><el-buttontype="primary"@click="onSubmit">确定el-button>el-form-item>el-row>el-form>el-dialog><el-table:data="tableData"style="width:100%"><el-table-columnv-for="item in tableLabel":key="item.prop":prop="item.prop":label="item.label":width="item.width"/><el-table-columnfixed="right"label="Operations"min-width="120"><template#="scoped"><el-buttontype="primary"size="small"@click="onEdit(scoped.row)">编辑 el-button><el-buttontype="danger"size="small"@click="onDelete(scoped.row)">删除el-button>template>el-table-column>el-table><el-paginationlayout="prev, pager, next":total="config.total"@current-change="handlerChangePage"class="page"/>div>div>template><scriptsetup>import{ ref,reactive,toRefs,onMounted,nextTick,getCurrentInstance }from'vue'import{ ElMessage,ElMessageBox }from'element-plus'const{ proxy }=getCurrentInstance()consttableData =ref([])consttableLabel =reactive([{ prop:"name",label:"姓名",},{ prop:"age",label:"年龄",},{ prop:"sex",label:"性别",},{ prop:"birth",label:"出生日期",width:200,},{ prop:"addr",label:"地址",width:400,},])constconfig =reactive({ name:"",total:0,})constformData =reactive({ keyWord:""})constdialogVisible =ref(false)constaction =ref("add")constformUser =ref({ sex:0,})//表单校验规则construles =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:'地址是必填项'}]})consthandlerSearch=()=>{ console.log("搜索",formData.keyWord);config.name =formData.keyWord // console.log("搜索", searchText);getUserData(config)}constgetUserData=async(query)=>{ constdata =awaitproxy.$apis.getUserData(query)console.log("UserView的数据",data);config.total =data.count tableData.value =data.list.map((item)=>{ return{ ...item,sex:item.sex ===1?'女':'男'}})}onMounted(()=>{ getUserData()})consthandlerChangePage=(value)=>{ console.log("当前页码",value);config.page =value getUserData(config)}constonDelete=(row)=>{ console.log("删除",row);ElMessageBox.confirm('你确定要删除吗?','删除提示',{ confirmButtonText:'确定删除',cancelButtonText:'取消',type:'danger ',}).then(()=>{ proxy.$apis.deleteUser({ id:row.id })ElMessage({ type:'success',message:'删除成功',})getUserData()}).catch(()=>{ ElMessage({ type:'info',message:'取消删除',})})}constonEdit=(row)=>{ console.log("编辑",row);action.value ="edit"dialogVisible.value =true/* nextTick 确保在 DOM 更新完成之后再执行回调函数 也就是编辑表单 */nextTick(()=>{ formUser.value ={ ...row,}})/* formUser.value = { ...row, } */}//这个方法之前定义过consthandleAdd=()=>{ action.value ="add"//打开对话窗dialogVisible.value =true}//对话框右上角的关闭事件consthandleClose=()=>{ //获取到表单dom,执行resetFields重置表单//关闭对话框dialogVisible.value =falseproxy.$refs["userForm"].resetFields()}//对话框右下角的取消事件consthandleCancel=()=>{ dialogVisible.value =falseproxy.$refs["userForm"].resetFields()}//格式化日期,格式化为:1997-01-02这种consttimeFormat=(time)=>{ vartime =newDate(time);varyear =time.getFullYear();varmonth =time.getMonth()+1;vardate =time.getDate();functionadd(m){ returnm <10?"0"+m :m;}returnyear +"-"+add(month)+"-"+add(date);}constonSubmit=async()=>{ // 获取表单数据console.log("添加的xxx",formUser.value);// 先进行校验proxy.$refs["userForm"].validate(async(validate)=>{ if(validate){ letres =null;//这里无论是新增或者是编辑,我们都要对这个日期进行一个格式化//如果不是1997-01-02这种格式,使用timeFormat方法进行格式化formUser.birth =/^\d{ 4}-\d{ 2}-\d{ 2}$/.test(formUser.birth)?formUser.birth :timeFormat(formUser.birth)// 提交表单时候,还需要判断是add or editif(action.value ==="add"){ res =awaitproxy.$apis.addUser(formUser.value)}else{ res =awaitproxy.$apis.editUser(formUser.value)}if(res){ ElMessage({ type:'success',message:action.value ==="add"?'添加成功':"编辑成功",})dialogVisible.value =falseproxy.$refs["userForm"].resetFields()// 刷新页面数据getUserData()}}else{ ElMessage({ type:'error',message:"请输入正确内容",})}})// 校验通过,执行添加操作proxy.$apis.addUser(formUser.value)}script><stylelang='less'scoped>.user{ height:100%; .user-head{ display:flex;justify-content:space-between;}.user-table{ height:540px;position:relative; .page{ position:absolute;bottom:50px;right:50px;}}}style>
5、每个组件之间需要共享配置导入pinia配置进行信息共享和传递。
pinia.js
import{ defineStore }from'pinia'import{ ref,watch }from'vue'functioninitData(){ return{ isCollapse:false,tags:[{ path:'/home',name:'home',label:'首页',icon:'hone'}],currentMenu:null,/* 展示菜单列表的数组 */menuList:[],token:null,routerList:[],}}exportconstuseAllDataStore =defineStore('allData',()=>{ // 全部数据的获取和修改conststate =ref(initData())// 进行数据持久化watch(state,newObj=>{ if(!newObj.token)returnlocalStorage.setItem('store',JSON.stringify(newObj))},{ deep:true,})functionselectMenu(val){ if(val.name ==='home'){ state.value.currentMenu =null}else{ state.value.currentMenu =val letindex =state.value.tags.findIndex(item=>item.name ===val.name)index ===-1?state.value.tags.push(val):""}}functiondeleteMenu(tag){ letindex =state.value.tags.findIndex(item=>item.name ==tag.name)// 将当前tags切除state.value.tags.splice(index,1);}functionupdateMenuList(val){ // 将当前tags切除state.value.menuList =val;}functionclean(){ // 将所有路由移除state.value.routerList.forEach(item=>{ if(item)item();state.value =initData();// 删除本地的缓存localStorage.removeItem('store')})}functionaddRoutes(router,type){ // 刷新页面时候if(type ==='refresh'){ if(JSON.parse(localStorage.getItem('store'))){ state.value =JSON.parse(localStorage.getItem('store'))//state.value.routerList =[]}else{ return;}}// 将当前tags切除constmenu =state.value.menuList;console.log("menu",menu);/* 执行该代码后 import.meta.glob可能返回的是这样的对象 '@/views/Home.vue': () => import('@/views/Home.vue'), '@/views/About.vue': () => import('@/views/About.vue'), '@/views/User/Profile.vue': () => import('@/views/User/Profile.vue') */constmodule =import.meta.glob('../views/*.vue')console.log("module",module);constrouteArr =[]menu.forEach(item=>{ if(item.children){ item.children.forEach(child=>{ leturl =`../views/${ child.url}.vue`console.log("url",url);child.component =module[url]console.log("child.component",child.component);routeArr.push(...item.children)})}else{ leturl =`../views/${ item.url}.vue`console.log("url",url);item.component =module[url]console.log("item.component",item.component);routeArr.push(item)}routeArr.forEach(item=>{ state.value.routerList.push(router.addRoute("main",item));})})console.log("state.value.routerList",state.value.routerList);console.log("state.value.routeArr",routeArr);}return{ /* 其实是直接返回的是state.value */state,selectMenu,deleteMenu,updateMenuList,addRoutes,clean,}})
5、进行对应各个环境的配置环境设置
config.js
// 用于获取对应的环境变量constenv =process.env.NODE_ENV||"prod";constEnvConfig ={ development:{ baseURL:"/api",mockApi:"https://mock.apipark.cn/m1/4068509-0-default/api"},test:{ baseURL:"//test.xuyuan.com/api",mockApi:"https://mock.apipark.cn/m1/4068509-0-default/api"},prod:{ baseURL:"//xuyuan.com/api",mockApi:"https://mock.apipark.cn/m1/4068509-0-default/api"},}exportdefault{ env,/* 将其重新解构成一个对象,并将其合并到默认配置中 */...EnvConfig[env],isMock:false,};