'admin-21.01.21:处理动态路由及路由无限级缓存'

This commit is contained in:
lyt 2021-01-21 18:59:16 +08:00
parent df3f55a7c0
commit 1db2cba283
15 changed files with 160 additions and 47 deletions

View File

@ -1,8 +1,11 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router" import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"
import { store } from "/@/store/index.ts";
// 定义动态路由
export const dynamicRoutes = [ export const dynamicRoutes = [
{ {
path: '/', path: '/',
name: '/',
component: () => import('/@/views/layout/index.vue'), component: () => import('/@/views/layout/index.vue'),
redirect: '/home', redirect: '/home',
meta: { meta: {
@ -14,6 +17,7 @@ export const dynamicRoutes = [
}, },
children: [{ children: [{
path: '/home', path: '/home',
name: 'home',
component: () => import('/@/views/home/index.vue'), component: () => import('/@/views/home/index.vue'),
meta: { meta: {
title: '首页', title: '首页',
@ -32,8 +36,8 @@ export const dynamicRoutes = [
title: '系统设置', title: '系统设置',
isLink: '', isLink: '',
isHide: false, isHide: false,
icon: 'iconfont icon-xitongshezhi', isKeepAlive: true,
isKeepAlive: true icon: 'iconfont icon-xitongshezhi'
}, },
children: [ children: [
{ {
@ -44,8 +48,8 @@ export const dynamicRoutes = [
title: '菜单管理', title: '菜单管理',
isLink: '', isLink: '',
isHide: false, isHide: false,
icon: 'iconfont icon-caidan', isKeepAlive: true,
isKeepAlive: true icon: 'iconfont icon-caidan'
} }
}, },
{ {
@ -56,78 +60,92 @@ export const dynamicRoutes = [
title: '用户管理', title: '用户管理',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-icon-', icon: 'iconfont icon-icon-',
isKeepAlive: false
} }
} }
] ]
}, },
{ {
path: '/limits', path: '/limits',
name: 'limits',
component: () => import('/@/views/layout/routerView/parent.vue'), component: () => import('/@/views/layout/routerView/parent.vue'),
redirect: '/limits/frontEnd', redirect: '/limits/frontEnd',
meta: { meta: {
title: '权限管理', title: '权限管理',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-quanxian' icon: 'iconfont icon-quanxian'
}, },
children: [ children: [
{ {
path: '/limits/frontEnd', path: '/limits/frontEnd',
name: 'limitsFrontEnd',
component: () => import('/@/views/layout/routerView/parent.vue'), component: () => import('/@/views/layout/routerView/parent.vue'),
redirect: '/limits/frontEnd/page', redirect: '/limits/frontEnd/page',
meta: { meta: {
title: '前端控制', title: '前端控制',
isLink: '', isLink: '',
isHide: false isHide: false,
isKeepAlive: true
}, },
children: [ children: [
{ {
path: '/limits/frontEnd/page', path: '/limits/frontEnd/page',
name: 'limitsFrontEndPage',
component: () => import('/@/views/limits/frontEnd/page/index.vue'), component: () => import('/@/views/limits/frontEnd/page/index.vue'),
meta: { meta: {
title: '页面权限', title: '页面权限',
isLink: '', isLink: '',
isHide: false isHide: false,
isKeepAlive: true
} }
}, },
{ {
path: '/limits/frontEnd/btn', path: '/limits/frontEnd/btn',
name: 'limitsFrontEndBtn',
component: () => import('/@/views/limits/frontEnd/btn/index.vue'), component: () => import('/@/views/limits/frontEnd/btn/index.vue'),
meta: { meta: {
title: '按钮权限', title: '按钮权限',
isLink: '', isLink: '',
isHide: false isHide: false,
isKeepAlive: true
} }
} }
] ]
}, },
{ {
path: '/limits/backEnd', path: '/limits/backEnd',
name: 'limitsBackEnd',
component: () => import('/@/views/layout/routerView/parent.vue'), component: () => import('/@/views/layout/routerView/parent.vue'),
meta: { meta: {
title: '后端控制', title: '后端控制',
isLink: '', isLink: '',
isHide: false isHide: false,
isKeepAlive: true
}, },
children: [ children: [
{ {
path: '/limits/backEnd/page', path: '/limits/backEnd/page',
name: 'limitsBackEndEndPage',
component: () => import('/@/views/limits/backEnd/page/index.vue'), component: () => import('/@/views/limits/backEnd/page/index.vue'),
meta: { meta: {
title: '页面权限', title: '页面权限',
isLink: '', isLink: '',
isHide: false isHide: false,
isKeepAlive: true
} }
}, },
{ {
path: '/limits/backEnd/btn', path: '/limits/backEnd/btn',
name: 'limitsBackEndEndBtn',
component: () => import('/@/views/limits/backEnd/btn/index.vue'), component: () => import('/@/views/limits/backEnd/btn/index.vue'),
meta: { meta: {
title: '按钮权限', title: '按钮权限',
isLink: '', isLink: '',
isHide: false isHide: false,
isKeepAlive: true
} }
} }
] ]
@ -136,64 +154,76 @@ export const dynamicRoutes = [
}, },
{ {
path: '/menu', path: '/menu',
name: 'menu',
component: () => import('/@/views/layout/routerView/parent.vue'), component: () => import('/@/views/layout/routerView/parent.vue'),
redirect: '/menu/menu1', redirect: '/menu/menu1',
meta: { meta: {
title: '路由嵌套', title: '路由嵌套',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-xitongshezhi' icon: 'iconfont icon-xitongshezhi'
}, },
children: [ children: [
{ {
path: '/menu/menu1', path: '/menu/menu1',
name: 'menu1',
component: () => import('/@/views/layout/routerView/parent.vue'), component: () => import('/@/views/layout/routerView/parent.vue'),
redirect: '/menu/menu1/menu11', redirect: '/menu/menu1/menu11',
meta: { meta: {
title: '菜单1', title: '菜单1',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-caidan' icon: 'iconfont icon-caidan'
}, },
children: [ children: [
{ {
path: '/menu/menu1/menu11', path: '/menu/menu1/menu11',
name: 'menu11',
component: () => import('/@/views/menu/menu1/menu11/index.vue'), component: () => import('/@/views/menu/menu1/menu11/index.vue'),
meta: { meta: {
title: '菜单11', title: '菜单11',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-caidan' icon: 'iconfont icon-caidan'
} }
}, },
{ {
path: '/menu/menu1/menu12', path: '/menu/menu1/menu12',
name: 'menu12',
component: () => import('/@/views/layout/routerView/parent.vue'), component: () => import('/@/views/layout/routerView/parent.vue'),
redirect: '/menu/menu1/menu12/menu121', redirect: '/menu/menu1/menu12/menu121',
meta: { meta: {
title: '菜单12', title: '菜单12',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-caidan' icon: 'iconfont icon-caidan'
}, },
children: [ children: [
{ {
path: '/menu/menu1/menu12/menu121', path: '/menu/menu1/menu12/menu121',
name: 'menu121',
component: () => import('/@/views/menu/menu1/menu12/menu121/index.vue'), component: () => import('/@/views/menu/menu1/menu12/menu121/index.vue'),
meta: { meta: {
title: '菜单121', title: '菜单121',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-caidan' icon: 'iconfont icon-caidan'
} }
}, },
{ {
path: '/menu/menu1/menu12/menu122', path: '/menu/menu1/menu12/menu122',
name: 'menu122',
component: () => import('/@/views/menu/menu1/menu12/menu122/index.vue'), component: () => import('/@/views/menu/menu1/menu12/menu122/index.vue'),
meta: { meta: {
title: '菜单122', title: '菜单122',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-caidan' icon: 'iconfont icon-caidan'
} }
} }
@ -201,11 +231,13 @@ export const dynamicRoutes = [
}, },
{ {
path: '/menu/menu1/menu13', path: '/menu/menu1/menu13',
name: 'menu13',
component: () => import('/@/views/menu/menu1/menu13/index.vue'), component: () => import('/@/views/menu/menu1/menu13/index.vue'),
meta: { meta: {
title: '菜单13', title: '菜单13',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-caidan' icon: 'iconfont icon-caidan'
} }
} }
@ -213,11 +245,13 @@ export const dynamicRoutes = [
}, },
{ {
path: '/menu/menu2', path: '/menu/menu2',
name: 'menu2',
component: () => import('/@/views/menu/menu2/index.vue'), component: () => import('/@/views/menu/menu2/index.vue'),
meta: { meta: {
title: '菜单2', title: '菜单2',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-caidan' icon: 'iconfont icon-caidan'
} }
} }
@ -225,87 +259,104 @@ export const dynamicRoutes = [
}, },
{ {
path: '/fun', path: '/fun',
name: 'funIndex',
component: () => import('/@/views/fun/index.vue'), component: () => import('/@/views/fun/index.vue'),
meta: { meta: {
title: '功能', title: '功能',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-crew_feature' icon: 'iconfont icon-crew_feature'
} }
}, },
{ {
path: '/pages', path: '/pages',
name: 'pagesIndex',
component: () => import('/@/views/pages/index.vue'), component: () => import('/@/views/pages/index.vue'),
meta: { meta: {
title: '页面', title: '页面',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-fuzhiyemian' icon: 'iconfont icon-fuzhiyemian'
} }
}, },
{ {
path: '/components', path: '/components',
name: 'componentsIndex',
component: () => import('/@/views/components/index.vue'), component: () => import('/@/views/components/index.vue'),
meta: { meta: {
title: '组件', title: '组件',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-zujian' icon: 'iconfont icon-zujian'
} }
}, },
{ {
path: '/chart', path: '/chart',
name: 'chartIndex',
component: () => import('/@/views/chart/index.vue'), component: () => import('/@/views/chart/index.vue'),
meta: { meta: {
title: '大数据图表', title: '大数据图表',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-ico_shuju' icon: 'iconfont icon-ico_shuju'
} }
}, },
{ {
path: '/personal', path: '/personal',
name: 'personal',
component: () => import('/@/views/personal/index.vue'), component: () => import('/@/views/personal/index.vue'),
meta: { meta: {
title: '个人中心', title: '个人中心',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-gerenzhongxin' icon: 'iconfont icon-gerenzhongxin'
} }
}, },
{ {
path: '/tools', path: '/tools',
name: 'tools',
component: () => import('/@/views/tools/index.vue'), component: () => import('/@/views/tools/index.vue'),
meta: { meta: {
title: '工具类集合', title: '工具类集合',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-gongju' icon: 'iconfont icon-gongju'
} }
}, },
{ {
path: '/link', path: '/link',
name: 'link',
component: () => import('/@/views/layout/routerView/parent.vue'), component: () => import('/@/views/layout/routerView/parent.vue'),
meta: { meta: {
title: '外链', title: '外链',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-caozuo-wailian' icon: 'iconfont icon-caozuo-wailian'
} }
}, },
{ {
path: '/iframe', path: '/iframes',
component: () => import('/@/views/layout/routerView/iframe.vue'), name: 'iframes',
component: () => import('/@/views/layout/routerView/iframes.vue'),
meta: { meta: {
title: '内嵌 iframe', title: '内嵌 iframe',
isLink: '', isLink: '',
isHide: false, isHide: false,
isKeepAlive: true,
icon: 'iconfont icon-neiqianshujuchucun' icon: 'iconfont icon-neiqianshujuchucun'
} }
}] }]
} }
] ]
// 定义静态路由
const staticRoutes: Array<RouteRecordRaw> = [ const staticRoutes: Array<RouteRecordRaw> = [
{ {
path: '/login', path: '/login',
@ -337,14 +388,61 @@ const staticRoutes: Array<RouteRecordRaw> = [
} }
] ]
// 添加静态路由
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
routes: staticRoutes routes: staticRoutes
}) })
dynamicRoutes.map((route) => { // 多级嵌套数组处理成一维数组
router.addRoute(route as RouteRecordRaw); export function formatFlatteningRoutes(arr: any) {
}); if (arr.length < 0) return false
for (let i = 0; i < arr.length; i++) {
if (arr[i].children) {
arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1))
}
}
return arr
}
// 多级嵌套数组处理后的一维数组,再处理成 `定义动态路由` 的格式
// 只保留二级也就是二级以上全部处理成只有二级keep-alive 支持二级缓存
// isKeepAlive 处理 `name` 值,进行缓存。顶级关闭,全部不缓存
export function formatTwoStageRoutes(arr: any) {
if (arr.length < 0) return false
const newArr: any = []
const cacheList: Array<string> = []
arr.map((v: any) => {
if (v.path === '/') {
newArr.push({ component: v.component, name: v.name, path: v.path, redirect: v.redirect, meta: v.meta, children: [] })
} else {
newArr[0].children.push({ ...v })
if (newArr[0].meta.isKeepAlive && v.meta.isKeepAlive) {
cacheList.push(v.name)
store.dispatch('setCacheKeepAlive', cacheList)
}
}
})
return newArr
}
// 添加动态路由
export function setAddRoute() {
formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)).map((route: any) => {
router.addRoute(route as RouteRecordRaw)
})
}
// 删除/重置路由
export function resetRoute() {
formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)).map((route: any) => {
const { name } = route
router.hasRoute(name) && router.removeRoute(name)
})
}
// 初始化执行函数
setAddRoute()
// router.afterEach((to, from) => { // router.afterEach((to, from) => {

View File

@ -1,7 +1,6 @@
import { InjectionKey } from 'vue' import { InjectionKey } from 'vue'
import { createStore, useStore as baseUseStore, Store } from 'vuex' import { createStore, useStore as baseUseStore, Store } from 'vuex'
import themeConfig from '/@/utils/themeConfig.ts' import themeConfig from '/@/utils/themeConfig.ts'
import { dynamicRoutes } from '/@/router/index.ts'
export interface RootStateTypes { export interface RootStateTypes {
themeConfig: { themeConfig: {
isDrawer: boolean, isDrawer: boolean,
@ -42,7 +41,8 @@ export interface RootStateTypes {
columnsAsideStyle: string, columnsAsideStyle: string,
layout: string layout: string
}, },
routes: Array<object> routes: Array<object>,
caches: Array<string>
} }
export const key: InjectionKey<Store<RootStateTypes>> = Symbol() export const key: InjectionKey<Store<RootStateTypes>> = Symbol()
@ -50,7 +50,8 @@ export const key: InjectionKey<Store<RootStateTypes>> = Symbol()
export const store = createStore<RootStateTypes>({ export const store = createStore<RootStateTypes>({
state: { state: {
themeConfig, themeConfig,
routes: [] routes: [],
caches: []
}, },
mutations: { mutations: {
getThemeConfig(state: any, data: object) { getThemeConfig(state: any, data: object) {
@ -58,15 +59,21 @@ export const store = createStore<RootStateTypes>({
}, },
getRoutes(state: any, data: Array<object>) { getRoutes(state: any, data: Array<object>) {
state.routes = data state.routes = data
},
getCacheKeepAlive(state: any, data: Array<string>) {
state.caches = data
} }
}, },
actions: { actions: {
setThemeConfig({ commit }, data: object) { setThemeConfig({ commit }, data: object) {
commit('getThemeConfig', data) commit('getThemeConfig', data)
}, },
async setRoutes({ commit }) { async setRoutes({ commit }, data: any) {
commit('getRoutes', dynamicRoutes) commit('getRoutes', data)
} },
async setCacheKeepAlive({ commit }, data: Array<string>) {
commit('getCacheKeepAlive', data)
},
} }
}) })

View File

@ -73,6 +73,11 @@ body,
.layout-mian-height-50 { .layout-mian-height-50 {
height: calc(100vh - 50px); height: calc(100vh - 50px);
} }
.layout-columns-warp {
flex: 1;
display: flex;
overflow: hidden;
}
} }
/* element plus 全局样式 /* element plus 全局样式

View File

@ -786,10 +786,6 @@
width: 24px; width: 24px;
text-align: center; text-align: center;
} }
// 获取焦点时
.el-menu-item:focus {
background: transparent !important;
}
/* Tabs 标签页 /* Tabs 标签页
------------------------------- */ ------------------------------- */

View File

@ -18,6 +18,7 @@ import {
onBeforeMount, onBeforeMount,
} from "vue"; } from "vue";
import { useStore } from "/@/store/index.ts"; import { useStore } from "/@/store/index.ts";
import { dynamicRoutes } from "/@/router/index.ts";
import Logo from "/@/views/layout/logo/index.vue"; import Logo from "/@/views/layout/logo/index.vue";
import Vertical from "/@/views/layout/navMenu/vertical.vue"; import Vertical from "/@/views/layout/navMenu/vertical.vue";
export default { export default {
@ -31,8 +32,8 @@ export default {
}); });
// // // //
const setFilterRoutes = () => { const setFilterRoutes = () => {
store.dispatch("setRoutes"); store.dispatch("setRoutes", dynamicRoutes[0].children);
state.menuList = filterRoutesFun(store.state.routes[0].children); state.menuList = filterRoutesFun(store.state.routes);
}; };
// //
const filterRoutesFun = (arr: Array<object>) => { const filterRoutesFun = (arr: Array<object>) => {

View File

@ -1,7 +1,7 @@
<template> <template>
<el-container class="layout-container"> <el-container class="layout-container">
<ColumnsAside /> <ColumnsAside />
<div style="flex:1;display: flex;"> <div class="layout-columns-warp">
<Aside /> <Aside />
<el-container class="flex-center"> <el-container class="flex-center">
<Header v-if="isFixedHeader" /> <Header v-if="isFixedHeader" />

View File

@ -172,7 +172,7 @@ export default {
margin-right: 5px; margin-right: 5px;
border-radius: 2px; border-radius: 2px;
position: relative; position: relative;
z-index: 3; z-index: 0;
cursor: pointer; cursor: pointer;
justify-content: space-between; justify-content: space-between;
&:hover { &:hover {

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
iframeIndex iframes
<el-input v-model="val"></el-input> <el-input v-model="val"></el-input>
</div> </div>
</template> </template>
@ -8,7 +8,7 @@
<script lang="ts"> <script lang="ts">
import { toRefs, reactive } from "vue"; import { toRefs, reactive } from "vue";
export default { export default {
name: "iframeIndex", name: "iframes",
setup() { setup() {
const state = reactive({ const state = reactive({
val: "", val: "",

View File

@ -2,8 +2,8 @@
<div> <div>
<router-view v-slot="{ Component, route }"> <router-view v-slot="{ Component, route }">
<transition :name="setTransitionName" mode="out-in"> <transition :name="setTransitionName" mode="out-in">
<keep-alive include="home,systemMenu"> <keep-alive :include="getCaches">
<component :is="Component" /> <component :is="Component" :key="route.path" />
</keep-alive> </keep-alive>
</transition> </transition>
</router-view> </router-view>
@ -18,12 +18,13 @@ import {
reactive, reactive,
getCurrentInstance, getCurrentInstance,
watch, watch,
onBeforeMount,
} from "vue"; } from "vue";
import { useRouter, RouteRecordRaw } from "vue-router";
import { useStore } from "/@/store/index.ts"; import { useStore } from "/@/store/index.ts";
export default defineComponent({ export default defineComponent({
name: "layoutParentView", name: "layoutParentView",
setup() { setup() {
const router = useRouter();
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const store = useStore(); const store = useStore();
const state = reactive({ const state = reactive({
@ -44,6 +45,10 @@ export default defineComponent({
const getThemeConfig = computed(() => { const getThemeConfig = computed(() => {
return store.state.themeConfig; return store.state.themeConfig;
}); });
// (name)
const getCaches = computed(() => {
return store.state.caches;
});
// themeConfig el-scrollbar // themeConfig el-scrollbar
watch(store.state.themeConfig, (val) => { watch(store.state.themeConfig, (val) => {
state.headerHeight = val.isTagsview ? "84px" : "50px"; state.headerHeight = val.isTagsview ? "84px" : "50px";
@ -54,6 +59,7 @@ export default defineComponent({
}); });
return { return {
getThemeConfig, getThemeConfig,
getCaches,
setTransitionName, setTransitionName,
...toRefs(state), ...toRefs(state),
}; };

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
menuMenu11 menu11
<el-input v-model="val"></el-input> <el-input v-model="val"></el-input>
</div> </div>
</template> </template>
@ -8,7 +8,7 @@
<script lang="ts"> <script lang="ts">
import { toRefs, reactive } from "vue"; import { toRefs, reactive } from "vue";
export default { export default {
name: "menuMenu11", name: "menu11",
setup() { setup() {
const state = reactive({ const state = reactive({
val: "", val: "",

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
menuMenu121 menu121
<el-input v-model="val"></el-input> <el-input v-model="val"></el-input>
</div> </div>
</template> </template>
@ -8,7 +8,7 @@
<script lang="ts"> <script lang="ts">
import { toRefs, reactive } from "vue"; import { toRefs, reactive } from "vue";
export default { export default {
name: "menuMenu121", name: "menu121",
setup() { setup() {
const state = reactive({ const state = reactive({
val: "", val: "",

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
menuMenu122 menu122
<el-input v-model="val"></el-input> <el-input v-model="val"></el-input>
</div> </div>
</template> </template>
@ -8,7 +8,7 @@
<script lang="ts"> <script lang="ts">
import { toRefs, reactive } from "vue"; import { toRefs, reactive } from "vue";
export default { export default {
name: "menuMenu122", name: "menu122",
setup() { setup() {
const state = reactive({ const state = reactive({
val: "", val: "",

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
menuMenu13 menu13
<el-input v-model="val"></el-input> <el-input v-model="val"></el-input>
</div> </div>
</template> </template>
@ -8,7 +8,7 @@
<script lang="ts"> <script lang="ts">
import { toRefs, reactive } from "vue"; import { toRefs, reactive } from "vue";
export default { export default {
name: "menuMenu13", name: "menu13",
setup() { setup() {
const state = reactive({ const state = reactive({
val: "", val: "",

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
menuMenu2 menu2
<el-input v-model="val"></el-input> <el-input v-model="val"></el-input>
</div> </div>
</template> </template>
@ -8,7 +8,7 @@
<script lang="ts"> <script lang="ts">
import { toRefs, reactive } from "vue"; import { toRefs, reactive } from "vue";
export default { export default {
name: "menuMenu2", name: "menu2",
setup() { setup() {
const state = reactive({ const state = reactive({
val: "", val: "",

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
toolsIndex tools
<el-input v-model="val"></el-input> <el-input v-model="val"></el-input>
</div> </div>
</template> </template>
@ -8,7 +8,7 @@
<script lang="ts"> <script lang="ts">
import { toRefs, reactive } from "vue"; import { toRefs, reactive } from "vue";
export default { export default {
name: "toolsIndex", name: "tools",
setup() { setup() {
const state = reactive({ const state = reactive({
val: "", val: "",