Vuetify 3 + Nuxt 3 从零开始整合一个多功能admin

3,918 阅读2分钟

开篇

话不多说,vuetify是vue生态里面最有高级感的一款框架,2022年底vuetify 3正式上线,nuxt 3也逐渐稳定,这两种技术的结合一定会是未来vue app的一大趋势

项目github地址vuetify3-nuxt3-app

目标技术栈

  • vue 3.2
  • vuetify 3
  • nuxt 3
  • vite nuxt3已集成
  • typescript nuxt3已集成
  • pinia
  • sass
  • vueuse
  • vue-echarts 施工中
  • 多语言切换 施工中
  • google登录集成 施工中
  • googleMap集成 施工中
  • vuedraggable集成 施工中
  • vue-virtual-scroller 虚拟列表 施工中
  • vue-masonry vue流体布局方案 施工中

流程

创建nuxt项目

npx nuxi init nuxt3-vuetify3-app

安装依赖

yarn install

启动

yarn serve

当前状态插件未安装且项目目录均未创建 image.png

安装 vueitfy 3

yarn add vuetify@next

安装 sass

yarn add sass

创建 vuetify.ts

首先在根目录下创建 plugins目录,再创建vuetify.ts

import { createVuetify } from "vuetify";
import * as components from "vuetify/components";

export default defineNuxtPlugin((nuxtApp) => {
  const vuetify = createVuetify({
    components,
  });

  nuxtApp.vueApp.use(vuetify);
});

创建style.scss

在根目录下创建 assets,再创建scss目录,再创建style.scss 并且继承使用vuetify的样式@use "vuetify/styles";

image.png

nuxt.config.ts追加vuetify设定

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  css: ["@/assets/scss/style.scss"],
  build: {
    // transpile 选项中可以配置需要转译的文件或模块的名称,这些文件或模块会在编译过程中被转译,以保证其兼容性。
    transpile: ["vuetify"],
  },
});

app.vue中使用vuetify的组件

image.png

组件正常显示 image.png

全局修改vuetify组件样式

@use "vuetify/styles";

html {
  .v-btn {
    color: black !important;
  }
}

vuetify组件样式被修改 image.png

集成 Material Design Icons

yarn add mdi

nuxt.config.ts追加mdi icon样式文件

export default defineNuxtConfig({
  css: ["@/assets/scss/style.scss", "mdi/css/materialdesignicons.min.css"],
  build: {
    // transpile 选项中可以配置需要转译的文件或模块的名称,这些文件或模块会在编译过程中被转译,以保证其兼容性。
    transpile: ["vuetify"],
  },
});

使用mdi icon image.png

icon正常显示 image.png

集成 Iconify

yarn add --dev @iconify/vue

使用iconify image.png iconify的icon正常使用 image.png

创建Vuetify的app框架

创建layouts目录,其下创建default.vue作为<NuxtLayout>的默认layout

image.png

// default.vue
<!--
* @Maintainer: J.K. Yang
* @Description: Default Layout
-->
<script setup lang="ts">
import { ref } from "vue";
const order = ref(1);
const navDrawer = ref(true);
</script>
<template>
  <div>
    <v-app>
      <!-- APP BAR -->
      <v-app-bar app :order="order">
        <v-app-bar-nav-icon @click.stop="navDrawer = !navDrawer">
        </v-app-bar-nav-icon>
        App Bar
      </v-app-bar>
      <!-- Navigation Drawer -->
      <v-navigation-drawer elevation="4" v-model="navDrawer">
        Navigation Drawer
      </v-navigation-drawer>
      <!-- Main -->
      <v-main class="bg-grey-lighten-3">
        <v-container fluid class="h-full">
          <slot />
        </v-container>
      </v-main>
      <!-- Footer -->
      <v-footer app height="72"> Footer </v-footer>
    </v-app>
  </div>
</template>

<style scoped lang="scss"></style>

创建pages目录,其下创建index.vue作为的默认page

// pages/index.vue
<!--
* @Router: /
* @Maintainer:
* @Description: HomePage
-->
<script setup lang="ts"></script>
<template>
  <v-card min-height="87vh"> Index </v-card>
</template>

<style scoped lang="scss"></style>

image.png

使用pinia

yarn add pinia @pinia/nuxt

nuxt.config.ts追加pinia模块

export default defineNuxtConfig({
  css: ["@/assets/scss/style.scss", "mdi/css/materialdesignicons.min.css"],
  modules: ["@pinia/nuxt"],
  build: {
    // transpile 选项中可以配置需要转译的文件或模块的名称,这些文件或模块会在编译过程中被转译,以保证其兼容性。
    transpile: ["vuetify"],
  },
});

利用pinia进行全局主题切换

创建 stores 目录,在其下创建 customizeTheme.ts

// customizeTheme.ts
import { defineStore } from "pinia";

interface State {
  miniSidebar: boolean;
  darkTheme: boolean;
}

export const useCustomizeThemeStore = defineStore({
  id: "customizeTheme",
  state: (): State => ({
    miniSidebar: false,
    darkTheme: false,
  }),

  getters: {},
  actions: {
    setMiniSideBar(payload: boolean) {
      this.miniSidebar = payload;
    },
  },
});

创建 CustomizeTheme组件

创建components目录,其下创建CustomizeTheme.vue

components下的组件会被Nuxt自动进行全局导入

主题切换组件,对store里面的状态进行更改

<!--
* @Component: CustomizeTheme
* @Maintainer: Yang J.K.
* @Description: HomePage
-->
<script setup lang="ts">
import { useCustomizeThemeStore } from "@/stores/customizeTheme";
const customizeTheme = useCustomizeThemeStore();
</script>

<template>
  <div>
    <v-navigation-drawer permanent location="right">
      <div class="ml-5 pa-5">
        <v-switch
          v-model="customizeTheme.darkTheme"
          label="主题切换"
          color="orange"
          hide-details
        ></v-switch>
      </div>
      <v-divider></v-divider>
      <v-btn
        class="ma-5"
        @click.stop="customizeTheme.setMiniSideBar(!customizeTheme.miniSidebar)"
        color="warning"
        >收缩侧面菜单</v-btn
      >
    </v-navigation-drawer>
  </div>
</template>

<style scoped></style>

layout里对store里面的状态进行绑定

<!--
* @Maintainer: J.K. Yang
* @Description: Default Layout
-->
<script setup lang="ts">
import { useCustomizeThemeStore } from "@/stores/customizeTheme";
import { ref } from "vue";
const order = ref(1);
const navDrawer = ref(true);
const customizeTheme = useCustomizeThemeStore();
</script>
<template>
  <div>
    <v-app :theme="customizeTheme.darkTheme ? 'dark' : 'light'">
      <!-- APP BAR -->
      <v-app-bar app :order="order">
        <v-app-bar-nav-icon @click.stop="navDrawer = !navDrawer">
        </v-app-bar-nav-icon>
        App Bar
      </v-app-bar>
      <!-- Navigation Drawer -->
      <v-navigation-drawer
        elevation="4"
        v-model="navDrawer"
        :rail="customizeTheme.miniSidebar"
      >
        <v-list density="compact" nav>
          <v-list-item
            prepend-icon="mdi-folder"
            title="My Files"
            value="myfiles"
          ></v-list-item>
          <v-list-item
            prepend-icon="mdi-account-multiple"
            title="Shared with me"
            value="shared"
          ></v-list-item>
          <v-list-item
            prepend-icon="mdi-star"
            title="Starred"
            value="starred"
          ></v-list-item>
        </v-list>
      </v-navigation-drawer>
      <!-- Main -->
      <v-main class="bg-grey-lighten-3">
        <v-container fluid class="h-full">
          <slot />
        </v-container>
      </v-main>
      <!-- Footer -->
      <v-footer app height="72" elevation="5"> Footer </v-footer>
      <CustomizeTheme />
    </v-app>
  </div>
</template>

<style scoped lang="scss"></style>

pinia进行全局主题切换可正常使用 image.png


后续待更新