在前几期中,我们已经深入探讨了 vue-element-plus-admin 项目的架构设计、工程化实践、状态管理、路由权限、HTTP 请求、组件设计和国际化等方面的核心功能。本期将重点关注一些高级功能和实战技巧,这些功能和技巧可以帮助开发者构建更加复杂、高效和友好的中后台应用。
1. 复杂表单设计与处理
中后台应用中,表单是最常见也是最复杂的交互界面之一。vue-element-plus-admin 提供了强大的表单设计能力,能够应对各种复杂场景。
1.1 Schema 驱动的表单设计
项目采用了 Schema 驱动的表单设计模式,通过 JSON 配置生成表单,大大简化了复杂表单的开发工作。这种方式的核心在于 src/components/Form 组件和 useForm Hook。
// src/views/Components/Form/UseFormDemo.vue
const schema = reactive<FormSchema[]>([
{
field: 'field1',
label: t('formDemo.input'),
component: 'Input',
formItemProps: {
rules: [required()]
}
},
{
field: 'field2',
label: t('formDemo.select'),
component: 'Select',
componentProps: {
options: [
{ label: 'option1', value: '1' },
{ label: 'option2', value: '2' }
]
}
},
// ... 其他表单项
])
使用 useForm Hook 可以方便地操作表单:
const { register, methods } = useForm()
const { setProps, setValues, getFormData, validate } = methods
// 设置表单属性
setProps({
labelWidth: '120px',
schemas: schema
})
// 设置表单值
setValues({
field1: 'value1',
field2: '1'
})
// 表单验证和获取数据
const submit = async () => {
const valid = await validate()
if (valid) {
const data = await getFormData()
console.log(data)
}
}
1.2 动态表单与条件渲染
vue-element-plus-admin 支持动态表单配置,可以根据条件显示/隐藏表单项、动态修改校验规则、动态加载选项等。
// 动态显示/隐藏表单项
const schema = reactive<FormSchema[]>([
{
field: 'useCustomRule',
label: '使用自定义规则',
component: 'Switch',
value: false
},
{
field: 'customRule',
label: '自定义规则',
component: 'Input',
// 根据其他字段值决定是否显示
ifShow: ({ values }) => values.useCustomRule === true,
formItemProps: {
rules: [required()]
}
}
])
// 动态加载选项
{
field: 'remoteSelect',
label: '远程选择',
component: 'Select',
// 远程加载选项数据
optionApi: async () => {
const res = await getRemoteData()
return res.map(item => ({
label: item.name,
value: item.id
}))
}
}
1.3 表单验证策略
项目提供了灵活的表单验证策略,包括内置校验规则、自定义校验函数和动态校验规则:
// src/hooks/web/useValidator.ts
export const useValidator = () => {
const required = (message?: string) => {
return {
required: true,
message: message || t('common.required')
}
}
// 长度范围校验
const lengthRange = (min: number, max: number) => {
return {
min,
max,
message: t('common.lengthRange', { min, max }),
trigger: 'blur'
}
}
// 自定义校验函数
const passwordMatch = (password: string, callback: Function) => {
return (rule: unknown, value: string, cb: Function) => {
if (value !== password) {
cb(new Error(t('common.isEqual')))
} else {
cb()
}
}
}
return {
required,
lengthRange,
passwordMatch
}
}
2. 大数据表格优化
表格是中后台应用中最常用的数据展示方式,vue-element-plus-admin 提供了多种表格优化策略,以应对大量数据展示的需求。
2.1 虚拟滚动与懒加载
处理大数据表格时,虚拟滚动是一种有效的优化手段。虽然项目本身没有直接实现虚拟滚动表格,但它集成了 Element Plus 的 el-table 和 el-select-v2 等具备虚拟滚动能力的组件:
// 使用带虚拟滚动的选择器处理大量选项
{
field: 'largeData',
label: '大数据选择',
component: 'SelectV2',
componentProps: {
options: largeDataOptions, // 大量数据
filterable: true,
style: { width: '100%' }
}
}
对于表格数据,采用分页加载的策略:
// src/views/Components/Table/DefaultTable.vue
const getTableList = async (params?: Params) => {
const res = await getTableListApi(
params || {
pageIndex: 1,
pageSize: 10
}
)
.catch(() => {})
.finally(() => {
loading.value = false
})
if (res) {
tableDataList.value = res.data.list
}
}
2.2 表格列配置与动态渲染
vue-element-plus-admin 提供了强大的表格列配置功能,支持动态列、自定义渲染、条件格式化等:
const columns: TableColumn[] = [
{
field: 'title',
label: t('tableDemo.title')
},
{
field: 'importance',
label: t('tableDemo.importance'),
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
return h(
ElTag,
{
type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
},
() =>
cellValue === 1
? t('tableDemo.important')
: cellValue === 2
? t('tableDemo.good')
: t('tableDemo.commonly')
)
}
},
{
field: 'action',
label: t('tableDemo.action'),
slots: {
default: (data) => {
return (
<BaseButton type="primary" onClick={() => actionFn(data)}>
{t('tableDemo.action')}
</BaseButton>
)
}
}
}
]
2.3 多种表格样式与交互模式
项目实现了多种表格样式和交互模式,如卡片式表格、可展开行、树形表格等:
<!-- src/views/Components/Table/CardTable.vue -->
<template>
<ContentWrap :title="t('tableDemo.cardTable')">
<div class="card-container">
<div v-for="(item, index) in tableDataList" :key="index" class="card-item">
<!-- 卡片式表格内容 -->
<div class="card-header">{{ item.title }}</div>
<div class="card-content">
<span>{{ t('tableDemo.author') }}: {{ item.author }}</span>
<span>{{ t('tableDemo.displayTime') }}: {{ item.display_time }}</span>
</div>
<div class="card-footer">
<BaseButton type="primary" size="small">
{{ t('tableDemo.action') }}
</BaseButton>
</div>
</div>
</div>
</ContentWrap>
</template>
3. 图表与可视化实现
数据可视化是中后台应用的重要部分,vue-element-plus-admin 集成了 ECharts 图表库,提供了丰富的图表展示能力。
3.1 图表组件封装与使用
项目对 ECharts 进行了封装,提供了易用的 Echart 组件:
<!-- src/views/Components/Echart.vue -->
<template>
<ContentWrap :title="t('echartDemo.echart')" :message="t('echartDemo.echartDes')">
<ElRow :gutter="20" justify="space-between">
<ElCol :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
<ElCard shadow="hover" class="mb-20px">
<Echart :options="pieOptions" :height="300" />
</ElCard>
</ElCol>
<ElCol :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
<ElCard shadow="hover" class="mb-20px">
<Echart :options="barOptions" :height="300" />
</ElCard>
</ElCol>
<ElCol :span="24">
<ElCard shadow="hover" class="mb-20px">
<Echart :options="lineOptions" :height="350" />
</ElCard>
</ElCol>
</ElRow>
</ContentWrap>
</template>
3.2 常见图表配置与实践
项目内置了多种常用图表配置,包括折线图、柱状图、饼图和词云等:
// src/views/Dashboard/echarts-data.ts
export const lineOptions: EChartsOption = {
title: {
text: t('analysis.monthlySales'),
left: 'center'
},
xAxis: {
data: [
t('analysis.january'),
t('analysis.february'),
// ... 其他月份
],
boundaryGap: false,
axisTick: {
show: false
}
},
// ... 其他配置
series: [
{
name: t('analysis.estimate'),
smooth: true,
type: 'line',
data: [100, 120, 161, 134, 105, 160, 165, 114, 163, 185, 118, 123],
animationDuration: 2800,
animationEasing: 'cubicInOut'
},
{
name: t('analysis.actual'),
smooth: true,
type: 'line',
itemStyle: {},
data: [120, 82, 91, 154, 162, 140, 145, 250, 134, 56, 99, 123],
animationDuration: 2800,
animationEasing: 'quadraticOut'
}
]
}
3.3 响应式图表与数据联动
图表组件支持响应式布局和数据联动,能够自适应容器大小变化:
// src/components/Echart/src/Echart.vue
import { useEventListener, useElementSize } from '@vueuse/core'
// 监听容器大小变化
const chartEl = ref<HTMLDivElement>()
const { width, height } = useElementSize(chartEl)
// 图表实例
const chartInstance = shallowRef<echarts.ECharts>()
// 监听容器尺寸变化,自动调整图表大小
watch([width, height], () => {
chartInstance.value?.resize()
})
// 数据更新时重新渲染图表
watch(
() => props.options,
(newOptions) => {
chartInstance.value?.setOption(newOptions)
},
{ deep: true }
)
4. 文件上传与处理
文件上传是中后台应用中常见的功能,vue-element-plus-admin 提供了多种文件上传解决方案。
4.1 基础文件上传组件
项目集成了 Element Plus 的上传组件,支持多种上传方式:
<template>
<Form @register="register">
<!-- 基础文件上传 -->
<Upload
v-model:file-list="fileList"
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
multiple
:limit="3"
:on-exceed="handleExceed"
>
<BaseButton type="primary">Click to upload</BaseButton>
<template #tip>
<div class="upload-tip">jpg/png files with a size less than 500KB</div>
</template>
</Upload>
</Form>
</template>
4.2 图片裁剪与预览
项目实现了图片裁剪功能,基于 cropper.js 封装的 ImageCropping 组件:
<!-- src/views/Components/ImageCropping.vue -->
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { ImageCropping } from '@/components/ImageCropping'
import { ref, unref } from 'vue'
import { ElInput } from 'element-plus'
const cropperExpose = ref<InstanceType<typeof ImageCropping>>()
const base64 = ref('')
const getBase64 = () => {
base64.value = unref(cropperExpose)?.cropperExpose?.getCroppedCanvas()?.toDataURL() ?? ''
}
</script>
<template>
<ContentWrap title="图片裁剪">
<BaseButton type="primary" class="mb-20px" @click="getBase64">裁剪</BaseButton>
<ElInput v-model="base64" class="mb-20px" type="textarea" />
<ImageCropping
ref="cropperExpose"
image-url="https://hips.hearstapps.com/hmg-prod/images/宋智孝-1597774015.jpg"
/>
</ContentWrap>
</template>
4.3 头像上传与个人中心
项目实现了头像上传功能,应用于个人中心模块:
<!-- src/views/Personal/PersonalCenter/components/UploadAvatar.vue -->
<script setup lang="ts">
import { ImageCropping } from '@/components/ImageCropping'
import { ref, unref } from 'vue'
defineProps({
url: {
type: String,
default: ''
}
})
const fileUrl = ref('')
const CropperRef = ref<ComponentRef<typeof ImageCropping>>()
const getBase64 = () => {
const base64 = unref(CropperRef)?.cropperExpose?.getCroppedCanvas()?.toDataURL() ?? ''
return base64
}
defineExpose({
getBase64
})
</script>
<template>
<div>
<ImageCropping ref="CropperRef" :image-url="fileUrl || url" />
</div>
</template>
5. 性能监控与用户体验优化
vue-element-plus-admin 实现了多种性能优化和用户体验提升策略。
5.1 路由过渡与加载状态
项目使用路由过渡效果和页面加载状态提升用户体验:
// src/hooks/web/usePageLoading.ts
export const usePageLoading = () => {
const loadStart = () => {
const appStore = useAppStoreWithOut()
appStore.setPageLoading(true)
}
const loadDone = () => {
const appStore = useAppStoreWithOut()
appStore.setPageLoading(false)
}
return {
loadStart,
loadDone
}
}
// 在路由守卫中使用
router.beforeEach(() => {
loadStart()
})
router.afterEach(() => {
loadDone()
})
5.2 数据缓存与性能优化
项目通过数据缓存策略提高性能,减少不必要的请求:
// 缓存用户信息
export const useUserStore = defineStore('user', {
// ...
persist: {
paths: ['token']
}
})
// 组件级别缓存
<keep-alive :include="cachedViews">
<component :is="Component" v-if="cachedViews.includes(name)" />
</keep-alive>
5.3 异常处理与错误边界
项目实现了全局异常处理和错误边界组件,提高应用的健壮性:
<!-- src/components/Error/src/Error.vue -->
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps({
error: {
type: Error,
default: null
},
title: {
type: String,
default: '页面出错了'
}
})
const errorDetail = ref(props.error?.message || '未知错误')
</script>
<template>
<div class="error-boundary">
<h2>{{ title }}</h2>
<p>{{ errorDetail }}</p>
<slot name="footer">
<BaseButton @click="() => window.location.reload()">重新加载</BaseButton>
</slot>
</div>
</template>
6. Docker 部署与生产环境配置
vue-element-plus-admin 提供了 Docker 部署支持,简化了应用部署流程。
6.1 Docker 配置文件
项目包含了开发和生产环境的 Docker 配置文件:
# Dockerfile.dev
FROM node:18.0.0
WORKDIR /app
RUN npm install -g pnpm@8.1.0
COPY package.json .
RUN pnpm install
COPY . .
CMD [ "pnpm", "run", "dev" ]
6.2 Docker Compose 配置
项目使用 Docker Compose 管理容器:
# docker-compose.dev.yaml
services:
vue-element-plus-admin:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "4000:4000"
volumes:
- /app/node_modules
- .:/app
6.3 多环境部署策略
项目支持多环境部署,通过环境变量区分不同环境:
// vite.config.ts
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
base: env.VITE_BASE_PATH,
plugins: [
// ...
],
build: {
target: 'es2015',
outDir: env.VITE_OUT_DIR || 'dist',
// 启用/禁用 CSS 代码拆分
cssCodeSplit: true,
sourcemap: env.VITE_SOURCEMAP === 'true'
}
}
})
7. 实战技巧与最佳实践
vue-element-plus-admin 项目中蕴含了大量实战技巧和最佳实践,值得开发者学习和应用。
7.1 Schema 驱动开发模式
项目大量使用 Schema 驱动的开发模式,通过配置生成UI界面,这种模式的优势在于:
- 降低重复代码量,提高开发效率
- 实现界面与逻辑分离,提高可维护性
- 支持动态配置,增强系统灵活性
// 使用 useCrudSchemas 统一管理列表、表单、详情配置
export const useCrudSchemas = (
crudSchema: CrudSchema[]
): {
allSchemas: AllSchemas
} => {
const allSchemas = reactive<AllSchemas>({
searchSchema: [], // 搜索表单配置
tableColumns: [], // 表格列配置
formSchema: [], // 编辑表单配置
detailSchema: [] // 详情展示配置
})
// 从同一个配置源生成不同场景的配置
const searchSchema = filterSearchSchema(crudSchema)
allSchemas.searchSchema = searchSchema || []
const tableColumns = filterTableSchema(crudSchema)
allSchemas.tableColumns = tableColumns || []
const formSchema = filterFormSchema(crudSchema)
allSchemas.formSchema = formSchema
const detailSchema = filterDescriptionsSchema(crudSchema)
allSchemas.detailSchema = detailSchema
return { allSchemas }
}
7.2 组合式 API 的最佳实践
项目充分利用 Vue 3 的组合式 API,实现了更清晰、可复用的代码结构:
// 可复用的表单处理逻辑
export const useForm = () => {
const formRef = ref<typeof Form & FormExpose>()
const elFormRef = ref<ComponentRef<typeof ElForm>>()
// 注册表单实例
const register = (ref: typeof Form & FormExpose, elRef: ComponentRef<typeof ElForm>) => {
formRef.value = ref
elFormRef.value = elRef
}
// 获取表单实例
const getForm = async () => {
await nextTick()
const form = unref(formRef)
if (!form) {
console.error('The form is not registered. Please use the register method to register')
}
return form
}
// 封装表单方法
const methods = {
setProps: async (props: FormProps = {}) => { /* ... */ },
setValues: async (data: Recordable) => { /* ... */ },
validate: async (): Promise<boolean> => { /* ... */ },
resetFields: async () => { /* ... */ }
}
return { register, methods }
}
7.3 开发效率提升技巧
项目中包含了许多提升开发效率的技巧:
- 类型安全开发:充分利用 TypeScript 的类型系统,减少运行时错误
// 定义严格的接口类型
interface TableColumn extends Partial<Omit<ElTableColumn, 'children' | 'renderHeader' | 'renderCell'>> {
// 自定义字段
field: string
label?: string
children?: TableColumn[]
// ... 其他属性
}
- 灵活的配置中心:通过集中管理配置,实现系统行为的统一调整
// 通过配置控制系统行为
export const useAppStore = defineStore('app', {
state: (): AppState => {
return {
// 布局配置
layout: 'classic',
// 主题配置
theme: {
elColorPrimary: '#409eff',
// ... 其他主题变量
},
// 功能开关
tagsView: true,
fixedHeader: true,
// ... 其他配置
}
}
})
- 按需加载:使用异步组件和路由懒加载,优化初始加载性能
// 路由懒加载
{
path: '/dashboard',
component: () => import('@/views/Dashboard/index.vue'),
meta: {
title: 'Dashboard',
icon: 'dashboard'
}
}
8. 总结
vue-element-plus-admin 项目通过一系列高级功能和实战技巧,提供了构建企业级中后台应用的全面解决方案:
-
复杂表单处理:通过 Schema 驱动的表单设计、动态表单配置和灵活的验证策略,满足复杂的业务需求。
-
大数据表格优化:采用虚拟滚动、分页加载、表格列配置等技术,提高大数据量表格的性能和用户体验。
-
数据可视化:集成 ECharts,封装响应式图表组件,支持多种图表类型和数据联动。
-
文件上传与处理:提供文件上传、图片裁剪和预览等功能,满足各种媒体处理需求。
-
性能监控与优化:通过路由过渡、数据缓存和异常处理,提升应用性能和用户体验。
-
容器化部署:提供 Docker 配置和多环境部署策略,简化应用部署流程。
-
开发效率提升:通过 Schema 驱动开发、组合式 API 和类型安全开发等最佳实践,提高开发效率和代码质量。
通过学习和应用这些高级功能和实战技巧,开发者可以更高效地构建高质量的中后台应用,提供更好的用户体验。