原创 琢磨先生TT 2025-06-24 08:31 重庆
作为一名开发者,我发现传统系统在 用户体验 和 视觉设计 上不能完全满足需求。这让我开始思考,如何设计一款既精致美观又能提供顺畅操作体验的后台系统。
启动项目本项目使用 pnpm 工具安装依赖,推荐使用 pnpm
安装依赖npm install -g pnpm
或
yarn global add pnpm
如果 pnpm install 安装失败,尝试使用下面的命令安装依赖pnpm install
启动项目pnpm install --ignore-scripts
项目启动后会自动打开浏览器运行,默认访问地址:http://localhost:3006如果你在启动项目遇到问题,官方文档社区按钮,加入社区,社区会帮助你解决问题。目录结构pnpm dev
新建页面在 src/views 下创建一个页面├── src
│ ├── api // 接口请求
│ │ ├── articleApi.ts
│ │ ├── menuApi.ts
│ │ ├── model
│ │ │ └── articleModel.ts
│ │ └── usersApi.ts
│ ├── App.vue // 入口文件
│ ├── assets // 资源
│ │ ├── fonts // 字体文件
│ │ │ ├── DMSans.woff2
│ │ │ └── Montserrat.woff2
│ │ ├── icons // 图标文件夹
│ │ │ └── system // 系统图标
│ │ ├── img // 图片目录
│ │ ├── styles // 全局样式
│ │ │ ├── app.scss
│ │ │ ├── change.scss
│ │ │ ├── dark.scss
│ │ │ ├── el-dark.scss
│ │ │ ├── el-light.scss
│ │ │ ├── el-ui.scss // 优化 element plus 组件库默认样式
│ │ │ ├── markdown.scss
│ │ │ ├── mixin.scss // scss 混合宏(函数)
│ │ │ ├── mobile.scss
│ │ │ ├── one-dark-pro.scss
│ │ │ ├── pages.scss
│ │ │ ├── reset.scss
│ │ │ ├── theme-animation.scss
│ │ │ ├── transition.scss
│ │ │ ├── tree.scss
│ │ │ └── variables.scss // scss 主题变量
│ │ └── svg
│ │ └── loading.ts
│ ├── components // 组件目录
│ │ ├── core // 系统组件目录
│ │ │ ├── banners
│ │ │ ├── base
│ │ │ ├── cards
│ │ │ ├── charts
│ │ │ ├── forms
│ │ │ ├── layouts
│ │ │ ├── media
│ │ │ ├── others
│ │ │ ├── tables
│ │ │ ├── text-effect
│ │ │ └── views
│ │ └── custom // 用户组件目录
│ │ └── comment-widget
│ ├── composables // hooks
│ │ ├── useAuth.ts // 权限
│ │ ├── useCeremony.ts // 礼花效果
│ │ ├── useChart.ts // 图表
│ │ ├── useCheckedColumns.ts // 表格列选择
│ │ ├── useCommon.ts // 公共
│ │ ├── useSystemInfo.ts // 系统信息
│ │ └── useTheme.ts // 主题
│ ├── config
│ │ ├── assets
│ │ │ └── images.ts
│ │ ├── festival.ts // 礼花配置
│ │ ├── index.ts // 系统配置
│ │ └── types
│ │ └── index.ts
│ ├── directives // 自定义指令
│ │ ├── highlight.ts // 代码高亮指令
│ │ ├── index.ts
│ │ ├── permission.ts // 权限指令
│ │ └── ripple.ts // 水波纹指令
│ ├── enums
│ │ ├── appEnum.ts // 公共枚举
│ │ └── formEnum.ts // 表格枚举
│ ├── env.d.ts
│ ├── language // 多语言
│ │ ├── index.ts
│ │ └── locales
│ │ ├── en.json
│ │ └── zh.json
│ ├── main.ts
│ ├── mock
│ │ ├── json
│ │ │ └── chinaMap.json
│ │ ├── temp
│ │ │ ├── articleList.ts
│ │ │ ├── commentDetail.ts
│ │ │ ├── commentList.ts
│ │ │ └── formData.ts
│ │ └── upgradeLog.ts // 系统升级日志
│ ├── plugins
│ │ └── echarts
│ │ └── index.ts
│ ├── router
│ │ ├── guards // 路由钩子
│ │ │ ├── afterEach.ts
│ │ │ └── beforeEach.ts
│ │ ├── index.ts
│ │ ├── routes // 路由配置文件,静态路由、动态路由
│ │ │ ├── asyncRoutes.ts
│ │ │ └── staticRoutes.ts
│ │ ├── routesAlias.ts // 路由别名
│ │ └── utils
│ │ ├── menuToRouter.ts
│ │ ├── registerRoutes.ts
│ │ └── utils.ts
│ ├── store // pinia
│ │ ├── index.ts
│ │ └── modules
│ │ ├── menu.ts
│ │ ├── setting.ts
│ │ ├── table.ts
│ │ ├── user.ts
│ │ └── worktab.ts
│ ├── types // 类型定义
│ ├── utils // 工具包
│ └── views // 页面
├── tsconfig.json
└── vite.config.ts
tips:上面的例子中我们添加了一个页面,页面 这个类名可以将盒子最小高度始终撑满屏幕剩余高度路由注册页面创建完成后需要注册路由才能访问页面,配置文件路径:src/router/routes/asyncRoutes.ts一级路由(一级菜单):<template>
<div class="page-content">
<h1>test page</h1>
</div>
</template>
完成上面的步骤后就可以访问页面了export const asyncRoutes: MenuListType[] = [
{
id: 0,
path: '/test/index',
name: 'Test',
component: '/test/index',
meta: {
title: '测试页',
keepAlive: true
}
}
]
静态路由配置:配置文件路径: src/router/routes/staticRoutes.tsexport const asyncRoutes: MenuListType[] = [
{
id: 1,
name: 'Form',
path: '/form',
component: RoutesAlias.Home,
meta: {
title: '表单',
icon: '',
keepAlive: false
},
children: [
{
id: 11,
path: 'basic',
name: 'Basic',
component: '/form/basic',
meta: {
title: '基础表单',
keepAlive: true
}
},
{
id: 12,
path: 'step',
name: 'Step',
component: '/form/step',
meta: {
title: '分步表单',
keepAlive: true
}
}
]
}
]
配置完成后你可以访问:http://localhost:3006/#/test 查看新建的页面,到这里静态路由注册完成。tips:如果静态路由在动态路由表也配置了,需要把静态路由中的配置去除,因为动态路由会自动注册路由。主页配置修改HOME_PAGE 属性,配置文件路径:src/router/routesAlias.tsexport const staticRoutes: AppRouteRecordRaw[] = [
{
path: '/test',
name: 'Test',
component: () => import('@views/test/index.vue'),
meta: { title: '测试页面', isHideTab: true, setTheme: true }
}
]
路由属性(菜单属性)// 主页路由
export const HOME_PAGE = RoutesAlias.Dashboard
路径别名用法系统定义了常用的文件夹路径,你使用这些路径别名就可以方便的导入图片等资源export type MenuListType = {
id: number
path: string // 路由
name: string // 组件名
component?: string // 组件路径
meta: {
title: string // 菜单名称
icon?: string // 菜单图标
showBadge?: boolean // 是否显示徽标
showTextBadge?: string // 是否显示新徽标
isHide?: boolean // 是否在菜单中隐藏
isHideTab?: boolean // 是否在标签页中隐藏
link?: string // 链接
isIframe?: boolean // 是否是 iframe
keepAlive: boolean // 是否缓存
authList?: Array // 可操作权限
isFirstLevel?: boolean // 是否为一级菜单(系统自动处理,不需要手动添加)
roles?: string[] // 角色
}
children?: MenuListType[] // 子菜单
}
使用示例:alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'@views': resolvePath('src/views'),
'@imgs': resolvePath('src/assets/img'),
'@icons': resolvePath('src/assets/icons'),
'@utils': resolvePath('src/utils'),
'@stores': resolvePath('src/store'),
'@plugins': resolvePath('src/plugins'),
'@styles': resolvePath('src/assets/styles')
}
CSS 变量写法项目定义了一套完整的 CSS 变量,你可以使用这些变量来方便优雅的开发出颜值逆天的页面文件路径:src/assets/styles/variables.scssCSS 变量包括主题颜色、背景颜色、文字颜色、边框颜色、阴影、媒体查询(设备尺寸),这些变量能自适应 Light 跟 Dark 模式。import imgUrl from '@imgs/state/500.png'
<img src="@imgs/state/500.png" />
使用示例:// 默认主题变量 | Light 主题变量
:root {
// Theme color
--art-primary: 93, 135, 255;
--art-secondary: 73, 190, 255;
--art-error: 250, 137, 107;
--art-info: 83, 155, 255;
--art-success: 19, 222, 185;
--art-warning: 255, 174, 31;
--art-danger: 255, 77, 79;
// Theme background color
--art-bg-primary: 236, 242, 255;
--art-bg-secondary: 232, 247, 255;
--art-bg-success: 230, 255, 250;
--art-bg-error: 253, 237, 232;
--art-bg-info: 235, 243, 254;
--art-bg-warning: 254, 245, 229;
--art-bg-danger: 253, 237, 232;
--art-hoverColor: 246, 249, 252;
--art-grey100: 242, 246, 250;
--art-grey200: 234, 239, 244;
--art-color:
--art-light:
--art-dark:
// Background color | Hover color
--art-text-muted:
--art-gray-100:
--art-gray-100-rgb: 249, 249, 249;
--art-gray-200:
--art-gray-200-rgb: 241, 241, 244;
--art-gray-300:
--art-gray-300-rgb: 219, 223, 233;
--art-gray-400:
--art-gray-400-rgb: 196, 202, 218;
--art-gray-500:
--art-gray-500-rgb: 153, 161, 183;
--art-gray-600:
--art-gray-600-rgb: 120, 130, 157;
--art-gray-700:
--art-gray-700-rgb: 75, 86, 117;
--art-gray-800:
--art-gray-800-rgb: 37, 47, 74;
--art-gray-900:
--art-gray-900-rgb: 7, 20, 55;
// Text color
--art-text-muted:
--art-text-gray-100:
--art-text-gray-200:
--art-text-gray-300:
--art-text-gray-400:
--art-text-gray-500:
--art-text-gray-600:
--art-text-gray-700:
--art-text-gray-800:
--art-text-gray-900:
// Border
--art-border-color:
--art-border-dashed-color:
--art-root-card-border-color:
// Shadow
--art-box-shadow-xs: 0 0.1rem 0.75rem 0.25rem rgba(0, 0, 0, 0.05);
--art-box-shadow-sm: 0 0.1rem 1rem 0.25rem rgba(0, 0, 0, 0.05);
--art-box-shadow: 0 0.5rem 1.5rem 0.5rem rgba(0, 0, 0, 0.075);
--art-box-shadow-lg: 0 1rem 2rem 1rem rgba(0, 0, 0, 0.1);
// Root card box、shadow
--art-root-card-box-shadow: 0px 3px 4px 0px rgba(0, 0, 0, 0.03);
--art-root-card-border-color:
// Theme background color
--art-bg-color:
--art-main-bg-color:
}
// Dark 主题变量
html.dark {
// Theme color
--art-primary: 93, 135, 255;
--art-secondary: 73, 190, 255;
--art-error: 250, 137, 107;
--art-info: 83, 155, 255;
--art-success: 19, 222, 185;
--art-warning: 255, 174, 31;
--art-danger: 255, 77, 79;
// Theme background color
--art-bg-primary: 37, 54, 98;
--art-bg-secondary: 28, 69, 93;
--art-bg-success: 27, 60, 72;
--art-bg-error: 75, 49, 61;
--art-bg-info: 34, 54, 98;
--art-bg-warning: 77, 58, 42;
--art-bg-danger: 100, 49, 61;
--art-hoverColor: 51, 63, 85;
--art-grey100: 51, 63, 85;
--art-grey200: 70, 86, 112;
--art-color:
--art-light:
--art-dark:
// Background color | Hover color
--art-text-muted:
--art-gray-100:
--art-gray-100-rgb: 27, 28, 34;
--art-gray-200:
--art-gray-200-rgb: 38, 39, 47;
--art-gray-300:
--art-gray-300-rgb: 54, 56, 67;
--art-gray-400:
--art-gray-400-rgb: 70, 72, 82;
--art-gray-500:
--art-gray-500-rgb: 99, 102, 116;
--art-gray-600:
--art-gray-600-rgb: 128, 130, 144;
--art-gray-700:
--art-gray-700-rgb: 154, 156, 174;
--art-gray-800:
--art-gray-800-rgb: 181, 183, 200;
--art-gray-900:
--art-gray-900-rgb: 245, 245, 245;
// Text color
--art-text-muted:
--art-text-gray-100:
--art-text-gray-200:
--art-text-gray-300:
--art-text-gray-400:
--art-text-gray-500:
--art-text-gray-600:
--art-text-gray-700:
--art-text-gray-800:
--art-text-gray-900:
// Border
--art-border-color:
--art-border-dashed-color:
--art-root-card-border-color:
// Shadow
--art-box-shadow-xs: 0 0.1rem 0.75rem 0.25rem rgba(0, 0, 0, 0.05);
--art-box-shadow-sm: 0 0.1rem 1rem 0.25rem rgba(0, 0, 0, 0.05);
--art-box-shadow: 0 0.5rem 1.5rem 0.5rem rgba(0, 0, 0, 0.075);
--art-box-shadow-lg: 0 1rem 2rem 1rem rgba(0, 0, 0, 0.1);
// Root card box、shadow
--art-root-card-box-shadow: none;
--art-root-card-border-color:
// Theme background color
--art-bg-color:
--art-main-bg-color:
}
// CSS 全局变量
:root {
--console-margin: 20px; // 工作台盒子间距
--art-card-border: rgba(var(--art-gray-300-rgb), 0.6); // 卡片边框颜色
--art-card-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.04); // 卡片阴影
}
// 媒体查询-设备尺寸
// notebook
$device-notebook: 1600px;
// ipad pro
$device-ipad-pro: 1180px;
// ipad
$device-ipad: 800px;
// ipad-竖屏
$device-ipad-vertical: 900px;
// mobile
$device-phone: 500px;
如果你觉得颜色太深可以使用 -rgb 的变量结合 rgba() 来调整# 文字
color: var(--art-gray-100);
color: var(--art-gray-900);
# 边框
border: 1px solid var(--art-border-color);
border: 1px solid var(--art-border-dashed-color);
# 背景颜色(白色|黑色)
background-color: var(--art-main-bg-color);
# 阴影
box-shadow: var(--art-box-shadow);
box-shadow: var(--art-box-shadow-xs);
box-shadow: var(--art-box-shadow-sm);
box-shadow: var(--art-box-shadow-lg);
主题色你可以通过 var(--main-color) 使用不透明的主题主色。同时,也提供了 9 个不同透明度等级的主题色变量,方便在不同场景中灵活应用:color: rgba(var(--art-gray-800-rgb), 0.6);
tips:--el-color-primary-light-1 到 --el-color-primary-light-9 分别代表从深到浅的九级亮色,适合用于悬浮态、边框、背景等场景。unplugin 自动导入项目已集成自动导入配置,无需手动导入 Vue API、Element Plus 组件及系统通用组件,使用时可直接书写,开发更高效。你不再需要这样:# 不透明的主色
color: var(--main-color);
# 具有不同透明度的主题背景色
background-color: var(--el-color-primary-light-1); # 最深
background-color: var(--el-color-primary-light-9); # 最浅
现在你只需这样:// demo1: Vue API
import { ref } from 'vue'
const count = ref(0)
// demo2: Element Plus 组件
import { ElButton } from 'element-plus'
<ElButton>按钮</ElButton>
// demo3: 系统组件
import Logo from 'components/Logo'
<Logo />
说明:由 unplugin-auto-import 与 unplugin-vue-components 自动完成引入操作,支持类型提示,支持 Tree-shaking,无需担心性能问题。Element Plus 组件库官网:element-plus.org/zh-CN/本项目基于 Element Plus 组件库构建,并在视觉设计与用户体验方面进行了特殊优化,进一步提升了整体界面效果与交互体验。iconfont 图标库项目图标使用 iconfont 提供,内置了 600+ 的图标,可以满足大部分的图标需求。如果你需要添加或者自定义图标库,可以访问这个链接 系统图标库 ,进入后你可以把它添加到自己的项目中进行使用。// demo1: Vue API
const count = ref(0)
// demo2: Element Plus 组件
<ElButton>按钮</ElButton>
// demo3: 系统组件
<Logo />
Font class 用法<i class="iconfont-sys"></i>
图标库目录:src/assets/icons/system注意:为了方便用户拓展图标库,系统图默认使用 iconfont-sys 类名,而不是 iconfont图标库链接过期:请点击 官网 顶部社区按钮,进入 QQ 群 @群主或者管理员更换链接环境变量说明环境变量位于项目根目录下 .env、.env.development、.env.production、1. .env作用:适用于所有环境,里面定义的变量会在任何环境下都能访问。用法:一般放置一些通用的配置,比如 API 基础地址、应用名称等。2. .env.development作用:仅适用于开发环境。当你运行 pnpm dev 时,Vue 会加载这个文件中的环境变量。用法:适合放置开发阶段的配置,比如本地 API 地址、调试设置等。3. .env.production作用:仅适用于生产环境。当你运行 pnpm build 时,Vue 会加载这个文件中的环境变量。用法:适合放置生产阶段的配置,比如生产 API 地址、禁用调试模式等。自定义环境变量自定义环境变量以 VITE_ 开头,比如:VITE_PROT你可以在项目代码中这样访问它们<i class="iconfont-sys iconsys-gou"></i>
.envconsole.log(import.meta.env.VITE_PROT);
.env.development# 【通用】环境变量
# 版本号
VITE_VERSION = 2.2.86
# 端口号
VITE_PORT = 3006
# 网站地址前缀
VITE_BASE_URL = /art-design-pro/
# API 地址前缀
VITE_API_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default
# 权限模式( frontend | backend )
VITE_ACCESS_MODE = frontend
# 是否打开路由信息
VITE_OPEN_ROUTE_INFO = false
# 锁屏加密密钥
VITE_LOCK_ENCRYPT_KEY = jfsfjk1938jfj
.env.production# 【开发】环境变量
# 网站地址前缀
VITE_BASE_URL = /
# API 地址前缀
VITE_API_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default
# Delete console
VITE_DROP_CONSOLE = false
组件封装为提高项目结构的可维护性与组件复用性,组件应按功能分类存放:@/components/custom:自定义业务组件目录 供开发者根据业务需求封装的组件,适用于项目中具体场景的复用组件。@/components/core:系统核心组件目录 包含系统级通用组件,例如布局容器、主题切换、菜单等,具备高度通用性与系统依赖性。系统 Logo系统 Logo 采用图片形式展示,如需更换 Logo,仅需修改图片资源路径,无需改动组件逻辑。配置文件路径:src/components/core/base/ArtLogo.vue# 【生产】环境变量
# 网站地址前缀
VITE_BASE_URL = /art-design-pro/
# API 地址前缀
VITE_API_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default
# Delete console
VITE_DROP_CONSOLE = true
系统名称系统名称统一通过配置文件管理,如需更改,只需修改 systemInfo.name 属性即可实现全局替换。配置文件路径:src/config/index.ts<template>
<div class="art-logo">
<img :style="logoStyle" src="@imgs/common/logo.png" alt="logo" />
</div>
</template>
多语言项目使用 vue-i18n 插件,目前集成了 中文 和 英文 两种语言。 目前对菜单、顶栏、设置中心等组件进行了国际化,其他地方根据需求自行配置。const appConfig: SystemConfig = {
systemInfo: {
name: 'Art Design Pro' // 系统名称
}
}
在模版中使用├── language
│ ├── index.ts // 配置文件
│ └── locales // 语言包目录
│ ├── zh.json // 中文包
│ └── en.json // 英文包
如何获取当前语言<p>{{ $t('setting.color.title') }}</p>
如何配置多语言修改 src/language/index.ts 在 messages 中增加你要的配置的语言,然后在 language 目录新建一个文件,如 en.tsimport { useI18n } from "vue-i18n";
const { locale } = useI18n();
权限参考官方文档:lingchen.kim/art-design-pro/docs/guide/in-depth/permission.htmlgit 提交代码提交校验与格式化:使用 ESLint、Prettier、Stylelint 等工具,配合 Husky、Lint-staged,实现代码提交时的自动校验与格式化,规范团队开发流程。代码提交规范化:使用 CommitLint、cz-git 等工具,规范 Git 提交信息,提升项目的可维护性和协作效率。自动化:代码提交会自动执行配置好的文件,自动完成代码校验、和格式。位于 package.json 中配置。import { createI18n } from "vue-i18n";
import en from "./en";
import zh from "./zh";
import { LanguageEnum } from "@/enums/appEnum";
const lang = createI18n({
locale: LanguageEnum.ZH, // 设置语言类型
legacy: false, // 如果要支持compositionAPI,此项必须设置为false;
globalInjection: true, // 全局注册$t方法
fallbackLocale: LanguageEnum.ZH, // 设置备用语言
messages: {
en,
zh,
},
});
export default lang;
提交规范"lint-staged": {
"*.{js,ts}": [
"eslint --fix",
"prettier --write"
],
"*.{cjs,json}": [
"prettier --write"
],
"*.{vue,html}": [
"eslint --fix",
"prettier --write",
"stylelint --fix"
],
"*.{scss,css}": [
"stylelint --fix",
"prettier --write"
],
"*.md": [
"prettier --write"
]
}
提交代码feat, // 新增功能
fix, // 修复缺陷
docs, // 文档变更
style, // 代码格式(不影响功能,例如空格、分号等格式修正)
refactor, // 代码重构(不包括 bug 修复、功能新增)
perf, // 性能优化
test, // 添加疏漏测试或已有测试改动
build, // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
ci, // 修改 CI 配置、脚本
revert, // 回滚 commit
chore, // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
wip // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
git add .
pnpm commit
...
git push
构建打包成功之后,会在根目录生成对应的应用下的 dist 文件夹,里面就是构建打包好的文件部署部署时可能会发现资源路径不对,只需要修改 .env.production 文件即可。pnpm build
部署到非根目录需要更改 .env.production 配置,把 VITE_BASE_URL 改成你存放项目的路径,比如:# 根据自己存放的静态资源路径来更改配置
VITE_BASE_URL = /art-design-pro/
然后在 nginx 配置文件中配置VITE_BASE_URL = /art-design-pro/
代码如何保持最新如果你在基于本项目进行开发,并希望后续持续同步开源仓库的更新,建议按以下方式配置和操作:1、克隆代码源码server {
location /art-design-pro {
alias /usr/local/nginx/html/art-design-pro;
index index.html index.htm;
}
}
2、添加自己的 git 仓库作为远程源
3、推送代码到你自己的仓库git remote add up <your-git-url>
4、同步你自己仓库的最新代码(可选)# 将代码推送到你自己的 Git 仓库
# 注意:main 为分支名,请根据你的实际分支进行替换
git push up main
5、同步开源项目的最新代码(推荐定期执行)# 从你自己的远程仓库拉取更新
git pull up main
注意:同步更新前建议先 git pull 拉取代码并处理冲突,确保本地变更不会被覆盖。合并后再推送到你的远程仓库。结语Art Design Pro 是一个将美学与实用性完美融合的 Vue 3 后台系统模板,旨在为开发者提供高效、优雅的开发体验。无论是精致的 UI 设计、流畅的交互体验,还是模块化的架构和灵活的定制能力,都体现了我们对细节的极致追求和对用户需求的深刻理解。希望这个开源项目能成为你快速构建高质量管理系统的得力助手,释放更多时间专注于业务创新。感谢每一位使用和支持 Art Design Pro 的开发者!我们期待你的反馈和贡献,欢迎加入官方社区,一起打造更出色的开发体验。立即开始,探索无限可能!立即体验 Art Design Pro | 查看代码仓库# 从开源仓库拉取最新代码(main 为默认主分支,请按需修改)
git pull origin main
AI编程资讯AI Coding专区指南:https://aicoding.juejin.cn/aicoding
点击"阅读原文"了解详情~