setting-center-systerm
v1.0.1
Published
项目里封装了一些组件,使用时一定要使用组件库组件,如果组件库组件场景不满足你的需求,如果扩展功能为通用功能,反馈到中台进行扩展,否则尽力和产品设计沟通使用通用组件,如果明确组件无法满足定制化需求可以自行封装业务组件或页面组件。
Readme
vuexy
开始项目
前言
关于组件
项目里封装了一些组件,使用时一定要使用组件库组件,如果组件库组件场景不满足你的需求,如果扩展功能为通用功能,反馈到中台进行扩展,否则尽力和产品设计沟通使用通用组件,如果明确组件无法满足定制化需求可以自行封装业务组件或页面组件。
关于可组合项
公共包composables封装了公共可组合项,开发列表和表单优先使用可组合项封装逻辑,后续如扩展通用功能,反馈中台进行扩展。
关于方法
公共包utils封装了如常用表单校验规则等方法,优先使用公共方法进行开发,后续如扩展通用功能,反馈中台进行扩展。
环境准备
工具配置
如果使用的 IDE 是vscode(推荐)的话,可以安装以下工具来提高开发效率及代码格式化
- ESLint - 脚本代码检查
- Editor Config - 编辑器配置文件识别
- Javascript Snippets - JS代码补全
- Stylelint - css 格式化
- Vue - Official - vue 开发必备
- Iconify IntelliSense - Iconify 图标插件
- cssrem - px转rem
- vue-nuxt-snippets - Vue/Nuxt语法补全
- AyncEnv - 环境变量配置
安装
安装 Node.js
如果您电脑未安装Node.js,请安装它。
安装依赖
必须使用 pnpm进行依赖安装(若其他包管理器安装不了需要自行处理)。
如果未安装pnpm,可以用下面命令来进行全局安装
# 全局安装pnpm
npm install -g pnpm
# 验证
pnpm -v # 出现对应版本号即代表安装成功依赖安装命令
在项目根目录下,打开命令窗口执行,耐心等待安装完成即可
# 安装依赖
pnpm inpm script
"scripts": {
# 本地环境运行
"dev": "vite",
# 打包构建
"build": "vite build",
# 打包构建的结果预览
"preview": "vite preview --port 5050",
# 生成icon.css文件
"build:icons": "tsx common-package/plugins/iconify/build-icons.ts",
# 初始化 Mock Service Worker,创建 public/mockServiceWorker.js 文件
"msw:init": "msw init public/ --save",
# 执行npm install之前的钩子,限制只能使用pnpm安装
"preinstall": "npx only-allow pnpm",
# 执行npm install之后的钩子,安装完成依赖包后
"postinstall": "npm run build:icons && npm run msw:init",
# 执行 eslint 校验
"lint": "eslint . -c .eslintrc.cjs --fix --rulesdir eslint-internal-rules/ --ext .ts,.js,.cjs,.vue,.tsx,.jsx",
# 执行 eslint 校验,并修复部分问题
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,build}/**/*.{vue,js,ts,tsx}\" --fix",
# 执行 stylelint 格式化
"lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/",
# git hooks
"prepare": "husky",
# 自动生成ts接口类型文件
"ytt": "ytt",
},目录说明
.
├── common-package # 框架公共包(Git子模块,由中台维护)
│ ├── @core # 框架源代码
│ ├── @layout # 框架布局组件
│ ├── assets # 静态资源
│ │ ├── images # 图片、svg、icon
│ │ └── styles # 全局样式文件
│ ├── components # 组件库组件
│ │ ├── common # 二次封装的公共组件
│ │ ├── timer # 计时器组件
│ ├── composables # 公共可组合项
│ ├── directives # 自定义指令
│ ├── enums # 枚举
│ ├── locales # 多语言
│ ├── plugins # 框架公共插件
│ │ ├── 1.router # 路由插件
│ │ ├── casl # 权限插件
│ │ ├── i18n # 国际化插件
│ │ ├── iconify # 图标插件
│ │ ├── vuetify # 组件库引入
│ │ ├── 2.pinia.ts # 持久化存储
│ │ └── layouts.ts # 布局文件引入
│ └── utils # 公共方法
├── public # 公共静态资源目录
├── build # 打包脚本相关
│ ├── config # 配置文件
│ ├── generate # 生成器
│ ├── script # 脚本
│ └── vite # vite配置
├── src # 主目录
│ ├── api # 接口文件
│ │ ├── modules # 文件夹与接口类型文件夹保持一致(文件名称自行定义)
│ │ └── types # 接口类型定义文件
│ ├── assets # 资源文件
│ │ ├── images # 项目存放图片的文件夹
│ │ └── styles # 全局样式文件夹
│ ├── components # 业务组件
│ ├── directives # 指令
│ ├── enums # 枚举/常量
│ ├── directives # 可组合项
│ ├── layouts # 布局文件
│ │ └── components # 页面布局组件
│ ├── locales # 多语言
│ ├── navigation # 菜单配置
│ ├── pages # 页面入口文件(根据该目录结构生成路由)
│ ├── router # 路由配置(拦截器、重定向等配置)
│ ├── store # 数据仓库
│ ├── types # ts接口
│ ├── utils # 项目工具类
│ ├── views # 页面中的子组件
│ ├── App.vue # 主入口模板文件
│ └── main.ts # 主入口
├── .eslintrc.cjs # eslint 配置文件
├── .stylelintrc.json # stylelint 配置文件
├── commitlint.config.js # commitlint配置文件
├── index.html # 应用入口
├── themeConfig.ts # 框架主题配置文件
├── tsconfig.json # ts配置文件
├── vite.config.ts # vite配置文件
└── ytt.config.ts # 自动生成接口类型配置文件项目配置
环境变量配置
项目的环境变量配置位于项目根目录下的 .env
具体可以参考 Vite 文档
.env # 在所有的环境中被载入多语言配置
用于配置多语言信息
在根目录的 themeConfig.ts 内配置增加新的语言且在 src/locales 文件夹中加入语言文件。
export const { themeConfig, layoutConfig } = defineThemeConfig({
app: {
i18n: {
enable: true,
defaultLocale: 'zh-CN',
langConfig: [
{
label: '简体中文',
i18nLang: 'zh-CN',
isRTL: false,
},
{
label: 'English',
i18nLang: 'en',
isRTL: false,
}
],
},
},
})主题配置
在 common-package/plugins/vuetify.ts 文件中修改主题色和其他颜色
import type { ThemeDefinition } from 'vuetify'
export const staticPrimaryColor = '#417AF6'
export const staticPrimaryDarkenColor = '#3A6EDD'
export const themes: Record<string, ThemeDefinition> = {
light: {
dark: false,
colors: {
'primary': staticPrimaryColor,
'on-primary': '#fff',
'primary-darken-1': staticPrimaryDarkenColor,
'secondary': '#808390',
'on-secondary': '#fff',
'secondary-darken-1': '#737682',
'success': '#28C76F',
'on-success': '#fff',
'success-darken-1': '#24B364',
'info': '#00BAD1',
'on-info': '#fff',
'info-darken-1': '#00A7BC',
'warning': '#FF9F43',
'on-warning': '#fff',
'warning-darken-1': '#E68F3C',
'error': '#FF4C51',
'on-error': '#fff',
'error-darken-1': '#E64449',
'background': '#F7F7FA',
'on-background': '#2F2B3D',
'chatbg': '#f2f3f5', // 扩展的设计要求的
'surface': '#fff',
'on-surface': '#2F2B3D',
'grey-50': '#FAFAFA',
'grey-100': '#F5F5F5',
'grey-200': '#EEEEEE',
'grey-300': '#E0E0E0',
'grey-400': '#BDBDBD',
'grey-500': '#9E9E9E',
'grey-600': '#757575',
'grey-700': '#616161',
'grey-800': '#424242',
'grey-900': '#212121',
'grey-light': '#FAFAFA',
'perfect-scrollbar-thumb': '#DBDADE',
'skin-bordered-background': '#fff',
'skin-bordered-surface': '#fff',
'expansion-panel-text-custom-bg': '#fafafa',
},
variables: {
'code-color': '#d400ff',
'overlay-scrim-background': '#2F2B3D',
'tooltip-background': '#2F2B3D',
'overlay-scrim-opacity': 0.5,
'hover-opacity': 0.06,
'focus-opacity': 0.1,
'selected-opacity': 0.08,
'activated-opacity': 0.16,
'pressed-opacity': 0.14,
'dragged-opacity': 0.1,
'disabled-opacity': 0.4,
'border-color': '#2F2B3D',
'border-opacity': 0.12,
'table-header-color': '#EAEAEC',
'high-emphasis-opacity': 0.9,
'medium-emphasis-opacity': 0.7,
'switch-opacity': 0.2,
'switch-disabled-track-opacity': 0.3,
'switch-disabled-thumb-opacity': 0.4,
'switch-checked-disabled-opacity': 0.3,
'track-bg': '#F0F0F2',
// Shadows
'shadow-key-umbra-color': '#2F2B3D',
'shadow-xs-opacity': 0.04,
'shadow-sm-opacity': 0.05,
'shadow-md-opacity': 0.06,
'shadow-lg-opacity': 0.12,
'shadow-xl-opacity': 0.14,
},
},
dark: {
dark: true,
colors: {
'primary': staticPrimaryColor,
'on-primary': '#fff',
'primary-darken-1': staticPrimaryDarkenColor,
'secondary': '#808390',
'on-secondary': '#fff',
'secondary-darken-1': '#737682',
'success': '#28C76F',
'on-success': '#fff',
'success-darken-1': '#24B364',
'info': '#00BAD1',
'on-info': '#fff',
'info-darken-1': '#00A7BC',
'warning': '#FF9F43',
'on-warning': '#fff',
'warning-darken-1': '#E68F3C',
'error': '#FF4C51',
'on-error': '#fff',
'error-darken-1': '#E64449',
'background': '#25293C',
'on-background': '#E1DEF5',
'chatbg': '#202534', // 扩展的设计要求的
'surface': '#2F3349',
'on-surface': '#E1DEF5',
'grey-50': '#26293A',
'grey-100': '#2F3349',
'grey-200': '#26293A',
'grey-300': '#4A5072',
'grey-400': '#5E6692',
'grey-500': '#7983BB',
'grey-600': '#AAB3DE',
'grey-700': '#B6BEE3',
'grey-800': '#CFD3EC',
'grey-900': '#E7E9F6',
'grey-light': '#353A52',
'perfect-scrollbar-thumb': '#4A5072',
'skin-bordered-background': '#2F3349',
'skin-bordered-surface': '#2F3349',
},
variables: {
'code-color': '#d400ff',
'overlay-scrim-background': '#171925',
'tooltip-background': '#F7F4FF',
'overlay-scrim-opacity': 0.6,
'hover-opacity': 0.06,
'focus-opacity': 0.1,
'selected-opacity': 0.08,
'activated-opacity': 0.16,
'pressed-opacity': 0.14,
'dragged-opacity': 0.1,
'disabled-opacity': 0.4,
'border-color': '#E1DEF5',
'border-opacity': 0.12,
'table-header-color': '#535876',
'high-emphasis-opacity': 0.9,
'medium-emphasis-opacity': 0.7,
'switch-opacity': 0.4,
'switch-disabled-track-opacity': 0.4,
'switch-disabled-thumb-opacity': 0.8,
'switch-checked-disabled-opacity': 0.3,
'track-bg': '#3A3F57',
// Shadows
'shadow-key-umbra-color': '#131120',
'shadow-xs-opacity': 0.12,
'shadow-sm-opacity': 0.14,
'shadow-md-opacity': 0.16,
'shadow-lg-opacity': 0.22,
'shadow-xl-opacity': 0.24,
},
},
}
export default themes流程图

目录详细内容介绍
common-component
公共子模块,具体内容前往 common-component REEDME 进行学习
src
api
接口内容目录,根据后端controller定义模块目录结构
types
存放ts接口数据类型,执行pnpm ytt自动生成,禁止修改
assets
存放静态资源
images:图片、icon、svg
styles:
- _template:可配置修改模板样式
- _vuetify:可配置修改组件库样式

components
项目全局组件(已全局导入,不需要组件引入)
composables
项目全局可组合项(已全局导入,不需要组件引入)
enums
存放枚举文件
directives
自定义指令(可以在index.ts中全局注册)
layouts
页面布局,布局中包含的功能由项目自行决定
- default:带头部、侧边栏和底部
- blank:不带头部和侧边栏和底部
locales
国际化数据,代码中除注释外不能写中文。默认包含 zh-CN.json 文件
navigation
菜单数据,横向菜单和纵向菜单需要单独配置
pages
项目页面, 只需创建一个新文件,新路由就会根据文件名及其在 pages 目录中的位置自动生成,如果样式文件代码量超过20行,单独在同一层级抽离scss文件
router
全局路由拦截配置和重定向配置
store
本地存贮数据
types
ts接口类型,根据模块新建文件
utils
公共方法,使用时自动导入
views
页面子组件,目录结构与pages文件夹保持一致,如果样式文件代码量超过20行,单独在同一层级抽离scss文件
路由
unplugin-vue-route:新建文件自动生成路由,动态路由变量只能有一个
默认情况,此插件会检查 src/pages 文件夹中的
.vue文件,并根据文件名生成相应的路由结构src/pages ├── index.vue ├── about.vue └── users ├── index.vue └── [id].vue- /:-》 index.vue
- /about:-》 about.vue
- /users:-》 users/index.vue
- /users/:id:-》 users/[id].vue
嵌套路由:通过 在同名文件夹旁边定义一个
.vue文件来自动定义的。如果你同时创建了 src/pages/users/index.vue 和 src/pages/users.vue 组件,则 src/pages/users/index.vue 将在 src/pages/users.vue 的<RouterView中呈现src/pages ├── users.vue └── users ├── index.vue └── [id].vue这样的目录最终生成路由如下
const routes = [ { path: '/users', component: () => import('src/pages/users.vue'), children: [ { path: '', component: () => import('src/pages/users/index.vue') }, ], }, ]如果需要自定义或者重定向的路由需要到
plugins/router/additional-routes添加路由扩展:
definePage()允许您定义与路由相关的任何额外属性 ,在页面中<script setup lang="ts"> definedPage({ meta: { layout: 'blank', public: true, }, }) </script>扩展字段为
import 'vue-router' declare module 'vue-router' { interface RouteMeta { action?: string subject?: string layoutWrapperClasses?: string navActiveLink?: RouteLocationRaw layout?: 'blank' | 'default' unauthenticatedOnly?: boolean public?: boolean } }action:'create' | 'read' | 'update' | 'delete' | 'manage':manage=create+read+update+delete
subject:'User' | 'all' :'User' 等可以自行扩展
layoutWrapperClasses:可配置给 layout 上加样式,例如,配置页面最大高度,不出现滚动条
definedPage({ meta: { layoutWrapperClasses: 'layout-content-height-fixed' } })navActiveLink:
layout:默认为 default:有头部、侧边栏、底部;blank:例如 404 页面
unauthenticatedOnly: 限制登录后访问应用程序中的某些页面。此页面包括 Login、Register、Forgot password 等,页面设置为
true时,用户已登录,如果请求的路由中存在unauthenticatedOnly路由元,那么我们会将用户重定向到主页public: 当用户导航到新页面时,我们会仔细验证指定为 public 的路由的可访问性。例如正在维护中的错误页面(例如 404)等,欢迎登录和非登录用户,不受任何限制。因此,所有用户都可以自由探索内容,而不会遇到障碍
权限
- public 为 true 的页面,是不需要权限验证的页面,用户不登录就可以进入的页面,例如推广页
- 页面不设置 action 和 subject 的话,只有权限为{action:'manage',subject:'all'}可以进入
- action:
- read 为最基础的权限,基础用户可以进入的页面都可以设置为 read,例如列表页、详情页
- create 为录入页或控制录入按钮是否显示,一般情况下,开发会把录入页和编辑页定为同一页面,此时权限 action 设置为 create
- update 为编辑页或控制编辑按钮是否显示
- delete 用来控制删除按钮是否有权限
- manage=create+read+update+delete,manage 权限比较高
- 如果还有其他功能的话,可以自定义 ,manage 可以进入自定义的 action
- subject:
- 自定义页面别名,比如用户页面可以定义为 User,
- 新增用户页:{action:'create',subject:'User'}
- 编辑用户页:如何和新增页面为同一页的话:{action:'create',subject:'User'},否则为:{action:'update',subject:'User'}
- 列表页/详情页:{action:'read',subject:'User'}
- 删除按钮:{action:'delete',subject:'User'}
- 自定义页面别名,比如用户页面可以定义为 User,
- 关键点
action:用户可以执行的操作(如manage、create、read、update、delete)。subject:操作的目标实体(如Post、Comment、User、all)。fields:可选,用户可以操作的字段(如['title', 'content'])。conditions:可选,操作的条件(如{ authorId: user.id })。inverted:可选,表示规则是否是反向的(即不能执行的操作)。reason:可选,表示规则的原因。
注:详细可查看CASL官网
开发须知
自动导入工具
使用Vite插件
unplugin-auto-import,以下文件夹下的方法会进行全局导入,可以在页面中直接使用// Docs: https://github.com/antfu/unplugin-auto-import#unplugin-auto-import AutoImport({ imports: ['vue', VueRouterAutoImports, '@vueuse/core', '@vueuse/math', 'vue-i18n', 'pinia'], dirs: [ './common-package/@core/utils', './common-package/@core/composable/', './common-package/composables/', './common-package/plugins/*/composables/*', './src/utils/!(**/*.d.ts)', // 包含 utils 目录下的所有文件,但排除以 .d.ts 结尾的文件 ], vueTemplate: true, // ℹ️ Disabled to avoid confusion & accidental usage ignore: ['useCookies', 'useStorage'], }),使用Vite插件
unplugin-vue-components,以下文件夹下的组件,使用时不需要进行导入Components({ dirs: ['common-package/@core/components', 'src/components', 'common-package/components'], dts: true, resolvers: [ componentName => { // Auto import `VueApexCharts` if (componentName === 'VueApexCharts') return { name: 'default', from: 'vue3-apexcharts', as: 'VueApexCharts' } }, ], }),别名
resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), '@common-package': fileURLToPath(new URL('./common-package', import.meta.url)), '@themeConfig': fileURLToPath(new URL('./themeConfig.ts', import.meta.url)), '@core': fileURLToPath(new URL('./common-package/@core', import.meta.url)), '@layouts': fileURLToPath(new URL('./common-package/@layouts', import.meta.url)), '@images': fileURLToPath(new URL('./src/assets/images/', import.meta.url)), '@styles': fileURLToPath(new URL('./src/assets/styles/', import.meta.url)), '@configured-variables': fileURLToPath(new URL('./src/assets/styles/variables/_template.scss', import.meta.url)), '@db': fileURLToPath(new URL('./src/fake-api/handlers/', import.meta.url)), '@api-utils': fileURLToPath(new URL('./src/fake-api/utils/', import.meta.url)), }, },
表单校验
通用表单校验规则写到common-package/composables/rules中,例如必填校验,手机号校验,身份证号校验,邮箱校验等
公共组件开发
框架公共组件统一在common-package/components目录下开发,Vuetify组件是以V开头,Vuexy公共组件以App开头,框架层面的组件以C开头
Mock&联调
axios 封装
- utils/api:axios 的封装
- api:存放 api 接口+接口请求和响应数据类型
自动生成接口 type
- api/type:存放根据接口自动生成 ts
- ytt.config:配置接口文档
mock 与 api 接口切换+mock 数据自动生成
通过切换
.env文件里的VITE_API_BASE_URL进行地址切换mock 数据:

注意,使用 mock,需要后端配置好是否必填,以及字段类型,不可以出现文档和接口不统一情况
mock 数据制定:
https://apifox.com/apiskills/yapi-mock-service-tutorial/ https://juejin.cn/post/6898213163464917005
https://www.bookstack.cn/read/yapi-1.9-zh/18abfb65db2814f0.md
git 提交规范
commit 规范
| 项目 | Value | | -------- | ------------------------------------------------------------ | | feat | 新功能 | | fix | 修复 bug | | perf | 性能优化 | | style | 格式化变动,不影响代码逻辑,比如删除分号、空白等 | | docs | 仅包含对文档的修改 | | test | 添加或修改测试代码 | | refactor | 重构 | | build | 项目打包 | | ci | 集成配置文件或者脚本的修改 | | chore | 构建流程、外部依赖变更,不修改 src 或 test 文件的更改(如升级 npm 包、修改 webpack 配置等) | | revert | 如果某次提交被撤销,可以使用这个类型,回滚 | | wip | 工作进行中,通常用于标记尚未完成的提交 | | workflow | 工作流相关的更改(例如 GitHub Actions, GitLab CI 等) | | types | 类型定义更改(例如 TypeScript 的类型定义) | | release | 发布新版本 |
提交代码时会 eslint 校验,检验不通过则,上传不成功,可执行
pnpm lint:eslint、pnpm lint:stylelint、pnpm lint,修改报错代码
升级指南(只中台须知)
大部分替换项目中的 @core 和 @layouts 文件夹就足够了 ,但还需要验证一下, @core 和 @layouts之外的内容是否受到影响,所以@core 和 @layouts 文件夹不可进行修改
- vscode 安装插件: Compare Folders
- 俩个项目都在工作区打开,选中两个项目后,右击选中
Compare Folders,则会进行文件对比

- 差异
- 只有我的目录里有,被比较的目录中没有的文件
- 只有被比较的目录里有,而我的目录里没有的文件
- 被比较的目录文件内容
- 我的目录文件内容
- 注意:页面统一采用 LF
