上期链接:vue3从0开始搭建的学习记录(二)-CSDN博客
vuedraggable
上次没有搞完的vuedraggable,在vue2和3中使用的依赖有所不相同
安装依赖 :npm install -S vuedraggable@next
<draggable
v-model="pageList"
animation="300"
ghost-class="ghostClass"
@end="onEnd"
item-key="path"
>
<template #item="{ element, index }">
<el-breadcrumb-item
@contextmenu.prevent.native="rightBtnClick($event,element)"
:class="{ 'active-page': $route.path === element.path }"
:to="{ path: element.path }"
:key="element.code"
>{{ element.name}}<i
v-if="element.path!=='/homepage'"
class="el-icon-error deleteIcon"
@click.stop="deletePage(element)"
/></el-breadcrumb-item>
</template>
</draggable>
在我这里使用的和vue2版本的主要区别是多了一个item-key的使用,并且必须使用item的作用域插槽,不需要在内部循环要拖拽的元素了,绑定的v-model就已经循环了
效果:
const pageList=ref([{
path:'/11122/33',
name:'candan1'
},{
path:'/11132/33',
name:'candan2'
}])
页签状态管理
现在通过vuex来对页签做管理,定义一个activeMenu数组来做页签的存储
先粘官网:开始 | Vuex
安装依赖:npm install vuex@next --save
首先在src下创建一个store文件夹,加入index.js主文件、getters.js、mutations.js、actions.js(暂时用不上)
index.js
我把state直接放在index.js里了,因为我觉得要是主文件一点干货都没有还是有点造孽的(虽然目前只有一个状态缓存)
index.js
import { createStore } from 'vuex'
import getters from './getters';
import mutations from './mutations';
import actions from './actions';
const store = createStore({
state: {
activeMenu:[],
},
getters,
mutations,
actions,
modules: {},
})
export default store
getters.js
getters.js里有个menuIndex,用来获取目标页签在当前的页签列表中的位置,目前应该还用不上,但是由于页签做了拖拽,所以有可能有业务需要获取这个位置,就先加上了
使用方法就是$store.getters.menuIndex(参数) //这里的参数为route对象
getters.js
const getters = {
//获取activeMenu
activeMenu(state){
return state.activeMenu
},
/**获取页签位置
* data 目标页签
*/
menuIndex(state){
return (data)=>{
var res=state.activeMenu.findIndex(item=>item.path===data.path)
return res||res===0?res:-1
}
},
}
export default getters
mutations.js
这里是一些写操作的方法,其中的data参数均为route对象
mutations.js
import $router from '@/router'
const mutations={
/**添加页面标签
* data 目标页签
*/
addActiveMenu(state,data){
if(!state.activeMenu.find(item=>item.path===data.path)){
state.activeMenu.push(data)
}
},
//删除指定页面标签
deleteActiveMenu(state,data){
const index=state.activeMenu.findIndex(item=>item.path===data.path)
state.activeMenu.splice(state.activeMenu.findIndex(item=>item.path===data.path),1)
// $router.go(-1)
if(data.path!==$router.router.currentRoute.value.path){//当被删除页签不是当前页面时,不做操作
}else if(index>0){
$router.router.replace(state.activeMenu[index-1].path)
}else{
$router.router.replace('/')
}
},
//删除其他全部页面标签
deleteOtherActiveMenu(state,data){
const current=state.activeMenu.find(item=>item.path===data.path)
state.activeMenu=[current]
},
//删除全部页面标签
clearActiveMenu(state){
state.activeMenu=[]
$router.replace('/');
},
//刷新页面标签
resetActiveMenu(state,list){
state.activeMenu=list
}
}
export default mutations
main.ts
将store引入main.ts
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app= createApp(App)
//引入store
app.use(store)
app.mount('#app')
菜单数据获取
接下来把菜单维护出来,目前干脆就直接用router数据来做菜单数据吧,一般来讲菜单要单独维护的,我就偷个懒
把路由表重新写了一下,这里没想要写什么菜单,先随便写了几个一级菜单,顺便加了个登录的路由,登录逻辑后面再说,先把文件结构给他加上
修改router.js
修改router/index.js并将routes也暴露出来
import {createRouter,createWebHistory } from 'vue-router'
import Layout from "../layout/index.vue";
const routes=[
{
path:'/login',
component:()=>import('@/login/index.vue'),
redirect:'/homepage',
meta:{
hide:true, //加一个标志位,作为隐藏菜单的标志
}
},
{
path:'/',
component:Layout,
redirect:'/pages/homepage',
meta:{
hide:true, //加一个标志位,作为隐藏菜单的标志
},
},
{
path:'/pages',
component:Layout,
redirect:'/pages/homepage',
meta:{},
children:[
{
path:'/pages/homepage',
code:'homepage',
name:'首页',
meta:{},
component:()=>import('@/view/homePage/index.vue')
},
{
path:'/pages/pagesManagement',
code:'pagesManagement',
name:'页面管理',
meta:{},
component:()=>import('@/view/pagesManagement/index.vue')
},
{
path:'/pages/componentsManagement',
code:'componentsManagement',
name:'组件管理',
meta:{},
component:()=>import('@/view/componentsManagement/index.vue')
},
]
},
]
const router = createRouter({
history:createWebHistory(),
routes:routes
})
export default {router,routes};
修改main.ts
这样的话需要把router引入的main.ts修改一下
然后我们的路由守卫这里也要加入一下了
import '@/router/guard.js'
app.use(router.router) //原来是app.use(router)
guard.js
import router from './index.js';
import $router from '@/router'
import store from '../store';
router.router.beforeEach(async (to, from, next) => {
console.log('to',to);
// 主页不添加页签
if(to.path!=='/pages/homepage'){
store.commit('addActiveMenu',to)
}
next();
});
修改layout/index.vue
修改layout/index.vue里菜单列表的来源,页面的样式问题修改我就不再贴上来了
<script setup lang="ts">
import menuTab from './menuTab.vue';
import myMenu from './myMenu.vue'
import myRouter from '@/router/index' //引入数据
const collapse=ref(false)
//只获取path==='/'的一级路由下的children作为菜单
const menuList:object[]=myRouter.routes.find(item=>item.path==='/').children
const handleOpen = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
</script>
修改layout/myMenu.vue
修改layout/myMenu.vue的逻辑,当meta.hide为true时,不显示菜单,给<el-menu>标签加上router属性开启index跳转
<template v-for="menu in props.menuList">
<!-- 隐藏的判断逻辑 -->
<template v-if="!menu.meta.hide">
<el-sub-menu v-if="menu.children&&menu.children.length>0" :index="menu.path" :key="menu.code">
<template #title>
<el-icon>
<component :is="menu.icon" /></el-icon>
<span>{{menu.name}}</span>
</template>
<myMenu :isChild="true" :menuList="menu.children"></myMenu>
</el-sub-menu>
<el-menu-item v-else :index="menu.path" :key="menu.code+'n'">
<el-icon>
<component :is="menu.icon" />
</el-icon>
<span>{{menu.name}}</span>
</el-menu-item>
</template>
修改layout/menuTab.vue
使用vuex的store需要用useStore()方法,这里把原来的变量名pageList换成了activeMenu,上方绑定的也要记得修改,删除页签的逻辑也加上去
<script setup lang="ts">
//获取vuex里的activeMenu
const store = useStore()
const activeMenu=computed(()=>store.getters.activeMenu) //原来的pageList
function deleteOthers() {
store.commit('deleteOtherActiveMenu',currentPage)
}
function deleteAll() {
store.commit('clearActiveMenu')
}
function deletePage(page:object) {
store.commit('deleteActiveMenu',page)
}
</script>
将useStore()加入auto-import
这里发现unplugin-auto-import/vite依赖里没有useStore,那不行,要给他加进去,我不想手动写
import { useStore } from 'vuex'
那就要就改一些文件
auto-imports.d.ts文件global里加入这一句
const useStore: typeof import('vuex')['useStore']
vite.config.ts文件imports里加入vuex
AutoImport({
imports: [
'vue',
'vue-router',
'vuex' //加入vuex
],
dts: resolve('./auto-imports.d.ts'),
}),
至此,基本框架就大功告成了