【Vue3+Vite+TS】9.0 改造项目整体结构

优化菜单

这里存在一个bug,修复如下:
修改src\components\baseline\menu\src\index.vue

<template>
    <div>
        <el-menu
            class="el-menu-vertical-demo"
            :default-active="defaultActive"
            :router="router"
            v-bind="$attrs"
        >
            <template v-for="(item, index) in data" :key="index">
                <!-- 一级无二级菜单的菜单栏 -->
                <el-menu-item
                    v-if="!item.children || !item.children.length"
                    :index="item.index"
                >
                    <component v-if="item.icon" :is="item.icon"></component>
                    <span>{{ item.name }}</span>
                </el-menu-item>
                <el-sub-menu
                    v-if="item.children && item.children.length"
                    :index="item.index"
                >
                    <template #title>
                        <component v-if="item.icon" :is="item.icon"></component>
                        <span>{{ item.name }}</span>
                    </template>
                    <!-- 二级菜单栏 -->
                    <el-menu-item
                        v-for="(item2, index2) in item.children"
                        :index="item2.index"
                    >
                        <component
                            v-if="item2.icon"
                            :is="item2.icon"
                        ></component>
                        <span>{{ item2.name }}</span>
                    </el-menu-item>
                </el-sub-menu>
            </template>
        </el-menu>
    </div>
</template>
<script lang="ts" setup>
import { PropType } from 'vue'
import { MenuItem } from './types'

const props = defineProps({
    //说明:
    data: {
        required: true,
        type: Array as PropType<MenuItem[]>,
    },
    // 默认选中的菜单
    defaultActive: {
        type: String,
        default: '',
    },
    //是否是路由模式 router,
    //是否启用 vue-router 模式
    //启用该模式会在激活导航时以 index 作为 path 进行路由跳转
    router: {
        type: Boolean,
        default: false,
    },
})
// console.log('data:', props.data)
</script>
<style lang="scss" scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
    width: 2rem;
    min-height: 4rem;
}
svg {
    margin-right: 0.04rem;
}
</style>

修改src\components\baseline\container\src\navAside\index.vue

<template>
    <div>
        <bs-menu
           :collapse="collapse"
            :data="data"
            router
            :default-active="$route.path"
        ></bs-menu>
    </div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
let props = defineProps<{ collapse: boolean }>()

const data = [
    {
        icon: 'el-icon-home-filled',
        name: '首页',
        index: '/',
    },
    {
        icon: 'el-icon-check',
        name: '图标选择器',
        index: '/chooseIcon',
    },
    {
        icon: 'el-icon-location',
        name: '省市区选择',
        index: '/chooseArea',
    },
    {
        icon: 'el-icon-sort',
        name: '趋势标记',
        index: '/trend',
    },
    {
        icon: 'el-icon-timer',
        name: '时间选择',
        index: '/chooseTime',
    },
    {
        icon: 'el-icon-bell',
        name: '通知菜单',
        index: '/notification',
    },
    {
        icon: 'el-icon-menu',
        name: '导航菜单',
        index: '/menu',
    },
    {
        icon: 'el-icon-turn-off',
        name: '城市选择',
        index: '/chooseCity',
    },
    {
        icon: 'el-icon-darrow-right',
        name: '进度条',
        index: '/progress',
    },
    {
        icon: 'el-icon-scale-to-original',
        name: '日历',
        index: '/calendar',
    },
    {
        icon: 'el-icon-setting',
        name: '表单',
        index: '/form',
    },
    {
        icon: 'el-icon-setting',
        name: '弹出框表单',
        index: '/modalForm',
    },
    {
        icon: 'el-icon-shopping-bag',
        name: '表格',
        index: '/table',
    },
]
const options = reactive([
    {
        value: '选项1',
        label: '基础组件',
    },
    {
        value: '选项2',
        label: '技术组件',
    },
    {
        value: '选项3',
        label: '业务组件',
    },
])
const value = ref('选项1')
</script>
<style lang="scss" scoped>

::deep .el-radio-button {
    padding: 0.12rem 0.16rem;
}
</style>

效果如下:

image.png

image.png

兼容后端字段名称

往往后端的字段名称和前端el-menu默认的name、index,和·我们定义的icon、children不一致。可以优化如下,变成名称自定义:
修改src\components\baseline\menu\src\index.vue

<!--
 * @Author: bobokaka
 * @Date: 2021-12-23 00:07:25
 * @LastEditTime: 2021-12-31 17:56:13
 * @LastEditors: Please set LastEditors
 * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 * @FilePath: \vue3-element-ui-baseline\src\components\baseline\trend\src\itemIndex.vue
-->
<template>
    <div>
        <el-menu
            class="el-menu-vertical-demo"
            :default-active="defaultActive"
            :router="router"
            v-bind="$attrs"
        >
            <template v-for="(item, itemIndex) in data" :key="itemIndex">
                <!-- 一级无二级菜单的菜单栏 -->
                <el-menu-item
                    v-if="!item[children] || !item[children].length"
                    :index="item[index]"
                >
                    <component v-if="item[icon]" :is="item[icon]"></component>
                    <span>{{ item[name] }}</span>
                </el-menu-item>
                <el-sub-menu
                    v-if="item[children] && item[children].length"
                    :index="item[index]"
                >
                    <template #title>
                        <component
                            v-if="item[icon]"
                            :is="item[icon]"
                        ></component>
                        <span>{{ item[name] }}</span>
                    </template>
                    <!-- 二级菜单栏 -->
                    <el-menu-item
                        v-for="(item2, index2) in item[children]"
                        :index="item2[index]"
                    >
                        <component
                            v-if="item2[icon]"
                            :is="item2[icon]"
                        ></component>
                        <span>{{ item[name] }}</span>
                    </el-menu-item>
                </el-sub-menu>
            </template>
        </el-menu>
    </div>
</template>
<script lang="ts" setup>
import { PropType } from 'vue'

const props = defineProps({
    //说明:
    data: {
        required: true,
        type: Array as PropType<any[]>,
    },
    // 默认选中的菜单
    defaultActive: {
        type: String,
        default: '',
    },
    //是否是路由模式 router,
    //是否启用 vue-router 模式
    //启用该模式会在激活导航时以 itemIndex 作为 path 进行路由跳转
    router: {
        type: Boolean,
        default: false,
    },
    //菜单标题的件名
    name: {
        type: String,
        default: 'name',
    },
    //菜单标识的键名
    index: {
        type: String,
        default: 'index',
    },
    // 菜单图标的键名
    icon: {
        type: String,
        default: 'icon',
    },
    // 菜单子菜单的键名
    children: {
        type: String,
        default: 'children',
    },
})
// console.log('data:', props.data)
</script>
<style lang="scss" scoped>
.el-menu-vertical-demo:not(.el-menu--collapse) {
    width: 2rem;
    min-height: 4rem;
}
svg {
    margin-right: 0.04rem;
}
</style>

修改src\components\baseline\container\src\navAside\index.vue

<template>
    <div>
        <bs-menu
            :collapse="collapse"
            :data="data"
            router
            name="text"
            index="code"
            icon="imgSrc"
            children="item"
            :default-active="$route.path"
        ></bs-menu>
    </div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
let props = defineProps<{ collapse: boolean }>()

const data = [
    {
        imgSrc: 'el-icon-home-filled',
        text: '首页',
        code: '/',
    },
    {
        imgSrc: 'el-icon-check',
        text: '图标选择器',
        code: '/chooseIcon',
    },
    {
        imgSrc: 'el-icon-location',
        text: '省市区选择',
        code: '/chooseArea',
    },
    {
        imgSrc: 'el-icon-sort',
        text: '趋势标记',
        code: '/trend',
    },
    {
        imgSrc: 'el-icon-timer',
        text: '时间选择',
        code: '/chooseTime',
    },
    {
        imgSrc: 'el-icon-bell',
        text: '通知菜单',
        code: '/notification',
    },
    {
        imgSrc: 'el-icon-menu',
        text: '导航菜单',
        code: '/menu',
    },
    {
        imgSrc: 'el-icon-turn-off',
        text: '城市选择',
        code: '/chooseCity',
    },
    {
        imgSrc: 'el-icon-darrow-right',
        text: '进度条',
        code: '/progress',
    },
    {
        imgSrc: 'el-icon-scale-to-original',
        text: '日历',
        code: '/calendar',
    },
    {
        imgSrc: 'el-icon-setting',
        text: '表单',
        code: '/form',
    },
    {
        imgSrc: 'el-icon-setting',
        text: '弹出框表单',
        code: '/modalForm',
    },
    {
        imgSrc: 'el-icon-shopping-bag',
        text: '表格',
        code: '/table',
    },
]
const options = reactive([
    {
        value: '选项1',
        label: '基础组件',
    },
    {
        value: '选项2',
        label: '技术组件',
    },
    {
        value: '选项3',
        label: '业务组件',
    },
])
const value = ref('选项1')
</script>
<style lang="scss" scoped>
::deep .el-radio-button {
    padding: 0.12rem 0.16rem;
}
</style>

运行后,效果一致。
将无限层级菜单,也同样修改:
修改src\components\baseline\menu\src\index.vue

/*
 * @Author: your name
 * @Date: 2021-12-30 18:07:50
 * @LastEditTime: 2021-12-31 18:09:09
 * @LastEditors: Please set LastEditors
 * @Description: 递归展开层级菜单
 * @FilePath: \vue3-element-ui-baseline\src\components\baseline\menu\src\menu.tsx
 */
import { defineComponent, PropType, useAttrs, h, resolveComponent } from 'vue'
import { MenuItem } from './types'

export default defineComponent({
    name: 'infiniteMenu',
    props: {
        //说明:
        data: {
            required: true,
            type: [],
        },
        // 默认选中的菜单
        defaultActive: {
            type: String,
            default: '',
        },
        //是否是路由模式 router,
        //是否启用 vue-router 模式
        //启用该模式会在激活导航时以 index 作为 path 进行路由跳转
        router: {
            type: Boolean,
            default: false,
        },
        //菜单标题的件名
        name: {
            type: String,
            default: 'name',
        },
        //菜单标识的键名
        index: {
            type: String,
            default: 'index',
        },
        // 菜单图标的键名
        icon: {
            type: String,
            default: 'icon',
        },
        // 菜单子菜单的键名
        children: {
            type: String,
            default: 'children',
        },
    },
    setup(props, ctx) {
        //****************** 封装渲染一个无限层菜单的方法 ******************/
        let renderMenu = (data: []) => {
            return data.map((item: any) => {
                //每个菜单的图标
                let slots = {
                    title: () => {
                        return (
                            <>
                                {item[props.icon]
                                    ? h(resolveComponent(item[props.icon]))
                                    : ''}
                                <span>{item[props.name]}</span>
                            </>
                        )
                    },
                }
                //递归渲染children
                if (item[props.children] && item[props.children].length) {
                    return (
                        <el-sub-menu index={item[props.index]} v-slots={slots}>
                            {/* 递归 */}
                            {renderMenu(item.children)}
                        </el-sub-menu>
                    )
                }
                //正常渲染普通的菜单
                return (
                    <el-menu-item index={item[props.index]}>
                        {/* 这里直接写<item[props.icon]/>无法渲染出来,需要进行如下处理 */}
                        {item[props.icon]
                            ? h(resolveComponent(item[props.icon]))
                            : ''}
                        <span>{item[props.name]}</span>
                    </el-menu-item>
                )
            })
        }
        let attrs = useAttrs()
        // console.log(attrs)

        return () => {
            return (
                <el-menu
                    default-active={props.defaultActive}
                    router={props.router}
                    {...attrs}
                >
                    {renderMenu(props.data)}
                </el-menu>
            )
        }
    },
})

修改src\components\baseline\container\src\navAside\index.vue

<!--
 * @Author: bobokaka
 * @Date: 2021-12-20 00:54:44
 * @LastEditTime: 2021-12-31 18:10:22
 * @LastEditors: Please set LastEditors
 * @Description: 左侧菜单栏
 * @FilePath: \vue3-element-ui-baseline\src\components\baseline\container\src\navAside\index.vue
-->
<template>
    <div>
        <!-- <bs-menu
            :collapse="collapse"
            :data="data"
            router
            name="text"
            index="code"
            icon="imgSrc"
            children="item"
            :default-active="$route.path"
        ></bs-menu> -->
          <bs-infinite-menu
            :collapse="collapse"
            :data="data"
            router
            name="text"
            index="code"
            icon="imgSrc"
            children="item"
            :default-active="$route.path"
        ></bs-infinite-menu>
        <!-- <el-menu
            default-active="1"
            :collapse="collapse"
            class="el-menu-vertical-demo"
        > -->
        <!-- <el-menu-item index="0">
                <el-select
                    v-model="value"
                    placeholder="请选择"
                    style="width: 2rem"
                    v-if="!collapse"
                >
                    <el-option
                        v-for="item in options"
                        :key="item.value"
                        :label="item.label"
                        :value="item.value"
                    ></el-option>
                </el-select>
            </el-menu-item> -->
        <!-- <el-menu-item index="1">
                <el-icon><el-icon-menu /></el-icon>
                <span>首页</span>
            </el-menu-item>
            <el-menu-item index="2">
                <el-icon><el-icon-menu /></el-icon>
                <span>图标选择器</span>
            </el-menu-item>
            <el-menu-item index="3">
                <el-icon><el-icon-menu /></el-icon>
                <span>省市区选择组件</span>
            </el-menu-item>
            <el-menu-item index="4">
                <el-icon><el-icon-menu /></el-icon>
                <span>趋势标记</span>
            </el-menu-item> -->
        <!-- </el-menu> -->
    </div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
let props = defineProps<{ collapse: boolean }>()

const data = [
    {
        imgSrc: 'el-icon-home-filled',
        text: '首页',
        code: '/',
    },
    {
        imgSrc: 'el-icon-check',
        text: '图标选择器',
        code: '/chooseIcon',
    },
    {
        imgSrc: 'el-icon-location',
        text: '省市区选择',
        code: '/chooseArea',
    },
    {
        imgSrc: 'el-icon-sort',
        text: '趋势标记',
        code: '/trend',
    },
    {
        imgSrc: 'el-icon-timer',
        text: '时间选择',
        code: '/chooseTime',
    },
    {
        imgSrc: 'el-icon-bell',
        text: '通知菜单',
        code: '/notification',
    },
    {
        imgSrc: 'el-icon-menu',
        text: '导航菜单',
        code: '/menu',
    },
    {
        imgSrc: 'el-icon-turn-off',
        text: '城市选择',
        code: '/chooseCity',
    },
    {
        imgSrc: 'el-icon-darrow-right',
        text: '进度条',
        code: '/progress',
    },
    {
        imgSrc: 'el-icon-scale-to-original',
        text: '日历',
        code: '/calendar',
    },
    {
        imgSrc: 'el-icon-setting',
        text: '表单',
        code: '/form',
    },
    {
        imgSrc: 'el-icon-setting',
        text: '弹出框表单',
        code: '/modalForm',
    },
    {
        imgSrc: 'el-icon-shopping-bag',
        text: '表格',
        code: '/table',
    },
]
const options = reactive([
    {
        value: '选项1',
        label: '基础组件',
    },
    {
        value: '选项2',
        label: '技术组件',
    },
    {
        value: '选项3',
        label: '业务组件',
    },
])
const value = ref('选项1')
</script>
<style lang="scss" scoped>
::deep .el-radio-button {
    padding: 0.12rem 0.16rem;
}
</style>

运行效果一致:

image.png

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):