過去一年,我花了無數個夜晚,在一次次打磨與推翻中,完成了自己最滿意的作品 —— Art Design Pro。
這不是一個普通的後台模板。
它是一場關於「設計美學」與「工程化開發」的融合實驗——
希望讓後台系統不再冰冷枯燥,而是像一件作品:優雅、流暢、有溫度。
在日常開發中,我幾乎體驗過所有主流後台模板。
它們的功能確實完整,但更多時候給人的感覺是——「工具」而非「產品」。
我希望做一個後台系統,
讓人打開的第一眼就覺得舒服,
用起來像是在和它對話。
許多後台系統普遍存在這些問題 👇
視覺疲勞:灰白配色、生硬佈局,難以長時間使用;
體驗割裂:邏輯不統一、入口分散,操作效率低;
重用困難:組件風格不一致,二次開發成本高。
於是我決定從零開始,花一年的時間去打造一個真正屬於自己的系統——
👉 一款「既好看、又好用,還能直接用於商業專案」的後台管理模板。
Art Design Pro 是一款基於 Vue 3 + TypeScript + Vite + Element Plus 打造的現代化後台管理系統模板。
它的核心理念是:
讓後台系統兼具設計美學與開發效率。
介面設計:現代化 UI 設計,流暢互動,以用戶體驗與視覺設計為核心
極速上手:簡潔架構 + 完整文檔,後端開發者也能輕鬆使用
豐富組件:內置數據展示、表單等多種高品質組件,滿足不同業務場景的需求
絲滑互動:按鈕點擊、主題切換、頁面過渡、圖表動畫,體驗媲美商業產品
高效開發:內置 useTable、ArtForm 等實用 API,顯著提升開發效率
精簡腳本:內置一鍵清理腳本,可快速清理演示數據,立即得到可開發的基礎專案
開發框架:Vue3、TypeScript、Vite、Element-Plus
程式碼規範:Eslint、Prettier、Stylelint、Husky、Lint-staged、cz-git
主頁儀表盤
電子商務儀表盤
卡片
橫幅
圖表
系統圖標庫
富文本編輯器
禮花效果
全局搜索
系統設定
表格
GitHub: github.com/Daymychen/a…
演示地址: www.artd.pro
官方文檔: www.artd.pro/docs
在後台管理系統開發中,表格頁面佔據了 80% 的工作量。每次都要寫分頁、搜尋、刷新、列配置...這些重複的程式碼讓人頭疼。今天分享一套我們團隊正在使用的表格開發方案,讓你的開發效率提升 10 倍!
在開發後台管理系統時,你是否遇到過這些問題:
如果你的答案是「是」,那這篇文章就是為你準備的。
我們的方案包含以下核心部分:
先看一個最簡單的例子,只需要幾行程式碼就能實現一個完整的表格:
const {
data,
columns,
columnChecks,
loading,
pagination,
refreshData,
handleSizeChange,
handleCurrentChange
} = useTable({
core: {
apiFn: fetchGetUserList,
apiParams: {
current: 1,
size: 20
},
columnsFactory: () => [
{ prop: 'id', label: 'ID' },
{ prop: 'userName', label: '用戶名' },
{ prop: 'userPhone', label: '手機號' }
]
}
})
就這麼簡單!你已經擁有了:
useTable({
core: {
/* ... */
},
performance: {
enableCache: true, // 啟用快取
cacheTime: 5 * 60 * 1000, // 快取 5 分鐘
debounceTime: 300, // 防抖 300ms
maxCacheSize: 50 // 最多快取 50 條
}
})
快取帶來的好處:
不同的業務場景需要不同的刷新策略:
// 新增數據後:回到首頁,清空分頁快取
await refreshCreate()
// 編輯數據後:保持當前頁,只清空當前搜尋快取
await refreshUpdate()
// 刪除數據後:智能處理頁碼,避免空頁面
await refreshRemove()
// 手動刷新:清空所有快取
await refreshData()
// 定時刷新:輕量刷新,保持分頁狀態
await refreshSoft()
這些方法讓你的程式碼更語義化,不用再糾結什麼時候該清快取。
有時候介面返回的數據需要處理一下才能用:
useTable({
core: {
/* ... */
},
transform: {
dataTransformer: (records) => {
return records.map((item, index) => ({
...item,
// 替換頭像
avatar: localAvatars[index % localAvatars.length].avatar,
// 格式化日期
createTime: dayjs(item.createTime).format('YYYY-MM-DD')
}))
}
}
})
useTable({
core: {
/* ... */
},
hooks: {
onSuccess: (data, response) => {
console.log('數據加載成功', data)
},
onError: (error) => {
ElMessage.error('加載失敗:' + error.message)
},
onCacheHit: (data) => {
console.log('從快取讀取', data)
}
}
})
搜尋是表格的核心功能,useTable 提供了完善的搜尋支持:
// 定義搜尋參數
const searchParams = reactive({
userName: '',
userPhone: '',
status: '1'
})
const { getData, resetSearchParams } = useTable({
core: {
apiFn: fetchGetUserList,
apiParams: searchParams
}
})
// 搜尋
const handleSearch = (params) => {
Object.assign(searchParams, params)
getData() // 自動回到首頁
}
// 重置
const handleReset = () => {
resetSearchParams() // 清空搜尋條件並重新加載
}
ArtTable 基於 Element Plus 的 ElTable 封裝,完全兼容原有 API,同時提供了更多增強功能:
<ArtTable
:loading="loading"
:data="data"
:columns="columns"
:pagination="pagination"
@selection-change="handleSelectionChange"
@pagination:size-change="handleSizeChange"
@pagination:current-change="handleCurrentChange"
/>
不用再手動計算表格高度了!ArtTable 會自動計算剩餘空間:
<div class="art-full-height">
<UserSearch />
<ElCard class="art-table-card">
<ArtTableHeader />
<ArtTable /> <!-- 自動佔滿剩餘高度 -->
</ElCard>
</div>
支持多種列類型和自定義渲染:
columnsFactory: () => [
{ type: 'selection' }, // 勾選列
{ type: 'index', width: 60, label: '序號' }, // 序號列
{ type: 'globalIndex' }, // 全球序號(跨頁)
// 自定義渲染
{
prop: 'avatar',
label: '用戶',
formatter: (row) => {
return h('div', { class: 'user-info' }, [
h(ElImage, { src: row.avatar }),
h('span', row.userName)
])
}
},
// 使用插槽
{
prop: 'status',
label: '狀態',
useSlot: true // 在模板中使用 #status 插槽
},
// 操作列
{
prop: 'operation',
label: '操作',
fixed: 'right',
formatter: (row) =>
h('div', [
h(ArtButtonTable, {
type: 'edit',
onClick: () => handleEdit(row)
}),
h(ArtButtonTable, {
type: 'delete',
onClick: () => handleDelete(row)
})
])
}
]
自動適配行動端、平板、桌面端:
// 行動端:prev, pager, next, sizes, jumper, total
// 平板:prev, pager, next, jumper, total
// 桌面端:total, prev, pager, next, sizes, jumper
<ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
<template #left>
<ElButton @click="handleAdd">新增用戶</ElButton>
</template>
</ArtTableHeader>
一行程式碼,你就擁有了:
用戶可以:
這些配置會自動同步到表格顯示。
<ArtTableHeader layout="refresh,size,fullscreen,columns" :show-zebra="false" :show-border="false" />
通過 layout
屬性控制顯示哪些功能按鈕。
不用再手寫一堆 ElFormItem 了,用配置就能搞定:
<template>
<ArtSearchBar
ref="searchBarRef"
v-model="formData"
:items="formItems"
:rules="rules"
@reset="handleReset"
@search="handleSearch"
/>
</template>
<script setup lang="ts">
const formData = ref({
userName: undefined,
userPhone: undefined,
status: '1'
})
const formItems = computed(() => [
{
label: '用戶名',
key: 'userName',
type: 'input',
placeholder: '請輸入用戶名',
clearable: true
},
{
label: '手機號',
key: 'userPhone',
type: 'input',
props: { placeholder: '請輸入手機號', maxlength: '11' }
},
{
label: '狀態',
key: 'status',
type: 'select',
props: {
placeholder: '請選擇狀態',
options: [
{ label: '在線', value: '1' },
{ label: '離線', value: '2' }
]
}
},
{
label: '性別',
key: 'userGender',
type: 'radiogroup',
props: {
options: [
{ label: '男', value: '1' },
{ label: '女', value: '2' }
]
}
}
])
const handleSearch = async () => {
await searchBarRef.value.validate()
emit('search', formData.value)
}
const handleReset = () => {
emit('reset')
}
</script>
開箱即用的組件類型:
input
- 輸入框select
- 下拉選擇date
/ datetime
/ daterange
- 日期選擇radiogroup
/ checkboxgroup
- 單選/多選cascader
- 级联选择treeselect
- 樹選擇當搜尋項過多時,自動顯示展開/收起按鈕:
<ArtSearchBar :items="formItems" :show-expand="true" :default-expanded="false" :span="6" />
支持異步加載選項數據:
const statusOptions = ref([])
onMounted(async () => {
// 模擬介面請求
statusOptions.value = await fetchStatusOptions()
})
const formItems = computed(() => [
{
label: '狀態',
key: 'status',
type: 'select',
props: {
options: statusOptions.value // 動態選項
}
}
])
自動適配不同螢幕尺寸:
// 通過 span 控制每行顯示的表單項數量
// span=6: 一行顯示 4 個(24/6=4)
// span=8: 一行顯示 3 個(24/8=3)
// span=12: 一行顯示 2 個(24/12=2)
行動端自動調整為單列佈局。
支持完整的表單驗證:
const rules = {
userName: [{ required: true, message: '請輸入用戶名', trigger: 'blur' }],
userPhone: [
{ required: true, message: '請輸入手機號', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手機號格式不正確', trigger: 'blur' }
]
}
const handleSearch = async () => {
// 驗證通過才執行搜尋
await searchBarRef.value.validate()
emit('search', formData.value)
}
ArtForm 和 ArtSearchBar 使用相同的配置方式,但更適合彈窗、詳情頁等場景:
<template>
<ArtForm
ref="formRef"
v-model="formData"
:items="formItems"
:rules="formRules"
:label-width="100"
:span="12"
@reset="handleReset"
@submit="handleSubmit"
/>
</template>
<script setup lang="ts">
const formData = ref({
userName: '',
userPhone: '',
userEmail: '',
userGender: '1',
status: true
})
const formItems = [
{
label: '用戶名',
key: 'userName',
type: 'input',
placeholder: '請輸入用戶名'
},
{
label: '手機號',
key: 'userPhone',
type: 'input',
props: { maxlength: 11 }
},
{
label: '郵箱',
key: 'userEmail',
type: 'input',
placeholder: '請輸入郵箱'
},
{
label: '性別',
key: 'userGender',
type: 'radiogroup',
props: {
options: [
{ label: '男', value: '1' },
{ label: '女', value: '2' }
]
}
},
{
label: '是否啟用',
key: 'status',
type: 'switch'
},
{
label: '備註',
key: 'remark',
type: 'input',
span: 24, // 占滿整行
props: {
type: 'textarea',
rows: 4
}
}
]
const formRules = {
userName: [{ required: true, message: '請輸入用戶名', trigger: 'blur' }],
userEmail: [{ type: 'email', message: '郵箱格式不正確', trigger: 'blur' }]
}
const handleSubmit = async () => {
await formRef.value.validate()
// 提交表單
console.log('表單數據:', formData.value)
}
</script>
支持使用 h 函數渲染任意組件:
import ArtIconSelector from '@/components/core/base/art-icon-selector/index.vue'
const formItems = [
{
label: '圖標選擇',
key: 'icon',
type: () =>
h(ArtIconSelector, {
iconType: IconTypeEnum.UNICODE,
width: '100%'
})
},
{
label: '文件上傳',
key: 'files',
type: () =>
h(
ElUpload,
{
action: '#',
multiple: true,
limit: 5,
onChange: (file, fileList) => {
formData.value.files = fileList
}
},
{
default: () => h(ElButton, { type: 'primary' }, () => '點擊上傳')
}
)
}
]
支持為表單項添加插槽:
<ArtForm v-model="formData" :items="formItems">
<template #customField>
<div class="custom-content">
<!-- 自定義內容 -->
</div>
</template>
</ArtForm>
或者在配置中使用插槽:
const formItems = [
{
label: '網站',
key: 'website',
type: 'input',
slots: {
prepend: () => h('span', 'https://'),
append: () => h('span', '.com')
}
}
]
根據條件動態顯示/隱藏表單項:
const formItems = computed(() => [
{
label: '用戶類型',
key: 'userType',
type: 'select',
props: {
options: [
{ label: '個人', value: 'personal' },
{ label: '企業', value: 'enterprise' }
]
}
},
{
label: '企業名稱',
key: 'companyName',
type: 'input',
// 只有選擇企業類型時才顯示
hidden: formData.value.userType !== 'enterprise'
}
])
通過 span
控制表單項寬度:
const formItems = [
{
label: '用戶名',
key: 'userName',
type: 'input',
span: 12 // 占半行
},
{
label: '手機號',
key: 'userPhone',
type: 'input',
span: 12 // 占半行
},
{
label: '地址',
key: 'address',
type: 'input',
span: 24 // 占整行
}
]
特性 | ArtSearchBar | ArtForm |
---|---|---|
使用場景 | 表格搜尋 | 表單提交、彈窗 |
展開/收起 | ✅ 支持 | ❌ 不支持 |
按鈕文案 | 搜尋/重置 | 提交/重置 |
樣式 | 卡片樣式 | 無背景 |
默認佈局 | 橫向排列 | 橫向排列 |
讓我們看一個完整的用戶管理頁面:
<template>
<div class="user-page art-full-height">
<!-- 搜尋欄 -->
<ArtSearchBar
v-model="searchForm"
:items="searchItems"
@search="handleSearch"
@reset="resetSearchParams"
/>
<ElCard class="art-table-card" shadow="never">
<!-- 工具欄 -->
<ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
<template #left>
<ElButton @click="handleAdd">新增用戶</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
:loading="loading"
:data="data"
:columns="columns"
:pagination="pagination"
@selection-change="handleSelectionChange"
@pagination:size-change="handleSizeChange"
@pagination:current-change="handleCurrentChange"
/>
</ElCard>
<!-- 彈窗 -->
<UserDialog
v-model:visible="dialogVisible"
:type="dialogType"
:user-data="currentUserData"
@submit="handleDialogSubmit"
/>
</div>
</template>
<script setup lang="ts">
import { useTable } from '@/composables/useTable'