必備UI組件
將用到的組件:
Menu 菜單
組件設計
新建src\components\baseline\menu\src\index.vue
<template>
<div>Menu</div>
</template>
<script lang="ts" setup>
import { PropType } from 'vue'
import { MenuItem } from './types'
const props = defineProps({
//說明:
data: {
required: true,
type: Array as PropType<MenuItem[]>,
},
})
console.log('data:', props.data)
</script>
<style lang="scss" scoped></style>
新建src\components\baseline\menu\src\types.ts
export interface MenuItem {
//導航菜單的圖標
icon?: string
//導航菜單的名字
name: string
//導航菜單的標識
code: string
//子菜單
children?: MenuItem[]
}
新建src\components\baseline\menu\index.ts
import { App } from 'vue'
import Menu from './src/index.vue'
export { Menu }
//組件可通過use的形式使用
export default {
Menu,
install(app: App) {
app.component('bs-menu', Menu)
},
}
修改src\components\baseline\index.ts
import { App } from 'vue'
import ChooseArea from './chooseArea'
import ChooseIcon from './chooseIcon'
import Container from './container'
import Trend from './trend'
import Notification from './notification'
import List from './list'
import Menu from './menu'
const components = [
ChooseArea,
ChooseIcon,
Container,
Trend,
Notification,
List,
Menu,
]
export { ChooseArea, ChooseIcon, Container, Trend, Notification, List, Menu }
//組件可通過use的形式使用
export default {
install(app: App) {
components.map(item => {
app.use(item)
})
},
ChooseArea,
ChooseIcon,
Container,
Trend,
Notification,
List,
Menu,
}
修改src\router\index.ts,新增路由參數(shù)
......
{
path: '/menu',
component: () =>
import('../views/baseline/menu/index.vue'),
},
......
新建src\views\baseline\menu\index.vue
<template>
<div><bs-menu :data="data"></bs-menu></div>
</template>
<script lang="ts" setup>
let data = [
{ name: '首頁', code: '1', icon: 'el-icon-document' },
{ name: '圖標選擇器', code: '2', icon: 'el-icon-document' },
{
name: '省市區(qū)選擇組件',
code: '3',
icon: 'el-icon-document',
children: [
{ name: '省市選擇組件', code: '3-1', icon: 'el-icon-document' },
{ name: '省市區(qū)村選擇組件', code: '3-2', icon: 'el-icon-document' },
],
},
]
</script>
<style lang="scss" scoped></style>
如下,可見數(shù)據(jù)已經(jīng)傳達到基礎組件:
完善組件
首先實現(xiàn)一級菜單术裸。
修改src\components\baseline\menu\src\index.vue
<template>
<div>
<el-menu>
<template v-for="(item, index) in data" :key="index">
<div>
<el-menu-item
v-if="!item.children || !item.children.length"
:index="item.code"
>
<component v-if="item.icon" :is="item.icon"></component>
<span>{{ item.name }}</span>
</el-menu-item>
</div>
</template>
</el-menu>
</div>
</template>
<script lang="ts" setup>
import { PropType } from 'vue'
import { MenuItem } from './types'
const props = defineProps({
//說明:
data: {
required: true,
type: Array as PropType<MenuItem[]>,
},
})
console.log('data:', props.data)
</script>
<style lang="scss" scoped></style>
修改src\views\baseline\menu\index.vue
<template>
<div style="width: 2rem"><bs-menu :data="data"></bs-menu></div>
</template>
<script lang="ts" setup>
let data = [
{ name: '首頁', code: '1', icon: 'el-icon-document' },
{ name: '圖標選擇器', code: '2', icon: 'el-icon-document' },
{
name: '省市區(qū)選擇組件',
code: '3',
icon: 'el-icon-document',
children: [
{ name: '省市選擇組件', code: '3-1', icon: 'el-icon-document' },
{ name: '省市區(qū)村選擇組件', code: '3-2', icon: 'el-icon-document' },
],
},
]
</script>
<style lang="scss" scoped></style>
效果如下:
接下來實現(xiàn)多級菜單
修改src\components\baseline\menu\src\types.ts
export interface MenuItem {
//導航菜單的圖標
icon?: string
//導航菜單的名字
name: string
//導航菜單的標識
index: string
//子菜單
children?: MenuItem[]
}
修改src\components\baseline\menu\src\index.vue
<template>
<div>
<el-menu
:default-active="defaultActive"
:router="router"
v-bind="$attrs"
>
<template v-for="(item, index) in data" :key="index">
<div>
<!-- 一級無二級菜單的菜單欄 -->
<el-menu-item
v-if="!item.children || !item.children.length"
:index="item.index"
>
<component v-if="item.icon" :is="item.icon"></component>
<span>{{ item.name }}</span>
</el-menu-item>
<el-sub-menu
v-if="item.children && item.children.length"
:index="item.index"
>
<template #title>
<component
v-if="item.icon"
:is="item.icon"
></component>
<span>{{ item.name }}</span>
</template>
<!-- 二級菜單欄 -->
<el-menu-item
v-for="(item2, index2) in item.children"
:index="item2.index"
>
<component
v-if="item2.icon"
:is="item2.icon"
></component>
<span>{{ item2.name }}</span>
</el-menu-item>
</el-sub-menu>
</div>
</template>
</el-menu>
</div>
</template>
<script lang="ts" setup>
import { PropType } from 'vue'
import { MenuItem } from './types'
const props = defineProps({
//說明:
data: {
required: true,
type: Array as PropType<MenuItem[]>,
},
// 默認選中的菜單
defaultActive: {
type: String,
default: '',
},
//是否是路由模式 router,
//是否啟用 vue-router 模式
//啟用該模式會在激活導航時以 index 作為 path 進行路由跳轉
router: {
type: Boolean,
default: false,
},
})
// console.log('data:', props.data)
</script>
<style lang="scss" scoped>
svg {
margin-right: 0.04rem;
}
</style>
修改src\views\baseline\menu\index.vue
<template>
<div style="width: 2rem">
<bs-menu :data="data" defaultActive="3-2"></bs-menu>
</div>
</template>
<script lang="ts" setup>
let data = [
{ name: '首頁', index: '1', icon: 'el-icon-document' },
{ name: '圖標選擇器', index: '2', icon: 'el-icon-document' },
{
name: '行政區(qū)域選擇組件',
index: '3',
icon: 'el-icon-document',
children: [
{ name: '省市選擇組件', index: '3-1', icon: 'el-icon-document' },
{ name: '省市區(qū)選擇組件', index: '3-2', icon: 'el-icon-document' },
{
name: '省市區(qū)村選擇組件',
index: '3-3',
icon: 'el-icon-document',
},
],
},
0,
]
</script>
<style lang="scss" scoped></style>
實現(xiàn)效果如下:
v-bind="$attrs"
:接受父組件傳入的數(shù)據(jù)和方法额嘿,并排除在組件的props中響應的參數(shù)铃在。
具體可以參考: vue中使用v-bind="$attrs"和v-on="$listeners"進行多層組件監(jiān)聽
TSX實現(xiàn)無限層級的導航菜單
首先需要安裝插件儿咱。
npm i -D @vitejs/plugin-vue-jsx
修改vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
//******************* Element自動導入 *******************
//目前語言包存在報錯颁独,無法自動導出打包翘单,暫時注釋
// import AutoImport from 'unplugin-auto-import/vite'
// import Components from 'unplugin-vue-components/vite'
// import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
//******************* Element自動導入 *******************
//******************* rollup 打包體積分析插件可視化工具 *******************
import { visualizer } from 'rollup-plugin-visualizer'
//******************* rollup 打包體積分析插件可視化工具 *******************
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
vueJsx(),
//******************* Element自動導入 *******************
// AutoImport({
// resolvers: [ElementPlusResolver()],
// }),
// Components({
// resolvers: [ElementPlusResolver()],
// }),
//******************* Element自動導入 *******************
//******************* 打包插件可視化工具 *******************
visualizer(),
//******************* 打包插件可視化工具 *******************
],
resolve: {
alias: {
'@': '/src',
'@style': '/src/style',
'@com': '/src/components',
'@baseline': '/src/components/baseline',
'@business': '/src/components/business',
},
},
server: {
port: 8080,
},
})
這里也順便提一下tsconfig.json的配置:
{
// 指定要編譯的路徑列表
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"compilerOptions": {
// target用于指定編譯之后的版本目錄
"target": "esnext",
"useDefineForClassFields": true,
//module用來指定要使用的模板標準
"module": "esnext",
// 用于選擇模塊解析策略梯皿,有"node"和"classic"兩種類型
"moduleResolution": "node",
"strict": true,
// 指定jsx代碼用于的開發(fā)環(huán)境:'preserve','react-native',or 'react
"jsx": "preserve",
// 指定是否將map文件內(nèi)容和js文件編譯在一個同一個js文件中
// 如果設為true,則map的內(nèi)容會以//#soureMappingURL=開頭,然后接base64字符串的形式插入在js文件底部
"sourceMap": true,
"skipLibCheck": true,
"resolveJsonModule": true,
//通過導入內(nèi)容創(chuàng)建命名空間县恕,實現(xiàn)CommonJS和ES模塊之間的互操作性
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
// 指定是否引入tslib里的復制工具函數(shù)东羹,默認為false
"importHelpers": true,
// 指定是否將每個文件作為單獨的模塊,默認為true忠烛,他不可以和declaration同時設定
"isolatedModules": true,
// removeComments用于指定是否將編譯后的文件注釋刪掉属提,設為true的話即刪除注釋,默認為false
"removeComments": true,
// lib用于指定要包含在編譯中的庫文件
"lib": ["esnext", "dom"],
//指定全局組件類型:element:"element-plus/global"
"types": ["vite/client", "element-plus/global"],
// ++ 這里加上baseUrl 和 path即可 ++
// 用于設置解析非相對模塊名稱的基本目錄美尸,相對模塊不會受到baseUrl的影響
"baseUrl": "./",
//用于設置模塊名到基于baseUrl的路徑映射
"paths": {
// 根據(jù)別名配置相關路徑
"@/*": ["./src/*"],
"@style/*": ["./src/style/*"],
"@com/*": ["./src/components/*"],
"@baseline/*": ["./src/components/baseline/*"],
"@business/*": ["./src/components/business/*"]
}
//****************** 未配置選項 ******************
// 用來指定是否允許編譯JS文件冤议,默認false,即不編譯JS文件
// "allowJs": false,
// 用來指定是否在編譯的時候生成相的d.ts聲明文件
// 如果設為true,編譯每個ts文件之后會生成一個js文件和一個聲明文件,但是declaration和allowJs不能同時設為true
// "declaration": true,
// 用來指定編譯時是否生成.map文件
// "declarationMap": true,
// 用來指定輸出文件夾师坎,值為一個文件夾路徑字符串恕酸,輸出的文件都將放置在這個文件夾
// "outDir": "./",
// 用來指定輸出文件夾,值為一個文件夾路徑字符串胯陋,輸出的文件都將放置在這個文件夾
// "outDir": "./",
// 用于指定輸出文件合并為一個文件
//只有設置module的值為amd和system模塊時才支持這個配置
// "outFile": "./",
// 是否編譯構建引用項目
// "composite": true,
// 不生成編譯文件
// "noEmit": true,
// 當target為"ES5"或"ES3"時蕊温,為"for-of" "spread"和"destructuring"中的迭代器提供完全支持
// "downlevelIteration": true,
// 用于指定是否啟動所有類型檢查袱箱,如果設為true這回同時開啟下面這幾個嚴格檢查,默認為false
// "strict": true,
// 如果我們沒有一些值設置明確類型义矛,編譯器會默認認為這個值為any類型发笔,如果將noImplicitAny設為true,則如果沒有設置明確的類型會報錯,默認值為false
// "noImplicitAny": true,
// 當設為true時凉翻,null和undefined值不能賦值給非這兩種類型的值了讨,別的類型的值也不能賦給他們,除了any類型制轰,還有個例外就是undefined可以賦值給void類型
// "strictNullChecks": true,
// 用來指定是否使用函數(shù)參數(shù)雙向協(xié)變檢查
// "strictFunctionTypes": true,
// 設為true后對bind前计、call和apply綁定的方法的參數(shù)的檢測是嚴格檢測
// "strictBindCallApply": true,
// 設為true后會檢查類的非undefined屬性是否已經(jīng)在構造函數(shù)里初始化,如果要開啟這項垃杖,需要同時開啟strictNullChecks,默認為false
// "strictPropertyInitialization": true,
// 當this表達式的值為any類型的時候残炮,生成一個錯誤
// "noImplicitThis": true,
// alwaysStrict指定始終以嚴格模式檢查每個模塊,并且在編譯之后的JS文件中加入"use strict"字符串缩滨,用來告訴瀏覽器該JS為嚴格模式
// "alwaysStrict": true,
// 用于檢查是否有定義了但是沒有使用變量势就,對于這一點的檢測,使用ESLint可以在你書寫代碼的時候做提示脉漏,你可以配合使用苞冯,他的默認值為false
// "noUnusedLocals": true,
// 用于檢測是否在函數(shù)中沒有使用的參數(shù)
// "noUnusedParameters": true,
// 用于檢查函數(shù)是否有返回值,設為true后侧巨,如果函數(shù)沒有返回值則會提示舅锄,默認為false
// "noImplicitReturns": true,
// 用于檢查switch中是否有case沒有使用break跳出switch,默認為false
// "noFallthroughCasesInSwitch": true,
// 可以指定一個路徑列表,在構建時編譯器會將這個路徑中的內(nèi)容都放到一個文件夾中
// "rootDirs": [],
// 用來指定聲明文件或文件夾的路徑列表司忱,如果指定了此項皇忿,則只有在這里列出的聲明文件才會被加載
// "typeRoots": [],
// types用于指定需要包含的模塊,只有在這里列出的模塊的聲明文件才會被加載
// "types": [],
// 用來指定允許從沒有默認導出的模塊中默認導入
// "allowSyntheticDefaultImports": true,
// 不把符號鏈接解析為真實路徑坦仍,具體可以了解下webpack和node.js的symlink相關知識
// "preserveSymlinks": true,
// 用于指定調(diào)試器應該找到TypeScript文件而不是源文件的位置鳍烁,這個值會被寫進.map文件里
// "sourceRoot": "",
// 用于指定調(diào)試器找到映射文件而非生成文件的位置,指定map文件的根路徑
// 該選項會影響.map文件中的sources屬性
// "mapRoot": "",
// s用于指定是否進一步將ts文件的內(nèi)容也包含到輸出文件中
// "inlineSources": true,
// 用于指定是否啟用實驗性的裝飾器特性
// "experimentalDecorators": true,
// 用于指定是否為裝上去提供元數(shù)據(jù)支持
// 關于元數(shù)據(jù)繁扎,也是ES6的新標準幔荒,可以通過Reflect提供的靜態(tài)方法獲取元數(shù)據(jù)
// 如果需要使用Reflect的一些方法,需要引用ES2015.Reflect這個庫
// "emitDecoratorMetadata": true,
// 可以配置一個數(shù)組列表
// "files":[],
// exclude表示要排除的梳玫,不編譯的文件
// "exclude":[]
// include也可以指定要編譯的路徑列表
// "include":[],
//它也可以指定一個列表爹梁,規(guī)則和include一樣,可以是文件可以是文件夾提澎,可以是相對路徑或絕對路徑姚垃,可以使用通配符
// "exclude":[]
// 可以通過指定一個其他的tsconfig.json文件路徑,來繼承這個配置文件里的配置盼忌,繼承來的文件的配置會覆蓋當前文件定義的配置
// "extends":""
// 如果設為true,在我們編輯了項目文件保存的時候积糯,編輯器會根據(jù)tsconfig.json的配置更新重新生成文本掂墓,不過這個編輯器支持
// "compileOnSave":true
// 一個對象數(shù)組,指定要引用的項目
// "references":[]
//****************** 未配置選項 ******************
}
}
新建src\components\baseline\menu\src\menu.tsx
import { defineComponent, PropType } from 'vue'
import { MenuItem } from './types'
export default defineComponent({
name: 'infiniteMenu',
props: {
//說明:
data: {
required: true,
type: Array as PropType<MenuItem[]>,
},
// 默認選中的菜單
defaultActive: {
type: String,
default: '',
},
//是否是路由模式 router,
//是否啟用 vue-router 模式
//啟用該模式會在激活導航時以 index 作為 path 進行路由跳轉
router: {
type: Boolean,
default: false,
},
},
setup(props, ctx) {
return () => {
return <div>menus</div>
}
},
})
修改src\views\baseline\menu\index.vue
<template>
<div style="width: 2rem">
<!-- <bs-menu :data="data" defaultActive="3-2"></bs-menu> -->
<bs-infinite-menu :data="data2"></bs-infinite-menu>
</div>
</template>
<script lang="ts" setup>
let data = [
{ name: '首頁', index: '1', icon: 'el-icon-document' },
{ name: '圖標選擇器', index: '2', icon: 'el-icon-document' },
{
name: '行政區(qū)域選擇組件',
index: '3',
icon: 'el-icon-document',
children: [
{ name: '省市選擇組件', index: '3-1', icon: 'el-icon-document' },
{ name: '省市區(qū)選擇組件', index: '3-2', icon: 'el-icon-document' },
{
name: '省市區(qū)村選擇組件',
index: '3-3',
icon: 'el-icon-document',
},
],
},
0,
]
let data2 = [
{ name: '首頁', index: '1', icon: 'el-icon-document' },
{ name: '圖標選擇器', index: '2', icon: 'el-icon-document' },
{
name: '行政區(qū)域選擇組件',
index: '3',
icon: 'el-icon-document',
children: [
{
name: '省市選擇組件',
index: '3-1',
icon: 'el-icon-document',
children: [
{
name: '組件演示',
index: '3-1-1',
icon: 'el-icon-document',
},
],
},
{ name: '省市區(qū)選擇組件', index: '3-2', icon: 'el-icon-document' },
{
name: '省市區(qū)村選擇組件',
index: '3-3',
icon: 'el-icon-document',
},
],
},
0,
]
</script>
<style lang="scss" scoped></style>