# acp-admin-web **Repository Path**: zhangbinhub/acp-admin-web ## Basic Information - **Project Name**: acp-admin-web - **Description**: 使用vue全家桶(Vue+Vue-router+axios)、Element-Plus 样式库构建的前端项目。该项目是前后端分离架构中的“前端部分” - **Primary Language**: JavaScript - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 25 - **Forks**: 6 - **Created**: 2021-08-27 - **Last Updated**: 2026-09-28 ## Categories & Tags **Categories**: backend **Tags**: vue3, element-plus, vite, file-viewer ## README # acp-admin-cloud-web ![Vue](https://img.shields.io/badge/Vue-3.5-4FC08D?logo=vuedotjs&logoColor=white) ![Vite](https://img.shields.io/badge/Vite-8-646CFF?logo=vite&logoColor=white) ![Element Plus](https://img.shields.io/badge/Element_Plus-2.14-409EFF) ![Node](https://img.shields.io/badge/node-%5E20.19%20%7C%7C%20%3E%3D22.12-339933?logo=nodedotjs&logoColor=white) ![License](https://img.shields.io/badge/License-Apache--2.0-blue) ###### v8.4.1 [版本更新日志](doc/version_history.md) ## 简介 该工程是 acp-admin 的前端,基于 Vue 3 全家桶(Vue + Vue Router + Pinia + Axios)与 Element Plus 组件库构建,是前后端分离架构中的"前端部分"。 ## 功能特性 * 认证安全:OAuth2 认证,登录密码国密 SM3 加密传输,密码过期策略,密码复杂度策略 * 系统配置:应用、菜单、模块功能、机构、角色、用户、权限、字典、运行参数在线配置 * 工作流:工作流部署与流程管理,内置 BPMN 流程在线设计器(solon-flow) * 监控审计:登录日志、操作日志、路由日志,在线用户统计与登录统计图表(ECharts) * 通用能力:文件在线预览(Flyfish Viewer)、富文本编辑(Fluent Editor / Quill)、头像裁剪、JSON 编辑、代码高亮 * 界面体验:多标签页导航、页面缓存(keep-alive)、国际化(简体中文 / 繁体中文 / 英文)、显示模式(浅色 / 深色 / 跟随系统)、响应式布局 ## 技术栈 | 分类 | 技术 | 说明 | |------|------|------| | 核心框架 | [Vue 3.5](https://cn.vuejs.org/) | Composition API | | 构建工具 | [Vite 8](https://cn.vite.dev/) | 基于 Rolldown 的构建引擎 | | 路由 | [Vue Router](https://router.vuejs.org/zh/) | Hash 模式,支持 views 目录自动扫描注册 | | 状态管理 | [Pinia](https://pinia.vuejs.org/zh/) | 结合 localStorage 持久化 | | UI 组件库 | [Element Plus](https://element-plus.org/zh-CN/) | 全量引入 | | 国际化 | [Vue I18n](https://vue-i18n.intlify.dev/) | Composition API 模式 | | HTTP 请求 | [Axios](https://axios-http.com/zh/) | 统一拦截器封装 | | 图表 | [ECharts](https://echarts.apache.org/zh/) | 按需引入 | | 日期处理 | [Day.js](https://day.js.org/zh-CN/) | 2KB 不可变日期库,替代已停止维护的 Moment.js | | 富文本 | [Fluent Editor](https://opentiny.github.io/fluent-editor/) / Quill | 基于 Quill 2.0 的富文本编辑器 | | 图片裁剪 | [Cropper.js](https://cropperjs.github.io/) | v2 | | 文件预览 | [Flyfish Viewer](https://doc.flyfish.dev/) | 文档、表格、音视频等在线预览 | | 组合式工具 | [@vueuse/core](https://vueuse.org/) | 提供 useStorage、usePreferredDark 等组合式工具 | | 模拟数据 | [MSW](https://mswjs.io/) | Mock Service Worker,仅开发环境自动启用 | ## 浏览器兼容性 支持的浏览器及最低版本: | 浏览器 | 最低版本 | 说明 | |--------|---------|------| | Chrome | 119+ | 推荐 | | Firefox | 121+ | 完全支持 | | Edge | 119+ | 完全支持 | | Safari | 17.2+ | 完全支持 | ## 环境要求 * Node.js:`^20.19.0 || >=22.12.0`(建议使用 LTS 版本) * 包管理器:npm(随 Node.js 附带) 可选:设置国内 npm 镜像加速依赖安装 ```bash npm config set registry https://mirrors.tencent.com/npm/ ``` ## 快速开始 ```bash # 安装依赖 npm install # 启动本地开发环境(默认 http://127.0.0.1:5173) npm run start ``` 开发环境下会自动启用 MSW(Mock Service Worker)拦截 `src/mock` 中注册的接口(浏览器控制台会输出 `[MSW] Mocking enabled`);未注册 mock 的请求通过 Vite 代理转发至 `http://127.0.0.1:8771` (可在 [vite.config.mjs](vite.config.mjs) 的 `server.proxy` 中修改)。 ### 常用命令 | 命令 | 说明 | |------|------| | `npm run start` | 启动本地开发环境 | | `npm run build-test` | 编译打包 test 环境 | | `npm run build-prod` | 编译打包 prod 环境 | | `npm run preview` | 本地预览生产构建产物 | | `npm run lint` | ESLint 检查并自动修复 `src` | | `npm run format` | Prettier 格式化 `src` 与 `index.html` | ### 依赖更新 推荐使用 [npm-check-updates](https://github.com/raineorshine/npm-check-updates) 批量检查并更新依赖版本: ```bash npm install -g npm-check-updates ncu -u npm install ``` ## 工程配置 ### 目录结构 ```text root -------工程根目录 ├─ doc -------公共文档 ├─ environment -------分环境变量配置 ├─ public -------静态资源(不参与编译) ├─ src -------源代码 │ ├─ api -------远程 api 封装 │ ├─ assets -------静态资源(参与编译的资源文件) │ ├─ components -------组件 │ │ ├─ layout -------框架布局 │ │ ├─ libs -------工具库 │ │ ├─ pages -------独立页面 │ │ └─ plugins -------插件 │ ├─ composables -------组合式函数(useCrudTable 等) │ ├─ lang -------国际化 │ ├─ mock -------mock 模拟数据(仅开发环境加载) │ ├─ router -------路由配置 │ ├─ stores -------状态管理 │ ├─ views -------自定义页面(自动路由扫描) │ ├─ App.vue -------根组件 │ └─ main.js -------应用入口 ├─ .prettierrc.json -------prettier 配置文件 ├─ eslint.config.js -------eslint 配置文件 ├─ index.html -------主页面,入口文件 ├─ jsconfig.json -------jsconfig 配置文件 ├─ package.json -------package.json └─ vite.config.mjs -------vite 配置文件 ``` ### 环境变量 分环境配置文件位于 `environment` 目录: | 环境 | 配置文件名 | |------|-----------| | 开发环境 | [environment/.env.development](environment/.env.development) | | test 环境 | [environment/.env.test](environment/.env.test) | | prod 环境 | [environment/.env.prod](environment/.env.prod) | 主要变量说明: | 变量 | 说明 | |------|------| | `VITE_APP_NAME` | 应用名称 | | `VITE_BACKEND_PREFIX` | 后端接口地址前缀(开发环境走 Vite 代理) | | `VITE_BACKEND_TIME_OUT` | 请求超时时间(毫秒) | | `VITE_APP_ID` / `VITE_APP_SECRET` | OAuth2 客户端标识;生产部署时建议通过构建环境注入,避免提交真实密钥 | ### 全局工程配置文件 [vite.config.mjs](vite.config.mjs),[配置参考](https://cn.vite.dev/config/)。 ### 项目配置文件 * 状态管理组件:Pinia,路径:`src/stores`,入口:[src/stores/index.js](src/stores/index.js) * 系统信息配置文件(应用名称、主题、首页路径、后端地址等):[src/stores/config/appInfo.js](src/stores/config/appInfo.js) * token、语言、标签页等状态通过 localStorage 持久化 * 显示模式偏好持久化(key: `vueuse-color-scheme`),应用启动时由 [src/stores/app.js](src/stores/app.js) 中的 `applyTheme()` 同步读取并应用,避免页面闪烁 ### 主题与样式 * 样式变量体系:[src/assets/styles/variable.less](src/assets/styles/variable.less) - 所有业务样式统一使用 CSS 自定义属性 `var(--xxx)`,不使用 Less 变量 - `:root` 定义浅色主题变量,`.dark` 选择器覆盖为深色主题(配合 Element Plus dark CSS vars) - 深色模式采用蓝灰底调,4 层背景纵深(L0 `#141619` → L1 `#1a1d24` → L2 `#22262e` → L3 `#282d38`) * 显示模式切换:[src/composables/useDarkMode.js](src/composables/useDarkMode.js) - 支持三种模式:`light`(浅色)、`dark`(深色)、`system`(跟随系统,默认) - 基于 `@vueuse/core` 的 `useStorage` + `usePreferredDark` 实现 - 导出 `applyTheme()` 纯函数(供 app 启动时调用)和 `useDarkMode()` composable(供组件响应式使用) - 切换入口在个人信息页面(`PersonalInformation.vue`)的 `el-radio-group` ### 路由 * 组件:vue-router,Hash 模式 * 固定路由配置:[src/router/routers.js](src/router/routers.js) * 自动扫描路由:[src/router/autoscan.js](src/router/autoscan.js),自动扫描 `src/views` 下所有 `.vue` 文件,读取组件导出的 `name` 与 `pageMeta` 属性生成路由 * `pageMeta` 属性说明: | 属性 | 说明 | |------|------| | `title` | 页面标题(i18n 表达式),用于浏览器标题、面包屑、标签导航 | | `icon` | 图标样式,用于面包屑、标签导航 | | `keepAlive` | 主界面是否缓存该页面,默认 `true` | | `requireAuth` | 访问该页面是否需要登录,默认 `false` | | `withInput` | 页面是否包含用户输入项,默认 `false` ;为 `true` 且 `keepAlive=false` 时,关闭标签前会进行确认 | ### HTTP 请求与 API * 组件:axios,实例配置:[src/components/plugins/axios/plugin-axios.js](src/components/plugins/axios/plugin-axios.js) * 请求与响应拦截器(token 注入、全局错误处理、页面跳转):[src/api/ApiComm.js](src/api/ApiComm.js) * API 定义:`src/api` 目录,按业务域拆分文件,并在 [src/api/ApiLists.js](src/api/ApiLists.js) 中注册 * 页面中调用方式:`import api from '@/api/ApiComm'`,通过 `api.request.<模块>.<方法>()` 发起请求 #### 前后端交互 HttpStatus Code 说明 | HttpStatus | 描述 | |------------|-------------| | 200 | 请求成功 | | 201 | 资源创建成功 | | 400 | 业务错误 | | 401 | token(登录)失效 | | 403 | 权限不足 | | 404 | 找不到资源 | | 500 | 系统异常 | ### 国际化 * 语言包路径:`src/lang`(内置简体中文 `zh-CN`、繁体中文 `zh-TW`、英文 `en-US`) * 新增或修改语言属性后,在 [src/lang/index.js](src/lang/index.js) 中注册新的语言包 ### Mock 基于 [MSW](https://mswjs.io/)(Mock Service Worker)在网络层拦截请求,DevTools Network 面板中可见 mock 请求: * 开发环境下自动启动 mock(见 [src/main.js](src/main.js) 的 `import.meta.env.DEV` 分支),无需手动引入;生产构建不含 mock 相关代码 * 接口拦截规则统一在 [src/mock/handlers.js](src/mock/handlers.js) 中注册,路径须带 `VITE_BACKEND_PREFIX` 前缀(如 `/v1/api/main/user`),与 `src/api` 中的请求路径一一对应 * 模拟数据按业务域放在 `src/mock` 的子目录中(如 `sysconfig`、`workflow`、`log`),以纯函数形式导出,由 handlers 引用 * 同方法下具体路径(如 `role-code-list`)须注册在路径参数通配(如 `:roleId`)之前,否则会被通配抢先匹配 * 未注册 mock 的请求直接放行(`onUnhandledRequest: 'bypass'`),经 Vite 代理转发至真实后端 * `public/mockServiceWorker.js` 由 `npx msw init public/ --save` 生成(CLI 工具脚本,已记录在 `package.json` 的 `msw.workerDirectory`),请勿手动修改 ### 插件 项目内置插件位于 `src/components/plugins` 目录,每个插件独立子目录并提供 `index.js` 入口文件: #### Axios 请求实例 * 路径:[src/components/plugins/axios/plugin-axios.js](src/components/plugins/axios/plugin-axios.js) * 作用:创建并导出 Axios 实例,统一配置 `baseURL`(取自环境变量 `VITE_BACKEND_PREFIX`)、`timeout` 和默认请求头 * 使用方式:在 [src/main.js](src/main.js) 中通过 `vue-axios` 注册为全局 `$http`,并在 [src/api/ApiComm.js](src/api/ApiComm.js) 中挂载请求/响应拦截器(token 注入、错误处理、登录失效跳转) #### AnimatedNumber 数字动画 * 路径:[src/components/plugins/animated-number/](src/components/plugins/animated-number/) * 作用:基于 `requestAnimationFrame` + easeOutCubic 缓动函数的数字滚动动画组件,包裹 `el-statistic` 实现数值从旧值平滑过渡到新值 * Props: | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `value` | Number | `0` | 目标数值 | | `duration` | Number | `1000` | 动画持续时间(毫秒) | * 插槽:`title`、`prefix`、`suffix`(透传至 `el-statistic`) * 使用示例: ```vue ``` #### Cropper 头像裁剪 * 路径:[src/components/plugins/cropper/](src/components/plugins/cropper/) * 作用:基于 [Cropper.js v2](https://cropperjs.github.io/) 的 Web Components 头像裁剪组件,支持拖拽、缩放、旋转、翻转、比例裁剪,并提供多种尺寸的实时预览 * Props: | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `src` | String | — | 图片 URL,变化时自动重置裁剪区域 | | `cropButtonText` | String | — | 裁剪按钮文字 | * 事件:`on-crop` — 裁剪完成后触发,参数为 Base64 DataURL * 功能按钮:重置、移动/裁剪模式切换、缩放、旋转、水平/垂直翻转、方向键微调、比例预设(16:9 / 4:3 / 1:1 / 2:3 / 自由) * 使用示例: ```vue ``` #### PropertiesField 属性字段渲染器 * 路径:[src/components/plugins/properties-field/](src/components/plugins/properties-field/) * 作用:根据 Properties 配置动态渲染对应的表单字段,支持 string / textarea / number / date / datetime / select / radio / picture / file 等多种字段类型,内置文件/图片上传与预览功能 * Props: | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `modelValue` | Object | `{ type: 'string' }` | 字段配置对象(含 type/value/options 等) | | `loading` | Boolean | `false` | 加载状态 | | `viewOnly` | Boolean | `true` | 仅查看模式 | | `size` | String | `'default'` | 组件尺寸 | | `edit` | String | `'auto'` | 编辑控制 | | `fileKeyPrefix` | String | `'files'` | 文件上传目录前缀 | | `imageKeyPrefix` | String | `'images'` | 图片上传目录前缀 | * 数据模式:select/radio 支持 `dataMode=1`(本地 options)和 `dataMode=2`(远程 URL 加载) * 使用示例: ```vue ``` #### SolonFlowDesigner BPMN 流程设计器 * 路径:[src/components/plugins/solon-flow-designer/](src/components/plugins/solon-flow-designer/) * 作用:封装 [solon-flow-bpmn-designer](https://www.npmjs.com/package/solon-flow-bpmn-designer) BPMN 流程在线设计器,支持可视化拖拽设计工作流 * Props: | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `modelValue` | String | `''` | 设计器值(v-model) | | `theme` | String | `'light'` | 主题(`light` / `dark`) | | `lang` | String | `'zh'` | 语言 | | `type` | String | `'yaml'` | 输出格式 | | `height` | Number | `60` | 设计器高度(vh) | * 暴露方法(通过 ref): | 方法 | 说明 | |------|------| | `panelShow()` | 属性面板显隐控制 | | `clear()` | 清除设计器内容 | | `getValue()` | 获取当前设计器值 | * 使用示例: ```vue ``` #### TinyEdit 富文本编辑器 * 路径:[src/components/plugins/tiny-edit/](src/components/plugins/tiny-edit/) * 作用:基于 [Fluent Editor](https://opentiny.github.io/fluent-editor/)(Quill 2.0)的富文本编辑器组件,支持代码高亮、KaTeX 数学公式、表格增强、截图粘贴、Markdown 快捷输入、工具栏提示等 * Props: | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `modelValue` | String | `''` | 编辑器 HTML 内容(v-model) | | `disabled` | Boolean | `false` | 只读模式 | | `count` | Boolean | `false` | 是否启用字数统计 | | `maxCount` | Number | `2000` | 最大字数 | * 使用示例: ```vue ``` #### UserSelect 人员选择器 * 路径:[src/components/plugins/user-select/](src/components/plugins/user-select/) * 作用:人员选择组件,支持两种模式:① 后端查询模式(默认):左侧机构树 + 右侧分页人员列表,可按机构/角色/账号/姓名筛选;② 候选人员模式:传入 `candidateUsers` 后仅在候选列表内本地过滤。支持单选和多选 * Props: | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `modelValue` | Array | `[]` | 已选人员数组(v-model),元素含 `id/loginNo/name` | | `candidateUsers` | Array | `null` | 候选人员清单,传入后切换为本地过滤模式;可选含 `roleSet`、`organizationSet` | | `multiple` | Boolean | `false` | 是否多选 | * 使用示例: ```vue ``` ### 工具库 项目工具方法位于 `src/components/libs` 目录,按职责拆分为 `tools.js`(通用工具)和 `propertiesTools.js`(属性配置工具)。 #### tools.js — 通用工具方法 | 方法 | 说明 | 用法 | |------|------|------| | `copy(srcObj)` | 对象深拷贝(基于 JSON 序列化),支持 null/undefined | `const clone = copy(obj)` | | `deepEqual(x, y)` | 递归比较两个对象/数组是否相等 | `if (deepEqual(a, b)) ...` | | `dateFormat(time)` | 格式化日期为 `YYYY-MM-DD` | `dateFormat(Date.now()) // '2025-01-01'` | | `dateTimeFormat(time)` | 格式化日期时间为 `YYYY-MM-DD HH:mm:ss` | `dateTimeFormat(time)` | | `dateTimeMisFormat(time)` | 格式化日期时间为 `YYYY-MM-DD HH:mm:ss.SSS`(精确到毫秒) | `dateTimeMisFormat(time)` | | `findFullMenuByPath(path, menuList)` | 根据 path 获取菜单全路径数组(含父级) | `const chain = findFullMenuByPath('/user', menus)` | | `findMenuByPath(path, menuList, isFullPath)` | 根据 path 查找单个菜单对象 | `const menu = findMenuByPath(path, menus)` | | `getOpenedNamesByActiveName(name, menuList)` | 获取菜单全路径展开名称数组,用于侧边菜单展开 | `const names = getOpenedNamesByActiveName(path, menus)` | | `findNodeUpperByClasses(ele, classes)` | 向上查找包含指定 class 的父 DOM 节点 | `findNodeUpperByClasses(el, ['wrapper'])` | | `updateTagNavList(store, tagNavList, menuList, route)` | 更新标签导航列表(路由切换时调用) | `updateTagNavList(store, tags, menus, route)` | | `getRouteState(store)` | 获取当前路由参数 | `const state = getRouteState(store)` | | `getRedirect(store, route)` | 获取重定向来源路径(优先 routeState > query > redirectedFrom) | `const from = getRedirect(store, route)` | | `processTreeNode(nodeList, showCode)` | 将后台返回的树数据转换为 el-tree 可用格式(设置 label) | `processTreeNode(treeData)` | | `findCheckedTreeNode(nodeList, idList)` | 区分全选和半选节点 ID,用于树形权限配置 | `const { checkedIdList, halfCheckedIdList } = findCheckedTreeNode(nodes, ids)` | | `arrayIsFullContainOtherArray(src, search)` | 判断数组是否完全包含另一数组的所有元素 | `arrayIsFullContainOtherArray([1,2,3], [1,2]) // true` | | `arrayIsContainOtherArray(src, search)` | 判断数组是否包含另一数组的至少一个元素 | `arrayIsContainOtherArray([1,2], [2,3]) // true` | | `sortTreeNodes(nodeList, property)` | 树节点按指定字段递归排序(默认 `sort`) | `sortTreeNodes(menus)` | | `getTreeFullPathTitle(treeData, targetId, property, separate)` | 获取树节点全路径 label 字符串(如 `A > B > C`) | `getTreeFullPathTitle(tree, id)` | | `getTreeFullPathArray(treeData, target, property)` | 获取树节点全路径节点数组 | `getTreeFullPathArray(tree, id)` | | `filterTreeNode(treeData, targetIds)` | 过滤树中指定 id 的节点 | `filterTreeNode(tree, removeIds)` | | `doDownLoadFile(action, method, params, token)` | 通过动态创建表单的方式下载文件(支持携带 token) | `doDownLoadFile('/api/export', 'get', {}, token)` | | `isMobileDevice()` | 通过 UserAgent 判断当前是否为移动设备 | `if (isMobileDevice()) ...` | | `isProd()` | 判断当前是否为生产环境 | `if (isProd()) ...` | | `getPublicPath(originPath)` | 获取 public 目录下的静态资源绝对路径 | `getPublicPath('logo/logo.png')` | #### propertiesTools.js — 属性配置工具方法 | 方法 | 说明 | 用法 | |------|------|------| | `optionText(options, value)` | 根据 value 在选项列表中查找对应的 text | `optionText([{value:'1',text:'男'}], '1') // '男'` | | `getInfoValue(infoList, name)` | 从 info 列表中按 name 获取 value | `getInfoValue(infoList, 'email')` | | `getInfo(fullInfoList)` | 提取列表中的 `{name, value}` 对,用于序列化提交 | `getInfo(fullList)` | | `getFieldValue(infoList, property)` | 根据字段类型(date/datetime/select/radio)格式化后获取显示值 | `getFieldValue(infoList, property)` | | `getFullInfoList(properties, infoList, showDefault)` | 将 Properties 配置与已有值合并,生成完整字段列表 | `getFullInfoList(props, values, true)` | | `getFilterFullInfoList(properties, infoList, showDefault)` | 同上,但只返回 `filterable=true` 的字段 | `getFilterFullInfoList(props, values)` | | `validateFullInfoValue(fullInfoList)` | 校验完整字段列表,必填项为空时弹出错误提示并返回 false | `if (!validateFullInfoValue(list)) return` | | `getProperties(referenceCode, referenceId)` | 远程获取 Properties 配置(支持 collection 递归展开),返回按 displayOrder 排序的字段列表 | `const props = await getProperties('user_form')` | | `parseFormData(formData)` | 解析 formData(兼容 JSON 字符串和数组格式)为 `[{name, value}]` 数组 | `parseFormData(jsonString)` | | `getProcessFormFields(referenceCode, referenceId, formData, showDefault)` | 组合方法:获取 Properties 配置并与 formData 合并,返回可直接渲染的完整字段列表 | `const fields = await getProcessFormFields('code', id, formData)` | | `serializeWorkFlowFormData(fullInfoList)` | 将完整字段列表序列化为流程 formData 格式(JSON 字符串),与后端 WorkFlowDraft.formData 一致 | `const json = serializeWorkFlowFormData(fields)` | ### 组合式函数 项目通用组合式函数位于 `src/composables` 目录: #### useDarkMode — 显示模式 管理应用的显示模式,支持浅色 / 深色 / 跟随系统三种模式。 * 导出: | 导出 | 类型 | 说明 | |------|------|------| | `useDarkMode()` | Function | 组合式函数,返回 `{ mode, isDark }`,供组件在 setup 中使用 | | `applyTheme()` | Function | 纯函数,读取当前模式并立即应用 `.dark` class 到 DOM,供 app 启动时调用 | * `useDarkMode()` 返回值: | 属性 | 类型 | 说明 | |------|------|------| | `mode` | `Ref<'light'\|'dark'\|'system'>` | 当前显示模式,双向绑定,持久化到 localStorage | | `isDark` | `ComputedRef` | 当前是否为深色(system 模式下自动跟随系统偏好) | * 使用示例: ```js // 组件中响应式使用 const { mode, isDark } = useDarkMode() // app 启动时同步调用(防止闪烁) import { applyTheme } from '@/composables/useDarkMode' applyTheme() ``` #### useCrudTable — CRUD 表格页面通用逻辑 封装“搜索抽屉 + 表格 + 分页 + 多选”的通用逻辑,适用于所有基于 el-table 的列表管理页面。 * 参数: | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `apiMethod` | Function | — | API 请求方法,接收 `searchParam` 返回 Promise | | `buildExtra` | Function | `() => ({})` | 构建页面独有搜索参数 | | `processData` | Function | `null` | 对返回的 content 数组做后处理 | | `beforeSearch` | Function | `null` | 搜索前置守卫,返回 false 则跳过请求 | | `defaultOrder` | String | `'id'` | 默认排序字段 | | `defaultDir` | String | `'descending'` | 默认排序方向 | | `searchOnMount` | Boolean | `true` | 是否初始化时自动搜索 | * 返回值:`searchForm`、`table`(模板引用)、`t`、`appStore`、`searchDrawer`、`modal_loading`、`searchData`、`selectedData`、`searchFormModel`、`isMobile`、`pickerShortcuts`、`handleSearch`、`handleSortChange`、`handlePageSizeSearch`、`handleSearchReset`、`handleSelect`、`selectableFun`、`handleRowClick`、`openSearchDrawer` * 使用示例: ```js const { searchForm, table, t, appStore, searchDrawer, modal_loading, searchData, selectedData, searchFormModel, isMobile, pickerShortcuts, handleSearch, handleSortChange, handlePageSizeSearch, handleSearchReset, handleSelect, selectableFun, handleRowClick, openSearchDrawer } = useCrudTable({ apiMethod: (param) => api.request.user.query(param), buildExtra: (form) => ({ name: form.name }), defaultOrder: 'sort', defaultDir: 'ascending' }) ``` ### 静态资源 * 路径:`src/assets` ## 部署 ### 编译打包 工程根目录下运行: * test 环境 ```bash npm run build-test ``` * prod 环境 ```bash npm run build-prod ``` 执行成功后工程根目录下会出现 `dist` 文件夹,将 `dist` 文件夹中的所有文件复制到 nginx 的 html 下即可。 * 如果部署在 nginx 里 html 的根目录,访问 url 为 http://nginxHost:port * 如果部署在 nginx 里 html 的子目录(如 platform/admin),访问 url 为 http://nginxHost:port/platform/admin 构建产物同时包含 `.gz` 压缩文件(由 vite-plugin-compression 生成),nginx 开启 `gzip_static on` 可直接使用预压缩文件,降低服务器实时压缩开销。 ### nginx 配置 假如工程部署在 nginx 中,需要修改 nginx.conf,增加后端接口的反向代理: * 代理后端 gateway ```nginx location ~ ^.*/v1/api/ { proxy_pass http://apiBackend$backend_raw_uri; proxy_set_header host $http_host; proxy_set_header x-real-ip $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 3600s; } ``` * 启用预压缩静态资源(可选) ```nginx gzip_static on; ``` ## 界面展示 * 登录 ![images](doc/images/pages/login.png) * 首页 ![images](doc/images/pages/index.png) ![images](doc/images/pages/index-dark.png) * 深色模式 ![images](doc/images/pages/dark.png) * 头像裁剪 ![images](doc/images/pages/avatar.png) * 字典配置 ![images](doc/images/pages/dictionary.png) * 文件在线预览 ![images](doc/images/pages/filePreview.png) * json在线编辑 ![images](doc/images/pages/jsonEdit.png) * 富文本编辑 ![images](doc/images/pages/richText.png) * SolonFlow编辑器 ![images](doc/images/pages/solonFlowEdit.png) * 机构配置 ![images](doc/images/pages/orgConfig.png) * 角色配置 ![images](doc/images/pages/roleConfig.png) * 用户配置 ![images](doc/images/pages/userlist.png) * 用户选择组件 ![images](doc/images/pages/userSelect.png) * 属性配置 ![images](doc/images/pages/propertieslist.png) ![images](doc/images/pages/propertiesConfig.png) * 流程管理 ![images](doc/images/pages/processManager.png) * 流程草稿 ![images](doc/images/pages/processDraft.png) * 流程查看 ![images](doc/images/pages/processView.png) * 流程任务处理 ![images](doc/images/pages/taskHandle.png) * 流程查看 ![images](doc/images/pages/processView.png) * 404 页面 ![images](doc/images/pages/404.png) * 500 页面 ![images](doc/images/pages/500.png) * demo - 文件上传 ![images](doc/images/pages/upload.png) ## 相关文档 * [Vue 3](https://cn.vuejs.org/) * [Vite](https://cn.vite.dev/) * [Pinia](https://pinia.vuejs.org/zh/) * [Vue Router](https://router.vuejs.org/zh/) * [Element Plus](https://element-plus.org/zh-CN/) * [Vue I18n](https://vue-i18n.intlify.dev/) * [@vueuse/core](https://vueuse.org/) * [ECharts](https://echarts.apache.org/zh/) * [Fluent Editor](https://opentiny.github.io/fluent-editor/) * [Cropper.js](https://cropperjs.github.io/) * [Flyfish Viewer](https://doc.flyfish.dev/) * [MSW](https://mswjs.io/) ## 开源协议 [Apache-2.0](LICENSE) ### Customize configuration See [Configuration Reference](https://cn.vite.dev/config/).