暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Vue3 + Vite:多布局

Nephilim 2024-06-30
89

Tips:一些记录,一些笔记



2024/06/29

SATURDAY

Living in the living heart, is not die.

活在生者的心里,就是不死。


米其林·2024摘星餐厅「新京韵 Peking Hotpot」 | 武汉·武昌万象城店


01

多布局



首先,是目前这个项目的架构:

vue

3.4.29

vite

5.3.1

@arco-design/web-vue

2.55.3


02

目录状态



目录描述:

vite.config.ts

「Components」中添加「src/layout」

src/router/index.ts
将「layout」配置为「一级路由」
src/layout/
「layout」存放布局配置的目录
src/layout/SwitchIndex.vue「layout」入口文件
src/layout/switch/
不同的布局的存放目录
src/layout/switch/DefaultLayotu.vue
默认布局
src/layout/switch/SidebarLayout.vue
边栏布局
src/layout/components/
布局中涉及到的「布局组件」


03

具体配置:初始配置

上面介绍了相关的目录与文件,下面来具体描述下其中的配置。


文件「vite.config.ts」

该文件的完整内容:

    import { fileURLToPath, URL } from 'node:url'


    import { defineConfig, loadEnv } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import vueJsx from '@vitejs/plugin-vue-jsx'


    import AutoImport from 'unplugin-auto-import/vite'
    import Components from 'unplugin-vue-components/vite'
    import {
    ArcoResolver,
    VueUseComponentsResolver,
    VueUseDirectiveResolver
    } from 'unplugin-vue-components/resolvers'


    import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'


    // Unocss 插件
    import Unocss from 'unocss/vite'
    // Unocss 默认预设
    import presetUno from '@unocss/preset-uno'
    // Unocss 属性模式预设
    import presetAttributify from '@unocss/preset-attributify'
    // Unocss 指令转换插件
    import transformerDirective from '@unocss/transformer-directives'


    // https://vitejs.dev/config/
    export default defineConfig(({ mode }) => {
    const env = loadEnv(mode, process.cwd(), '')


    return {
    base: env.VITE_BASE,
    server: {
    host: '0.0.0.0',
    port: Number(env.VITE_PORT),
    open: false,
    strictPort: true,
    // 本地服务 CORS 是否开启
    cors: true,
    proxy: {
    [env.VITE_BASE_FRONTEND_URL]: {
    target: env.VITE_BASE_BACKEND_URL,
    // 允许跨域
    changeOrigin: true,
    rewrite: path => path.replace(new RegExp('^' + env.VITE_BASE_FRONTEND_URL), '')
    }
    }
    },
    plugins: [
    vue(),
    vueJsx(),
    createSvgIconsPlugin({
    iconDirs: [fileURLToPath(new URL('./src/assets/icons', import.meta.url))],
    symbolId: 'icon-[dir]-[name]'
    }),
    AutoImport({
    // 需要去解析的文件
    include: [
    /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
    \.vue$/,
    /\.vue\?vue/, // .vue
    \.md$/ // .md
    ],
    // imports 指定自动引入的包位置(名)
    imports: ['vue', 'pinia', 'vue-router', '@vueuse/core'],
    // 生成相应的自动导入json文件。
    eslintrc: {
    // 启用
    enabled: true,
    // 生成自动导入json文件位置
    filepath: './.eslintrc-auto-import.json',
    // 全局属性值
    globalsPropValue: true
    },
    resolvers: [ArcoResolver()]
    }),
    Components({
    // imports 指定组件所在目录,默认为 src/components
    dirs: ['src/components/', 'src/view/', 'src/layout'],
    // 需要去解析的文件
    include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
    resolvers: [
    ArcoResolver({
    sideEffect: true
    }),
    VueUseComponentsResolver(),
    VueUseDirectiveResolver()
    ]
    }),
    Unocss({
    // 预设
    presets: [presetUno(), presetAttributify()],
    // 指令转换插件
    transformers: [transformerDirective()],
    // 自定义规则
    rules: []
    })
    ],
    resolve: {
    alias: {
    '@': fileURLToPath(new URL('./src', import.meta.url))
    }
    },
    build: {
    outDir: 'dist',
    assetsDir: 'static/assets',
    // sourcemap: true,
    // 规定触发警告的 chunk 大小,消除打包大小超过500kb警告
    chunkSizeWarningLimit: 2000,
    // 静态资源打包到dist下的不同目录
    rollupOptions: {
    output: {
    chunkFileNames: 'static/js/[name]-[hash].js',
    entryFileNames: 'static/js/[name]-[hash].js',
    assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
    }
    }
    }
    }
    })



    文件「src/router/index.ts」

      import { createRouter, createWebHistory } from 'vue-router'


      const router = createRouter({
      history: createWebHistory(import.meta.env.BASE_URL),
      routes: [
      {
      path: '/',
      name: 'Layout',
      component: () => import('@/layout/SwitchIndex.vue'),
      redirect: '/',
      children: [
      {
      path: '/',
      name: 'HomeView',
      component: () => import('@/views/HomeView.vue'),
      meta: {
      title: '首页'
      }
      }
      ]
      }
      ]
      })


      export default router



      文件「src/layout/SwitchIndex.vue」

        <script setup></script>


        <template>
        <div class="switch-index">
        <!-- <component :is="" > -->
        <DefaultLayout >
        </div>
        </template>


        <style scoped></style>


        上面的代码中,「div」中目前是写死的「DefaultLayout」,后期会定义为「component」


        文件「src/layout/switch/DefaultLayout.vue」

          <script setup></script>


          <template>
          <div>
          <div class="default-layout">
          <a-layout class="min-h-[calc(100vh+48px)]">
          <a-affix>
          <a-layout-header> Navbar </a-layout-header>
          </a-affix>
          <a-layout-content>
          <router-view v-slot="{ Component }">
          <component :is="Component" >
          </router-view>
          </a-layout-content>
          <a-layout-footer> Footer </a-layout-footer>
          </a-layout>
          </div>
          </div>
          </template>


          <style scoped>
          @apply .default-layout :deep(.arco-layout-header),
          .default-layout :deep(.arco-layout-footer),
          .default-layout :deep(.arco-layout-content) {
          @apply text-[var(--color-text-1)] text-14px;
          }


          .default-layout :deep(.arco-layout-header) {
          @apply w-full h-58px overflow-hidden;
          @apply bg-[var(--color-bg-3)] border-b-[var(--color-border-1)] border-b-solid border-b-width-1px box-border;
          }
          .default-layout :deep(.arco-layout-content) {
          @apply flex flex-col justify-center items-center;
          @apply bg-[var(--color-bg-1)] relative;
          }
          .default-layout :deep(.arco-layout-footer) {
          @apply w-full flex justify-center items-center;
          @apply border-t-[var(--color-border-1)] border-t-solid border-t-width-1px box-border;
          @apply bg-[var(--color-bg-2)] text-[var(--color-text-1)] text-14px;
          }
          </style>



          当你完成了上述的配置后,你的前端页面就会变成这样:


          可以看到,在「DefaultLayout」中配置的「header footer」已经生效了:


          04

          具体配置:进阶配置(组件等)


          像上面设置的那样:

          这部分设计到的组件会在「src/layout/components」中创建。


          05

          导航栏的「Logo」组件的定制

          文件说明:

          src/layout/components/Nav.vue设置「左 中 右」的「slot」留给调用该组件的时候进行设置
          src/layout/components/Logo.vue设置Logo,包括图标与文字
          src/layout/switch/DefaultLayout.vue引用「Nav」与「Logo」组件,定义布局


          文件「src/layout/switch/DefaultLayout.vue」

            <script setup></script>


            <template>
            <div>
            <div class="default-layout">
            <a-layout class="min-h-[calc(100vh+48px)]">
            <a-affix>
            <a-layout-header>
            <Navbar>
            <!-- left插槽 -->
            <template #left>
            <Logo >
            </template>


            <!-- 默认插槽和center插槽,默认插槽可不加template直接写内容,作用同center插槽 -->
            <template #center></template>


            <!-- right插槽 -->
            <template #right></template>
            </Navbar>
            </a-layout-header>
            </a-affix>
            <a-layout-content>
            <router-view v-slot="{ Component }">
            <component :is="Component" >
            </router-view>
            </a-layout-content>
            <a-layout-footer> Footer </a-layout-footer>
            </a-layout>
            </div>
            </div>
            </template>


            <style scoped>
            @apply .default-layout :deep(.arco-layout-header),
            .default-layout :deep(.arco-layout-footer),
            .default-layout :deep(.arco-layout-content) {
            @apply text-[var(--color-text-1)] text-14px;
            }


            .default-layout :deep(.arco-layout-header) {
            @apply w-full h-58px overflow-hidden;
            @apply bg-[var(--color-bg-3)] border-b-[var(--color-border-1)] border-b-solid border-b-width-1px box-border;
            }
            .default-layout :deep(.arco-layout-content) {
            @apply flex flex-col justify-center items-center;
            @apply bg-[var(--color-bg-1)] relative;
            }
            .default-layout :deep(.arco-layout-footer) {
            @apply w-full flex justify-center items-center;
            @apply border-t-[var(--color-border-1)] border-t-solid border-t-width-1px box-border;
            @apply bg-[var(--color-bg-2)] text-[var(--color-text-1)] text-14px;
            }
            </style>



            文件「src/layout/components/Nav.vue

              <template>
              <div class="w-full h-full flex px-20px box-border">
              <div class="h-full flex">
              <slot name="left" >
              </div>
              <div class="h-full flex-1">
              <slot >
              <slot name="center" >
              </div>
              <div class="h-full flex flex-shrink-0 items-center">
              <div>
              <slot name="right" >
              </div>
              </div>
              </div>
              </template>



              文件「src/layout/components/Logo.vue

                <script setup>
                const route = useRoute()
                const title = useTitle()


                watchEffect(() => {
                title.value = route.meta.title || 'Admin Meta'
                })
                </script>
                <template>
                <div
                class="h-full flex items-center text-16px font-700 text-shadow-sm cursor-pointer"
                @click="$router.push('/')"
                >
                <div
                class="w-36px h-36px rounded-[50%] flex justify-center items-center mr-2px cursor-pointer"
                hover="bg-[var(--color-fill-1)]"
                >
                <!-- Logo的图标 -->
                <!--
                <icon-ri-hammer-fill class="text-18px" >
                -->
                <svg-icon class="icon" size="50" name="星"></svg-icon>
                </div>
                <!-- Logo的文字 -->
                {{ title }}
                </div>
                </template>



                这个时候,前端页面的效果是:


                06

                导航栏的「Github」组件的定制


                文件「src/layout/components/Github.vue」

                  <script setup>
                  import { getConfig } from '@/config/config'
                  const openNewWindow = () => window.open(getConfig('github'), '_blank')
                  </script>
                  <template>
                  <a-button type="text" @click="openNewWindow">
                  <template #icon>
                  <!--
                  <icon-mdi-github class="text-[var(--color-text-1)] text-16px" >
                  -->
                  <svg-icon class="icon" size="35" name="github-fill"></svg-icon>
                  </template>
                  </a-button>
                  </template>



                  文件「src/layout/switch/DefaultLayout.vue」

                    <script setup></script>


                    <template>
                    <div>
                    <div class="default-layout">
                    <a-layout class="min-h-[calc(100vh+48px)]">
                    <a-affix>
                    <a-layout-header>
                    <Navbar>
                    <!-- left插槽 -->
                    <template #left>
                    <Logo >
                    </template>


                    <!-- 默认插槽和center插槽,默认插槽可不加template直接写内容,作用同center插槽 -->
                    <template #center></template>


                    <!-- right插槽 -->
                    <template #right>
                    <Github >
                    </template>
                    </Navbar>
                    </a-layout-header>
                    </a-affix>
                    <a-layout-content>
                    <router-view v-slot="{ Component }">
                    <component :is="Component" >
                    </router-view>
                    </a-layout-content>
                    <a-layout-footer> Footer </a-layout-footer>
                    </a-layout>
                    </div>
                    </div>
                    </template>


                    <style scoped>
                    @apply .default-layout :deep(.arco-layout-header),
                    .default-layout :deep(.arco-layout-footer),
                    .default-layout :deep(.arco-layout-content) {
                    @apply text-[var(--color-text-1)] text-14px;
                    }


                    .default-layout :deep(.arco-layout-header) {
                    @apply w-full h-58px overflow-hidden;
                    @apply bg-[var(--color-bg-3)] border-b-[var(--color-border-1)] border-b-solid border-b-width-1px box-border;
                    }
                    .default-layout :deep(.arco-layout-content) {
                    @apply flex flex-col justify-center items-center;
                    @apply bg-[var(--color-bg-1)] relative;
                    }
                    .default-layout :deep(.arco-layout-footer) {
                    @apply w-full flex justify-center items-center;
                    @apply border-t-[var(--color-border-1)] border-t-solid border-t-width-1px box-border;
                    @apply bg-[var(--color-bg-2)] text-[var(--color-text-1)] text-14px;
                    }
                    </style>



                    文件「src/config/config.js」


                    最终效果:


                    07

                    菜单组件

                    为了演示菜单功能,先创建样例页面「About」


                    文件「src/views/About/index.vue」

                      <script setup></script>


                      <template>
                      <div>About</div>
                      </template>


                      <style scoped></style>


                      然后,修改路由的配置:

                      文件「src/router/index.ts」

                        import { createRouter, createWebHistory } from 'vue-router'
                        import {menuRouter} from './menuRouter'


                        const router = createRouter({
                        history: createWebHistory(import.meta.env.BASE_URL),
                        routes: [
                        {
                        path: '/',
                        name: 'Layout',
                        component: () => import('@/layout/SwitchIndex.vue'),
                        redirect: '/',
                        children: [
                        {
                        path: '/',
                        name: 'HomeView',
                        component: () => import('@/views/HomeView.vue'),
                        meta: {
                        // title: '首页'
                        }
                        },
                        // 菜单路由
                        ...menuRouter,
                        ]
                        }
                        ]
                        })


                        export default router



                        文件「src/router/menuRouter.js」

                          export const menuRouter = [
                          {
                          path: 'meta',
                          name: 'Meta',
                          meta: {
                          title: '元信息',
                          // icon: xxx
                          },
                          redirect: {
                          name: 'About'
                          },
                          children: [
                          {
                          path: 'about',
                          name: 'About',
                          meta: {
                          title: "关于"
                          },
                          component: () => import('@/views/About/index.vue')
                          }
                          ]
                          }
                          ]


                          export const menuRouterFormat = (router, parentPath) => {
                          return router.map(item => {
                          // 拼接路由,例:'devtools' -> '/devtools' 'regular' -> '/devtools/regular'
                          item.path = parentPath ? `${parentPath}/${item.path}` : `/${item.path}`


                          // 存在 children 属性,且 children 数组长度大于 0,开始递归
                          if (item.children && item.children.length > 0) {
                          item.children = menuRouterFormat(item.children, item.path)
                          }


                          return Object.assign({}, item, item.meta || {})
                          })
                          }


                          // 解析后 路由菜单列表
                          export const menuRouterFormatList = menuRouterFormat([...menuRouter])



                          前端页面访问路由配置,看看是否生效:


                          路由配置修改好了以后,开始配置「多布局」中的组件「components/Menu」


                          文件「src/layout/components/Menu/index.vue」

                            <script setup>
                            import { menuRouterFormatList } from '@/router/menuRouter.js'


                            const props = defineProps({
                            mode: {
                            type: String,
                            default: 'horizontal'
                            }
                            })
                            // 菜单模式,horizontal 水平,vertical 垂直
                            const mode = toRef(props, 'mode')


                            // 菜单数据
                            const menuList = ref(menuRouterFormatList)


                            const router = useRouter()
                            // 子菜单点击事件
                            const onClickMenuItem = key => {
                            router.push(key)
                            }


                            const route = useRoute()
                            // 当前选中菜单
                            const selectedKeys = computed(() => [route.path])
                            </script>
                            <template>
                            <a-menu
                            class="menu"
                            auto-open-selected
                            :selected-keys="selectedKeys"
                            @menuItemClick="onClickMenuItem"
                            :mode="mode"
                            :accordion="true"
                            >
                            <MenuItem v-for="menu of menuList" :key="menu.path" :menu="menu" >
                            </a-menu>
                            </template>


                            <style scoped>
                            .menu.arco-menu-horizontal {
                            @apply bg-[var(--color-bg-3)];
                            }
                            .menu.arco-menu-horizontal :deep(.arco-menu-icon) {
                            @apply mr-4px leading-[1.2] flex-none align-inherit;
                            }
                            .menu.arco-menu-horizontal :deep(.arco-menu-pop-header) {
                            @apply bg-transparent;
                            }
                            .menu.arco-menu-horizontal :deep(.arco-menu-pop-header):hover {
                            @apply bg-[var(--color-fill-2)];
                            }
                            .menu :deep(.arco-menu-overflow-wrap) {
                            @apply flex justify-end;
                            }
                            </style>


                            文件「src/layout/components/Menu/MenuItem.vue」

                              <script setup>
                              const props = defineProps({
                              menu: {
                              type: Object,
                              required: true
                              }
                              })
                              const { menu } = toRefs(props)
                              </script>


                              <template>
                              <template v-if="!menu.children">
                              <a-menu-item :key="menu.path">
                              <template #icon v-if="menu?.icon">
                              <component :is="menu?.icon"></component>
                              </template>
                              {{ menu.title }}
                              </a-menu-item>
                              </template>
                              <a-sub-menu v-else :key="menu.path" :title="menu.title">
                              <template #icon v-if="menu?.icon">
                              <component :is="menu?.icon"></component>
                              </template>
                              <MenuItem
                              v-for="menuChild of menu.children"
                              :key="menuChild.path"
                              :menu="menuChild"
                              >
                              </a-sub-menu>
                              </template>


                              <style scoped></style>


                              最后,修改默认布局「src/layout/switch/DefaultLayout.vue」

                                <script setup></script>


                                <template>
                                <div>
                                <div class="default-layout">
                                <a-layout class="min-h-[calc(100vh+48px)]">
                                <a-affix>
                                <a-layout-header>
                                <Navbar>
                                <!-- left插槽 -->
                                <template #left>
                                <Logo >
                                </template>


                                <!-- 默认插槽和center插槽,默认插槽可不加template直接写内容,作用同center插槽 -->
                                <template #center>
                                <Menu >
                                </template>


                                <!-- right插槽 -->
                                <template #right>
                                <Github >
                                </template>
                                </Navbar>
                                </a-layout-header>
                                </a-affix>
                                <a-layout-content>
                                <router-view v-slot="{ Component }">
                                <component :is="Component" >
                                </router-view>
                                </a-layout-content>
                                <a-layout-footer> Footer </a-layout-footer>
                                </a-layout>
                                </div>
                                </div>
                                </template>


                                <style scoped>
                                @apply .default-layout :deep(.arco-layout-header),
                                .default-layout :deep(.arco-layout-footer),
                                .default-layout :deep(.arco-layout-content) {
                                @apply text-[var(--color-text-1)] text-14px;
                                }


                                .default-layout :deep(.arco-layout-header) {
                                @apply w-full h-58px overflow-hidden;
                                @apply bg-[var(--color-bg-3)] border-b-[var(--color-border-1)] border-b-solid border-b-width-1px box-border;
                                }
                                .default-layout :deep(.arco-layout-content) {
                                @apply flex flex-col justify-center items-center;
                                @apply bg-[var(--color-bg-1)] relative;
                                }
                                .default-layout :deep(.arco-layout-footer) {
                                @apply w-full flex justify-center items-center;
                                @apply border-t-[var(--color-border-1)] border-t-solid border-t-width-1px box-border;
                                @apply bg-[var(--color-bg-2)] text-[var(--color-text-1)] text-14px;
                                }
                                </style>



                                最终效果:


                                08

                                页脚组件


                                文件「src/config/config.js」

                                当前文件完整内容:

                                  const env = import.meta.env


                                  let config = {}


                                  const configSource = {
                                  appCode: env.VITE_APP_CODE,
                                  projectCode: `${env.VITE_APP_CODE}_${env.VITE_APP_ENV}`,
                                  projectName: env.VITE_APP_NAME,
                                  projectDesc: env.VITE_APP_DESCRIPTION,
                                  base: env.VITE_BASE,
                                  frontendUrl: env.VITE_BASE_FRONTEND_URL,
                                  backendUrl: env.VITE_BASE_BACKEND_URL,
                                  mockUrl: env.VITE_BASE_MOCK_URL,
                                  // 代码仓库
                                  github: 'https://gitee.com/leviathan-litan/beast-harbor',
                                  // 个人信息
                                  me: {
                                  name: 'Litan',
                                  email: 'example@example.com',
                                  wechatChannel: 'Nephilim',
                                  gitee: 'https://gitee.com/leviathan-litan',
                                  github: 'https://github.com/leviathan-litan'
                                  }
                                  }


                                  const setConfig = cfg => {
                                  config = Object.assign(config, cfg)
                                  return config
                                  }


                                  const resetConfig = () => {
                                  config = { ...configSource }
                                  return config
                                  }
                                  resetConfig()


                                  const getConfig = key => {
                                  if (typeof key === 'string') {
                                  const arr = key.split('.')
                                  if (arr && arr.length) {
                                  let data = config
                                  arr.forEach(v => {
                                  if (data && typeof data[v] !== 'undefined') {
                                  data = data[v]
                                  } else {
                                  data = null
                                  }
                                  })
                                  return data
                                  }
                                  }
                                  if (Array.isArray(key)) {
                                  const data = config
                                  if (key && key.length > 1) {
                                  let res = {}
                                  key.forEach(v => {
                                  if (data && typeof data[v] !== 'undefined') {
                                  res[v] = data[v]
                                  } else {
                                  res[v] = null
                                  }
                                  })
                                  return res
                                  }
                                  return data[key]
                                  }
                                  return { ...config }
                                  }


                                  export { getConfig, setConfig, resetConfig }



                                  文件「src/layout/components/Footer.vue

                                    <script setup>
                                    import { getConfig } from '@/config/config'
                                    </script>
                                    <template>
                                    <div class="w-1200px flex justify-between items-center min-h-48px">
                                    <div class="w-full h-48px flex justify-center items-center">
                                    <a-trigger
                                    position="top"
                                    auto-fit-position
                                    :unmount-on-close="false"
                                    :popup-offset="10"
                                    :show-arrow="true"
                                    >
                                    <a-link :href="getConfig('me.gzh')">
                                    {{ getConfig('me.wechatChannel') }}
                                    </a-link>
                                    <template #content>
                                    <a-image width="100" :src="getConfig('me.wechatChannelImage')" >
                                    </template>
                                    </a-trigger>
                                          <span> Copyright ⓒ 2024</span>
                                    <a-link :href="getConfig('me.github')" target="_blank">
                                    {{ getConfig('me.name') }}
                                    </a-link>
                                    <!--
                                    <a-link href="<https://beian.miit.gov.cn/>" target="_blank">
                                    {{ getConfig('icp') }}
                                    </a-link>
                                    -->
                                    </div>
                                    </div>
                                    </template>


                                    文件「src/layout/switch/DefaultLayout.vue」

                                      <script setup></script>


                                      <template>
                                      <div>
                                      <div class="default-layout">
                                      <a-layout class="min-h-[calc(100vh+48px)]">
                                      <a-affix>
                                      <a-layout-header>
                                      <Navbar>
                                      <!-- left插槽 -->
                                      <template #left>
                                      <Logo >
                                      </template>


                                      <!-- 默认插槽和center插槽,默认插槽可不加template直接写内容,作用同center插槽 -->
                                      <template #center>
                                      <Menu >
                                      </template>


                                      <!-- right插槽 -->
                                      <template #right>
                                      <Github >
                                      </template>
                                      </Navbar>
                                      </a-layout-header>
                                      </a-affix>
                                      <a-layout-content>
                                      <router-view v-slot="{ Component }">
                                      <component :is="Component" >
                                      </router-view>
                                      </a-layout-content>
                                      <a-layout-footer>
                                      <Footer >
                                      </a-layout-footer>
                                      </a-layout>
                                      </div>
                                      </div>
                                      </template>


                                      <style scoped>
                                      @apply .default-layout :deep(.arco-layout-header),
                                      .default-layout :deep(.arco-layout-footer),
                                      .default-layout :deep(.arco-layout-content) {
                                      @apply text-[var(--color-text-1)] text-14px;
                                      }


                                      .default-layout :deep(.arco-layout-header) {
                                      @apply w-full h-58px overflow-hidden;
                                      @apply bg-[var(--color-bg-3)] border-b-[var(--color-border-1)] border-b-solid border-b-width-1px box-border;
                                      }
                                      .default-layout :deep(.arco-layout-content) {
                                      @apply flex flex-col justify-center items-center;
                                      @apply bg-[var(--color-bg-1)] relative;
                                      }
                                      .default-layout :deep(.arco-layout-footer) {
                                      @apply w-full flex justify-center items-center;
                                      @apply border-t-[var(--color-border-1)] border-t-solid border-t-width-1px box-border;
                                      @apply bg-[var(--color-bg-2)] text-[var(--color-text-1)] text-14px;
                                      }
                                      </style>



                                      页面效果:


                                      09

                                      多布局:边栏布局


                                      至此,上面完成了「多布局」的默认布局。


                                      下面开始设置「边栏布局」。


                                      首先,将布局写死为「边栏布局」,以便于调试代码:


                                      文件「src/layout/SwitchIndex.vue」

                                        <script setup></script>


                                        <template>
                                        <div class="switch-index">
                                        <!-- <component :is="" > -->
                                        <!-- <DefaultLayout > -->
                                        <SidebarLayout >
                                        </div>
                                        </template>


                                        <style scoped></style>



                                        文件「src/layout/switch/SidebarLayout.vue」

                                          <script setup></script>


                                          <template>
                                          <div>
                                          SidebarLayout
                                          <router-view v-slot="{ Component }">
                                          <component :is="Component" />
                                          </router-view>
                                          </div>
                                          </template>


                                          <style scoped></style>



                                          这时候的效果:

                                          可以看到「SidebarLayout」的效果已经生效,接下来需要「精调」。


                                          「Menu」组件添加参数:

                                          文件「src/layout/components/Menu/index.vue」

                                            <script setup>
                                            import { menuRouterFormatList } from '@/router/menuRouter.js'


                                            const props = defineProps({
                                            mode: {
                                            type: String,
                                            default: 'horizontal'
                                            }
                                            })
                                            // 菜单模式,horizontal 水平,vertical 垂直
                                            const mode = toRef(props, 'mode')


                                            // 菜单数据
                                            const menuList = ref(menuRouterFormatList)


                                            const router = useRouter()
                                            // 子菜单点击事件
                                            const onClickMenuItem = key => {
                                            router.push(key)
                                            }


                                            const route = useRoute()
                                            // 当前选中菜单
                                            const selectedKeys = computed(() => [route.path])
                                            </script>
                                            <template>
                                            <a-menu
                                            class="menu"
                                            auto-open-selected
                                            :selected-keys="selectedKeys"
                                            @menuItemClick="onClickMenuItem"
                                            :mode="mode"
                                            :accordion="true"
                                            >
                                            <MenuItem v-for="menu of menuList" :key="menu.path" :menu="menu" />
                                            </a-menu>
                                            </template>


                                            <style scoped>
                                            .menu.arco-menu-horizontal {
                                            @apply bg-[var(--color-bg-3)];
                                            }
                                            .menu.arco-menu-horizontal :deep(.arco-menu-icon) {
                                            @apply mr-4px leading-[1.2] flex-none align-inherit;
                                            }
                                            .menu.arco-menu-horizontal :deep(.arco-menu-pop-header) {
                                            @apply bg-transparent;
                                            }
                                            .menu.arco-menu-horizontal :deep(.arco-menu-pop-header):hover {
                                            @apply bg-[var(--color-fill-2)];
                                            }
                                            .menu :deep(.arco-menu-overflow-wrap) {
                                            @apply flex justify-end;
                                            }
                                            </style>



                                            文件「src/layout/switch/SidebarLayout.vue」

                                              <script>
                                              export default {
                                              name: 'SidebarLayout',
                                              title: '边栏布局'
                                              }
                                              </script>


                                              <script setup>
                                              // 侧边栏收缩状态
                                              const collapsed = ref(false)


                                              // 侧边栏收缩触发事件
                                              const handleCollapse = (val, type) => {
                                              const content = type === 'responsive' ? '响应式触发' : '点击触发'
                                              console.log(`${content}侧边栏,当前状态:${val}`)
                                              collapsed.value = val
                                              }
                                              </script>


                                              <template>
                                              <div class="sidebar-layout">
                                              <a-layout>
                                              <a-affix>
                                              <a-layout-header>
                                              <Navbar>
                                              <template #left> <Logo /> </template>


                                              <template #right>
                                              <SwitchMode />
                                              <SwitchLayout />
                                              <Github />
                                              </template>
                                              </Navbar>
                                              </a-layout-header>
                                              </a-affix>


                                              <a-layout>
                                              <a-affix :offsetTop="58">
                                              <a-layout-sider
                                              breakpoint="lg"
                                              :width="220"
                                              height="calc(100vh-58px)"
                                              collapsible
                                              :collapsed="collapsed"
                                              @collapse="handleCollapse"
                                              >
                                              <Menu mode="vertical" />
                                              </a-layout-sider>
                                              </a-affix>


                                              <a-layout>
                                              <a-layout-content class="min-h-[calc(100vh-58px)]">
                                              <router-view v-slot="{ Component }">
                                              <transition name="fade-x">
                                              <component :is="Component" />
                                              </transition>
                                              </router-view>
                                              </a-layout-content>
                                              <a-layout-footer> <Footer /> </a-layout-footer>
                                              </a-layout>
                                              </a-layout>
                                              </a-layout>
                                              </div>
                                              </template>


                                              <style scoped>
                                              .sidebar-layout :deep(.arco-layout-header),
                                              .sidebar-layout :deep(.arco-layout-footer),
                                              .sidebar-layout :deep(.arco-layout-content) {
                                              @apply text-[var(--color-text-1)] text-14px;
                                              }


                                              .sidebar-layout :deep(.arco-layout-header) {
                                              @apply w-full h-58px;
                                              @apply bg-[var(--color-bg-3)] border-b-[var(--color-border-1)] border-b-solid border-b-width-1px box-border;
                                              }
                                              .sidebar-layout :deep(.arco-layout-content) {
                                              @apply flex flex-col items-center;
                                              @apply bg-[var(--color-bg-1)] relative;
                                              }
                                              .sidebar-layout :deep(.arco-layout-footer) {
                                              @apply w-full flex justify-center items-center;
                                              @apply border-t-[var(--color-border-1)] border-t-solid border-t-width-1px box-border;
                                              @apply bg-[var(--color-bg-2)] text-[var(--color-text-1)] text-14px;
                                              }


                                              .sidebar-layout :deep(.arco-layout-sider) {
                                              @apply h-[calc(100vh-58px)];
                                              }
                                              .sidebar-layout :deep(.arco-layout-sider),
                                              .sidebar-layout :deep(.arco-layout-sider-trigger) {
                                              @apply border-r-[var(--color-border-1)] border-r-solid border-r-width-1px box-border;
                                              }
                                              </style>



                                              这时候的页面效果:





                                              END




                                              温馨提示



                                              如果你喜欢本文,请分享到朋友圈,想要获得更多信息,请关注我。


                                              文章转载自Nephilim,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                              评论