Add fast-crud integration with Element Plus demo

Integrate the fast-crud framework with Element Plus adapter, global
component registration, request transform utilities, and a demo CRUD
page with router entry.
This commit is contained in:
2026-10-26 21:26:36 +08:00
parent 20a594d834
commit 80b8218b23
9 changed files with 511 additions and 0 deletions
+4
View File
@@ -16,6 +16,10 @@
},
"dependencies": {
"@element-plus/icons-vue": "2.3.2",
"@fast-crud/fast-crud": "1.29.0",
"@fast-crud/ui-element": "1.29.0",
"@fast-crud/ui-interface": "1.29.0",
"@iconify/vue": "4.3.0",
"axios": "1.18.1",
"dayjs": "1.11.21",
"element-plus": "2.14.2",
+136
View File
@@ -11,6 +11,18 @@ importers:
'@element-plus/icons-vue':
specifier: 2.3.2
version: 2.3.2(vue@3.5.39(typescript@5.9.3))
'@fast-crud/fast-crud':
specifier: 1.29.0
version: 1.29.0(vue@3.5.39(typescript@5.9.3))
'@fast-crud/ui-element':
specifier: 1.29.0
version: 1.29.0
'@fast-crud/ui-interface':
specifier: 1.29.0
version: 1.29.0
'@iconify/vue':
specifier: 4.3.0
version: 4.3.0(vue@3.5.39(typescript@5.9.3))
axios:
specifier: 1.18.1
version: 1.18.1
@@ -514,6 +526,15 @@ packages:
resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
'@fast-crud/fast-crud@1.29.0':
resolution: {integrity: sha512-Wse1OpkIKb6e10zHHTK18FU5uKktUqdbv90JFwCcHo7gWQJ8BWtNSNnoTpDjxs+AcNEOyaMKInBE8hN65m+LVw==}
'@fast-crud/ui-element@1.29.0':
resolution: {integrity: sha512-xDljG4dVgZfysfhn/olNO7rw/RDw6bn3ssA+RSK2acLijs7QQAvVpK/npS8Y8rwEC6520DTaENWvX4MQgA+nDA==}
'@fast-crud/ui-interface@1.29.0':
resolution: {integrity: sha512-SZkgaCt5Bg4WBpPZbgcKZdYe3BpjIRWw3FF5r+B91SxbH/ImomCtdpXQdrotgnYhzCVfw4ma0E5K3pP1AxJ+aA==}
'@floating-ui/core@1.7.5':
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
@@ -555,6 +576,11 @@ packages:
'@iconify/utils@3.1.4':
resolution: {integrity: sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==}
'@iconify/vue@4.3.0':
resolution: {integrity: sha512-Xq0h6zMrHBbrW8jXJ9fISi+x8oDQllg5hTDkDuxnWiskJ63rpJu9CvJshj8VniHVTbsxCg9fVoPAaNp3RQI5OQ==}
peerDependencies:
vue: '>=3'
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
@@ -1709,6 +1735,10 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
adler-32@1.3.1:
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
engines: {node: '>=0.8'}
agent-base@6.0.2:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
@@ -1859,6 +1889,10 @@ packages:
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
cfb@1.2.2:
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
engines: {node: '>=0.8'}
chai@6.2.2:
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
engines: {node: '>=18'}
@@ -1903,6 +1937,10 @@ packages:
cloneable-readable@1.1.3:
resolution: {integrity: sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==}
codepage@1.15.0:
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
engines: {node: '>=0.8'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -2006,6 +2044,11 @@ packages:
resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==}
engines: {node: '>= 0.10'}
crc-32@1.2.2:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
engines: {node: '>=0.8'}
hasBin: true
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -2538,6 +2581,9 @@ packages:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
file-saver@2.0.5:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
@@ -2589,6 +2635,10 @@ packages:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
frac@1.1.2:
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
engines: {node: '>=0.8'}
fresh@2.0.0:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'}
@@ -3785,6 +3835,9 @@ packages:
resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==}
engines: {node: '>=20'}
sortablejs@1.14.0:
resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -3806,6 +3859,10 @@ packages:
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
engines: {node: '>=0.10.0'}
ssf@0.11.2:
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
engines: {node: '>=0.8'}
stable@0.1.8:
resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==}
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
@@ -4248,6 +4305,11 @@ packages:
typescript:
optional: true
vuedraggable-es@4.1.1:
resolution: {integrity: sha512-F35pjSwC8HS/lnaOd+B59nYR4FZmwuhWAzccK9xftRuWds8SU1TZh5myKVM86j5dFOI7S26O64Kwe7LUHnXjlA==}
peerDependencies:
vue: ^3.2.31
vxe-table@4.6.25:
resolution: {integrity: sha512-rFhGh8w+420cdnIasQKisiKagz9F/iNieB/z6v0j4GcsMfGHEmSJ72YrHcXogQh4wNlCzKVfb7rl7nREL5eIOg==}
peerDependencies:
@@ -4278,10 +4340,18 @@ packages:
resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==}
engines: {node: '>= 12.0.0'}
wmf@1.0.2:
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
engines: {node: '>=0.8'}
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
word@0.3.0:
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
engines: {node: '>=0.8'}
wrap-ansi@10.0.0:
resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==}
engines: {node: '>=20'}
@@ -4316,6 +4386,11 @@ packages:
xe-utils@3.9.1:
resolution: {integrity: sha512-Ujk5UmoH6Iaqhgz3oGwfCXVcMdUJKlXnfvLABdnMyseMG0eHsX2mcCvLd/8sGlIXtfwsprI9bW7vgcVognLmqQ==}
xlsx@0.18.5:
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
engines: {node: '>=0.8'}
hasBin: true
xml-name-validator@4.0.0:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
engines: {node: '>=12'}
@@ -4687,6 +4762,22 @@ snapshots:
'@eslint/core': 1.2.1
levn: 0.4.1
'@fast-crud/fast-crud@1.29.0(vue@3.5.39(typescript@5.9.3))':
dependencies:
'@iconify/types': 2.0.0
file-saver: 2.0.5
lru-cache: 10.4.3
vuedraggable-es: 4.1.1(vue@3.5.39(typescript@5.9.3))
xlsx: 0.18.5
transitivePeerDependencies:
- vue
'@fast-crud/ui-element@1.29.0': {}
'@fast-crud/ui-interface@1.29.0':
dependencies:
lodash-es: 4.18.1
'@floating-ui/core@1.7.5':
dependencies:
'@floating-ui/utils': 0.2.11
@@ -4726,6 +4817,11 @@ snapshots:
'@iconify/types': 2.0.0
import-meta-resolve: 4.2.0
'@iconify/vue@4.3.0(vue@3.5.39(typescript@5.9.3))':
dependencies:
'@iconify/types': 2.0.0
vue: 3.5.39(typescript@5.9.3)
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
@@ -5774,6 +5870,8 @@ snapshots:
acorn@8.17.0: {}
adler-32@1.3.1: {}
agent-base@6.0.2:
dependencies:
debug: 4.4.3
@@ -5924,6 +6022,11 @@ snapshots:
ccount@2.0.1: {}
cfb@1.2.2:
dependencies:
adler-32: 1.3.1
crc-32: 1.2.2
chai@6.2.2: {}
change-case@5.4.4: {}
@@ -5963,6 +6066,8 @@ snapshots:
process-nextick-args: 2.0.1
readable-stream: 2.3.8
codepage@1.15.0: {}
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@@ -6040,6 +6145,8 @@ snapshots:
object-assign: 4.1.1
vary: 1.1.2
crc-32@1.2.2: {}
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
@@ -6684,6 +6791,8 @@ snapshots:
dependencies:
flat-cache: 4.0.1
file-saver@2.0.5: {}
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -6734,6 +6843,8 @@ snapshots:
forwarded@0.2.0: {}
frac@1.1.2: {}
fresh@2.0.0: {}
fs.realpath@1.0.0: {}
@@ -8102,6 +8213,8 @@ snapshots:
ansi-styles: 6.2.3
is-fullwidth-code-point: 5.1.0
sortablejs@1.14.0: {}
source-map-js@1.2.1: {}
source-map@0.6.1: {}
@@ -8117,6 +8230,10 @@ snapshots:
speakingurl@14.0.1: {}
ssf@0.11.2:
dependencies:
frac: 1.1.2
stable@0.1.8: {}
stack-trace@0.0.10: {}
@@ -8599,6 +8716,11 @@ snapshots:
optionalDependencies:
typescript: 5.9.3
vuedraggable-es@4.1.1(vue@3.5.39(typescript@5.9.3)):
dependencies:
sortablejs: 1.14.0
vue: 3.5.39(typescript@5.9.3)
vxe-table@4.6.25(vue@3.5.39(typescript@5.9.3)):
dependencies:
dom-zindex: 1.0.7
@@ -8638,8 +8760,12 @@ snapshots:
triple-beam: 1.4.1
winston-transport: 4.9.0
wmf@1.0.2: {}
word-wrap@1.2.5: {}
word@0.3.0: {}
wrap-ansi@10.0.0:
dependencies:
ansi-styles: 6.2.3
@@ -8670,6 +8796,16 @@ snapshots:
xe-utils@3.9.1: {}
xlsx@0.18.5:
dependencies:
adler-32: 1.3.1
cfb: 1.2.2
codepage: 1.15.0
crc-32: 1.2.2
ssf: 0.11.2
wmf: 1.0.2
word: 0.3.0
xml-name-validator@4.0.0: {}
xpath@0.0.34: {}
+54
View File
@@ -0,0 +1,54 @@
import type { DictRequest, PageQuery, TransformQuery, TransformRes } from "@fast-crud/fast-crud"
import { request } from "@/http/axios"
/**
* fast-crud 查询参数 -> 后端分页参数
*
* 默认按 serverschool 的 MyBatis-Plus 约定: pageNumber(从 1 开始) + pageSize
* JPA 分页接口(从 0 开始)需要在页面里覆写 transformQuery
*/
export const transformQuery: TransformQuery = (query: PageQuery) => {
const { page, form, sort } = query
const params: Record<string, any> = {
...form,
pageNumber: page?.currentPage ?? 1,
pageSize: page?.pageSize ?? 20
}
// serverschool PaginationUtils 约定: orderBy + orderDirection
if (sort?.prop) {
params.orderBy = sort.prop
params.orderDirection = sort.asc === false ? "desc" : "asc"
}
return params
}
/**
* 后端分页结果 -> fast-crud 分页结果
*
* 兼容 MyBatis-Plus(records/total/current/size) 与 JPA(content/totalElements/number)
*/
export const transformRes: TransformRes = ({ res, query }) => {
const payload = res?.data ?? res ?? {}
const records = payload.records ?? payload.content ?? payload.list ?? []
const total = payload.total ?? payload.totalElements ?? records.length
const currentPage = payload.current
?? (payload.number != null ? payload.number + 1 : (query?.pageNumber ?? query?.currentPage ?? 1))
const pageSize = payload.size ?? payload.pageSize ?? query?.pageSize ?? query?.size ?? records.length
return { currentPage, pageSize, total, records }
}
/**
* 字典数据请求
*
* 兼容 serverschool 字典字段(dictLabel/dictValue) 与 fast-crud 默认字段(label/value)
*/
export const dictRequest: DictRequest = async ({ url }) => {
const res = await request<ApiResponseData<any>>({ url, method: "get" })
const payload = res?.data
const rows: any[] = Array.isArray(payload) ? payload : (payload?.list ?? payload?.records ?? [])
return rows.map(item => ({
...item,
label: item.label ?? item.dictLabel,
value: item.value ?? item.dictValue
}))
}
+2
View File
@@ -11,6 +11,8 @@ import "nprogress/nprogress.css"
import "element-plus/dist/index.css"
import "element-plus/theme-chalk/dark/css-vars.css"
import "vxe-table/lib/style.css"
import "@fast-crud/fast-crud/dist/style.css"
import "@fast-crud/ui-element/dist/style.css"
import "@@/assets/styles/index.scss"
import "virtual:uno.css"
+108
View File
@@ -0,0 +1,108 @@
import type { TableRequestData } from "@@/apis/tables/type"
import type { AddReq, CreateCrudOptionsRet, DelReq, EditReq, PageQuery, UserPageQuery } from "@fast-crud/fast-crud"
import { createTableDataApi, deleteTableDataApi, getTableDataApi, updateTableDataApi } from "@@/apis/tables"
import { dict } from "@fast-crud/fast-crud"
export default function createCrudOptions(): CreateCrudOptionsRet {
/**
* Mock 接口用 currentPage/size,与后端默认的 pageNumber/pageSize 不一致,这里覆写
* transformRes 用全局的即可(已兼容 list/total)
*/
const transformQuery = (query: PageQuery) => ({
username: query.form?.username ?? "",
phone: query.form?.phone ?? "",
currentPage: query.page?.currentPage ?? 1,
size: query.page?.pageSize ?? 10
})
const pageRequest = async (query: UserPageQuery): Promise<any> => {
const { currentPage, size, username, phone } = query as TableRequestData
return await getTableDataApi({ currentPage, size, username, phone })
}
const addRequest = async ({ form }: AddReq) => {
return await createTableDataApi(form)
}
const editRequest = async ({ form, row }: EditReq) => {
return await updateTableDataApi({ ...form, id: form.id ?? row.id })
}
const delRequest = async ({ row }: DelReq) => {
return await deleteTableDataApi(row.id)
}
return {
crudOptions: {
request: {
transformQuery,
pageRequest,
addRequest,
editRequest,
delRequest
},
columns: {
id: {
title: "ID",
type: "number",
column: {
width: 80
},
form: {
show: false
}
},
username: {
title: "用户名",
type: "text",
search: {
show: true
},
column: {
minWidth: 160
},
form: {
rules: [{ required: true, message: "请输入用户名" }]
}
},
roles: {
title: "角色",
type: "dict-select",
search: {
show: true
},
dict: dict({
data: [
{ value: "admin", label: "管理员", color: "success" },
{ value: "user", label: "普通用户", color: "info" }
]
})
},
phone: {
title: "手机号",
type: "text",
search: {
show: true
}
},
email: {
title: "邮箱",
type: "text"
},
status: {
title: "状态",
type: "dict-select",
column: {
width: 100
},
dict: dict({
data: [
{ value: true, label: "启用", color: "success" },
{ value: false, label: "禁用", color: "danger" }
]
})
}
}
}
}
}
+48
View File
@@ -0,0 +1,48 @@
<script lang="ts" setup>
import { useFs } from "@fast-crud/fast-crud"
import createCrudOptions from "./crud"
defineOptions({
// 命名当前组件
name: "FastCrud"
})
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions })
onMounted(() => {
crudExpose.doRefresh()
})
</script>
<template>
<div class="app-container fast-crud-page">
<el-alert
title="数据来源"
type="success"
description="由 Apifox 提供在线 Mock,数据不具备真实性,仅供简单的 CRUD 操作演示"
show-icon
/>
<!-- fs-page 是绝对定位,父节点需要 position: relative -->
<div class="fast-crud-page__body">
<fs-page>
<fs-crud ref="crudRef" v-bind="crudBinding" />
</fs-page>
</div>
</div>
</template>
<style lang="scss" scoped>
.fast-crud-page {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.fast-crud-page__body {
position: relative;
flex: 1;
min-height: 0;
margin-top: 20px;
}
</style>
+149
View File
@@ -0,0 +1,149 @@
import type { App } from "vue"
import { FastCrud } from "@fast-crud/fast-crud"
import ui from "@fast-crud/ui-element"
import {
ElAutocomplete,
ElAutoResizer,
ElBadge,
ElButton,
ElButtonGroup,
ElCard,
ElCascader,
ElCheckbox,
ElCheckboxGroup,
ElCol,
ElCollapse,
ElCollapseItem,
ElCollapseTransition,
ElColorPicker,
ElDatePicker,
ElDialog,
ElDivider,
ElDrawer,
ElDropdown,
ElDropdownItem,
ElDropdownMenu,
ElForm,
ElFormItem,
ElIcon,
ElImage,
ElInput,
ElInputNumber,
ElMessage,
ElMessageBox,
ElNotification,
ElPagination,
ElPopover,
ElProgress,
ElRadio,
ElRadioButton,
ElRadioGroup,
ElRate,
ElRow,
ElSelect,
ElSlider,
ElSwitch,
ElTable,
ElTableColumn,
ElTableV2,
ElTabPane,
ElTabs,
ElTag,
ElTimePicker,
ElTooltip,
ElTransfer,
ElTreeSelect,
ElUpload
} from "element-plus"
import { dictRequest, transformQuery, transformRes } from "@/common/utils/crud-request"
/**
* fast-crud 内部用 resolveComponent("el-xxx") 按名字取组件,
* 所以这里需要把 Element Plus 组件再全局注册一份(项目其它地方仍是按需引入)
*/
const elementComponents = {
ElAutocomplete,
ElAutoResizer,
ElBadge,
ElButton,
ElButtonGroup,
ElCard,
ElCascader,
ElCheckbox,
ElCheckboxGroup,
ElCol,
ElCollapse,
ElCollapseItem,
ElCollapseTransition,
ElColorPicker,
ElDatePicker,
ElDialog,
ElDivider,
ElDrawer,
ElDropdown,
ElDropdownItem,
ElDropdownMenu,
ElForm,
ElFormItem,
ElIcon,
ElImage,
ElInput,
ElInputNumber,
ElPagination,
ElPopover,
ElProgress,
ElRadio,
ElRadioButton,
ElRadioGroup,
ElRate,
ElRow,
ElSelect,
ElSlider,
ElSwitch,
ElTable,
ElTableColumn,
ElTableV2,
ElTabPane,
ElTabs,
ElTag,
ElTimePicker,
ElTooltip,
ElTransfer,
ElTreeSelect,
ElUpload
}
export function installFastCrud(app: App) {
// 注册 Element Plus 组件
for (const [name, component] of Object.entries(elementComponents)) {
app.component(name, component)
}
// 消息类组件依赖 app 上下文
app.use(ElMessage)
app.use(ElNotification)
app.use(ElMessageBox)
// fast-crud 的 Element Plus 适配器
// 图标已由 installElementPlusIcons 全局注册过,这里不能再注册一次(会重复注册告警)
const elementUi = ui.set()
// Element Plus 图标库没有 Copy / Left,指向实际存在的图标
elementUi.icons.copy = "copy-document"
elementUi.icons.arrowLeft = "arrow-left"
app.use(FastCrud, {
ui: elementUi,
dictRequest,
commonOptions: () => ({
request: {
transformQuery,
transformRes
}
}),
logger: {
off: {
tableColumns: false
}
}
})
}
+2
View File
@@ -1,5 +1,6 @@
import type { App } from "vue"
import { installElementPlusIcons } from "./element-plus-icons"
import { installFastCrud } from "./fast-crud"
import { installPermissionDirective } from "./permission-directive"
import { installSvgIcon } from "./svg-icon"
import { installVxeTable } from "./vxe-table"
@@ -9,4 +10,5 @@ export function installPlugins(app: App) {
installPermissionDirective(app)
installSvgIcon(app)
installVxeTable(app)
installFastCrud(app)
}
+8
View File
@@ -99,6 +99,14 @@ export const constantRoutes: RouteRecordRaw[] = [
keepAlive: true
}
},
{
path: "fast-crud",
component: () => import("@/pages/demo/fast-crud/index.vue"),
name: "FastCrud",
meta: {
title: "Fast Crud"
}
},
{
path: "level2",
component: () => import("@/pages/demo/level2/index.vue"),