{"id":13821280,"url":"https://github.com/wzc520pyfm/clover-admin-vue","last_synced_at":"2025-07-18T10:09:26.873Z","repository":{"id":62398262,"uuid":"559923371","full_name":"wzc520pyfm/clover-admin-vue","owner":"wzc520pyfm","description":"A admin template, use the latest technology, based on Vue3,Vite3,Typescript,Pinia,Unocss,Element-plus,pnpm [一个基于Vue3，Vite3，Typescript，Pinia，Unocss，Element-plus，pnpm，使用最新技术栈的的中后台模板] ","archived":false,"fork":false,"pushed_at":"2024-12-21T05:05:11.000Z","size":1292,"stargazers_count":190,"open_issues_count":4,"forks_count":23,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-05-24T16:18:09.255Z","etag":null,"topics":["admin","admin-template","axios","dashboard","element-admin","element-plus","element-ui","management-system","pinia","pnpm","typescript","unocss","vite","vite-admin","vscode","vue","vue-admin","vue-admin-template","vue3","vueuse"],"latest_commit_sha":null,"homepage":"https://clover.wzc520pyf.cn/","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wzc520pyfm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-31T11:38:43.000Z","updated_at":"2025-05-13T20:38:19.000Z","dependencies_parsed_at":"2025-01-16T01:08:15.784Z","dependency_job_id":"9a4a4989-9b81-4ccb-a543-7a076436fadf","html_url":"https://github.com/wzc520pyfm/clover-admin-vue","commit_stats":{"total_commits":183,"total_committers":1,"mean_commits":183.0,"dds":0.0,"last_synced_commit":"187c6536c565f0939dfac3e8bc986004d0850560"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/wzc520pyfm/clover-admin-vue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzc520pyfm%2Fclover-admin-vue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzc520pyfm%2Fclover-admin-vue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzc520pyfm%2Fclover-admin-vue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzc520pyfm%2Fclover-admin-vue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wzc520pyfm","download_url":"https://codeload.github.com/wzc520pyfm/clover-admin-vue/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wzc520pyfm%2Fclover-admin-vue/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265738574,"owners_count":23820167,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admin","admin-template","axios","dashboard","element-admin","element-plus","element-ui","management-system","pinia","pnpm","typescript","unocss","vite","vite-admin","vscode","vue","vue-admin","vue-admin-template","vue3","vueuse"],"created_at":"2024-08-04T08:01:19.061Z","updated_at":"2025-07-18T10:09:26.846Z","avatar_url":"https://github.com/wzc520pyfm.png","language":"Vue","funding_links":[],"categories":["Vue"],"sub_categories":[],"readme":"# clover-admin-vue\n\n一个使用Vue3，Vite3，Typescript，Pinia，Unocss，Element-plus，pnpm的中后台模板，易上手，可配置，多功能，有点潮。\n\n\n## Recommended IDE Setup\n\n[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).\n\n## Type Support for `.vue` Imports in TS\n\nTypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.\n\nIf the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:\n\n1. Disable the built-in TypeScript Extension\n    1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette\n    2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`\n2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.\n\n## Customize configuration\n\nSee [Vite Configuration Reference](https://vitejs.dev/config/).\n\n## Project Setup\n\n```sh\npnpm i\n```\n\n### Compile and Hot-Reload for Development\n\n```sh\npnpm dev\n```\n\n### Type-Check, Compile and Minify for Production\n\n```sh\npnpm build\n```\n\n### Run Unit Tests with [Vitest](https://vitest.dev/)\n\n```sh\npnpm test:unit\n```\n\n### Run End-to-End Tests with [Cypress](https://www.cypress.io/)\n\n```sh\npnpm test:e2e:dev\n```\n\nThis runs the end-to-end tests against the Vite development server.\nIt is much faster than the production build.\n\nBut it's still recommended to test the production build with `test:e2e` before deploying (e.g. in CI environments):\n\n```sh\npnpm build\npnpm test:e2e\n```\n\n### Lint with [ESLint](https://eslint.org/)\n\n```sh\npnpm lint\n```\n\n### Docker部署\n\n - 构建镜像\n  \u003e docker build -t clover-admin-vue:v0.0.0 -f docker/Dockerfile .\n\n - 创建并启动容器\n  \u003e docker run --name clover -p 80:80 -d clover-admin-vue:v0.0.0\n\n - 访问CloverAdmin\n  浏览器访问`http://localhost`\n\n如果您未安装Docker, 以下步骤将从零安装Docker(以Ubuntu20.04为例):\n - 更新软件包索引, 安装必要依赖, 添加一个新的https软件源\n  \u003e sudo apt update\n  \u003e\n  \u003e sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common\n\n - 导入源仓库GPG key\n  \u003e curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -\n\n - 添加Docker APT软件源\n  \u003e sudo add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\"\n\n - 安装最新版本的Docker, 一旦安装完成, Docker服务会自动启动\n  \u003e sudo apt update\n  \u003e\n  \u003e sudo apt install docker-ce docker-ce-cli containerd.io\n\n - 安装指定版本的Docker\n   - 查看可以的版本\n    \u003e sudo apt update\n    \u003e\n    \u003e apt list -a docker-ce\n   - 安装(可用的Docker版本看起来是`5:19.03.9~3-0~ubuntu-focal`)\n    \u003e sudo apt install docker-ce=\u003cVERSION\u003e docker-ce-cli=\u003cVERSION\u003e containerd.io\n\n\n## 项目配置\n\n### 目录结构\n```tree\nclover-admin\n|-- .devcontainer               // vscode远程开发配置\n|   |-- devcontainer.json\n|   |-- Dockerfile\n|-- .husky                      // git commit提交钩子\n|-- .vscode                     // vscode插件、设置、代码片段\n|   |-- clover.code-snippets    // 代码片段\n|   |-- extensions.json         // 插件\n|   |-- setting.json            // 设置\n|-- build                       // 构建的相关配置和插件\n|   |-- config\n|   |-- plugins                 // vite插件\n|   |   |-- compress.ts         // 代码压缩\n|   |   |-- html.ts             // html（注入变量、压缩代码）\n|   |   |-- mock.ts             // mock插件\n|   |   |-- unplugin.ts         // vue宏增强、自动导入、注册注册声明、Icon封装、UI组件\n|   |   |-- visualizer.ts       // 打包依赖分析\n|   |-- utils\n|-- cypress                     // e2e测试框架\n|-- doc                         // 文档相关\n|-- docker                      // docker配置\n|   |-- .dockerignore\n|   |-- Dockerfile\n|   |-- nginx.conf\n|-- mock                        // mock服务\n|-- public\n|-- src\n|   |-- assets                  // 静态资源\n|   |   |-- animation           // lottie动画资源\n|   |   |-- svg-icon            // 本地svg图标\n|   |-- components              // 全局组件(自动导入)\n|   |   |--business             // 业务相关组件\n|   |   |--common               // 公共组件(常用组件)\n|   |   |--custom               // 自定义组件\n|   |-- composables             // 组合式函数\n|   |-- config                  // 全局静态配置\n|   |-- constants               // 全局常量\n|   |-- directives              // vue指令\n|   |-- enum                    // TS枚举\n|   |-- hooks                   // 组合式的函数hooks(内部状态)\n|   |-- layout                  // 布局组件\n|   |-- plugins                 // 插件\n|   |-- router                  // vue路由\n|   |   |-- guard\n|   |   |-- helper\n|   |   |-- modules\n|   |   |-- routes\n|   |-- sdk                     // 三方sdk\n|   |-- service                 // 网络请求\n|   |   |-- api\n|   |   |-- request\n|   |-- stores                  // pinia状态管理\n|   |-- styles                  // 全局样式\n|   |   |-- css\n|   |   |-- less\n|   |   |-- scss\n|   |-- typings                 // TS类型声明\n|   |   |-- api.d.ts            // 接口相关的类型声明\n|   |   |-- auto-import.d.ts\n|   |   |-- business.d.ts       // 业务相关的类型声明\n|   |   |-- components.d.ts\n|   |   |-- env.d.ts            // 项目级、vite相关配置\n|   |   |-- expose.d.ts         // vue组件导出类型\n|   |   |-- global.d.ts         // 全局类型\n|   |   |-- package.d.ts        // 包类型\n|   |   |-- route.d.ts          // 路由类型\n|   |   |-- router.d.ts         // 路由类型\n|   |   |-- system.d.ts         // 系统类型\n|   |   |-- vue.d.ts            // vue相关类型\n|   |-- utils                   // 工具函数\n|   |   |-- auth\n|   |   |-- common\n|   |   |-- crypto\n|   |   |-- filters\n|   |   |-- router\n|   |   |-- service\n|   |   |-- storage\n|   |-- views                   // 页面\n|   |-- App.vue                 // vue文件入口\n|   |-- globalProperties.ts     // vue全局变量\n|   |-- main.ts                 // 项目入口文件\n|-- .editorconfig               // 统一编辑器配置\n|-- .env                        // 通用环境配置\n|-- .env.config.ts              // 请求环境的配置\n|-- .env.development            // 开发环境配置\n|-- .env.production             // 生产环境配置\n|-- .eslintignore               // 配置哪些文件忽略eslint检查\n|-- .eslintrc-auto-import.json  // auto-import插件的eslint生成文件\n|-- .eslintrc.cjs               // eslint配置文件\n|-- .gitattributes              // git配置\n|-- .gitignore                  // 配置git记录时哪些文件忽略\n|-- .npmignore                  // 可忽略\n|-- .npmrc                      // npm配置\n|-- .prettierrc.json            // prettier代码格式化插件配置\n|-- commitlint.config.js        // commitlint提交规范插件配置\n|-- commitlint.config.ts        // commitlint提交规范插件配置\n|-- cypress.config.ts           // e2e测试框架配置\n|-- index.html\n|-- LICENSE\n|-- package.json\n|-- pnpm-lock.yaml\n|-- README.md\n|-- tsconfig.app.json\n|-- tsconfig.config.json\n|-- tsconfig.json               // TS配置\n|-- tsconfig.vitest.json\n|-- uno.config.ts               // unocss配置\n|-- vite.config.ts              // vite配置\n|-- vitest.config.ts            // vitest配置\n```\n\n如果你觉得项目根目录太过于庞大，你可以开启VsCode的文件嵌套功能(fileNesting.enabled)，因为根目录中大多为配置文件，不同的软件包提供的配置文件格式不统一（不单单是json，软件包为提供更好的定制能力，将配置文件格式定为ts、js等），且部分软件包不支持更改配置文件目录，因此，根目录庞大不是本项目文件组织的问题，此问题更应该归由IDE解决。\n\n推荐插件: antfu.file-nesting\n\n\n### vscode\n项目期望保持一致的开发体验, 配置了一系列vscode配置:\n1. 项目级的vscode扩展\n   - see: `./.vscode/extensions.json`\n2. 项目级的vscode设置\n   - see: `./.vscode/settings.json`\n3. 项目级的代码模板\n   - see: `./.vscode/clover.code-snippets`\n4. 如果你熟悉vscode的远程开发功能, 项目也做好了相关配置以保持一致的开发体验\n   - see: `./.devcontainer`\n5. 项目级的精心挑选的vscode图标, 包括文件和文件夹图标\n   - see: `./.vscode/settings.json`\n   - \u003cimg src=\"./doc/pic/icons-root.png\"\u003e\u003c/img\u003e\u003cimg src=\"./doc/pic/icons-src.png\"\u003e\u003c/img\u003e\n\n\n### 以新的姿态使用vue\n\n#### 自动导入、自动注册组件、响应式语法糖\n项目使用自动导入的方式来使用vue、vue-router、pinia、etc， 并且结合自动注册组件和vue3的[响应式语法糖](https://cn.vuejs.org/guide/extras/reactivity-transform.html).\n在以前, 我们使用vue可能是这样的:\n```vue\n\u003ctemplate\u003e\n    \u003cdiv\u003e{{ count }}\u003c/div\u003e\n    \u003cHelloComponent /\u003e\n\u003c/template\u003e\n\u003cscript lang='ts' setup\u003e\nimport HelloComponent from '@/components'\nimport { ref, computed } from 'vue'\nconst count = ref(0)\nconst doubled = computed(() =\u003e count.value * 2)\n\u003c/script\u003e\n```\n\n现在将使用更加简洁的方式:\n无需再手动导入vue、vue-router、pinia，所有在`./src/components`目录下的组件都将自动注册声明, 响应式语法糖可以自动脱取`.value`等.\n```vue\n\u003ctemplate\u003e\n    \u003cdiv\u003e{{ count }}\u003c/div\u003e\n    \u003cHelloComponent /\u003e\n\u003c/template\u003e\n\u003cscript lang='ts' setup\u003e\nconst count = $ref(0)\nconst doubled = computed(() =\u003e count * 2)\n\u003c/script\u003e\n```\n\n请务必使用`\u003cscript lang='ts' setup\u003e`或者`\u003cscript lang='ts'\u003e`, 不要再写纯JS, 当使用TS时, TypeScript可以和ESLint很好地配合提供类型检查,但当混入纯JS时他们无法很好地工作, 目前前端社区没有好的解决方案。\n\n#### 为vue注册全局属性\n1. 在`./src/globalProperties.ts`中\n2. 以注册全局属性`$filters`为例\n```typescript\n// globalProperties.ts\nexport function setupGlobalProperties(app: App) {\n  installGlobalProperties(filters, \"$filters\");\n  // ...\n}\n```\n还需要在`./src/typings/vue.d.ts`中为$filters定义类型\n```typescript\nexport {};\n\ndeclare module \"vue\" {\n  /** 定义在vue实例上自定义的全局属性的类型 */\n  export interface ComponentCustomProperties {\n    $filters: typeof import(\"@/utils/filters\");\n  }\n}\n```\n3. 如何访问?\n```vue\n// 模板中\n\u003ctemplate\u003e\n  \u003cp\u003e{{ $filters }}\u003c/p\u003e\n\u003c/template\u003e\n```\n\n```vue\n\u003cscript lang=\"ts\" setup\u003e\n// setup中\nconst vm = getCurrentInstance()!;\nconst { $filters } = vm.appContext.config.globalProperties;\n\u003c/script\u003e\n```\n\n#### 引入资源\n一些三方库在使用时需要导入其css样式文件, 一般会把他们导入在main.ts中, 这导致main.ts会越来越臃肿, 因此本项目将其拆分, 所有的资源文件统一放置在`./src/plugins/assets.ts`进行导入.\n\n### vite\nvite的配置在vite.config.ts中, 但其plugins配置被拆分到了build目录下, 因为plugins往往会有很长的配置.\n\n### 使用图标\n#### 使用element-plus的图标\n直接从官网复制来的图标如下:\n```html\n\u003cel-icon\u003e\u003cSearch /\u003e\u003c/el-icon\u003e\n```\n因为项目使用了自动导入图标的方式, 所以需要改为如下格式:\n```html\n\u003cel-icon\u003e\u003ci-ep-Search /\u003e\u003c/el-icon\u003e\n```\nelement-plus的图标自动导入见官方文档: https://element-plus.org/zh-CN/component/icon.html#%E8%87%AA%E5%8A%A8%E5%AF%BC%E5%85%A5\n\n#### 使用其他图标集\n本项目支持源自https://iconify.design/的所有图标, 只需简单的配置即可.\nelement-plus的图标也是https://iconify.design/中的一个图标集合, 现在在https://iconify.design/中挑选一个新图标集合:\n1. 以Material Design Icons为例: https://icon-sets.iconify.design/mdi/\n2. 点击任意一个图标, 可以看到此图标集合的前缀名(mdi):\n   \u003cimg src=\"./doc/pic/iconify-mdi.png\"\u003e\u003c/img\u003e\n3. 在`./build/plugins/unplugin.ts`中写入此图标集合的前缀名(mdi):\n```typescript\nComponents({\n  dts: \"src/typings/components.d.ts\",\n  resolvers: [\n    IconsResolver({\n      // 自动注册图标组件 how to use: \u003ci-ep-location /\u003e\n      enabledCollections: [\"ep\", \"mdi\"], // 'ep'是element图标集在https://iconify.design/ 里的集合名, 如果你引入或使用了其他图标集, 需要在此把其集合名写上\n      // ...\n    }),\n  ]\n}),\n```\n4. 现在可以像使用element-plus的图标一样使用Material Design Icons了:\n```html\n\u003ci-mdi-github class=\"text-22px\" /\u003e\n```\n\n#### 使用本地图标\n本项目也支持使用本地图标, 首先需要下载一个svg图标并放在`./src/assets/svg-icon`目录下, 接着便可在项目使用:\n```html\n// 假设我们有一个名为403.svg的图标在@/src/assets/svg-icon目录下\n\u003ci-local-403 /\u003e\n// 也可以使用el-icon将其包裹\n\u003cel-icon :size=\"400\"\u003e\u003ci-local-403 /\u003e\u003c/el-icon\u003e\n```\n\n#### 动态渲染图标\n本项目支持动态渲染图标集图标和本地图标, 参见如下示例:\n```html\n\u003c!-- iconify图标(动态渲染) --\u003e\n\u003csvg-icon icon=\"mdi-github\" /\u003e\n\u003c!-- 本地图标(动态渲染) --\u003e\n\u003csvg-icon local-icon=\"link-icon\" /\u003e\n```\n\ntip: 如果在使用svg-icon组件动态渲染本地图标后, 出现icon颜色不跟随父级而改变, 这是由于本地图标中的fill属性造成了覆盖, 删除本地图标中的fill属性即可.\n\n\n### 使用unocss\n本项目使用unocss, 当需要编写css时, 可去官方查找相应的css类名: https://uno.antfu.me/, 或者也可以参考[tailwindcss](https://www.tailwindcss.cn/)和[WindiCSS](https://windicss.org/).\nunocss的文档: https://github.com/unocss/unocss\n项目默认集成了unocss的presetUno属性包, 你可以继续集成其他属性包, 这一切配置在uno.config.ts中.\n\n```html\n// 一张圆形的图片\n\u003cimg class=\"w-32 h-32 rounded-full\" src=\"\" /\u003e\n// 等价于\n\u003cimg style=\"width: 32rem; height: 32rem; border-radius: 9999px;\" src=\"\" /\u003e\n```\n项目同时也开启了unocss的无值写法(无需再写class):\n```html\n// 一张圆形的图片\n\u003cimg w-32 h-32 rounded-full src=\"\" /\u003e\n```\n\n如果现有的css快捷写法不能满足你, 你可以自行配置:\n```ts\n// uno.config.ts\nexport default defineConfig({\n  // ...\n  shortcuts: {\n    \"wh-full\": \"w-full h-full\",\n    \"flex-center\": \"flex justify-center items-center\"\n  }\n})\n```\n```html\n\u003cdiv class=\"wh-full\"\u003e\u003c/div\u003e\n// 等价于\n\u003cdiv class=\"w-full h-full\"\u003e\u003c/div\u003e\n```\n\n如果上述写法仍无法满足需求, 也可以为unocss配置解析规则:\n```ts\nexport default defineConfig({\n  // ...\n  rules: [\n    /^wh-(\\d+)px$/,\n    ([, d]) =\u003e ({\n      width: `${d}px`,\n      height: `${d}px`,\n    }),\n  ]\n})\n```\n```html\n\u003cdiv class=\"wh-20\"\u003e\u003c/div\u003e\n// 等价于\n\u003cdiv class=\"w-20px h-20px\"\u003e\u003c/div\u003e\n```\n\nunocss也支持配置主题:\n```typescript\nexport default defineConfig({\n  // ...\n  // see: https://tailwindcss.com/docs/theme\n  theme: {\n    colors: {\n      dark: \"#18181c\",\n    },\n  }\n})\n```\n```html\n\u003cdiv class=\"bg-dark\"\u003e\u003c/div\u003e\n// 等价于\n\u003cdiv class=\"bg-#18181c\"\u003e\u003c/div\u003e\n// 等价于普通css\n\u003cdiv style=\"background-color: #18181c;\"\u003e\u003c/div\u003e\n```\n\nunocss暗黑模式:\n```typescript\nexport default defineConfig({\n  // 开启\n  presets: [presetUno({ dark: \"class\" })],\n})\n```\n```html\n// bg-dark仅在暗黑模式下生效\n\u003cdiv class=\"dark:bg-dark\"\u003e\u003c/div\u003e\n```\n\n### 配置路由\n1. 路由配置在`./src/router/modules`中, 以`关于`页面为例\n2. 在`./src/router/modules`新建`about.ts`\n```typescript\nimport Layout from \"@/layout/index.vue\";\n\nconst about = [\n  {\n    name: \"about\",\n    path: \"/about\",\n    component: Layout, // 全局框架\n    redirect: \"/about/index\", // 重定向\n    meta: { title: \"关于\", hidden: true /** 在侧边菜单中隐藏这一级菜单 */, order: 7 /** 此菜单在侧边菜单中的显示顺序 */ },\n    children: [\n      {\n        path: \"index\",\n        name: \"about_index\", // 请遵循此书写规则\"about_index\", about即父级的name, index即当前的path\n        component: () =\u003e import(\"@/views/about/index.vue\"), /** 页面组件 */\n        meta: {\n          title: \"关于\", /** 侧边菜单中显示的label */\n          icon: \"ep-warning\", /** 侧边菜单中显示的icon */\n        },\n      },\n    ],\n  },\n];\n\nexport default about;\n```\n\nicon配置支持element-plus内置icon、iconify图标(需先在vite的unplugin中引入)以及本地图标，如下：\n - 使用element-plus内置的图标：`icon: \"ep-icon-name\"` (将`icon-name`替换为你的图标名, ep为element-plus图标在iconify的图标集前缀)\n - 使用iconify中mdi图表集的图标: `icon: \"mdi-icon-name\"` (将`icon-name`替换为你的图标名, mdi为图表集的前缀)\n - 使用本地图标: `icon: \"local-icon-name\"` (将`icon-name`替换为`@/assets/svg-icon`目录下的文件名)\n\n3. 页面写在`./src/views`中\n在`./src/views`中新建目录about, 在about目录中新建index.vue:\n```vue\n\u003ctemplate\u003e\n  \u003cdark-mode-container class=\"h-full\"\u003e\n    \u003cel-card class=\"h-full\"\u003e关于\u003c/el-card\u003e\n  \u003c/dark-mode-container\u003e\n\u003c/template\u003e\n\n\u003cscript lang=\"ts\" setup\u003e\u003c/script\u003e\n\n\u003cstyle lang=\"less\" scoped\u003e\n.el-card {\n  #ep.el-card-rounded();\n}\n\u003c/style\u003e\n```\n\n### 发起网络请求\n本项目将网络请求统一放置在`./src/service`目录下.\n如果你想参考示例, 请见: `./src/views/function/request`.\n要声明一个api, 需按照如下步骤:\n1. 在`./src/service/request/index.ts`中创建一个Request实例\n```typescript\nimport { createRequest } from \"./request\";\n\nexport const mockRequest = createRequest({ baseURL: \"/mock\" });\n```\n2. 在`./src/service/api`目录下声明api\n```typescript\n// /api/auth.ts\nimport { mockRequest } from \"../request\";\n\nexport function fetchLogin(username: string, password: string) {\n  return mockRequest.post\u003cApiAuth.Token\u003e(\"/login\", { username, password });\n}\n```\n3. 在`./src/typings/api.d.ts`中声明api相关的数据类型\n```typescript\ndeclare namespace ApiAuth {\n  /** token */\n  interface Token {\n    token: string;\n  }\n}\n```\n4. 在合适的位置调用fetchLogin, 假如就在login.vue中:\n```vue\n\u003cscript lang=\"ts\" setup\u003e\nimport { fetchLogin } from \"@/service\";\n\nconst requestLogin = async () =\u003e {\n  const { data } = await fetchLogin(\"lalala\", \"123456\");\n  if (data) {\n    console.log(\"data\", data); // { token: \"这是token\" }\n  }\n}\n\u003c/script\u003e\n```\n5. 注意你无需做任何的code判断, 因为`./src/service/request`中已经统一做好了处理:\n - 当axios请求发生错误时, 会弹出el-message提示, 会依据不同的错误类型弹出不同提示, 见: `./src/utils/service/error.ts`中的handleAxiosError\n - 当http状态失败时, 会弹出el-message提示, 会根据http状态码不同而匹配显示不同的错误信息, 见: `./src/config/service.ts`中的ERROR_STATUS\n - 当后端接口一切正常但返回业务上的错误时, 会弹出el-message提示, 提示内容是后端返回的message字段信息\n6. 项目默认后端接口返回的数据结构如下:\n```typescript\nconst response = {\n  code: 200,\n  message: \"成功\",\n  data: \"SomeObjectOrOther\"\n}\n```\n如果后端返回的数据结构与上述不符, 可以在创建Request时进行配置:\n```typescript\nexport const mockRequest = createRequest({ baseURL: \"/mock\" }, {\n  codeField: \"statusCode\",\n  dataField: \"data\",\n  msgField: \"msg\",\n  successCode: 0,\n});\n```\n现在, 当你使用mockRequest发起请求时, 当后端返回的statusCode不为0时, 将弹出el-message错误提示, 提示内容就是msg字段的内容.\n\n7. 默认情况下, 发起请求将为你返回其data内容:\n```vue\n\u003cscript lang=\"ts\" setup\u003e\nimport { fetchLogin } from \"@/service\";\n\nconst requestLogin = async () =\u003e {\n  const { data } = await fetchLogin(\"lalala\", \"123456\");\n  if (data) {\n    console.log(\"data\", data); // { token: \"这是token\" }\n  }\n}\n\u003c/script\u003e\n```\n如你所见, 你只能拿到接口返回的data的内容, 如果你想获取其他内容, 比如响应头, 你可以在声明api时进行配置:\n```typescript\nexport function fetchLogin(username: string, password: string) {\n  // entries可以配置所有的AxiosResponse的key\n  return mockRequest.post\u003cApiAuth.Token\u003e(\"/login\", { username, password }, { entries: [\"data\", \"headers\"] });\n}\n```\n现在, 你可以拿到响应头了\n```vue\n\u003cscript lang=\"ts\" setup\u003e\nimport { fetchLogin } from \"@/service\";\n\nconst requestLogin = async () =\u003e {\n  const { data, headers } = await fetchLogin(\"lalala\", \"123456\");\n  console.log(\"data\", data); // { token: \"这是token\" }\n  console.log(\"headers\", headers);\n}\n\u003c/script\u003e\n```\n8. 有些变更类的后端接口, 返回的数据中不会包含data, 例如:\n```typescript\nconst message = {\n  code: 200,\n  message: \"成功\"\n}\n```\n本项目会自动为其补充data:\n```typescript\nconst message = {\n  code: 200,\n  message: \"成功\",\n  data: \"success\"\n}\n```\n9. 开启代理\n```typescript\n// .env.config.ts\nconst serviceEnv: ServiceEnv = {\n  // 项目默认启动在5574端口\n  dev: [\n    /**\n     * 代理:\n     *  - 将http://127.0.0.1:5574/my-api/xx代理到http://127.0.0.1:5976/backend-api/xx\n     *  - 也可以不写rewritten, 默认情况下将: http://127.0.0.1:5574/my-api/xx代理到http://127.0.0.1:5976/xx\n     */\n    {\n      url: \"http://127.0.0.1:5976\",\n      urlPattern: \"/my-api\",\n      rewritten: \"/backend-api\"\n    }\n  ],\n  test: [],\n  prod: []\n}\n```\n\n### 设计模式\n项目鼓励使用设计模式解决实际问题, 下面例举一些场景.\n\n注意: 具体属于哪种设计模式也取决于看待问题的角度, 比如组件, 可以认为是工厂模式(传入简单的参数即可获取到一段模板), 也可以认为是门面模式(组件内部会执行复杂的逻辑, 而使用者无需关心它们).\n* 策略模式: 见`@/utils/common/pattern.ts`\n  * use:\n  ```ts\n  // 策略模式会执行所有条件为true的策略\n  const actions = [\n    [\n      true,\n      () =\u003e \"执行\"\n    ],\n    [\n      false,\n      () =\u003e \"不执行\"\n    ]\n  ]\n  exeStrategyActions(actions);\n  ```\n  * 在axios里面无法使用useRouter和useRoute, 我们可以通过globalRouter来操作router, 我们可以依据是否在axios为策略, 执行不同的获取router方式.\n* 中介模式: 在引入三方库时(比如crypto), 我们会对其做封装, 之后使用我们的封装对象来对三方库进行操作, 这便是中介模式.\n* 代理模式: 我们使用的vue就是代理模式的好例子!\n* 门面模式: 我们将复杂操作封装, 提供简单调用, 便是门面模式.\n* 适配器模式: 我们定义了一个处理接口返回值的函数, 但有一些三方接口与我们后端接口返回结果不一致, 导致接口处理函数不能工作, 这时就可提供一个处理三方接口返回值的适配器函数.\n* 原型模式: 有时我们需要一个数组,我们会对其内部做处理但不希望影响原数组, 此时就可以利用原型模式获取原数组一个copy(数组常常使用slice方法获取副本).\n* 工厂模式: 我们为组件传入一些参数即可或得一段html模板, 这便是一种工程模式.\n* 责任链模式: 为实现某种功能, 依次尝试可使用的方法. 见`@utils/common/responsibilitiesChain.ts`\n  * use: 提供灵活的调用方式\n  ```ts\n  // 示例1: 按照添加顺序, 进行链处理\n  const canBuyChain = new CustomChain();\n  // 按照添加顺序组成责任链\n  canBuyChain.append(\n    new ResponsibilitiesChainNode(\n      () =\u003e false, // 条件\n      () =\u003e \"一号\" // 条件匹配时执行的动作\n    )\n  );\n  canBuyChain.append(\n    new ResponsibilitiesChainNode(\n      () =\u003e true,\n      () =\u003e \"二号\"\n    )\n  );\n  canBuyChain.append(\n    new ResponsibilitiesChainNode(\n      () =\u003e true,\n      () =\u003e \"三号\"\n    )\n  );\n  // 一旦执行成功就返回\n  const res = canBuyChain.execute(); // 二号\n  ```\n  ```ts\n  // 示例2: 灵活设置链处理顺序\n  const canBuyChain2 = new CustomChain();\n  const chain1 = new ResponsibilitiesChainNode(\n    () =\u003e true,\n    () =\u003e \"哦耶1\"\n  );\n  const chain2 = new ResponsibilitiesChainNode(\n    () =\u003e true,\n    () =\u003e \"哦耶2\"\n  );\n  const chain3 = new ResponsibilitiesChainNode(\n    () =\u003e true,\n    () =\u003e \"哦耶3\" // 在最后一个chain设置的成功函数的返回值会返回给链外\n  );\n  chain1.setNext(chain2);\n  chain2.setNext(chain3);\n  canBuyChain2.append(chain1);\n  // 执行到底返回最后结果, 遇到失败则结束\n  const res2 = canBuyChain2.executeAll(); // 哦耶3\n  // 执行到底返回全部结果, 遇到失败则结束\n  const res22 = canBuyChain2.executeAllSettled(); // [\"哦耶1\", \"哦耶2\", \"哦耶3\"]\n  ```\n  ```ts\n  // 示例3: 支持自定义chainNode类\n  class MyChainNode1 extends ResponsibilitiesChain {\n    public canHandle(): boolean {\n      return false;\n    }\n    public doHandle() {\n      return \"一号节点执行成功\";\n    }\n    public errHandle() {\n      return;\n    }\n  }\n  class MyChainNode2 extends ResponsibilitiesChain {\n    public canHandle(): boolean {\n      return true;\n    }\n    public doHandle() {\n      return \"二号节点执行成功\";\n    }\n    public errHandle() {\n      return;\n    }\n  }\n  const myChainNode1 = new MyChainNode1();\n  const myChainNode2 = new MyChainNode2();\n\n  const myChain = new CustomChain();\n  myChain.append(myChainNode1);\n  myChain.append(myChainNode2);\n\n  const myRes = myChain.execute();\n  ```\n  你也可以查看`@/views/component/complex-form/components/ResponsibilityValidatorForm.vue`, 在这里提供了一个小游戏来解释责任链的使用\n\n\n### 三方功能库\n项目使用了诸多三方库, 以下列出其文档\n* BatterScroll: https://better-scroll.github.io/docs/zh-CN/\n* Swiper: https://swiperjs.com/\n* 高德地图: https://lbs.amap.com/\n* ECharts：https://echarts.apache.org/zh/index.html\n* Iconify：https://icones.netlify.app/\n* AliOSS：https://help.aliyun.com/product/31815.html\n* markdown编辑器：https://github.com/Vanessa219/vditor\n* 富文本编辑器：https://github.com/wangeditor-team/wangEditor\n* 视频播放器：https://github.com/bytedance/xgplayer\n\n### 常见问题\n#### 安装Cypress太慢\n参考自: https://www.lfhacks.com/tech/cypress-download-failure/\n\n网络不畅, 且此资源较大, 官方给出的解决方式: 设置CYPRESS_DOWNLOAD_MIRROR常量为https://download.cypress.io/desktop,但不同操作系统设置常量的方式不同, 以下列出几种:\n\nwindows命令行\n```shell\nset CYPRESS_DOWNLOAD_MIRROR=https://download.cypress.io/desktop\n```\nwindowsPowerShell\n```shell\n$env:CYPRESS_DOWNLOAD_MIRROR=\"https://download.cypress.io/desktop\"\n```\nLinux、Mac\n```shell\nCYPRESS_DOWNLOAD_MIRROR=\"https://download.cypress.io/desktop\"\n```\n\n设置完成后再次install即可\n\n#### 自动导入eslint报错\n按照AutoImport插件介绍, 需要在第一个运行项目时设置`eslintrc: {enabled: true}`生成eslint文件, 后续可改为false.\n\n本项目AutoImport位置在./build/plugins/unplugin.ts\n```typescript\nAutoImport({\n    // ...\n    eslintrc: {\n        enabled: false, // 自动生成全局声明文件, 不需要eslint检查(在.eslintrc-auto-import.json生成成功之后就可以改为false)\n        filepath: \"./.eslintrc-auto-import.json\",\n        globalsPropValue: true,\n    },\n}),\n```\n\n#### 项目依赖的库有bug, 需要修改\n\n开发时, 遇到项目依赖的库有bug, 等不及库作者修复, 可以自行修改打补丁\n - 注意: pnpm在v7.4.0开始支持打补丁\n\n流程如下:\n 1. 执行下述命令生成一个依赖库的拷贝(yourPkg必须指定版本)\n  \u003e pnpm patch yourPkg@0.0.1\n  \u003e\n  \u003eYou can now edit the following folder: C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\482a1b2c5aaad6b4abb4d39bab8ef39c\\user\n\n 2. 打开提示的目录, 对库代码进行更改\n 3. 执行下述命令, 生成库代码的补丁文件(依据你的系统, 决定是否需要为文件路径使用转移符号)\n  \u003e pnpm patch-commit  C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\482a1b2c5aaad6b4abb4d39bab8ef39c\\user\n\n  这会在项目目录下生成`patches/yourPkg@0.0.1.patch`文件, 且在package.json中自动更新如下配置:\n  ```json\n  {\n    \"pnpm\": {\n      \"patchedDependencies\": {\n        \"yourPkg@0.0.1\": \"patches/yourPkg@0.0.1.patch\"\n      }\n    }\n  }\n  ```\n\n\n### 提交规范\n\n好的提交规范可以清晰地了解到开发者试图做什么, 并且有助于自动生成更改日志.\n\n推荐使用项目内置的`pnpm cz`命令进行git提交, 这是一个交互式的git提交界面.\n\n#### 提交消息的模板\n\n```md\nfeat(components): [HoverLink] 增加悬浮链接组件(使用命令式语气)\n\n主体行和主体内容之间用空白行隔开(可以有预期时间)\n通过一行或多行描述你的修改信息(大批量更改务必描述修改详情)\n每一行的首字母大写\n且每一行的总字符数限制在72个以内最优, 超过了将不易于他人理解\n\n- 你也可以通过添加子项列表符号来为内容布局\n```\n主题标题的格式是：\n\n```md\n[type](scope 域): [messages]\n```\n\n#### 通用git惯例\n\u003ca href=\"https://www.conventionalcommits.org/\" class=\"vp-link\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e通用惯例\u003csvg preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 24 24\" width=\"1.2em\" height=\"1.2em\" class=\"link-icon\"\u003e\u003cpath fill=\"currentColor\" d=\"M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm11-3v8h-2V6.413l-7.793 7.794l-1.414-1.414L17.585 5H13V3h8z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\n\n#### 保持git提交历史简介\n\n\u003ca href=\"https://about.gitlab.com/blog/2018/06/07/keeping-git-commit-history-clean/\" class=\"vp-link\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e保持 git 提交历史简洁\u003csvg preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 24 24\" width=\"1.2em\" height=\"1.2em\" class=\"link-icon\"\u003e\u003cpath fill=\"currentColor\" d=\"M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm11-3v8h-2V6.413l-7.793 7.794l-1.414-1.414L17.585 5H13V3h8z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\n\n如果你更喜欢中文, 项目也对此作好了翻译, \u003ca href=\"./doc/git-commit-history-clean-cn.md\" target=\"_blank\"\u003e点这里浏览\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwzc520pyfm%2Fclover-admin-vue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwzc520pyfm%2Fclover-admin-vue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwzc520pyfm%2Fclover-admin-vue/lists"}