Skip to content

VJSP Vue3 Framework 主文档

项目概述

VJSP Vue3 Framework 是一个基于 Vue 3.5 + TypeScript 5.7 + Element Plus 的企业级中后台前端解决方案。框架提供了一套完整的开发工具和组件库,帮助开发者快速构建现代化的 Web 应用。

技术栈架构

基于项目的实际配置,框架采用以下核心技术栈:

  • 前端框架: Vue 3.5.13 + Composition API
  • 构建工具: Vite 6.0.7
  • 类型系统: TypeScript 5.7.3
  • UI 组件库: Element Plus 2.11.7
  • 状态管理: Pinia 3.0.0 + 持久化插件
  • 路由管理: Vue Router 4.5.0
  • 国际化: Vue I18n 11.0.1
  • HTTP 客户端: Axios 1.12.0

项目结构

项目采用模块化架构设计,主要目录结构如下:

shell
src/
├── api/                 # API 接口层
   ├── login/          # 登录相关接口
   ├── user/           # 用户管理接口
   └── system/         # 系统管理接口
├── components/         # 组件层
   ├── SvgIcon/        # SVG 图标组件
   └── dict/           # 字典组件
├── stores/             # 状态管理
   ├── app.ts          # 应用状态
   ├── user.ts         # 用户状态
   └── modules/        # 模块化状态
├── utils/              # 工具函数
   ├── cache.ts        # 缓存管理
   ├── encryption.ts   # 加密工具
   └── tree.ts         # 树形数据处理
├── views/              # 页面组件
   ├── system/         # 系统管理页面
   └── dashboard/      # 仪表板页面
└── main.ts            # 应用入口

核心功能机制

1. 应用启动机制

框架采用异步启动机制,确保所有依赖正确初始化:

typescript
// src/main.ts 中的启动流程
async function bootstrap() {
  const app = createApp(App)

  // 1. 注册 Element Plus 图标
  for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
  }

  // 2. 初始化 Pinia 状态管理
  app.use(createPinia())

  // 3. 初始化国际化
  await setupI18n(app)

  // 4. 配置 Element Plus
  app.use(ElementPlus, {
    locale: currentLocale.elLocale,
  })

  // 5. 设置路由(等待动态路由加载)
  await setupRouter(app)

  app.mount('#app')
}

2. 权限管理机制

框架实现基于角色的权限控制系统:

  • 菜单权限: 动态路由过滤,只显示有权限的菜单
  • 按钮权限: 通过自定义指令控制按钮显示
  • 数据权限: 基于用户角色过滤数据访问范围

权限验证通过路由守卫和自定义指令实现:

typescript
// 路由守卫权限检查
router.beforeEach(async (to, from, next) => {
  // 检查用户登录状态
  if (to.meta.requiresAuth && !isAuthenticated()) {
    next('/login')
    return
  }

  // 检查路由权限
  if (to.meta.permission && !hasPermission(to.meta.permission)) {
    next('/403')
    return
  }

  next()
})

3. 国际化机制

框架支持多语言切换,基于 vue-i18n 实现:

  • 语言包管理: 按模块组织语言包文件
  • 动态切换: 支持运行时语言切换
  • 组件集成: Element Plus 组件自动适配当前语言

语言配置通过独立的插件模块管理:

typescript
// src/plugins/vueI18n 中的配置
export async function setupI18n(app: App) {
  const options = await createI18nOptions()
  const i18n = createI18n(options)
  app.use(i18n)
}

4. 状态管理机制

采用 Pinia 进行状态管理,支持模块化组织和持久化:

  • 模块化设计: 按功能模块划分 Store
  • 类型安全: 完整的 TypeScript 类型支持
  • 持久化: 关键状态自动持久化到本地存储

状态持久化通过 pinia-plugin-persistedstate 实现:

typescript
// Store 配置示例
const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    userInfo: {},
    permissions: [],
  }),

  persist: {
    key: 'user-store',
    storage: localStorage,
  },
})

5. 构建优化机制

Vite 构建工具提供以下优化机制:

  • 依赖预构建: 第三方依赖预构建提升加载速度
  • 代码分割: 自动分割 vendor 包和业务代码
  • Tree Shaking: 自动移除未使用的代码
  • 压缩优化: 生产环境自动启用压缩

构建配置通过 vite.config.ts 文件管理:

typescript
// 构建优化配置
export default defineConfig({
  build: {
    // 代码分割配置
    rollupOptions: {
      output: {
        manualChunks: {
          vue: ['vue'],
          'element-plus': ['element-plus'],
        },
      },
    },
  },
})

Product 模块开发示例

1. 创建 Product 模块结构

假设开发 Product 模块,按照项目统一规范创建目录结构:

shell
src/
├── api/                 # API 接口层(统一管理)
   └── product/        # Product 相关接口
       └── index.ts    # Product API 服务
├── types/               # 类型定义(统一管理)
   └── api/            # API 相关类型
       └── product.ts  # Product 相关类型定义
├── locales/             # 国际化文案
   └── module/         # 模块文案
       └── product/    # Product 模块文案
           └── zh-CN.ts
└── views/modules/product/  # Product 页面组件
    ├── components/          # Product 相关组件(如有需要)
   ├── ProductForm.vue  # 产品表单组件
   └── ProductTable.vue # 产品表格组件
    └── index.vue           # 产品管理主页面

2. 定义 Product 类型

在统一类型定义目录中创建产品相关的 TypeScript 类型定义:

typescript
// src/types/api/product.ts
export interface Product {
  id: number
  name: string
  price: number
  description: string
  category: string
  status: 'active' | 'inactive'
  createdAt: string
  updatedAt: string
}

export interface ProductForm {
  name: string
  price: number
  description?: string
  category: string
}

export interface ProductFilters {
  category?: string
  status?: string
  search?: string
  page: number
  pageSize: number
}

export interface ProductListResponse {
  list: Product[]
  total: number
}

3. 实现 Product API 服务

在统一API目录中封装产品相关的 API 接口:

typescript
// src/api/product/index.ts
import request from '@/axios'
import type { Product, ProductForm, ProductFilters, ProductListResponse } from '@/types/api/product'

export class ProductApiService {
  // 获取产品列表
  static async getProducts(params: ProductFilters) {
    return request.get<ProductListResponse>({
      url: '/api/products',
      params,
    })
  }

  // 获取产品详情
  static async getProduct(id: number) {
    return request.get<Product>({
      url: `/api/products/${id}`,
    })
  }

  // 创建产品
  static async createProduct(data: ProductForm) {
    return request.post<Product>({
      url: '/api/products',
      data,
    })
  }

  // 更新产品
  static async updateProduct(id: number, data: ProductForm) {
    return request.put<Product>({
      url: `/api/products/${id}`,
      data,
    })
  }

  // 删除产品
  static async deleteProduct(id: number) {
    return request.delete({
      url: `/api/products/${id}`,
    })
  }
}

export default ProductApiService

4. 创建 Product 国际化文案

在统一国际化目录中创建产品模块的文案:

typescript
// src/locales/module/product/zh-CN.ts
export default {
  product: {
    title: '产品管理',
    list: '产品列表',
    name: '产品名称',
    category: '产品分类',
    price: '价格',
    status: '状态',
    createdAt: '创建时间',
    operation: '操作',
    search: '查询',
    reset: '重置',
    add: '新增产品',
    edit: '编辑',
    delete: '删除',
    export: '导出数据',
    placeholder: {
      name: '请输入产品名称',
      category: '请选择分类',
    },
    statusText: {
      active: '激活',
      inactive: '停用',
    },
    message: {
      loadFailed: '获取产品列表失败',
      deleteSuccess: '删除成功',
      deleteFailed: '删除失败',
      exportSuccess: '导出功能开发中',
      exportFailed: '导出失败',
      confirmDelete: '确定要删除产品"{name}"吗?此操作不可恢复。',
    },
    categories: {
      electronics: '电子产品',
      home: '家居用品',
      clothing: '服装服饰',
      food: '食品饮料',
    },
  },
}

5. 创建 Product 管理页面

实现产品管理的主页面,采用响应式设计,支持桌面端和移动端,集成国际化、权限控制和数据管理功能:

页面结构

产品管理页面采用卡片式布局,包含以下主要区域:

  1. 筛选条件区域 - 支持产品名称、产品编码、分类、状态等条件筛选
  2. 数据展示区域 - 桌面端表格视图和移动端卡片视图
  3. 操作按钮区域 - 新增、批量删除、导出等功能
  4. 表单对话框 - 产品新增/编辑表单
  5. 导出对话框 - 数据导出配置

样式已集成src\styles\components\tables.less,如无特殊需求,无需额外配置

核心功能特性

  • 响应式设计:自动适配桌面端和移动端显示
  • 国际化支持:使用 t('product.xxx') 语法调用多语言文案
  • 权限控制:通过 v-permission 指令控制按钮显示
  • 数据管理:支持分页、筛选、排序、状态切换
  • 批量操作:支持批量删除和导出
  • 表单验证:完整的表单验证规则

主要组件结构

vue
<template>
  <div class="product-container query-container">
    <!-- 筛选条件区域 -->
    <el-card class="filter-container">
      <!-- 移动端展开收起功能 -->
      <template #header v-if="isMobile">
        <div class="filter-header">
          <span>{{ t('common.filter') }}</span>
          <el-button @click="filterExpanded = !filterExpanded">
            {{ filterExpanded ? t('common.collapse') : t('common.expand') }}
          </el-button>
        </div>
      </template>

      <!-- 筛选表单 -->
      <el-form :inline="!isMobile" :model="queryParams">
        <el-row>
          <el-col :xs="24" :sm="12" :md="8" :lg="8">
            <el-form-item :label="t('product.productName')">
              <el-input v-model="queryParams.productName" />
            </el-form-item>
          </el-col>
          <!-- 其他筛选条件 -->
        </el-row>
      </el-form>
    </el-card>

    <!-- 数据展示区域 -->
    <el-card class="table-container">
      <template #header>
        <div class="card-header">
          <span v-if="!isMobile">{{ t('product.list') }}</span>
          <div class="header-actions">
            <el-button type="primary" @click="handleAdd">
              {{ t('common.add') }}
            </el-button>
            <!-- 其他操作按钮 -->
          </div>
        </div>
      </template>

      <!-- 桌面端表格视图 -->
      <div v-if="!isMobile" class="desktop-view">
        <el-table :data="productList" v-loading="loading">
          <!-- 表格列定义 -->
        </el-table>
      </div>

      <!-- 移动端卡片视图 -->
      <div v-else class="mobile-view">
        <div v-loading="loading" class="mobile-cards-container">
          <!-- 卡片列表 -->
        </div>
      </div>

      <!-- 分页和加载提示 -->
    </el-card>

    <!-- 导出对话框 -->
    <el-dialog :title="t('product.exportTitle')" v-model="exportVisible">
      <!-- 导出配置表单 -->
    </el-dialog>

    <!-- 产品编辑对话框 -->
    <el-dialog :title="dialog.title" v-model="dialog.visible">
      <!-- 产品表单 -->
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { ElMessage, ElMessageBox } from 'element-plus'
import ProductFormDialog from './components/ProductFormDialog.vue'
import ProductApiService from '@/api/product'
import type { Product, ProductFilters } from '@/types/api/product'

const { t } = useI18n()

// 响应式数据
const loading = ref(false)
const productList = ref<Product[]>([])

// 搜索表单
const searchForm = reactive({
  name: '',
  category: '',
})

// 分页配置
const pagination = reactive({
  current: 1,
  size: 10,
  total: 0,
})

// 表单对话框配置
const formDialog = reactive({
  visible: false,
  data: null as Product | null,
  mode: 'add' as 'add' | 'edit',
})

// 产品分类选项(基于国际化文案)
const categories = computed(() => {
  return Object.entries(t('product.categories', {}, { returnObjects: true })).map(
    ([value, label]) => ({
      value,
      label,
    })
  )
})

// 加载产品列表
const loadProductList = async () => {
  loading.value = true

  try {
    const params: ProductFilters = {
      ...searchForm,
      page: pagination.current,
      pageSize: pagination.size,
    }

    const response = await ProductApiService.getProducts(params)
    productList.value = response.data.list
    pagination.total = response.data.total
  } catch (error) {
    ElMessage.error(t('product.message.loadFailed'))
    console.error('加载产品列表错误:', error)
  } finally {
    loading.value = false
  }
}

// 搜索处理
const handleSearch = () => {
  pagination.current = 1
  loadProductList()
}

// 重置搜索
const handleReset = () => {
  Object.assign(searchForm, { name: '', category: '' })
  handleSearch()
}

// 分页处理
const handleSizeChange = (size: number) => {
  pagination.size = size
  pagination.current = 1
  loadProductList()
}

const handleCurrentChange = (current: number) => {
  pagination.current = current
  loadProductList()
}

// 新增产品
const handleAdd = () => {
  formDialog.mode = 'add'
  formDialog.data = null
  formDialog.visible = true
}

// 编辑产品
const handleEdit = (product: Product) => {
  formDialog.mode = 'edit'
  formDialog.data = product
  formDialog.visible = true
}

// 删除产品
const handleDelete = async (product: Product) => {
  try {
    await ElMessageBox.confirm(
      t('product.message.confirmDelete', { name: product.name }),
      t('product.delete'),
      {
        type: 'warning',
        confirmButtonText: t('common.confirm'),
        cancelButtonText: t('common.cancel'),
      }
    )

    await ProductApiService.deleteProduct(product.id)
    ElMessage.success(t('product.message.deleteSuccess'))
    loadProductList()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(t('product.message.deleteFailed'))
      console.error('删除产品错误:', error)
    }
  }
}

// 导出数据
const handleExport = async () => {
  try {
    // 实现导出逻辑
    ElMessage.success(t('product.message.exportSuccess'))
  } catch (error) {
    ElMessage.error(t('product.message.exportFailed'))
    console.error('导出产品数据错误:', error)
  }
}

// 表单提交成功处理
const handleFormSuccess = () => {
  formDialog.visible = false
  loadProductList()
}

// 页面加载时初始化数据
onMounted(() => {
  loadProductList()
})
</script>

6. 配置 Product 模块路由

在路由配置中添加产品管理模块:

前端控制模式

typescript
// src/router/modules/product.ts
import type { RouteRecordRaw } from 'vue-router'

const productRoutes: RouteRecordRaw[] = [
  {
    path: '/product',
    name: 'Product',
    component: () => import('@/views/modules/product/index.vue'),
    meta: {
      title: 'product.title', // 使用国际化key
      icon: 'shopping-bag',
      requiresAuth: true,
      permissions: ['product:view'],
    },
    children: [
      {
        path: 'list',
        name: 'ProductList',
        component: () => import('@/views/modules/product/index.vue'),
        meta: {
          title: 'product.list', // 使用国际化key
          requiresAuth: true,
          permissions: ['product:view'],
        },
      },
    ],
  },
]

export default productRoutes

然后在主路由文件中导入:

typescript
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import productRoutes from './modules/product'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    // ... 其他路由
    ...productRoutes,
    // ... 更多路由
  ],
})

export default router

后端控制模式

在预制的系统管理-菜单管理下配置产品模块的路由,包括列表、新增、编辑、删除等操作。

框架核心优势

1. 开发效率提升

  • 组件化开发: 基于 Element Plus 组件库,提供丰富的 UI 组件
  • 类型安全: 完整的 TypeScript 支持,减少运行时错误
  • 热重载: Vite 开发服务器支持实时预览
  • 代码规范: ESLint + Prettier 确保代码质量

2. 性能优化

  • 构建优化: Vite 提供快速的冷启动和热更新
  • 代码分割: 自动分割 vendor 包,优化加载性能
  • 缓存策略: 多级缓存机制提升应用响应速度
  • 懒加载: 路由和组件懒加载减少初始包大小

3. 企业级功能

  • 权限管理: 完整的 RBAC 权限控制系统
  • 国际化: 多语言支持,适应全球化需求
  • 数据字典: 统一的数据管理机制
  • 错误处理: 完善的错误捕获和处理机制

4. 可维护性

  • 模块化架构: 清晰的目录结构和模块划分
  • 类型定义: 完整的接口和类型定义
  • 文档完善: 详细的开发文档和示例
  • 工具集成: 与开发工具深度集成

总结

VJSP Vue3 Framework 提供了一个完整的企业级前端解决方案,基于现代化的技术栈和最佳实践。框架的核心优势在于其完善的权限管理、国际化支持、性能优化和开发效率提升。通过 Product 模块的开发示例,展示了如何在框架基础上快速构建业务功能模块。

框架的设计理念是"约定优于配置",通过合理的默认配置和模块化设计,降低开发复杂度,提升开发效率。同时,框架保持了良好的扩展性,可以根据具体业务需求进行定制和扩展。