vue-element-plus-admin 第8期|高级功能与实战技巧

514 阅读9分钟

在前几期中,我们已经深入探讨了 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-tableel-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 开发效率提升技巧

项目中包含了许多提升开发效率的技巧:

  1. 类型安全开发:充分利用 TypeScript 的类型系统,减少运行时错误
// 定义严格的接口类型
interface TableColumn extends Partial<Omit<ElTableColumn, 'children' | 'renderHeader' | 'renderCell'>> {
  // 自定义字段
  field: string
  label?: string
  children?: TableColumn[]
  // ... 其他属性
}
  1. 灵活的配置中心:通过集中管理配置,实现系统行为的统一调整
// 通过配置控制系统行为
export const useAppStore = defineStore('app', {
  state: (): AppState => {
    return {
      // 布局配置
      layout: 'classic',
      // 主题配置
      theme: {
        elColorPrimary: '#409eff',
        // ... 其他主题变量
      },
      // 功能开关
      tagsView: true,
      fixedHeader: true,
      // ... 其他配置
    }
  }
})
  1. 按需加载:使用异步组件和路由懒加载,优化初始加载性能
// 路由懒加载
{
  path: '/dashboard',
  component: () => import('@/views/Dashboard/index.vue'),
  meta: {
    title: 'Dashboard',
    icon: 'dashboard'
  }
}

8. 总结

vue-element-plus-admin 项目通过一系列高级功能和实战技巧,提供了构建企业级中后台应用的全面解决方案:

  1. 复杂表单处理:通过 Schema 驱动的表单设计、动态表单配置和灵活的验证策略,满足复杂的业务需求。

  2. 大数据表格优化:采用虚拟滚动、分页加载、表格列配置等技术,提高大数据量表格的性能和用户体验。

  3. 数据可视化:集成 ECharts,封装响应式图表组件,支持多种图表类型和数据联动。

  4. 文件上传与处理:提供文件上传、图片裁剪和预览等功能,满足各种媒体处理需求。

  5. 性能监控与优化:通过路由过渡、数据缓存和异常处理,提升应用性能和用户体验。

  6. 容器化部署:提供 Docker 配置和多环境部署策略,简化应用部署流程。

  7. 开发效率提升:通过 Schema 驱动开发、组合式 API 和类型安全开发等最佳实践,提高开发效率和代码质量。

通过学习和应用这些高级功能和实战技巧,开发者可以更高效地构建高质量的中后台应用,提供更好的用户体验。