马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
Vben Admin企业级管理系统框架
目次
- 框架先容
- 核心特点
- 项目结构
- 快速开始
- 核心功能详解
- 最佳实践
- 常见问题与解决方案
- 资源与社区
1. 框架先容
1.1 什么是Vben Admin
Vben Admin是一个基于Vue 3、Vite、TypeScript和Ant Design Vue的企业级中后台管理系统框架。它提供了一套完备的解决方案,资助开发者快速搭建当代化的管理系统。
1.2 框架优势
- 开箱即用:提供丰富的组件和功能,减少重复开发
- 高度可定制:支持主题定制、布局定制、组件定制等
- 性能良好:基于Vue 3和Vite,提供极速的开发体验和良好的性能
- 类型安全:全面支持TypeScript,提供类型安全和更好的开发体验
- 社区活跃:拥有活跃的社区和持续的更新维护
1.3 适用场景
- 企业级中后台管理系统
- 数据可视化平台
- 内容管理系统
- 工作流管理系统
- 权限管理系统
2. 核心特点
2.1 技术栈先进
- Vue 3:接纳最新的Vue 3框架,享受Composition API带来的开发体验提拔
- TypeScript:全面支持TypeScript,提供类型安全和更好的开发体验
- Vite:使用Vite作为构建工具,提供极速的开发体验
- Ant Design Vue:基于Ant Design设计规范,提供丰富的UI组件
- Pinia:使用Pinia进行状态管理,提供更好的TypeScript支持
- Vue Router:使用Vue Router进行路由管理,支持动态路由
2.2 功能完满
- 权限管理:基于角色的访问控制(RBAC),支持动态路由和权限验证
- 主题设置:支持多主题切换,可自定义主题色和布局
- 国际化:内置国际化解决方案,支持多语言切换
- 组件封装:封装了常用的业务组件,如表格、表单、图表等
- Mock数据:内置Mock数据服务,方便前后端分离开发
- 错误处置处罚:内置全局错误处置处罚机制,提供友爱的错误提示
- 日志系统:内置日志系统,方便问题排查和用户举动分析
2.3 开发体验
- 代码规范:接纳ESLint、Prettier等工具包管代码质量
- Git提交规范:使用commitlint规范Git提交信息
- 自动导入:支持组件和API的自动导入,减少重复代码
- 热更新:支持模块热更新,提高开发服从
- TypeScript支持:提供完备的类型定义,提高开发服从和代码质量
- 开发工具:提供丰富的开发工具,如调试工具、性能分析工具等
3. 项目结构
- ├── build # 构建相关配置
- ├── mock # Mock数据
- ├── public # 静态资源
- ├── src # 源代码
- │ ├── api # API接口
- │ ├── assets # 静态资源
- │ ├── components # 公共组件
- │ ├── design # 样式文件
- │ ├── enums # 枚举定义
- │ ├── hooks # Vue Hooks
- │ ├── layouts # 布局组件
- │ ├── locales # 国际化资源
- │ ├── router # 路由配置
- │ ├── settings # 项目配置
- │ ├── store # 状态管理
- │ ├── utils # 工具函数
- │ ├── views # 页面组件
- │ ├── App.vue # 根组件
- │ └── main.ts # 入口文件
- ├── types # 类型定义
- ├── .env # 环境变量
- ├── .eslintrc.js # ESLint配置
- ├── .prettierrc # Prettier配置
- ├── package.json # 项目依赖
- ├── tsconfig.json # TypeScript配置
- └── vite.config.ts # Vite配置
复制代码 3.1 核心目次阐明
src/api
API接口定义,按模块划分,每个模块对应一个文件。
- // src/api/sys/user.ts
- import { defHttp } from '/@/utils/http/axios';
- enum Api {
- UserList = '/system/user/list',
- UserInfo = '/system/user/info',
- UserAdd = '/system/user/add',
- UserEdit = '/system/user/edit',
- UserDelete = '/system/user/delete',
- }
- export const getUserList = (params) => defHttp.get({ url: Api.UserList, params });
- export const getUserInfo = (id) => defHttp.get({ url: Api.UserInfo, params: { id } });
- export const addUser = (data) => defHttp.post({ url: Api.UserAdd, data });
- export const editUser = (data) => defHttp.post({ url: Api.UserEdit, data });
- export const deleteUser = (id) => defHttp.delete({ url: Api.UserDelete, params: { id } });
复制代码 src/components
公共组件,包括基础组件和业务组件。
- <!-- src/components/Button/src/BasicButton.vue -->
- <template>
- <a-button v-bind="getBindValue" :class="getButtonClass" @click="handleClick">
- <template v-if="icon">
- <component :is="icon" />
- </template>
- <slot></slot>
- </a-button>
- </template>
- <script lang="ts">
- import { defineComponent, computed, unref } from 'vue';
- import { Button } from 'ant-design-vue';
- import { useAttrs } from '/@/hooks/component/useAttrs';
- import { useDesign } from '/@/hooks/web/useDesign';
- export default defineComponent({
- name: 'BasicButton',
- components: { [Button.name]: Button },
- props: {
- type: {
- type: String,
- default: 'default',
- },
- icon: {
- type: String,
- default: '',
- },
- },
- emits: ['click'],
- setup(props, { emit }) {
- const { prefixCls } = useDesign('basic-button');
- const { getAttrs } = useAttrs();
- const getBindValue = computed(() => ({ ...getAttrs(), ...props }));
- const getButtonClass = computed(() => [prefixCls]);
- function handleClick(e: MouseEvent) {
- emit('click', e);
- }
- return {
- getBindValue,
- getButtonClass,
- handleClick,
- };
- },
- });
- </script>
复制代码 src/hooks
Vue Hooks,提供可复用的逻辑。
- // src/hooks/web/usePermission.ts
- import { useUserStoreWithOut } from '/@/store/modules/user';
- export function usePermission() {
- const userStore = useUserStoreWithOut();
- const { role } = userStore.getUserInfo;
- function hasPermission(value: string | string[]): boolean {
- if (!value) {
- return true;
- }
- if (!role) {
- return false;
- }
- if (Array.isArray(value)) {
- return value.some((item) => role.includes(item));
- }
- return role.includes(value);
- }
- return {
- hasPermission,
- };
- }
复制代码 src/router
路由设置,包括静态路由和动态路由。
- // src/router/routes/index.ts
- import type { AppRouteRecordRaw } from '/@/router/types';
- import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
- import { PageEnum } from '/@/enums/pageEnum';
- import { t } from '/@/hooks/web/useI18n';
- // 根路由
- export const RootRoute: AppRouteRecordRaw = {
- path: '/',
- name: 'Root',
- redirect: PageEnum.BASE_HOME,
- meta: {
- title: 'Root',
- },
- };
- // 登录路由
- export const LoginRoute: AppRouteRecordRaw = {
- path: '/login',
- name: 'Login',
- component: () => import('/@/views/sys/login/Login.vue'),
- meta: {
- title: t('routes.basic.login'),
- },
- };
- // 基础路由
- export const basicRoutes = [
- LoginRoute,
- RootRoute,
- ...PAGE_NOT_FOUND_ROUTE,
- ...REDIRECT_ROUTE,
- ];
复制代码 src/store
状态管理,使用Pinia进行状态管理。
- // src/store/modules/user.ts
- import { defineStore } from 'pinia';
- import { store } from '/@/store';
- import { UserInfo } from '/@/api/sys/model/userModel';
- import { getAuthCache, setAuthCache } from '/@/utils/auth';
- import { TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum';
- interface UserState {
- userInfo: Nullable<UserInfo>;
- token?: string;
- roleList: string[];
- sessionTimeout?: boolean;
- }
- export const useUserStore = defineStore({
- id: 'app-user',
- state: (): UserState => ({
- userInfo: null,
- token: undefined,
- roleList: [],
- sessionTimeout: false,
- }),
- getters: {
- getUserInfo(): Nullable<UserInfo> {
- return this.userInfo || getAuthCache<UserInfo>(USER_INFO_KEY);
- },
- getToken(): string {
- return this.token || getAuthCache<string>(TOKEN_KEY);
- },
- getRoleList(): string[] {
- return this.roleList.length > 0 ? this.roleList : [];
- },
- getSessionTimeout(): boolean {
- return !!this.sessionTimeout;
- },
- },
- actions: {
- setToken(token: string | undefined) {
- this.token = token;
- setAuthCache(TOKEN_KEY, token);
- },
- setUserInfo(info: UserInfo | null) {
- this.userInfo = info;
- setAuthCache(USER_INFO_KEY, info);
- },
- setRoleList(roleList: string[]) {
- this.roleList = roleList;
- },
- setSessionTimeout(flag: boolean) {
- this.sessionTimeout = flag;
- },
- resetState() {
- this.userInfo = null;
- this.token = '';
- this.roleList = [];
- this.sessionTimeout = false;
- },
- },
- });
- // 在setup外使用
- export function useUserStoreWithOut() {
- return useUserStore(store);
- }
复制代码 src/utils
工具函数,提供常用的工具方法。
- // src/utils/http/axios/index.ts
- import type { AxiosRequestConfig, AxiosInstance, AxiosResponse, AxiosError } from 'axios';
- import axios from 'axios';
- import { clone } from 'lodash-es';
- import { ContentTypeEnum } from '/@/enums/httpEnum';
- import { useUserStoreWithOut } from '/@/store/modules/user';
- import { joinTimestamp, formatRequestDate } from './helper';
- import { createAxiosTransform } from './axiosTransform';
- import { checkStatus } from './checkStatus';
- import { useGlobSetting } from '/@/hooks/setting';
- import { RequestOptions, Result } from '/@/types/axios';
- import { AxiosRetry } from './axiosRetry';
- import { getToken } from '/@/utils/auth';
- const globSetting = useGlobSetting();
- const urlPrefix = globSetting.urlPrefix;
- export class VAxios {
- private axiosInstance: AxiosInstance;
- private readonly options: CreateAxiosOptions;
- constructor(options: CreateAxiosOptions) {
- this.options = options;
- this.axiosInstance = axios.create(options);
- this.setupInterceptors();
- }
- private createAxios(config: AxiosRequestConfig) {
- this.axiosInstance = axios.create(config);
- }
- private getTransform() {
- const { transform } = this.options;
- if (!transform) {
- return;
- }
- return transform;
- }
- getAxios(): AxiosInstance {
- return this.axiosInstance;
- }
- configAxios(config: AxiosRequestConfig) {
- if (!this.axiosInstance) {
- return;
- }
- this.createAxios(config);
- }
- setupInterceptors() {
- const transform = this.getTransform();
- if (!transform) {
- return;
- }
- const {
- requestInterceptors,
- requestInterceptorsCatch,
- responseInterceptors,
- responseInterceptorsCatch,
- } = transform;
- // 请求拦截器配置处理
- this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
- // 请求之前处理config
- if (requestInterceptors) {
- config = requestInterceptors(config, this.options);
- }
- return config;
- }, undefined);
- // 请求拦截器错误捕获
- requestInterceptorsCatch &&
- this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
- // 响应结果拦截器处理
- this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
- // 响应数据处理
- if (responseInterceptors) {
- res = responseInterceptors(res);
- }
- return res;
- }, undefined);
- // 响应结果拦截器错误捕获
- responseInterceptorsCatch &&
- this.axiosInstance.interceptors.response.use(undefined, responseInterceptorsCatch);
- }
- // 请求方法
- request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
- let conf: AxiosRequestConfig = clone(config);
- const transform = this.getTransform();
- const { requestOptions } = this.options;
- const opt: RequestOptions = Object.assign({}, requestOptions, options);
- const { beforeRequestHook, requestCatchHook, transformResponseHook } = transform || {};
- if (beforeRequestHook) {
- conf = beforeRequestHook(conf, opt);
- }
- conf.requestOptions = opt;
- conf = this.supportFormData(conf);
- return new Promise((resolve, reject) => {
- this.axiosInstance
- .request<any, AxiosResponse<Result>>(conf)
- .then((res: AxiosResponse<Result>) => {
- if (transformResponseHook) {
- try {
- res = transformResponseHook(res, opt);
- } catch (err) {
- reject(err || new Error('request error!'));
- }
- return res;
- }
- resolve(res as unknown as Promise<T>);
- })
- .catch((e: Error | AxiosError) => {
- if (requestCatchHook && e) {
- reject(requestCatchHook(e, opt));
- return;
- }
- if (Axios.isAxiosError(e)) {
- // 这里对错误信息进行细分处理
- const error = this.handleAxiosError(e);
- reject(error);
- } else {
- let error = new Error(e.message) as Error & { code?: string };
- error.code = 'NO_AXIOS_ERROR';
- reject(error);
- }
- });
- });
- }
- // 支持FormData
- supportFormData(config: AxiosRequestConfig) {
- const { requestOptions } = config;
- const contentType = requestOptions?.headers?.['Content-Type'];
- const isFormData =
- contentType !== ContentTypeEnum.FORM_URLENCODED &&
- contentType !== ContentTypeEnum.FORM_DATA;
- if (isFormData && config.method?.toUpperCase() === 'GET') {
- config.params = Object.assign({}, config.params || {});
- Object.keys(config.params).forEach((key) => {
- if (
- config.params[key] !== null &&
- typeof config.params[key] === 'object' &&
- !Array.isArray(config.params[key])
- ) {
- config.params[key] = JSON.stringify(config.params[key]);
- }
- });
- } else {
- config.data = config.data || config.params;
- }
- return config;
- }
- // 处理Axios错误
- private handleAxiosError(error: AxiosError) {
- const { response } = error;
- let errorMessage = '请求错误';
- if (response) {
- const { status, data } = response;
- errorMessage = checkStatus(status, data);
- }
- const errorObj = new Error(errorMessage) as Error & { code?: string };
- errorObj.code = error.response?.status?.toString() || 'NO_STATUS';
- return errorObj;
- }
- }
- // 创建axios实例
- export const createAxios = (opt?: Partial<CreateAxiosOptions>) => {
- return new VAxios(
- deepMerge(
- {
- timeout: 10 * 1000,
- headers: { 'Content-Type': ContentTypeEnum.JSON },
- // 数据处理方式
- transform: clone(createAxiosTransform()),
- // 配置项,下面的选项都可以在独立的接口请求中覆盖
- requestOptions: {
- // 默认将prefix 添加到url
- joinPrefix: true,
- // 是否返回原生响应头 比如:需要获取响应头时使用该属性
- isReturnNativeResponse: false,
- // 需要对返回数据进行处理
- isTransformResponse: true,
- // post请求的时候添加参数到url
- joinParamsToUrl: false,
- // 格式化提交参数时间
- formatDate: true,
- // 消息提示类型
- errorMessageMode: 'message',
- // 接口地址
- apiUrl: globSetting.apiUrl,
- // 请求拼接地址
- urlPrefix: urlPrefix,
- // 是否加入时间戳
- joinTime: true,
- // 忽略重复请求
- ignoreCancelToken: true,
- // 是否携带token
- withToken: true,
- retryRequest: {
- isOpenRetry: true,
- count: 5,
- waitTime: 100,
- },
- },
- },
- opt || {},
- ),
- );
- };
- // 导出axios实例
- export const defHttp = createAxios();
复制代码 src/views
页面组件,按模块划分。
- <!-- src/views/dashboard/analysis/index.vue -->
- <template>
- <div>
- <PageHeader title="分析页" />
- <a-card :bordered="false">
- <a-row :gutter="16">
- <a-col :span="6">
- <a-statistic
- title="总销售额"
- :value="112893"
- :precision="2"
- :value-style="{ color: '#3f8600' }"
- >
- <template #prefix>
- <TrendCharts option="up" />
- </template>
- </a-statistic>
- </a-col>
- <a-col :span="6">
- <a-statistic
- title="访问人数"
- :value="8846"
- :value-style="{ color: '#cf1322' }"
- >
- <template #prefix>
- <UserOutlined />
- </template>
- </a-statistic>
- </a-col>
- <a-col :span="6">
- <a-statistic
- title="支付笔数"
- :value="6560"
- :value-style="{ color: '#3f8600' }"
- >
- <template #prefix>
- <ShoppingOutlined />
- </template>
- </a-statistic>
- </a-col>
- <a-col :span="6">
- <a-statistic
- title="运营活动效果"
- :value="78"
- :suffix="'%'"
- :value-style="{ color: '#3f8600' }"
- >
- <template #prefix>
- <LikeOutlined />
- </template>
- </a-statistic>
- </a-col>
- </a-row>
- <a-divider />
- <a-row :gutter="16">
- <a-col :span="12">
- <VisitData />
- </a-col>
- <a-col :span="12">
- <SalesData />
- </a-col>
- </a-row>
- </a-card>
- </div>
- </template>
- <script lang="ts">
- import { defineComponent } from 'vue';
- import { UserOutlined, ShoppingOutlined, LikeOutlined } from '@ant-design/icons-vue';
- import { PageHeader } from '/@/components/Page';
- import { TrendCharts } from '/@/components/Charts';
- import VisitData from './components/VisitData.vue';
- import SalesData from './components/SalesData.vue';
- export default defineComponent({
- name: 'Analysis',
- components: {
- PageHeader,
- TrendCharts,
- VisitData,
- SalesData,
- UserOutlined,
- ShoppingOutlined,
- LikeOutlined,
- },
- });
- </script>
复制代码 4. 快速开始
4.1 环境准备
- Node.js >= 14.0.0
- pnpm >= 6.0.0 (保举使用pnpm作为包管理工具)
4.2 安装依赖
- # 使用pnpm
- pnpm install
- # 使用yarn
- yarn install
- # 使用npm
- npm install
复制代码 4.3 开发环境运行
- # 使用pnpm
- pnpm dev
- # 使用yarn
- yarn dev
- # 使用npm
- npm run dev
复制代码 4.4 生产环境构建
- # 使用pnpm
- pnpm build
- # 使用yarn
- yarn build
- # 使用npm
- npm run build
复制代码 4.5 代码格式检查
- # 使用pnpm
- pnpm lint
- # 使用yarn
- yarn lint
- # 使用npm
- npm run lint
复制代码 5. 核心功能详解
5.1 权限管理
Vben Admin接纳基于角色的权限管理系统,支持以下功能:
- 动态路由:根据用户角色动态生成路由
- 权限指令:提供v-auth指令控制元素表现
- 权限函数:提供hasPermission函数判断权限
- 菜单权限:根据用户角色表现对应菜单
- // 权限指令使用示例
- <button v-auth="'user:add'">添加用户</button>
- // 权限函数使用示例
- import { usePermission } from '/@/hooks/web/usePermission';
- const { hasPermission } = usePermission();
- if (hasPermission('user:edit')) {
- // 有编辑权限
- }
复制代码 5.1.1 权限指令实现
- // src/directives/auth/index.ts
- import type { App } from 'vue';
- import { usePermission } from '/@/hooks/web/usePermission';
- function isAuth(el: Element, binding: any) {
- const { hasPermission } = usePermission();
- const { value } = binding;
- if (value && value instanceof Array && value.length > 0) {
- const hasAuth = value.some((item) => {
- return hasPermission(item);
- });
- if (!hasAuth) {
- el.parentNode && el.parentNode.removeChild(el);
- }
- } else {
- throw new Error('请设置操作权限标签值');
- }
- }
- export function setupAuthDirective(app: App) {
- app.directive('auth', {
- mounted(el: Element, binding: any) {
- isAuth(el, binding);
- },
- updated(el: Element, binding: any) {
- isAuth(el, binding);
- },
- });
- }
复制代码 5.1.2 动态路由实现
- // src/router/helper/permissionHelper.ts
- import type { AppRouteRecordRaw } from '/@/router/types';
- import { usePermissionStoreWithOut } from '/@/store/modules/permission';
- import { useUserStoreWithOut } from '/@/store/modules/user';
- import { toRaw } from 'vue';
- import { unref } from 'vue';
- import { getDynamicParams } from '/@/router/helper';
- export function filterAsyncRoutes(routes: AppRouteRecordRaw[], roles: string[]) {
- const res: AppRouteRecordRaw[] = [];
- routes.forEach((route) => {
- const tmp = { ...route };
- if (hasPermission(roles, tmp)) {
- if (tmp.children) {
- tmp.children = filterAsyncRoutes(tmp.children, roles);
- }
- res.push(tmp);
- }
- });
- return res;
- }
- function hasPermission(roles: string[], route: AppRouteRecordRaw) {
- if (route.meta && route.meta.roles) {
- return roles.some((role) => route.meta?.roles?.includes(role));
- } else {
- return true;
- }
- }
- export async function generateDynamicRoutes() {
- const permissionStore = usePermissionStoreWithOut();
- const userStore = useUserStoreWithOut();
- const { roles } = userStore.getUserInfo;
- let accessedRoutes;
- if (roles.includes('admin')) {
- accessedRoutes = permissionStore.getRoutes;
- } else {
- accessedRoutes = filterAsyncRoutes(toRaw(permissionStore.getRoutes), roles);
- }
- return accessedRoutes;
- }
复制代码 5.2 主题设置
支持多主题切换和自定义主题:
- 预设主题:提供多种预设主题
- 自定义主题:支持自定义主题色和布局
- 暗黑模式:支持暗黑模式切换
- 主题持久化:主题设置可持久化到本地存储
- // 切换主题示例
- import { useTheme } from '/@/hooks/web/useTheme';
- const { setTheme } = useTheme();
- setTheme('dark'); // 切换到暗黑主题
复制代码 5.2.1 主题设置实现
- // src/hooks/web/useTheme.ts
- import { useDesign } from '/@/hooks/web/useDesign';
- import { useStorage } from '/@/hooks/web/useStorage';
- import { ThemeEnum } from '/@/enums/appEnum';
- const { prefixCls } = useDesign('app');
- const { setStorage, getStorage } = useStorage();
- export function useTheme() {
- const getTheme = () => {
- return getStorage(prefixCls + 'theme') || ThemeEnum.LIGHT;
- };
- const setTheme = (theme: ThemeEnum) => {
- setStorage(prefixCls + 'theme', theme);
- document.documentElement.setAttribute('data-theme', theme);
- };
- return {
- getTheme,
- setTheme,
- };
- }
复制代码 5.3 国际化
内置国际化解决方案,支持多语言切换:
- 语言包:内置中文和英文语言包
- 动态加载:支持按需加载语言包
- 语言持久化:语言选择可持久化到本地存储
- // 切换语言示例
- import { useLocale } from '/@/hooks/web/useLocale';
- const { setLocale } = useLocale();
- setLocale('en'); // 切换到英文
复制代码 5.3.1 国际化实现
- // src/locales/index.ts
- import type { App } from 'vue';
- import { createI18n } from 'vue-i18n';
- import { useLocaleStoreWithOut } from '/@/store/modules/locale';
- import { localeSetting } from '/@/settings/localeSetting';
- import { setHtmlPageLang } from '/@/utils/locale';
- import { getLocale } from './helper';
- const { fallback, availableLocales } = localeSetting;
- export let i18n: ReturnType<typeof createI18n>;
- async function createI18nOptions() {
- const locale = getLocale();
- const defaultLocal = await import(`./lang/${locale}.ts`);
- const message = defaultLocal.default?.message ?? {};
- return {
- legacy: false,
- locale,
- fallbackLocale: fallback,
- messages: {
- [locale]: message,
- },
- };
- }
- export async function setupI18n(app: App) {
- const options = await createI18nOptions();
- i18n = createI18n(options) as ReturnType<typeof createI18n>;
- app.use(i18n);
- setHtmlPageLang(options.locale);
- return i18n;
- }
复制代码 5.4 组件封装
封装了常用的业务组件,提高开发服从:
- 表格组件:支持自定义列、排序、筛选、分页等
- 表单组件:支持动态表单、表单验证、自定义校验等
- 图表组件:集成ECharts,提供常用图表组件
- 上传组件:支持单文件、多文件上传,支持图片预览等
- <!-- 表格组件使用示例 -->
- <template>
- <BasicTable @register="registerTable">
- <template #toolbar>
- <a-button type="primary" @click="handleCreate">新增</a-button>
- </template>
- <template #action="{ record }">
- <TableAction
- :actions="[
- {
- icon: 'clarity:note-edit-line',
- tooltip: '编辑',
- onClick: handleEdit.bind(null, record),
- },
- {
- icon: 'ant-design:delete-outlined',
- color: 'error',
- tooltip: '删除',
- popConfirm: {
- title: '是否确认删除',
- confirm: handleDelete.bind(null, record),
- },
- },
- ]"
- />
- </template>
- </BasicTable>
- </template>
- <script lang="ts">
- import { defineComponent, ref, unref } from 'vue';
- import { BasicTable, useTable, TableAction } from '/@/components/Table';
- import { useMessage } from '/@/hooks/web/useMessage';
- import { columns, searchFormSchema } from './user.data';
- import { getUserList, deleteUser } from '/@/api/sys/user';
- export default defineComponent({
- name: 'UserManagement',
- components: { BasicTable, TableAction },
- setup() {
- const { createMessage } = useMessage();
- const [registerTable, { reload }] = useTable({
- title: '用户列表',
- api: getUserList,
- columns,
- formConfig: {
- labelWidth: 120,
- schemas: searchFormSchema,
- },
- useSearchForm: true,
- showTableSetting: true,
- bordered: true,
- actionColumn: {
- width: 80,
- title: '操作',
- dataIndex: 'action',
- slots: { customRender: 'action' },
- fixed: false,
- },
- });
- function handleCreate() {
- // 处理创建用户
- }
- function handleEdit(record: Recordable) {
- // 处理编辑用户
- }
- async function handleDelete(record: Recordable) {
- await deleteUser(record.id);
- createMessage.success('删除成功');
- reload();
- }
- return {
- registerTable,
- handleCreate,
- handleEdit,
- handleDelete,
- };
- },
- });
- </script>
复制代码 5.4.1 表格组件实现
- // src/components/Table/src/hooks/useTable.ts
- import type { BasicTableProps, TableActionType } from '../types/table';
- import { ref, onMounted, unref } from 'vue';
- import { isProdMode } from '/@/utils/env';
- import { error } from '/@/utils/log';
- import { getDynamicProps } from '/@/utils';
- export function useTable(
- getProps?: (props: Partial<BasicTableProps>) => Partial<BasicTableProps>,
- ) {
- const tableRef = ref<Nullable<TableActionType>>(null);
- const loadedRef = ref<Nullable<boolean>>(false);
- const innerPropsRef = ref<Partial<BasicTableProps>>({});
- function register(instance: TableActionType) {
- isProdMode() &&
- tableRef.value &&
- tableRef.value.setProps(getDynamicProps(getProps, unref(innerPropsRef)));
- tableRef.value = instance;
- }
- function getTableInstance() {
- const table = unref(tableRef);
- if (!table) {
- error('表格实例尚未注册,请在onMounted后调用');
- }
- return table;
- }
- const methods: TableActionType = {
- reload: async (opt?: any) => {
- getTableInstance().reload(opt);
- },
- setProps: (props: Partial<BasicTableProps>) => {
- getTableInstance().setProps(props);
- },
- // 其他方法...
- };
- onMounted(() => {
- loadedRef.value = true;
- });
- return [register, methods];
- }
复制代码 5.5 状态管理
使用Pinia进行状态管理,提供更好的TypeScript支持:
- 模块化:按功能模块划分状态
- 类型安全:提供完备的类型定义
- 持久化:支持状态持久化到本地存储
- 开发工具:支持Vue DevTools调试
- // 状态管理使用示例
- import { useUserStoreWithOut } from '/@/store/modules/user';
- const userStore = useUserStoreWithOut();
- const { userInfo } = userStore.getUserInfo;
复制代码 5.5.1 状态管理实现
- // src/store/index.ts
- import type { App } from 'vue';
- import { createPinia } from 'pinia';
- import { registerStores } from '/@/store/helper';
- export function setupStore(app: App) {
- const store = createPinia();
- app.use(store);
- registerStores(store);
- return store;
- }
复制代码 6. 最佳实践
6.1 项目初始化
- # 克隆项目
- git clone https://github.com/vbenjs/vue-vben-admin.git
- # 进入项目目录
- cd vue-vben-admin
- # 安装依赖
- pnpm install
- # 启动项目
- pnpm dev
复制代码 6.2 开发规范
- 定名规范:组件名使用PascalCase,文件名使用kebab-case
- 目次结构:按功能模块划分目次,保持结构清楚
- 代码风格:遵照ESLint和Prettier设置的代码风格
- Git提交:遵照commitlint规范的提交信息格式
6.3 性能优化
- 按需加载:路由和组件按需加载,减少首屏加载时间
- 缓存优化:合理使用keep-alive缓存组件
- 打包优化:设置splitChunks分割代码,减少包体积
- 图片优化:使用webp格式和懒加载优化图片加载
6.3.1 按需加载实现
- // 路由按需加载
- const routes = [
- {
- path: '/dashboard',
- name: 'Dashboard',
- component: () => import('/@/views/dashboard/index.vue'),
- },
- ];
复制代码 6.3.2 打包优化实现
- // vite.config.ts
- export default defineConfig({
- build: {
- target: 'es2015',
- outDir: 'dist',
- assetsDir: 'assets',
- minify: 'terser',
- terserOptions: {
- compress: {
- keep_infinity: true,
- drop_console: true,
- drop_debugger: true,
- },
- },
- rollupOptions: {
- output: {
- manualChunks: {
- vue: ['vue', 'vue-router', 'pinia'],
- antd: ['ant-design-vue'],
- },
- },
- },
- },
- });
复制代码 6.4 项目部署
- 环境设置:根据环境设置不同的变量
- 构建优化:优化构建设置,减少构建时间
- 部署脚本:提供部署脚本,简化部署流程
- 监控告警:集成监控告警系统,及时发现问题
6.4.1 环境设置实现
- // .env.development
- VITE_PORT=3100
- VITE_USE_MOCK=true
- VITE_USE_PWA=false
- VITE_PUBLIC_PATH=/
- VITE_PROXY=[["/basic-api","http://localhost:3000"],["/upload","http://localhost:3300/upload"]]
- VITE_GLOB_APP_TITLE=Vben Admin Dev
- VITE_GLOB_APP_SHORT_NAME=Vben Admin Dev
- VITE_USE_CDN=false
- VITE_DROP_CONSOLE=false
- VITE_BUILD_COMPRESS="gzip"
- VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE=false
- VITE_LEGACY=false
- VITE_USE_IMAGEMIN=false
- VITE_GENERATE_UI=false
复制代码 7. 常见问题与解决方案
7.1 权限问题
问题:用户无法访问某些页面或功能。
解决方案:
- 检查用户角色是否正确
- 检查路由权限设置
- 检查菜单权限设置
- 使用权限调试工具排盘问题
7.2 性能问题
问题:页面加载速度慢或操作响应慢。
解决方案:
- 使用Chrome DevTools分析性能瓶颈
- 优化组件渲染,减少不必要的渲染
- 使用虚拟滚动优化长列表
- 优化API哀求,减少不必要的哀求
7.3 兼容性问题
问题:在某些浏览器中页面表现异常。
解决方案:
- 检查浏览器兼容性设置
- 使用polyfill解决兼容性问题
- 针对特定浏览器添加兼容性样式
- 使用babel-preset-env设置合适的浏览器目的
7.4 构建问题
问题:构建失败或构建产物异常。
解决方案:
- 检查依赖版本兼容性
- 检查构建设置是否正确
- 清理构建缓存,重新构建
- 使用更稳固的Node.js版本
8. 资源与社区
8.1 官方资源
- Vben Admin官网
- GitHub仓库
- 在线演示
8.2 社区资源
8.3 学习资源
- Vue 3文档
- Vite文档
- TypeScript文档
- Ant Design Vue文档
总结
Vben Admin是一个功能完满、易于使用的企业级管理系统框架,它基于最新的前端技术栈,提供了一套完备的解决方案,资助开发者快速搭建当代化的管理系统。通过使用Vben Admin,开发者可以专注于业务逻辑的开发,而不必耗费大量时间在基础架构的搭建上。
无论是小型项目照旧大型企业应用,Vben Admin都能提供精良的支持,是开发企业级管理系统的抱负选择。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |