许苑后台管理系统
- 一、项目介绍
- 1、技术栈
- 2、业务功能
- 3、应用场景
- 4、项目源码
- 二、项目实战
- 2.1、项目初始化
- 2.2、项目实战
- 1、引入router配置
- 2、App.html" title=vue>vue组件引入
- 3、创建RootView根路由组件
- 4、依次创建
- 5、进行对应各个环境的配置环境设置
一、项目介绍
1、技术栈
2、业务功能
- 登录
- 首页
- 商品
- 用户管理
3、应用场景
- 进行后台管理项目的
- 根据不同用户的权限授予不同的功能
4、项目源码
xuyuan-upward 希望每位大佬获取时候点个小小的赞!
二、项目实战
2.1、项目初始化
1、构建项目
# npm 7+,需要添加额外的 --:
npm create vite@latest my-html" title=vue>vue-app -- --template html" title=vue>vue
2、安装成功后进行安装依赖
npm install
3、修改路径替代符
import { defineConfig } from 'vite'
import html" title=vue>vue from '@vitejs/plugin-html" title=vue>vue'// https://vite.dev/config/
export default defineConfig({plugins: [html" title=vue>vue()],/* 添加的是别名 */resolve: {alias: {'@': '/src',},},
})
4、引入element-plus依赖,axios,router
安装依赖内容过于简单,请自行取相关内容官网查看文档进行安装
element-plus官网
axios官网
router
2.2、项目实战
1、引入router配置
import { createRouter, createWebHashHistory, createWebHistory } from 'html" title=vue>vue-router'
const routes = [{path: '/',name: 'main',component: () => import('@/views/RootView.html" title=vue>vue'),redirect: '/home',/* 访问 /home 时的行为
当你访问 /home 时,会发生以下情况:
匹配父路由:
首先匹配到 path: '/' 的路由配置。
由于 Main.html" title=vue>vue 是父组件,它会被渲染。
匹配子路由:
在 Main.html" title=vue>vue 内部的 <router-view> 中,会匹配到 path: '/home' 的子路由。
因此,Home.html" title=vue>vue 会在 Main.html" title=vue>vue 的 <router-view> 中渲染。 */children: [{path: '/home',name: 'home',component: () => import('@/views/MainView.html" title=vue>vue')},]},]
const router = createRouter({history: createWebHistory(),routes
})
export default router
接着还需要导入main.js一些配置文件
- 全局样式
- pinia等等
import { createApp } from 'html" title=vue>vue'
import App from './App.html" title=vue>vue'
// 导入全局样式
import "@/assets/less/index.less"
import router from '@/router'
import ElementPlus from 'element-plus'
// 导入element-plus组件的全局样式
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-html" title=vue>vue'
import { createPinia } from 'pinia'
import { useAllDataStore } from '@/stores'
/* 引入mock */
import "@/apis/mock.js"
/* 引入apis 管理请求接口 */
import apis from "@/apis/apis.js"
const pinia = createPinia()
const app = createApp(App)
/* 定义全局配置使用 */
app.config.globalProperties.$apis = apisapp.use(ElementPlus)
app.use(pinia)
// localStorage.removeItem("store")
const store = 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}) */return resFil
}
/*
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] of Object.entries(ElementPlusIconsVue)) {app.component(key, component)
}
app.mount('#app')
html" title=vue>vue_166">2、App.html" title=vue>vue组件引入
html"><script setup>
</script><template><div class="app"><router-view /></div>
</template><style >
#app,
.app {width: 100%;height: 100%;overflow: hidden;
}
</style>
3、创建RootView根路由组件
html"><template><div class="common-layout"><el-container class="main-container"><div class="aside-container"><CommonAside /></div><el-container class="right-container"><el-header style="height: 40px;"><CommonHeader /></el-header><el-divider style="margin: 5px 0;" /><CommonTab /><el-main><router-view /></el-main><el-footer><Footer /></el-footer></el-container></el-container></div>
</template><script setup>
import { reactive, toRefs, onMounted, nextTick } from 'html" title=vue>vue'
import CommonAside from '@/components/CommonAside.html" title=vue>vue';
import CommonHeader from '@/components/CommonHeader.html" title=vue>vue';
import CommonTab from '@/components/CommonTab.html" title=vue>vue';
import Footer from '@/components/Footer.html" title=vue>vue';
</script>
<style lang='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.html" title=vue>vue
html"><template><!-- default-active通常与对应的index相关 --><el-aside :width="width"><el-menu text-color="#fff" background-color="#545c64" :collapse="isCollapse" :collapse-transition="false" :default-active="activeMenu"class="el-menu-vertical-demo"><div class="title"><i class="iconfont icon-quanpingtai"> </i><h4 v-show="!isCollapse">许苑后台管理</h4></div><!-- 没有子菜单 --><el-menu-item v-for="item in noChildren" :key="item.path" :index="item.path" @click="handleMenu(item)"><component :is="item.icon" class="icon" /><span style="margin-left: 10px">{{ item.label }}</span></el-menu-item><!-- 有子菜单 --><el-sub-menu v-for="item in hasChildren" :key="item.path" :index="item.path"><template #title><component :is="item.icon" class="icon" /><span style="margin-left: 10px">{{ item.label }}</span></template><el-menu-item-group><el-menu-item v-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><script setup>
import { ref, reactive, toRefs, onMounted, nextTick, computed } from 'html" title=vue>vue'
import { useRouter, useRoute } from 'html" title=vue>vue-router'
import { useAllDataStore } from '@/stores'
const store = useAllDataStore()
const router = useRouter()
const route = 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'}]}
])*/
const list = computed(() => store.state.menuList)
const noChildren = computed(() => list.value.filter(item => !item.children))
const hasChildren = computed(() => list.value.filter(item => item.children))
const width = computed(() => store.state.isCollapse ? '60px' : '200px')
// 涉及组件之间的传递 => 使用pinia进行各组件之间的传递
const isCollapse = computed(() => store.state.isCollapse)
const activeMenu = computed(() => route.path)
const handleMenu = (item) => {if (item.children) {return}router.push(item.path)store.selectMenu(item)
}
</script>
<style lang='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.html" title=vue>vue
html"><template><div class="header"><div class="l-header"><el-button size="small" @click="store.state.isCollapse = !store.state.isCollapse"><el-icon><Menu /></el-icon></el-button></div><div class="r-header"><el-dropdown><span class="el-dropdown-link"><img src="@/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><script setup>
import { ref, reactive, toRefs, onMounted, nextTick, computed } from 'html" title=vue>vue'
import { useAllDataStore } from '@/stores'
import { ArrowRight } from '@element-plus/icons-html" title=vue>vue'
import { useRouter } from 'html" title=vue>vue-router'
const store = useAllDataStore()
const router = useRouter()
let currentPath = computed(() => {console.log("store.state.currentMenu", store.state.currentMenu);return store.state.currentMenu;
})
const handlerLogout = () => {store.clean()router.push('/login')
}
</script>
<style lang='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><!-- -- 面包屑功能 <el-breadcrumb :separator-icon="ArrowRight"><el-breadcrumb-item :to=" '/' ">首页</el-breadcrumb-item><el-breadcrumb-item v-if="currentPath" :to="currentPath.path" @cl>{{ currentPath.label}}</el-breadcrumb-item></el-breadcrumb> -->
- Footer.html" title=vue>vue
html"><template><div class="footer"><a href="https://github.com/xuyuan-upward" class="toLearn"><span> <i class="iconfont icon-github"></i>站长: 许苑向上</span></a><a href="https://blog.csdn.net/a147775" class="toLearn"><span> <i class="iconfont icon-bokeyuan"></i>博客: xuyuan-upward</span></a><a href="https://user.qzone.qq.com/2517115657/main" class="toLearn"><span> <i class="iconfont icon-shouye"></i>联系方式: 许苑向上</span></a></div>
</template><script setup>
import { ref, reactive, toRefs, onMounted, nextTick } from 'html" title=vue>vue'
</script>
<style lang='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.html" title=vue>vue路由组件
html"><template><div class="home"><el-row><!-- 左侧 --><el-col :span="7"><div class="l-user"><el-card style="max-width: 480px" shadow="hover" class="user-info"><div class="user"><img src="@/assets/images/xuyuan.jpg" alt=""style="width: 100px;height: 100px;border-radius: 50%;margin-right: 10px;"><div class="userInfo"><h>admin</h><p style="margin-top: 20px; color: #999;">超级管理员</p></div></div><el-divider /><div class="login-info"><p>上次登录时间:<span>2024-11-18 1:00:00</span></p><p style="margin-top: 10px;">上次登录地点:<span>广西</span></p></div></el-card><el-card style="max-width: 480px" shadow="hover" class="user-table"><el-table :data="tableData" style="width: 100%"><!-- 遍历val是值 key是键 --><el-table-column v-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"><div class="r-echart"><div class="top"><el-card v-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 }" /><div class="detail"><p class="num">¥{{ item.value }}</p><p class="txt">¥{{ item.name }}</p></div></el-card></div><div class="bottom"><!-- 三个图表容器 --><div class="echart-top"><el-card shadow="hover"><div ref="echart" style="height: 220px;"></div></el-card></div><div class="echart-bottom"><el-card shadow="hover"><div ref="userEchart" style="height: 140px"></div></el-card><el-card shadow="hover"><div ref="videoEchart" style="height: 140px"></div></el-card></div></div></div></el-col></el-row></div>
</template><script setup>
import { ref, reactive, toRefs, onMounted, nextTick, getCurrentInstance } from 'html" title=vue>vue'
import * as echarts from 'echarts';//这个tableData是假数据,等会我们使用axios请求mock数据
const { proxy } = getCurrentInstance()
const tableData = ref([])
const counterData = ref([])
//observer 接收观察器实例对象
const observer = ref(null)//这个是折线图和柱状图 两个图表共用的公共配置
const xOptions = 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: [],
})
const pieOptions = reactive({tooltip: {trigger: "item",},legend: {},color: ["#0f78f4","#dd536b","#9462e5","#a6a6a6","#e1bb22","#39c362","#3ed1cf",],series: []
})const getTableData = async () => {const data = await proxy.$apis.getTableData()console.log("home,tableData获取到的数据:", data);tableData.value = data.tableData
}
const getCounterData = async () => {const data = await proxy.$apis.getCounterData()console.log("home,counterData获取到的数据:", data);counterData.value = data
}const getChartData = async () => {// 获取图标信息 ,解构const { orderData, userData, videoData } = await proxy.$apis.getChartData()console.log("home,orderData获取到的数据:", orderData);//对第一个图表的xAxis和series赋值xOptions.xAxis.data = orderData.datexOptions.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对象const OneEcharts = 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",}]//twoconst TwoEcharts = echarts.init(proxy.$refs["userEchart"])TwoEcharts.setOption(xOptions)//对第三个图表的series赋值pieOptions.series = [{data: videoData,type: "pie",},]//threeconst ThreeEcharts = echarts.init(proxy.$refs["videoEchart"])ThreeEcharts.setOption(pieOptions);//ResizeObserver 如果监视的容器大小变化,如果改变会执行传递的回调observer.value = new ResizeObserver(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);
})
const tableLabel = ref({name: "课程",todayBuy: "今日购买",monthBuy: "本月购买",totalBuy: "总购买",
})</script>
<style lang='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.html" title=vue>vue组件
html"><template><div class="user"><div class="user-head"><el-button type="primary" @click="handleAdd">新增</el-button><el-form :inline="true" :model="formData"><el-form-item label="请输入"><el-input placeholder="请输入姓名" v-model="formData.keyWord"></el-input></el-form-item><el-form-item><el-button type="primary" @click="handlerSearch">搜索</el-button></el-form-item></el-form></div><div class="user-table"><el-dialog v-model="dialogVisible" :title="action == 'add' ? '新增用户' : '编辑用户'" width="35%":before-close="handleClose"><!--需要注意的是设置了:inline="true",会对el-select的样式造成影响,我们通过给他设置一个class=select-在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"><!-- 使用:inline="true"会对select造成影响,此时长度应该设置最大 --><el-form-item label="性别" prop="sex" style="width: 80%;"><el-select v-model="formUser.sex" placeholder="请选择" class="select-clean"><el-option label="男" :value="1" /><!-- 注意这里的 :value 表示绑定一个表达式即所谓的"1" 其实代表的是number类型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><el-table :data="tableData" style="width: 100%"><el-table-column v-for="item in tableLabel" :key="item.prop" :prop="item.prop" :label="item.label":width="item.width" /><el-table-column fixed="right" label="Operations" min-width="120"><template #="scoped"><el-button type="primary" size="small" @click="onEdit(scoped.row)">编辑</el-button><el-button type="danger" size="small" @click="onDelete(scoped.row)">删除</el-button></template></el-table-column></el-table><el-pagination layout="prev, pager, next" :total="config.total" @current-change="handlerChangePage"class="page" /></div></div>
</template><script setup>
import { ref, reactive, toRefs, onMounted, nextTick, getCurrentInstance } from 'html" title=vue>vue'
import { ElMessage, ElMessageBox } from 'element-plus'
const { proxy } = getCurrentInstance()
const tableData = ref([])
const tableLabel = reactive([{prop: "name",label: "姓名",},{prop: "age",label: "年龄",},{prop: "sex",label: "性别",},{prop: "birth",label: "出生日期",width: 200,},{prop: "addr",label: "地址",width: 400,},
])
const config = reactive({name: "",total: 0,
}
)
const formData = reactive({keyWord: ""
})
const dialogVisible = ref(false)
const action = ref("add")
const formUser = ref({sex: 0,
})//表单校验规则
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 handlerSearch = () => {console.log("搜索", formData.keyWord);config.name = formData.keyWord// console.log("搜索", searchText);getUserData(config)}
const getUserData = async (query) => {const data = await proxy.$apis.getUserData(query)console.log("UserView的数据", data);config.total = data.counttableData.value = data.list.map((item) => {return {...item,sex: item.sex === 1 ? '女' : '男'}})}
onMounted(() => {getUserData()
})
const handlerChangePage = (value) => {console.log("当前页码", value);config.page = valuegetUserData(config)
}
const onDelete = (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: '取消删除',})})
}
const onEdit = (row) => {console.log("编辑", row);action.value = "edit"dialogVisible.value = true/* nextTick 确保在 DOM 更新完成之后再执行回调函数也就是编辑表单*/nextTick(() => {formUser.value = {...row,}})/* formUser.value = {...row,} */
}//这个方法之前定义过
const handleAdd = () => {action.value = "add"//打开对话窗dialogVisible.value = true
}//对话框右上角的关闭事件
const handleClose = () => {//获取到表单dom,执行resetFields重置表单//关闭对话框dialogVisible.value = falseproxy.$refs["userForm"].resetFields()
}//对话框右下角的取消事件
const handleCancel = () => {dialogVisible.value = falseproxy.$refs["userForm"].resetFields()
}//格式化日期,格式化为: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 = async () => {// 获取表单数据console.log("添加的xxx", formUser.value);// 先进行校验proxy.$refs["userForm"].validate(async (validate) => {if (validate) {let res = 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 = await proxy.$apis.addUser(formUser.value)} else {res = await proxy.$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>
<style lang='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 'html" title=vue>vue'
function initData() {return {isCollapse: false,tags: [{path: '/home',name: 'home',label: '首页',icon: 'hone'}],currentMenu: null,/* 展示菜单列表的数组 */menuList: [],token: null,routerList: [],}
}export const useAllDataStore = defineStore('allData', () => {// 全部数据的获取和修改const state = ref(initData())// 进行数据持久化watch(state, newObj => {if (!newObj.token) returnlocalStorage.setItem('store', JSON.stringify(newObj))}, {deep: true,})function selectMenu(val) {if (val.name === 'home') {state.value.currentMenu = null}else {state.value.currentMenu = vallet index = state.value.tags.findIndex(item => item.name === val.name)index === -1 ? state.value.tags.push(val) : ""}}function deleteMenu(tag) {let index = state.value.tags.findIndex(item => item.name == tag.name)// 将当前tags切除state.value.tags.splice(index, 1);}function updateMenuList(val) {// 将当前tags切除state.value.menuList = val;}function clean() {// 将所有路由移除state.value.routerList.forEach(item => {if (item) item();state.value = initData();// 删除本地的缓存localStorage.removeItem('store')})}function addRoutes(router, type) {// 刷新页面时候if (type === 'refresh') {if (JSON.parse(localStorage.getItem('store'))) {state.value = JSON.parse(localStorage.getItem('store'))//state.value.routerList = []}else {return;}}// 将当前tags切除const menu = state.value.menuList;console.log("menu", menu);/* 执行该代码后 import.meta.glob可能返回的是这样的对象'@/views/Home.html" title=vue>vue': () => import('@/views/Home.html" title=vue>vue'),'@/views/About.html" title=vue>vue': () => import('@/views/About.html" title=vue>vue'),'@/views/User/Profile.html" title=vue>vue': () => import('@/views/User/Profile.html" title=vue>vue')*/const module = import.meta.glob('../views/*.html" title=vue>vue')console.log("module", module);const routeArr = []menu.forEach(item => {if (item.children) {item.children.forEach(child => {let url = `../views/${child.url}.html" title=vue>vue`console.log("url", url);child.component = module[url]console.log("child.component", child.component);routeArr.push(...item.children)})}else {let url = `../views/${item.url}.html" title=vue>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
// 用于获取对应的环境变量
const env = process.env.NODE_ENV || "prod";
const EnvConfig = {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"},
}
export default {env,/* 将其重新解构成一个对象,并将其合并到默认配置中 */...EnvConfig[env],isMock:false,
};