国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源)

這篇具有很好參考價(jià)值的文章主要介紹了Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

vue3-element-admin 是基于 vue-element-admin 升級(jí)的 Vue3 + Element Plus 版本的后臺(tái)管理前端解決方案,技術(shù)棧為 Vue3 + Vite4 + TypeScript + Element Plus + Pinia + Vue Router 等當(dāng)前主流框架。

相較于其他管理前端框架,vue3-element-admin 的優(yōu)勢(shì)在于一有一無 (有配套后端、無復(fù)雜封裝):

  • 配套完整 Java 后端 權(quán)限管理系統(tǒng),開箱即用,提供 OpenAPI 文檔 搭配 Apifox 生成 Node、Python、Go等其他服務(wù)端代碼;

  • 完全基于 vue-element-admin 升級(jí)的 Vue3 版本,沒有對(duì)框架(Element Plus)的組件再封裝,上手成本低和擴(kuò)展性高。

前言

本篇是 vue3-element-admin v2.x 版本從 0 到 1,相較于 v1.x 版本 主要增加了對(duì)原子CSS(UnoCSS)、按需自動(dòng)導(dǎo)入、暗黑模式的支持。

項(xiàng)目預(yù)覽

在線預(yù)覽

https://vue3.youlai.tech/

首頁(yè)控制臺(tái)

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源
Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

接口文檔

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

權(quán)限管理系統(tǒng)

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源
Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

擴(kuò)展生態(tài)

youlai-mall 有來開源商城:Spring Cloud微服務(wù)+ vue3-element-admin+uni-app

youlai-mall 商品管理 mall-app 移動(dòng)端
Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

項(xiàng)目指南

功能清單

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

技術(shù)棧&官網(wǎng)

技術(shù)棧 描述 官網(wǎng)
Vue3 漸進(jìn)式 JavaScript 框架 https://cn.vuejs.org/
Element Plus 基于 Vue 3,面向設(shè)計(jì)師和開發(fā)者的組件庫(kù) https://element-plus.gitee.io/zh-CN/
Vite 前端開發(fā)與構(gòu)建工具 https://cn.vitejs.dev/
TypeScript 微軟新推出的一種語言,是 JavaScript 的超集 https://www.tslang.cn/
Pinia 新一代狀態(tài)管理工具 https://pinia.vuejs.org/
Vue Router Vue.js 的官方路由 https://router.vuejs.org/zh/
wangEditor Typescript 開發(fā)的 Web 富文本編輯器 https://www.wangeditor.com/
Echarts 一個(gè)基于 JavaScript 的開源可視化圖表庫(kù) https://echarts.apache.org/zh/
vue-i18n Vue 國(guó)際化多語言插件 https://vue-i18n.intlify.dev/
VueUse 基于Vue組合式API的實(shí)用工具集(類比HuTool工具) http://www.vueusejs.com/

前/后端源碼

Gitee Github
前端 vue3-element-admin vue3-element-admin
后端 youlai-boot youlai-boot

接口文檔

  • 接口調(diào)用地址:https://vapi.youlai.tech
  • 接口文檔地址:在線接口文檔
  • OpenAPI 3.0 文檔地址:http://vapi.youlai.tech/v3/api-docs

環(huán)境準(zhǔn)備

名稱 備注
開發(fā)工具 VSCode 下載 -
運(yùn)行環(huán)境 Node 16+ 下載 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源
VSCode插件(必裝) 插件市場(chǎng)搜索 Vue Language Features (Volar) TypeScript Vue Plugin (Volar) 安裝,且禁用 Vetur Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

項(xiàng)目初始化

按照 ??Vite 官方文檔 - 搭建第一個(gè) Vite 項(xiàng)目 說明,執(zhí)行以下命令完成 vue 、typescirpt 模板項(xiàng)目的初始化

 npm init vite@latest vue3-element-admin --template vue-ts
  • **vue3-element-admin **: 自定義的項(xiàng)目名稱

  • vue-tsvue + typescript 模板的標(biāo)識(shí),查看 create-vite 以獲取每個(gè)模板的更多細(xì)節(jié):vue,vue-ts,react,react-ts

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

初始化完成項(xiàng)目位于 D:\project\demo\vue3-element-admin , 使用 VSCode 導(dǎo)入,執(zhí)行以下命令啟動(dòng):

npm install
npm run dev

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

瀏覽器訪問 localhost:5173 預(yù)覽

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

路徑別名配置

相對(duì)路徑別名配置,使用 @ 代替 src

Vite 配置

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

TypeScirpt 編譯器配置

// tsconfig.json
"compilerOptions": {
    ...
    "baseUrl": "./", // 解析非相對(duì)模塊的基地址,默認(rèn)是當(dāng)前目錄
    "paths": { // 路徑映射,相對(duì)于baseUrl
    	"@/*": ["src/*"] 
    }
}

路徑別名使用

// src/App.vue
import HelloWorld from '/src/components/HelloWorld.vue'import HelloWorld from '@/components/HelloWorld.vue'

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

安裝自動(dòng)導(dǎo)入

Element Plus 官方文檔中推薦 按需自動(dòng)導(dǎo)入 的方式,而此需要使用額外的插件 unplugin-auto-importunplugin-vue-components 來導(dǎo)入要使用的組件。所以在整合 Element Plus 之前先了解下自動(dòng)導(dǎo)入的概念和作用

概念

為了避免在多個(gè)頁(yè)面重復(fù)引入 API組件,由此而產(chǎn)生的自動(dòng)導(dǎo)入插件來節(jié)省重復(fù)代碼和提高開發(fā)效率。

插件 概念 自動(dòng)導(dǎo)入對(duì)象
unplugin-auto-import 按需自動(dòng)導(dǎo)入API ref,reactive,watch,computed 等API
unplugin-vue-components 按需自動(dòng)導(dǎo)入組件 Element Plus 等三方庫(kù)和指定目錄下的自定義組件

看下自動(dòng)導(dǎo)入插件未使用和使用的區(qū)別:

插件名 未使用自動(dòng)導(dǎo)入 使用自動(dòng)導(dǎo)入
unplugin-auto-import Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源
unplugin-vue-components Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

安裝插件依賴

npm install -D unplugin-auto-import unplugin-vue-components 

vite.config.ts - 自動(dòng)導(dǎo)入配置

新建 /src/types 目錄用于存放自動(dòng)導(dǎo)入函數(shù)和組件的TS類型聲明文件

import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";

plugins: [
  AutoImport({
    // 自動(dòng)導(dǎo)入 Vue 相關(guān)函數(shù),如:ref, reactive, toRef 等
    imports: ["vue"],
    eslintrc: {
      enabled: true, // 是否自動(dòng)生成 eslint 規(guī)則,建議生成之后設(shè)置 false 
      filepath: "./.eslintrc-auto-import.json", // 指定自動(dòng)導(dǎo)入函數(shù) eslint 規(guī)則的文件
    },
    dts: path.resolve(pathSrc, "types", "auto-imports.d.ts"), // 指定自動(dòng)導(dǎo)入函數(shù)TS類型聲明文件路徑
  }),
  Components({
    dts: path.resolve(pathSrc, "types", "components.d.ts"), // 指定自動(dòng)導(dǎo)入組件TS類型聲明文件路徑
  }),
]

.eslintrc.cjs - 自動(dòng)導(dǎo)入函數(shù) eslint 規(guī)則引入

"extends": [
    "./.eslintrc-auto-import.json"
],

tsconfig.json - 自動(dòng)導(dǎo)入TS類型聲明文件引入

{
  "include": ["src/**/*.d.ts"]
}

自動(dòng)導(dǎo)入效果

運(yùn)行項(xiàng)目 npm run dev 自動(dòng)

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

整合 Element Plus

參考: element plus 按需自動(dòng)導(dǎo)入

需要完成上面一節(jié)的 自動(dòng)導(dǎo)入 的安裝和配置

安裝 Element Plus

npm install element-plus

安裝自動(dòng)導(dǎo)入 Icon 依賴

npm i -D unplugin-icons

vite.config.ts 配置

參考: element-plus-best-practices - vite.config.ts

// vite.config.ts
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";

export default ({ mode }: ConfigEnv): UserConfig => {

  return {
    plugins: [
      // ...
      AutoImport({
        // ...  
        resolvers: [
          // 自動(dòng)導(dǎo)入 Element Plus 相關(guān)函數(shù),如:ElMessage, ElMessageBox... (帶樣式)
          ElementPlusResolver(),
          // 自動(dòng)導(dǎo)入圖標(biāo)組件
          IconsResolver({}),
        ]
        vueTemplate: true, // 是否在 vue 模板中自動(dòng)導(dǎo)入
        dts: path.resolve(pathSrc, 'types', 'auto-imports.d.ts') // 自動(dòng)導(dǎo)入組件類型聲明文件位置,默認(rèn)根目錄
          
      }),
      Components({ 
        resolvers: [
          // 自動(dòng)導(dǎo)入 Element Plus 組件
          ElementPlusResolver(),
          // 自動(dòng)注冊(cè)圖標(biāo)組件
          IconsResolver({
            enabledCollections: ["ep"] // element-plus圖標(biāo)庫(kù),其他圖標(biāo)庫(kù) https://icon-sets.iconify.design/
          }),
        ],
        dts: path.resolve(pathSrc, "types", "components.d.ts"), //  自動(dòng)導(dǎo)入組件類型聲明文件位置,默認(rèn)根目錄
      }),
      Icons({
        // 自動(dòng)安裝圖標(biāo)庫(kù)
        autoInstall: true,
      }),
    ],
  };
};

示例代碼

<!-- src/components/HelloWorld.vue -->
<div>
  <el-button type="success"><i-ep-SuccessFilled />Success</el-button>
  <el-button type="info"><i-ep-InfoFilled />Info</el-button>
  <el-button type="warning"><i-ep-WarningFilled />Warning</el-button>
  <el-button type="danger"><i-ep-WarnTriangleFilled />Danger</el-button>
</div>

效果預(yù)覽

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

整合 SVG 圖標(biāo)

通過 vite-plugin-svg-icons 插件整合 Iconfont 第三方圖標(biāo)庫(kù)實(shí)現(xiàn)本地圖標(biāo)

參考: vite-plugin-svg-icons 安裝文檔

安裝依賴

npm install -D fast-glob@3.2.11 
npm install -D vite-plugin-svg-icons@2.0.1 

創(chuàng)建 src/assets/icons 目錄 , 放入從 Iconfont 復(fù)制的 svg 圖標(biāo)

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

main.ts 引入注冊(cè)腳本

// src/main.ts
import 'virtual:svg-icons-register';

vite.config.ts 配置插件

// vite.config.ts
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';

export default ({command, mode}: ConfigEnv): UserConfig => {
 return (
     {
         plugins: [
             createSvgIconsPlugin({
                 // 指定需要緩存的圖標(biāo)文件夾
                 iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
                 // 指定symbolId格式
                 symbolId: 'icon-[dir]-[name]',
             })
         ]
     }
 )
}

SVG 組件封裝

<!-- src/components/SvgIcon/index.vue -->
<script setup lang="ts">
const props = defineProps({
prefix: {
 type: String,
 default: "icon",
},
iconClass: {
 type: String,
 required: false,
},
color: {
 type: String,
},
size: {
 type: String,
 default: "1em",
},
});

const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
</script>

<template>
<svg
 aria-hidden="true"
 class="svg-icon"
 :style="'width:' + size + ';height:' + size"
>
 <use :xlink:href="symbolId" :fill="color" />
</svg>
</template>

<style scoped>
.svg-icon {
display: inline-block;
outline: none;
width: 1em;
height: 1em;
vertical-align: -0.15em; /* 因icon大小被設(shè)置為和字體大小一致,而span等標(biāo)簽的下邊緣會(huì)和字體的基線對(duì)齊,故需設(shè)置一個(gè)往下的偏移比例,來糾正視覺上的未對(duì)齊效果 */
fill: currentColor; /* 定義元素的顏色,currentColor是一個(gè)變量,這個(gè)變量的值就表示當(dāng)前元素的color值,如果當(dāng)前元素未設(shè)置color值,則從父元素繼承 */
overflow: hidden;
}
</style>

組件使用

<!-- src/components/HelloWorld.vue -->
<template>
 <el-button type="info"><svg-icon icon-class="block"/>SVG 本地圖標(biāo)</el-button>
</template>

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

整合 SCSS

一款CSS預(yù)處理語言,SCSS 是 Sass 3 引入新的語法,其語法完全兼容 CSS3,并且繼承了 Sass 的強(qiáng)大功能。

安裝依賴

npm i -D sass 

創(chuàng)建 variables.scss 變量文件,添加變量 $bg-color 定義,注意規(guī)范變量以 $ 開頭

// src/styles/variables.scss
$bg-color:#242424;

Vite 配置導(dǎo)入 SCSS 全局變量文件

// vite.config.ts
css: {
    // CSS 預(yù)處理器
    preprocessorOptions: {
        //define global scss variable
        scss: {
            javascriptEnabled: true,
            additionalData: `@use "@/styles/variables.scss" as *;`
        }
    }
}

style 標(biāo)簽使用SCSS全局變量

<!-- src/components/HelloWorld.vue -->
<template>
  <div class="box" />
</template>

<style lang="scss" scoped>
.box {
  width: 100px;
  height: 100px;
  background-color: $bg-color;
}
</style>

上面導(dǎo)入的 SCSS 全局變量在 TypeScript 不生效的,需要?jiǎng)?chuàng)建一個(gè)以 .module.scss 結(jié)尾的文件

// src/styles/variables.module.scss

// 導(dǎo)出 variables.scss 文件的變量
:export{
    bgColor:$bg-color
}

TypeScript 使用 SCSS 全局變量

<!-- src/components/HelloWorld.vue -->
<script setup lang="ts">
  import variables from "@/styles/variables.module.scss";
  console.log(variables.bgColor)  
</script>

<template>
  <div style="width:100px;height:100px" :style="{ 'background-color': variables.bgColor }" />
</template>

整合 UnoCSS

UnoCSS 是一個(gè)具有高性能且極具靈活性的即時(shí)原子化 CSS 引擎 。

參考:Vite 安裝 UnoCSS 官方文檔

安裝依賴

npm install -D unocss

vite.config.ts 配置

// vite.config.ts
import UnoCSS from 'unocss/vite'

export default {
  plugins: [
    UnoCSS({ /* options */ }),
  ],
}

main.ts 引入 uno.css

// src/main.ts
import 'uno.css'

VSCode 安裝 UnoCSS 插件

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

再看下具體使用方式和實(shí)際效果:

代碼 效果
Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

如果UnoCSS 插件智能提示不生效,請(qǐng)參考:VSCode插件UnoCSS智能提示不生效解決 。

整合 Pinia

Pinia 是 Vue 的專屬狀態(tài)管理庫(kù),它允許你跨組件或頁(yè)面共享狀態(tài)。

參考:Pinia 官方文檔

安裝依賴

npm install pinia

main.ts 引入 pinia

// src/main.ts
import { createPinia } from "pinia";
import App from "./App.vue";

createApp(App).use(createPinia()).mount("#app");

定義 Store

根據(jù) Pinia 官方文檔-核心概念 描述 ,Store 定義分為選項(xiàng)式組合式 , 先比較下兩種寫法的區(qū)別:

選項(xiàng)式 Option Store 組合式 Setup Store
Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

至于如何選擇,官方給出的建議 :選擇你覺得最舒服的那一個(gè)就好 。

這里選擇組合式,新建文件 src/store/counter.ts

// src/store/counter.ts
import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", () => {
  // ref變量 → state 屬性
  const count = ref(0);
  // computed計(jì)算屬性 → getters
  const double = computed(() => {
    return count.value * 2;
  });
  // function函數(shù) → actions
  function increment() {
    count.value++;
  }

  return { count, double, increment };
});

父組件

<!-- src/App.vue -->
<script setup lang="ts">
import HelloWorld from "@/components/HelloWorld.vue";

import { useCounterStore } from "@/store/counter";
const counterStore = useCounterStore();
</script>

<template>
  <h1 class="text-3xl">vue3-element-admin-父組件</h1>
  <el-button type="primary" @click="counterStore.increment">count++</el-button>
  <HelloWorld />
</template>

子組件

<!-- src/components/HelloWorld.vue -->
<script setup lang="ts">
import { useCounterStore } from "@/store/counter";
const counterStore = useCounterStore();
</script>

<template>
  <el-card  class="text-left text-white border-white border-1 border-solid mt-10 bg-[#242424]" >
    <template #header> 子組件 HelloWorld.vue</template>
    <el-form>
      <el-form-item label="數(shù)字:"> {{ counterStore.count }}</el-form-item>
      <el-form-item label="加倍:"> {{ counterStore.double }}</el-form-item>
    </el-form>
  </el-card>
</template>

效果預(yù)覽

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

環(huán)境變量

Vite 環(huán)境變量主要是為了區(qū)分開發(fā)、測(cè)試、生產(chǎn)等環(huán)境的變量

參考: Vite 環(huán)境變量配置官方文檔

env配置文件

項(xiàng)目根目錄新建 .env.development 、.env.production

  • 開發(fā)環(huán)境變量配置:.env.development

    # 變量必須以 VITE_ 為前綴才能暴露給外部讀取
    VITE_APP_TITLE = 'vue3-element-admin'
    VITE_APP_PORT = 3000
    VITE_APP_BASE_API = '/dev-api'
    
  • 生產(chǎn)環(huán)境變量配置:.env.production

    VITE_APP_TITLE = 'vue3-element-admin'
    VITE_APP_PORT = 3000
    VITE_APP_BASE_API = '/prod-api'
    

環(huán)境變量智能提示

新建 src/types/env.d.ts文件存放環(huán)境變量TS類型聲明

// src/types/env.d.ts
interface ImportMetaEnv {
  /**
   * 應(yīng)用標(biāo)題
   */
  VITE_APP_TITLE: string;
  /**
   * 應(yīng)用端口
   */
  VITE_APP_PORT: number;
  /**
   * API基礎(chǔ)路徑(反向代理)
   */
  VITE_APP_BASE_API: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

使用自定義環(huán)境變量就會(huì)有智能提示,環(huán)境變量的讀取和使用請(qǐng)看下一節(jié)的跨域處理中的 vite.config.ts的配置。

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

跨域處理

跨域原理

瀏覽器同源策略: 協(xié)議、域名和端口都相同是同源,瀏覽器會(huì)限制非同源請(qǐng)求讀取響應(yīng)結(jié)果。

本地開發(fā)環(huán)境通過 Vite 配置反向代理解決瀏覽器跨域問題,生產(chǎn)環(huán)境則是通過 nginx 配置反向代理 。

vite.config.ts 配置代理

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

表面肉眼看到的請(qǐng)求地址: http://localhost:3000/dev-api/api/v1/users/me

真實(shí)訪問的代理目標(biāo)地址: http://vapi.youlai.tech/api/v1/users/me

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

整合 Axios

Axios 基于promise可以用于瀏覽器和node.js的網(wǎng)絡(luò)請(qǐng)求庫(kù)

參考: Axios 官方文檔

安裝依賴

npm install axios

Axios 工具類封裝

//  src/utils/request.ts
import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import { useUserStoreHook } from '@/store/modules/user';

// 創(chuàng)建 axios 實(shí)例
const service = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: 50000,
  headers: { 'Content-Type': 'application/json;charset=utf-8' }
});

// 請(qǐng)求攔截器
service.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    const userStore = useUserStoreHook();
    if (userStore.token) {
      config.headers.Authorization = userStore.token;
    }
    return config;
  },
  (error: any) => {
    return Promise.reject(error);
  }
);

// 響應(yīng)攔截器
service.interceptors.response.use(
  (response: AxiosResponse) => {
    const { code, msg } = response.data;
    // 登錄成功
    if (code === '00000') {
      return response.data;
    }

    ElMessage.error(msg || '系統(tǒng)出錯(cuò)');
    return Promise.reject(new Error(msg || 'Error'));
  },
  (error: any) => {
    if (error.response.data) {
      const { code, msg } = error.response.data;
      // token 過期,跳轉(zhuǎn)登錄頁(yè)
      if (code === 'A0230') {
        ElMessageBox.confirm('當(dāng)前頁(yè)面已失效,請(qǐng)重新登錄', '提示', {
          confirmButtonText: '確定',
          type: 'warning'
        }).then(() => {
          localStorage.clear(); // @vueuse/core 自動(dòng)導(dǎo)入
          window.location.href = '/';
        });
      }else{
          ElMessage.error(msg || '系統(tǒng)出錯(cuò)');
      }
    }
    return Promise.reject(error.message);
  }
);

// 導(dǎo)出 axios 實(shí)例
export default service;

登錄接口實(shí)戰(zhàn)

訪問 vue3-element-admin 在線接口文檔, 查看登錄接口請(qǐng)求參數(shù)和響應(yīng)數(shù)據(jù)類型

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

點(diǎn)擊 生成代碼 獲取登錄響應(yīng)數(shù)據(jù) TypeScript 類型定義

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

將類型定義復(fù)制到 src/api/auth/types.ts 文件中

/**
 * 登錄請(qǐng)求參數(shù)
 */
export interface LoginData {
  /**
   * 用戶名
   */
  username: string;
  /**
   * 密碼
   */
  password: string;
}

/**
 * 登錄響應(yīng)
 */
export interface LoginResult {
  /**
   * 訪問token
   */
  accessToken?: string;
  /**
   * 過期時(shí)間(單位:毫秒)
   */
  expires?: number;
  /**
   * 刷新token
   */
  refreshToken?: string;
  /**
   * token 類型
   */
  tokenType?: string;
}

登錄 API 定義

// src/api/auth/index.ts
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { LoginData, LoginResult } from './types';

/**
 * 登錄API 
 * 
 * @param data {LoginData}
 * @returns
 */
export function loginApi(data: LoginData): AxiosPromise<LoginResult> {
  return request({
    url: '/api/v1/auth/login',
    method: 'post',
    params: data
  });
}

登錄 API 調(diào)用

// src/store/modules/user.ts
import { loginApi } from '@/api/auth';
import { LoginData } from '@/api/auth/types';

/**
 * 登錄調(diào)用
 *
 * @param {LoginData}
 * @returns
 */
function login(loginData: LoginData) {
  return new Promise<void>((resolve, reject) => {
    loginApi(loginData)
      .then(response => {
        const { tokenType, accessToken } = response.data;
        token.value = tokenType + ' ' + accessToken; // Bearer eyJhbGciOiJIUzI1NiJ9.xxx.xxx
        resolve();
      })
      .catch(error => {
        reject(error);
      });
  });
}

動(dòng)態(tài)路由

安裝 vue-router

npm install vue-router@next

路由實(shí)例

創(chuàng)建路由實(shí)例,順帶初始化靜態(tài)路由,而動(dòng)態(tài)路由需要用戶登錄,根據(jù)用戶擁有的角色進(jìn)行權(quán)限校驗(yàn)后進(jìn)行初始化

// src/router/index.ts
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';

export const Layout = () => import('@/layout/index.vue');

// 靜態(tài)路由
export const constantRoutes: RouteRecordRaw[] = [
  {
    path: '/redirect',
    component: Layout,
    meta: { hidden: true },
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index.vue')
      }
    ]
  },

  {
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    meta: { hidden: true }
  },

  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index.vue'),
        name: 'Dashboard',
        meta: { title: 'dashboard', icon: 'homepage', affix: true }
      }
    ]
  }
];

/**
 * 創(chuàng)建路由
 */
const router = createRouter({
  history: createWebHashHistory(),
  routes: constantRoutes as RouteRecordRaw[],
  // 刷新時(shí),滾動(dòng)條位置還原
  scrollBehavior: () => ({ left: 0, top: 0 })
});

/**
 * 重置路由
 */
export function resetRouter() {
  router.replace({ path: '/login' });
  location.reload();
}

export default router;

全局注冊(cè)路由實(shí)例

// main.ts
import router from "@/router";

app.use(router).mount('#app')

動(dòng)態(tài)權(quán)限路由

路由守衛(wèi) src/permission.ts ,獲取當(dāng)前登錄用戶的角色信息進(jìn)行動(dòng)態(tài)路由的初始化

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

最終調(diào)用 permissionStore.generateRoutes(roles) 方法生成動(dòng)態(tài)路由

// src/store/modules/permission.ts 
import { listRoutes } from '@/api/menu';

export const usePermissionStore = defineStore('permission', () => {
  const routes = ref<RouteRecordRaw[]>([]);

  function setRoutes(newRoutes: RouteRecordRaw[]) {
    routes.value = constantRoutes.concat(newRoutes);
  }
  /**
   * 生成動(dòng)態(tài)路由
   *
   * @param roles 用戶角色集合
   * @returns
   */
  function generateRoutes(roles: string[]) {
    return new Promise<RouteRecordRaw[]>((resolve, reject) => {
      // 接口獲取所有路由
      listRoutes()
        .then(({ data: asyncRoutes }) => {
          // 根據(jù)角色獲取有訪問權(quán)限的路由
          const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
          setRoutes(accessedRoutes);
          resolve(accessedRoutes);
        })
        .catch(error => {
          reject(error);
        });
    });
  }
  // 導(dǎo)出 store 的動(dòng)態(tài)路由數(shù)據(jù) routes 
  return { routes, setRoutes, generateRoutes };
});

接口獲取得到的路由數(shù)據(jù)

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

根據(jù)路由數(shù)據(jù) (routes)生成菜單的關(guān)鍵代碼

src/layout/componets/Sidebar/index.vue src/layout/componets/Sidebar/SidebarItem.vue
Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

按鈕權(quán)限

除了 Vue 內(nèi)置的一系列指令 (比如 v-modelv-show) 之外,Vue 還允許你注冊(cè)自定義的指令 (Custom Directives),以下就通過自定義指令的方式實(shí)現(xiàn)按鈕權(quán)限控制。

參考:Vue 官方文檔-自定義指令

**自定義指令 **

// src/directive/permission/index.ts

import { useUserStoreHook } from '@/store/modules/user';
import { Directive, DirectiveBinding } from 'vue';

/**
 * 按鈕權(quán)限
 */
export const hasPerm: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    // 「超級(jí)管理員」擁有所有的按鈕權(quán)限
    const { roles, perms } = useUserStoreHook();
    if (roles.includes('ROOT')) {
      return true;
    }
    // 「其他角色」按鈕權(quán)限校驗(yàn)
    const { value } = binding;
    if (value) {
      const requiredPerms = value; // DOM綁定需要的按鈕權(quán)限標(biāo)識(shí)

      const hasPerm = perms?.some(perm => {
        return requiredPerms.includes(perm);
      });

      if (!hasPerm) {
        el.parentNode && el.parentNode.removeChild(el);
      }
    } else {
      throw new Error(
        "need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\""
      );
    }
  }
};

全局注冊(cè)自定義指令

// src/directive/index.ts
import type { App } from 'vue';

import { hasPerm } from './permission';

// 全局注冊(cè) directive 方法
export function setupDirective(app: App<Element>) {
  // 使 v-hasPerm 在所有組件中都可用
  app.directive('hasPerm', hasPerm);
}
// src/main.ts
import { setupDirective } from '@/directive';

const app = createApp(App);
// 全局注冊(cè) 自定義指令(directive)
setupDirective(app);

組件使用自定義指令

// src/views/system/user/index.vue
<el-button v-hasPerm="['sys:user:add']">新增</el-button>
<el-button v-hasPerm="['sys:user:delete']">刪除</el-button>

國(guó)際化

國(guó)際化分為兩個(gè)部分,Element Plus 框架國(guó)際化(官方提供了國(guó)際化方式)和自定義國(guó)際化(通過 vue-i18n 國(guó)際化插件)

Element Plus 國(guó)際化

簡(jiǎn)單的使用方式請(qǐng)參考 Element Plus 官方文檔-國(guó)際化示例,以下介紹 vue3-element-admin 整合 pinia 實(shí)現(xiàn)國(guó)際化語言切換。

Element Plus 提供了一個(gè) Vue 組件 ConfigProvider 用于全局配置國(guó)際化的設(shè)置。

<!-- src/App.vue -->
<script setup lang="ts">
import { ElConfigProvider } from 'element-plus';
import { useAppStore } from '@/store/modules/app';
const appStore = useAppStore();
</script>

<template>
  <el-config-provider :locale="appStore.locale" >
    <router-view />
  </el-config-provider>
</template>

定義 store

// src/store/modules/app.ts
import { defineStore } from 'pinia';
import { useStorage } from '@vueuse/core';
import defaultSettings from '@/settings';

// 導(dǎo)入 Element Plus 中英文語言包
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import en from 'element-plus/es/locale/lang/en';

// setup
export const useAppStore = defineStore('app', () => {
    
  const language = useStorage('language', defaultSettings.language);
    
  /**
   * 根據(jù)語言標(biāo)識(shí)讀取對(duì)應(yīng)的語言包
   */
  const locale = computed(() => {
    if (language?.value == 'en') {
      return en;
    } else {
      return zhCn;
    }
  });

  /**
   * 切換語言
   */
  function changeLanguage(val: string) {
    language.value = val;
  }

  return {
    language,
    locale,
    changeLanguage
  };
});

切換語言組件調(diào)用

<!-- src/components/LangSelect/index.vue -->
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import SvgIcon from '@/components/SvgIcon/index.vue';
import { useAppStore } from '@/store/modules/app';

const appStore = useAppStore();
const { locale } = useI18n();

function handleLanguageChange(lang: string) {
  locale.value = lang;
  appStore.changeLanguage(lang);
  if (lang == 'en') {
    ElMessage.success('Switch Language Successful!');
  } else {
    ElMessage.success('切換語言成功!');
  }
}
</script>

<template>
  <el-dropdown trigger="click" @command="handleLanguageChange">
    <div>
      <svg-icon icon-class="language" />
    </div>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item
          :disabled="appStore.language === 'zh-cn'"
          command="zh-cn"
        >
          中文
        </el-dropdown-item>
        <el-dropdown-item :disabled="appStore.language === 'en'" command="en">
          English
        </el-dropdown-item>
      </el-dropdown-menu>
    </template>
  </el-dropdown>
</template>

Element Plus 分頁(yè)組件看下國(guó)際化的效果

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

vue-i18n 自定義國(guó)際化

i18n 英文全拼 internationalization ,國(guó)際化的意思,英文 i 和 n 中間18個(gè)英文字母

參考:vue-i18n 官方文檔 - installation

安裝 vue-i18n

npm install vue-i18n@9

自定義語言包

創(chuàng)建 src/lang/package 語言包目錄,存放自定義的語言文件

中文語言包 zh-cn.ts 英文語言包 en.ts
Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源 Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

創(chuàng)建 i18n 實(shí)例

// src/lang/index.ts
import { createI18n } from 'vue-i18n';
import { useAppStore } from '@/store/modules/app';

const appStore = useAppStore();
// 本地語言包
import enLocale from './package/en';
import zhCnLocale from './package/zh-cn';

const messages = {
  'zh-cn': {
    ...zhCnLocale
  },
  en: {
    ...enLocale
  }
};
// 創(chuàng)建 i18n 實(shí)例
const i18n = createI18n({
  legacy: false,
  locale: appStore.language,
  messages: messages
});
// 導(dǎo)出 i18n 實(shí)例
export default i18n;

i18n 全局注冊(cè)

// main.ts

// 國(guó)際化
import i18n from '@/lang/index';

app.use(i18n).mount('#app');

登錄頁(yè)面國(guó)際化使用

$t 是 i18n 提供的根據(jù) key 從語言包翻譯對(duì)應(yīng)的 value 方法

<span>{{ $t("login.title") }}</span>

在登錄頁(yè)面 src/view/login/index.vue 查看如何使用

效果預(yù)覽

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

暗黑模式

Element Plus 2.2.0 版本開始支持暗黑模式,啟用方式參考 Element Plus 官方文檔 - 暗黑模式, 官方也提供了示例 element-plus-vite-starter 模版 。

這里根據(jù)官方文檔和示例講述 vue3-element-admin 是如何使用 VueUse 的 useDark 方法實(shí)現(xiàn)暗黑模式的動(dòng)態(tài)切換。

導(dǎo)入 Element Plus 暗黑模式變量

// src/main.ts
import 'element-plus/theme-chalk/dark/css-vars.css'

切換暗黑模式設(shè)置

<!-- src/layout/components/Settings/index.vue -->
<script setup lang="ts">

import IconEpSunny from '~icons/ep/sunny';
import IconEpMoon from '~icons/ep/moon';

/**
 * 暗黑模式
 */
const settingsStore = useSettingsStore();
const isDark = useDark();
const toggleDark = () => useToggle(isDark);

</script>

<template>
  <div class="settings-container">
    <h3 class="text-base font-bold">項(xiàng)目配置</h3>
    <el-divider>主題</el-divider>

    <div class="flex justify-center" @click.stop>
      <el-switch
        v-model="isDark"
        @change="toggleDark"
        inline-prompt
        :active-icon="IconEpMoon"
        :inactive-icon="IconEpSunny"
        active-color="var(--el-fill-color-dark)"
        inactive-color="var(--el-color-primary)"
      />
    </div>
  </div>
</template>

自定義變量

除了 Element Plus 組件樣式之外,應(yīng)用中還有很多自定義的組件和樣式,像這樣的:

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

應(yīng)對(duì)自定義組件樣式實(shí)現(xiàn)暗黑模式步驟如下:

新建 src/styles/dark.scss

html.dark {
  /* 修改自定義元素的樣式 */   
  .navbar {
    background-color: #141414;
  }
}

在 Element Plus 的樣式之后導(dǎo)入它

// main.ts
import 'element-plus/theme-chalk/dark/css-vars.css'
import '@/styles/dark.scss';

效果預(yù)覽

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

組件封裝

wangEditor 富文本

參考: wangEditor 官方文檔

安裝 wangEditor

npm install @wangeditor/editor @wangeditor/editor-for-vue@next 

wangEditor 組件封裝

<!-- src/components/WangEditor/index.vue -->
<template>
  <div style="border: 1px solid #ccc">
    <!-- 工具欄 -->
    <Toolbar
      :editor="editorRef"
      :defaultConfig="toolbarConfig"
      style="border-bottom: 1px solid #ccc"
      :mode="mode"
    />
    <!-- 編輯器 -->
    <Editor
      :defaultConfig="editorConfig"
      v-model="defaultHtml"
      @onChange="handleChange"
      style="height: 500px; overflow-y: hidden"
      :mode="mode"
      @onCreated="handleCreated"
    />
  </div>
</template>

<script setup lang="ts">
import { onBeforeUnmount, shallowRef, reactive, toRefs } from 'vue';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';

// API 引用
import { uploadFileApi } from '@/api/file';

const props = defineProps({
  modelValue: {
    type: [String],
    default: ''
  }
});

const emit = defineEmits(['update:modelValue']);

// 編輯器實(shí)例,必須用 shallowRef
const editorRef = shallowRef();

const state = reactive({
  toolbarConfig: {},
  editorConfig: {
    placeholder: '請(qǐng)輸入內(nèi)容...',
    MENU_CONF: {
      uploadImage: {
        // 自定義圖片上傳
        async customUpload(file: any, insertFn: any) {
          uploadFileApi(file).then(response => {
            const url = response.data.url;
            insertFn(url);
          });
        }
      }
    }
  },
  defaultHtml: props.modelValue,
  mode: 'default'
});

const { toolbarConfig, editorConfig, defaultHtml, mode } = toRefs(state);

const handleCreated = (editor: any) => {
  editorRef.value = editor; // 記錄 editor 實(shí)例,重要!
};

function handleChange(editor: any) {
  emit('update:modelValue', editor.getHtml());
}

// 組件銷毀時(shí),也及時(shí)銷毀編輯器
onBeforeUnmount(() => {
  const editor = editorRef.value;
  if (editor == null) return;
  editor.destroy();
});
</script>

<style src="@wangeditor/editor/dist/css/style.css"></style>

使用案例

<!-- wangEditor富文本編輯器示例 -->
<script setup lang="ts">
import Editor from '@/components/WangEditor/index.vue';
const value = ref('初始內(nèi)容');
</script>

<template>
  <div class="app-container">
    <editor v-model="value" style="height: 600px" />
  </div>
</template>

效果預(yù)覽

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

Echarts 圖表

參考:?? Echarts 官方示例

安裝 Echarts

npm install echarts

組件封裝

<!-- src/views/dashboard/components/Chart/BarChart.vue --> 
<template>
  <el-card>
    <template #header> 線 + 柱混合圖 </template>
    <div :id="id" :class="className" :style="{ height, width }" />
  </el-card>
</template>

<script setup lang="ts">
import * as echarts from 'echarts';

const props = defineProps({
  id: {
    type: String,
    default: 'barChart'
  },
  className: {
    type: String,
    default: ''
  },
  width: {
    type: String,
    default: '200px',
    required: true
  },
  height: {
    type: String,
    default: '200px',
    required: true
  }
});

const options = {
  grid: {
    left: '2%',
    right: '2%',
    bottom: '10%',
    containLabel: true
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'cross',
      crossStyle: {
        color: '#999'
      }
    }
  },
  legend: {
    x: 'center',
    y: 'bottom',
    data: ['收入', '毛利潤(rùn)', '收入增長(zhǎng)率', '利潤(rùn)增長(zhǎng)率'],
    textStyle: {
      color: '#999'
    }
  },
  xAxis: [
    {
      type: 'category',
      data: ['浙江', '北京', '上海', '廣東', '深圳'],
      axisPointer: {
        type: 'shadow'
      }
    }
  ],
  yAxis: [
    {
      type: 'value',
      min: 0,
      max: 10000,
      interval: 2000,
      axisLabel: {
        formatter: '{value} '
      }
    },
    {
      type: 'value',
      min: 0,
      max: 100,
      interval: 20,
      axisLabel: {
        formatter: '{value}%'
      }
    }
  ],
  series: [
    {
      name: '收入',
      type: 'bar',
      data: [7000, 7100, 7200, 7300, 7400],
      barWidth: 20,
      itemStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          { offset: 0, color: '#83bff6' },
          { offset: 0.5, color: '#188df0' },
          { offset: 1, color: '#188df0' }
        ])
      }
    },
    {
      name: '毛利潤(rùn)',
      type: 'bar',
      data: [8000, 8200, 8400, 8600, 8800],
      barWidth: 20,
      itemStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          { offset: 0, color: '#25d73c' },
          { offset: 0.5, color: '#1bc23d' },
          { offset: 1, color: '#179e61' }
        ])
      }
    },
    {
      name: '收入增長(zhǎng)率',
      type: 'line',
      yAxisIndex: 1,
      data: [60, 65, 70, 75, 80],
      itemStyle: {
        color: '#67C23A'
      }
    },
    {
      name: '利潤(rùn)增長(zhǎng)率',
      type: 'line',
      yAxisIndex: 1,
      data: [70, 75, 80, 85, 90],
      itemStyle: {
        color: '#409EFF'
      }
    }
  ]
};

onMounted(() => {
  // 圖表初始化
  const chart = echarts.init(
    document.getElementById(props.id) as HTMLDivElement
  );
  chart.setOption(options);

  // 大小自適應(yīng)
  window.addEventListener('resize', () => {
    chart.resize();
  });
});
</script>

組件使用

<script setup lang="ts">
import BarChart from './components/BarChart.vue';
</script>

<template>
  <BarChart id="barChart" height="400px"width="300px" />
</template>

效果預(yù)覽

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

圖標(biāo)選擇器

組件封裝

<!-- src/components/IconSelect/index.vue -->
<script setup lang="ts">
const props = defineProps({
  modelValue: {
    type: String,
    require: false
  }
});

const emit = defineEmits(['update:modelValue']);
const inputValue = toRef(props, 'modelValue');

const visible = ref(false); // 彈窗顯示狀態(tài)

const iconNames: string[] = []; // 所有的圖標(biāo)名稱集合

const filterValue = ref(''); // 篩選的值
const filterIconNames = ref<string[]>([]); // 過濾后的圖標(biāo)名稱集合

const iconSelectorRef = ref(null);
/**
 * 加載 ICON
 */
function loadIcons() {
  const icons = import.meta.glob('../../assets/icons/*.svg');
  for (const icon in icons) {
    const iconName = icon.split('assets/icons/')[1].split('.svg')[0];
    iconNames.push(iconName);
  }
  filterIconNames.value = iconNames;
}

/**
 * 篩選圖標(biāo)
 */
function handleFilter() {
  if (filterValue.value) {
    filterIconNames.value = iconNames.filter(iconName =>
      iconName.includes(filterValue.value)
    );
  } else {
    filterIconNames.value = iconNames;
  }
}

/**
 * 選擇圖標(biāo)
 */
function handleSelect(iconName: string) {
  emit('update:modelValue', iconName);
  visible.value = false;
}

/**
 * 點(diǎn)擊容器外的區(qū)域關(guān)閉彈窗 VueUse onClickOutside
 */
onClickOutside(iconSelectorRef, () => (visible.value = false));

onMounted(() => {
  loadIcons();
});
</script>

<template>
  <div class="iconselect-container" ref="iconSelectorRef">
    <el-input
      v-model="inputValue"
      readonly
      @click="visible = !visible"
      placeholder="點(diǎn)擊選擇圖標(biāo)"
    >
      <template #prepend>
        <svg-icon :icon-class="inputValue" />
      </template>
    </el-input>

    <el-popover
      shadow="none"
      :visible="visible"
      placement="bottom-end"
      trigger="click"
      width="400"
    >
      <template #reference>
        <div
          @click="visible = !visible"
          class="cursor-pointer text-[#999] absolute right-[10px] top-0 height-[32px] leading-[32px]"
        >
          <i-ep-caret-top v-show="visible"></i-ep-caret-top>
          <i-ep-caret-bottom v-show="!visible"></i-ep-caret-bottom>
        </div>
      </template>

      <!-- 下拉選擇彈窗 -->
      <el-input
        class="p-2"
        v-model="filterValue"
        placeholder="搜索圖標(biāo)"
        clearable
        @input="handleFilter"
      />
      <el-divider border-style="dashed" />

      <el-scrollbar height="300px">
        <ul class="icon-list">
          <li
            class="icon-item"
            v-for="(iconName, index) in filterIconNames"
            :key="index"
            @click="handleSelect(iconName)"
          >
            <el-tooltip :content="iconName" placement="bottom" effect="light">
              <svg-icon
                color="var(--el-text-color-regular)"
                :icon-class="iconName"
              />
            </el-tooltip>
          </li>
        </ul>
      </el-scrollbar>
    </el-popover>
  </div>
</template>

組件使用

<!-- src/views/demo/IconSelect.vue -->
<script setup lang="ts">
const iconName = ref('edit');
</script>

<template>
  <div class="app-container">
    <icon-select v-model="iconName" />
  </div>
</template>

效果預(yù)覽

Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

規(guī)范配置

代碼統(tǒng)一規(guī)范

【vue3-element-admin】ESLint+Prettier+Stylelint+EditorConfig 約束和統(tǒng)一前端代碼規(guī)范

Git 提交規(guī)范

【vue3-element-admin】Husky + Lint-staged + Commitlint + Commitizen + cz-git 配置 Git 提交規(guī)范

啟動(dòng)部署

項(xiàng)目啟動(dòng)

# 安裝 pnpm
npm install pnpm -g

# 安裝依賴
pnpm install

# 項(xiàng)目運(yùn)行
pnpm run dev

項(xiàng)目部署

# 項(xiàng)目打包
pnpm run build:prod

生成的靜態(tài)文件在工程根目錄 dist 文件夾

FAQ

1: defineProps is not defined

  • 問題描述

    ‘defineProps’ is not defined.eslint no-undef

    Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

  • 解決方案

    根據(jù) Eslint 官方解決方案描述,解析器使用 vue-eslint-parser v9.0.0 + 版本

    Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

    安裝 vue-eslint-parser 解析器

    npm install -D vue-eslint-parser
    

    Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

    .eslintrc.js 關(guān)鍵配置( v9.0.0 及以上版本無需配置編譯宏 vue/setup-compiler-macros)如下 :

      parser: 'vue-eslint-parser',
      extends: [
        'eslint:recommended',
    	// ...		
      ],
    

    重啟 VSCode 已無報(bào)錯(cuò)提示

    Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

2: Vite 首屏加載慢(白屏久)

  • 問題描述

    Vite 項(xiàng)目啟動(dòng)很快,但首次打開界面加載慢?

    參考文章:為什么有人說 vite 快,有人卻說 vite 慢

    vite 啟動(dòng)時(shí),并不像 webpack 那樣做一個(gè)全量的打包構(gòu)建,所以啟動(dòng)速度非???。啟動(dòng)以后,瀏覽器發(fā)起請(qǐng)求時(shí), Dev Server 要把請(qǐng)求需要的資源發(fā)送給瀏覽器,中間需要經(jīng)歷預(yù)構(gòu)建、對(duì)請(qǐng)求文件做路徑解析、加載源文件、對(duì)源文件做轉(zhuǎn)換,然后才能把內(nèi)容返回給瀏覽器,這個(gè)時(shí)間耗時(shí)蠻久的,導(dǎo)致白屏?xí)r間較長(zhǎng)。

    解決方案升級(jí) vite 4.3 版本
    https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md
    Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源),# vue3-element-admin,前端框架,# Vue,vue,前端,開源

結(jié)語

本篇從項(xiàng)目介紹、環(huán)境準(zhǔn)備、VSCode 的代碼規(guī)范配置 、整合各種框架 、再到最后的啟動(dòng)部署,完整講述如何基于 Vue3 + Vite4 + TypeScript + Element Plus 等主流技術(shù)棧從 0 到 1構(gòu)建一個(gè)企業(yè)應(yīng)用級(jí)管理前端框架。

項(xiàng)目有問題建議 issue 或者可以通過項(xiàng)目 關(guān)于我們 加入交流群反饋。文章來源地址http://www.zghlxwxcb.cn/news/detail-697572.html

到了這里,關(guān)于Vue3.3 + Vite4.3 + TypeScript5+ Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 超級(jí)詳細(xì) 最新 vite4+vue3+ts+element-plus+eslint-prettier 項(xiàng)目搭建流程

    系列文章目錄 【element-plus】 table表格每行圓角解決方案 element也通用 【Vue3+Vite+Ts+element-plus】使用tsx實(shí)現(xiàn)左側(cè)欄菜單無限層級(jí)封裝 超級(jí)詳細(xì)GitBook和GitLab集成步驟【linux環(huán)境】 1.1、項(xiàng)目創(chuàng)建 執(zhí)行以下代碼將vite將會(huì)自動(dòng)生成初始的 vite4+vue3+ts的項(xiàng)目模板,pnpm、npm、yarn 選擇一種執(zhí)

    2024年02月04日
    瀏覽(23)
  • Vue3 + Vite + TypeScript + Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源)

    Vue3 + Vite + TypeScript + Element-Plus:從零到一構(gòu)建企業(yè)級(jí)后臺(tái)管理系統(tǒng)(前后端開源)

    vue3-element-admin 是基于 vue-element-admin 升級(jí)的 Vue3 + Element Plus 版本的后臺(tái)管理前端解決方案,技術(shù)棧為 Vue3 + Vite4 + TypeScript + Element Plus + Pinia + Vue Router 等當(dāng)前主流框架。 相較于其他管理前端框架,vue3-element-admin 的優(yōu)勢(shì)在于 一有一無 (有配套后端、無復(fù)雜封裝): 配套完整 Java 后

    2024年02月05日
    瀏覽(94)
  • 快速搭建Electron+Vite3+Vue3+TypeScript5腳手架 (無需梯子,快速安裝Electron)

    快速搭建Electron+Vite3+Vue3+TypeScript5腳手架 (無需梯子,快速安裝Electron)

    Electron是一個(gè)使用 JavaScript、HTML 和 CSS 構(gòu)建桌面應(yīng)用程序的框架。 嵌入?Chromium?和?Node.js?到 二進(jìn)制的 Electron 允許您保持一個(gè) JavaScript 代碼代碼庫(kù)并創(chuàng)建 在Windows上運(yùn)行的跨平臺(tái)應(yīng)用 macOS和Linux——不需要本地開發(fā)經(jīng)驗(yàn)(這段話是來自官網(wǎng))。 根據(jù)vite官網(wǎng)文檔 項(xiàng)目創(chuàng)建完成后進(jìn)

    2024年02月02日
    瀏覽(103)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(3) - vite配置

    Vite4+Typescript+Vue3+Pinia 從零搭建(3) - vite配置

    項(xiàng)目代碼同步至碼云 weiz-vue3-template 關(guān)于vite的詳細(xì)配置可查看 vite官方文檔,本文簡(jiǎn)單介紹vite的常用配置。 項(xiàng)目初建后, vite.config.ts 的默認(rèn)內(nèi)容如下: 比如,修改 App.vue : 根目錄下新建 .env 、 .env.development 、 .env.production 三個(gè)文件。 .env 文件內(nèi)新增內(nèi)容: .env.development 文件內(nèi)

    2024年02月05日
    瀏覽(81)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(4) - 代碼規(guī)范

    項(xiàng)目代碼同步至碼云 weiz-vue3-template 要求代碼規(guī)范,主要是為了提高多人協(xié)同和代碼維護(hù)效率,結(jié)合到此項(xiàng)目,具體工作就是為項(xiàng)目配置 eslint 和 prettier 。 安裝 EditorConfig for VS Code 插件,根目錄下新建 .editorconfig 文件,增加以下配置 如果是非windows系統(tǒng), end_of_line 設(shè)置為 cr 安

    2024年02月05日
    瀏覽(90)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(7) - request封裝

    Vite4+Typescript+Vue3+Pinia 從零搭建(7) - request封裝

    項(xiàng)目代碼同步至碼云 weiz-vue3-template 基于 axios 封裝請(qǐng)求,支持多域名請(qǐng)求地址 utils 目錄下新建 request 文件夾,并新建 index.ts 、 request.ts 和 status.ts 文件。 此時(shí),eslint會(huì)報(bào) switch 前面的空格錯(cuò)誤,需要修改 .eslintrc.cjs 里的 indent ,修改后,錯(cuò)誤消失。 src 目錄下新建 api 文件夾,

    2024年02月04日
    瀏覽(95)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(5) - 路由router

    Vite4+Typescript+Vue3+Pinia 從零搭建(5) - 路由router

    項(xiàng)目代碼同步至碼云 weiz-vue3-template Vue Router 是 Vue.js 的官方路由。它與 Vue.js 核心深度集成,讓用 Vue.js 構(gòu)建單頁(yè)應(yīng)用變得輕而易舉。 在 src/view 下新建 home.vue 和 login.vue ,內(nèi)容如下: login.vue 里修改下對(duì)應(yīng)name即可 index.ts 作為路由入口, static.ts 作為靜態(tài)路由, modules 內(nèi)還可以

    2024年02月05日
    瀏覽(97)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(2) - ts配置

    項(xiàng)目代碼同步至碼云 weiz-vue3-template 關(guān)于tsconfig的配置字段可查看其他文檔,如 typeScript tsconfig配置詳解 文件修改如下: 修改文件如下: 新建文件夾 types ,用來存放類型定義。比如新建 index.d.ts : 后續(xù)也可以新增其他文件,比如 global.d.ts 存放全局定義, router.d.ts 存放路由定

    2024年02月05日
    瀏覽(98)
  • Vite4+Typescript+Vue3+Pinia 從零搭建(6) - 狀態(tài)管理pina

    Vite4+Typescript+Vue3+Pinia 從零搭建(6) - 狀態(tài)管理pina

    項(xiàng)目代碼同步至碼云 weiz-vue3-template pina 是 vue3 官方推薦的狀態(tài)管理庫(kù),由 Vue 核心團(tuán)隊(duì)維護(hù),旨在替代 vuex。pina 的更多介紹,可從 pina官網(wǎng) 查看 更簡(jiǎn)潔直接的 API,提供組合式風(fēng)格的 API 支持模塊熱更新和服務(wù)端渲染 對(duì)TS支持更為友好 src目錄下新建store文件夾,并新建index.t

    2024年02月05日
    瀏覽(19)
  • rouyi-vue-pro+vue3+vite4+Element Plus項(xiàng)目中使用生成Vue2+Element UI標(biāo)準(zhǔn)模板

    rouyi-vue-pro+vue3+vite4+Element Plus項(xiàng)目中使用生成Vue2+Element UI標(biāo)準(zhǔn)模板

    運(yùn)行一個(gè)pro-vue3的前端項(xiàng)目,以及后端服務(wù) 在基礎(chǔ)設(shè)施-代碼生成模塊中選擇某張數(shù)據(jù)庫(kù)表導(dǎo)入,并編輯生成信息,前端類型:Vue2+Element UI標(biāo)準(zhǔn)模板 在vue3項(xiàng)目中創(chuàng)建一個(gè)vue文件 1.4 srcapitest.js Vue2+Element UI標(biāo)準(zhǔn)模板生成的前端封裝好的request請(qǐng)求接口對(duì)象 1.5 報(bào)錯(cuò) 問題 :在 更新

    2024年02月03日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包