Vue3 全局配置案例

Vue\vue3_admin\src\main.ts

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' //持久化插件
// 引入element-plus插件与样式
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

// 配置element-plus 国际化  报红忽略://@ts-ignore
import zhCn from 'element-plus/es/locale/lang/zh-cn'

// 引入暗黑模式需要的样式
import 'element-plus/theme-chalk/dark/css-vars.css'

import App from './App.vue'
import router from './router'

// 获取应用实例对象
const app = createApp(App)

// 安装element-plus插件 并配置语言为zhCn中文
// app.use(ElementPlus)
app.use(ElementPlus, {
  locale: zhCn,
})

// svg插件需要的配置代码
import 'virtual:svg-icons-register'

// 引用svg图标库   // 使用自定义组件全局注册
// import SvgIcon from '@/components/SvgIcon/index.vue'
// app.component('SvgIcon',SvgIcon)

// 引入自定义插件对象:注册整个项目全局组件
import allGloablComponent from '@/components/index'
// 安装自定义插件
app.use(allGloablComponent)

// 引入模板的全局样式
import '@/styles/main.scss'

// // 测试代码,测试假接口能否使用
// import axios from 'axios'
// axios({
//   // 请求地址
//   url: '/api/user/login',
//   // 请求方式
//   method: 'post',
//   data: {
//     username: 'admin',
//     password: '111111'
//   }
// })

//引入粒子效果开始
// import Particles from 'particles.vue3'
// app.use(Particles)

// 安装pinia插件并启用持久化插件
// app.use(createPinia())
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
// 引入路由鉴权文件
import './permission'

// 初始化路由
import { anyRoutes } from './router/routes'

const userAsyncRoute = JSON.parse(localStorage.getItem('userAsyncRoute') as any) // 获取本地动态权限路由

if (userAsyncRoute && userAsyncRoute.length > 0) {
  userAsyncRoute.forEach((route: any) => {
    router.addRoute(route)
  });
  anyRoutes.forEach((route: any) => {
    router.addRoute(route)
  });
}

// 默认开启暗黑模式
if (!localStorage.getItem("darkMode")) {
  localStorage.setItem("darkMode", String(true))
}

// console.log('main', router.getRoutes())
//引入自定义指令文件
import { isHasButton } from '@/directive/has'
isHasButton(app)
// 注册模板路由
app.use(router)
// 将应用挂载到挂载点上
app.mount('#app')

Vue\vue3_admin\vite.config.ts

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

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import VueDevTools from 'vite-plugin-vue-devtools'

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

// https://vitejs.dev/config/
// export default defineConfig({
//   plugins: [
//     vue(),
//     vueJsx(),
//     VueDevTools(),
//     createSvgIconsPlugin({
//       // Specify the icon folder to be cached
//       iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
//       // Specify symbolId format
//       symbolId: 'icon-[dir]-[name]',
//     }),
//   ],
//   base: './',
//   resolve: {
//     alias: {
//       '@': fileURLToPath(new URL('./src', import.meta.url))
//     }
//   },
//   css: {
//     preprocessorOptions: {
//       scss: {
//         javascriptEnabled: true,
//         additionalData: '@import "./src/styles/variables.scss";',
//       },
//     },
//   },
// })

// import { UserConfigExport, ConfigEnv, loadEnv } from 'vite'
import { loadEnv } from 'vite'
import { viteMockServe } from 'vite-plugin-mock'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'

// export default ({ command, mode }: ConfigEnv): UserConfigExport => {
// export default defineConfig(({ command, mode }) => {
export default defineConfig(({ mode }) => {

  // 获取各种环境下对应的变量
  const env = loadEnv(mode, process.cwd())
  return {
    esbuild:{//打包时去除console和debugger代码
      drop:["console","debugger"]
    },
    plugins: [
      vue(),
      VueSetupExtend(),
      vueJsx(),
      VueDevTools(),
      createSvgIconsPlugin({
        // Specify the icon folder to be cached
        iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
        // Specify symbolId format
        symbolId: 'icon-[dir]-[name]',
      }),
      viteMockServe({
        mockPath: 'mock',
        enable: true,
      }),
    ],
    base: '/',
    // base: './',
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url))
      }
    },
    css: {
      preprocessorOptions: {
        scss: {
          javascriptEnabled: true,
          additionalData: '@import "./src/styles/variables.scss";',
        },
      },
    },
    server: {
      proxy: {
        // 字符串简写写法:http://localhost:5173/foo -> http://localhost:4567/foo
        // '/foo': 'http://localhost:4567',
        // 带选项写法:http://localhost:5173/api/bar -> http://jsonplaceholder.typicode.com/bar
        [env.VITE_APP_BASE_API]: {
          // 后端代理服务器地址
          target: env.VITE_SERVER,
          // 是否代理跨域
          changeOrigin: true,
          // 路径是否重写
          // rewrite: (path) => path.replace(/^\/api/, ''),
        },
        // 正则表达式写法:http://localhost:5173/fallback/ -> http://jsonplaceholder.typicode.com/
        // '^/fallback/.*': {
        //   target: 'http://jsonplaceholder.typicode.com',
        //   changeOrigin: true,
        //   rewrite: (path) => path.replace(/^\/fallback/, ''),
        // },
        // // 使用 proxy 实例
        // '/api': {
        //   target: 'http://jsonplaceholder.typicode.com',
        //   changeOrigin: true,
        //   configure: (proxy, options) => {
        //     // proxy 是 'http-proxy' 的实例
        //   }
        // },
        // // 代理 websockets 或 socket.io 写法:ws://localhost:5173/socket.io -> ws://localhost:5174/socket.io
        // '/socket.io': {
        //   target: 'ws://localhost:5174',
        //   ws: true,
        // },
      },
    },
  }
})

Vue\vue3_admin.env.development

# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
VITE_APP_TITLE = '硅谷甄选运营平台'
VITE_APP_BASE_API = '/api'
# VITE_SERVER = 'http://168.12.1.50:5000'
VITE_SERVER = 'http://localhost:5000'   # 常用工具包\Python_Pyjwt_Flask.py

Vue\vue3_admin\src\components\index.ts

//引入项目中全部的全局组件
import SvgIcon from './SvgIcon/index.vue'
import ProductCategory from './ProductCategory/index.vue'

//引入element-plus提供全部图标组件
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
//全局对象
const allGloablComponent: any = { SvgIcon, ProductCategory }

// console.log(Object.keys(allGloablComponent))

//对外暴露插件对象
export default {
  //务必叫做install方法
  install(app: any) {
    //注册项目全部的全局组件
    Object.keys(allGloablComponent).forEach((key) => {
      //注册为全局组件
      app.component(key, allGloablComponent[key])
    })
    //将element-plus提供图标注册为全局组件
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
      // console.log(key, component)
      app.component(key, component)
    }
  },
}

Vue\vue3_admin\src\styles\variables.scss

/**
 * Variables
 */

// 左侧的菜单样式
$base-menu-width: 220px;
$base-menu-min-width: 80px;
$base-menu-background: #001529;
// $base-font-corlor: #001529;
$base-font-corlor: var(--base-font-corlor, #001529);

$base-menu-border-radius: 1px 2px 2px 1px;

// 左侧菜单按钮效果
$base-el-menu-background-color: transparent; // 默认背景透明
$base-el-menu-background-color-hover: rgb(28, 145, 208); // 悬停背景颜色
$base-el-menu-background-color-active: rgb(5, 121, 183); // 点击背景颜色
$base-el-menu-font-color: var(--base-el-menu-font-color, rgb(226, 149, 7)); // 菜单字体颜色
$base-el-menu-font-color-active: orangered; // 菜单激活字体颜色
$base-el-menu-font-weight: bold; // 字体粗细
$base-el-menu-font-size: 18px; // 默认字体大小
$base-el-menu-font-size-hover: 20px; // 悬停字体大小
$base-el-menu-font-size-active: 19px; // 点击后字体大小
$base-el-menu-filter-drop-shadow-hover: 0px 0px 8px white; // 悬停阴影
$base-el-menu-filter-drop-shadow-active: 0px 0px 10px black; //  点击阴影

// 左侧logo区域样式
$base-menu-logo-height: 50px;
$base-menu-logo-font-size: 20px;

// 顶部导航区域样式
$base-tabbar-height: 50px;
$base-tabbar-background: var(--base-tabbar-background, linear-gradient(to right, #8faecb, #17426a, #8faecb));
// $base-tabbar-background: var(--base-tabbar-background, linear-gradient(to right, #102131, #11283e, #102131));

// MAIN内容展示区域
$base-main-background: var(--base-main-background, rgb(181, 181, 181));
// $base-main-background: var(--base-main-background, rgb(79, 78, 78));

// 滚动条样式
$base-el-menu-scrollbar-color: orangered; // 滚动条颜色

// 全局过渡效果
$base-global-transition:
    all 0.33s,
    0s height;

// // 暗黑模式
// $is-dark-mode: false; // 默认设置为 false,表示不是暗黑模式
// // 当启用暗黑模式时,替换颜色
// @if $is-dark-mode {
//     $base-font-corlor: white;
// }

/*
 * Fonts
 * ========================================================================== */
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300&display=swap");
$font-family-google: "Open Sans", sans-serif;

/*设置英文字体*/
$font-family-default: Arial, Helvetica, sans-serif;
$font-family-title: Arial, Helvetica, sans-serif;

/*设置中文字体两种*/
/* 第一种设置方式 */
$font-family-zh: "Microsoft JhengHei", "微软雅黑", STXihei;
/* 第二种设置方式 */
$font-family-zh-hw: "华文细黑", "SimSun", "宋体";

$body-font: $font-family-default;
$body-font-weight: 400;

$header-font: $font-family-title;

$basic-font-size: 100%;
$paragraph: 16px;

// Golden ration for 16px font-size
$line-height: 24px;

// For REM / EM
//$basic-font-size: 62.5%;
//$paragraph: 1.6rem;
//$line-height: 2.4rem;

/*
 * Palette
 * ========================================================================== */

/*
 * Basic Colors
 * ========================================================================== */

$blue: #00f;
$red: #f00;
$white: #fff;
$black: #000;
$gray: #ccc;

/*
 * Mixing Colors
 * ========================================================================== */
$text-color: $black;
$inverted-text-color: $white;

Vue\vue3_admin\src\styles\typography.scss

@use 'variables' as *;

/**
 * Typography
 */

html,
body {
  font-family: $body-font;
  font-weight: $body-font-weight;
}

html {
  font-size: $basic-font-size;
}

body {
  color: $text-color;
  font-size: $paragraph;
  line-height: $line-height;

}

h1,
h2,
h3 {
  font-family: $header-font;
  font-weight: 600;
}

html,
button,
input,
select,
textarea {
  color: $text-color;
}

a {
  color: $text-color;
  text-decoration: none;

  &:visited {
    outline: 0;
  }

  &:focus,
  &:active,
  &:hover {
    text-decoration: underline;
    outline: 0;
  }
}

i,
em {
  font-style: italic;
}

b,
strong {
  font-weight: bold;
}

.br {
  display: block;
}

Vue\vue3_admin\src\styles\main.scss

@import "scss-reset/reset";

// @use '../node_modules/scss-reset/src/scss/_reset.scss';

// @import 'scss-reset/reset';

// <link rel="stylesheet" href="https://unpkg.com/scss-reset/reset.css">

// 'node_modules/scss-reset/src/scss/_variables.scss';
// 'node_modules/scss-reset/src/scss/_typography.scss';

// 全局样式
body {
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
    font-family: $body-font;
    overflow: hidden; // 弹窗影响滚动条以上全局设置
}

// 全局样式
#app {
    height: 100%;
    overflow: auto; // 弹窗影响滚动条以上全局设置
}

// 气泡删除弹窗样式全局
.el-popconfirm__main {
    font-size: 18px;

    // 删除气泡图标
    .el-popconfirm__icon {
        font-size: 22px;
        font-weight: bold;
    }
}

// 滚动条外观设置
::-webkit-scrollbar {
    width: 9px;
}

::-webkit-scrollbar-track {
    background-color: $base-menu-background;
}

::-webkit-scrollbar-thumb {
    width: 8px;
    // background-color: $base-el-menu-scrollbar-color;
    background-color: $base-el-menu-font-color;
    border-radius: 5px;
}

// 折叠菜单弹出窗样式
.el-popper {
    transition: all 0.35s;
    .el-menu--popup-container {
        border-radius: 5px;
        border: 5px solid #001529;
        margin: 0;
        padding: 0;

        .el-menu {
            border-radius: 10px;
            border: 5px solid #001529;
            margin: 0;
            padding: 0;
        }
    }
}

// ElMessage提示信息样式
.el-message {
    margin-top: 50px;
    .el-message__content {
        font-size: 20px;
        font-weight: bold;
    }
}

// notification提示信息弹出窗样式
.el-form-item__error {
    font-size: 18px;
    font-weight: bold;
}

.success-message {
    margin-top: 60px;
    .el-notification__group {
        .el-notification__title {
            font-size: 20px;
            color: green;
            text-shadow: 2px 2px 2px $base-font-corlor;
        }

        .el-notification__content {
            p {
                font-size: 20px;
                // font-weight: 300;
                color: rgb(73, 128, 0);
            }
        }
    }
}

.error-message {
    margin-top: 60px;
    .el-notification__group {
        .el-notification__title {
            font-size: 20px;
            color: red;
            text-shadow: 2px 2px 2px $base-font-corlor;
        }

        .el-notification__content {
            p {
                font-size: 20px;
                // font-weight: 300;
                color: rgb(205, 50, 50);
            }
        }
    }
}

/*进度条效果*/
/* Make clicks pass-through */
#nprogress {
    pointer-events: none;
}

#nprogress .bar {
    //   background: #29d;
    background-image: linear-gradient(to right, cyan, pink, yellow, orangered, red);

    position: fixed;
    z-index: 1031;
    top: 0;
    left: 0;

    width: 100%;
    height: 2px;
}

/* Fancy blur effect */
#nprogress .peg {
    display: block;
    position: absolute;
    right: 0px;
    width: 100px;
    height: 100%;
    box-shadow:
        0 0 10px #29d,
        0 0 5px #29d;
    opacity: 1;

    -webkit-transform: rotate(3deg) translate(0px, -4px);
    -ms-transform: rotate(3deg) translate(0px, -4px);
    transform: rotate(3deg) translate(0px, -4px);
}

/* Remove these to get rid of the spinner */
#nprogress .spinner {
    display: block;
    position: fixed;
    z-index: 1031;
    top: 15px;
    right: 15px;
}

#nprogress .spinner-icon {
    width: 18px;
    height: 18px;
    box-sizing: border-box;

    border: solid 2px transparent;
    border-top-color: #29d;
    border-left-color: #29d;
    border-radius: 50%;

    -webkit-animation: nprogress-spinner 400ms linear infinite;
    animation: nprogress-spinner 400ms linear infinite;
}

.nprogress-custom-parent {
    overflow: hidden;
    position: relative;
}

.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
    position: absolute;
}

@-webkit-keyframes nprogress-spinner {
    0% {
        -webkit-transform: rotate(0deg);
    }

    100% {
        -webkit-transform: rotate(360deg);
    }
}

@keyframes nprogress-spinner {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}