From 80b8218b2320ca7dc65a51a85ec099446518dc79 Mon Sep 17 00:00:00 2001 From: # Date: Mon, 26 Oct 2026 21:26:36 +0800 Subject: [PATCH] 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. --- package.json | 4 + pnpm-lock.yaml | 136 ++++++++++++++++++++++++++ src/common/utils/crud-request.ts | 54 +++++++++++ src/main.ts | 2 + src/pages/demo/fast-crud/crud.ts | 108 +++++++++++++++++++++ src/pages/demo/fast-crud/index.vue | 48 ++++++++++ src/plugins/fast-crud.ts | 149 +++++++++++++++++++++++++++++ src/plugins/index.ts | 2 + src/router/index.ts | 8 ++ 9 files changed, 511 insertions(+) create mode 100644 src/common/utils/crud-request.ts create mode 100644 src/pages/demo/fast-crud/crud.ts create mode 100644 src/pages/demo/fast-crud/index.vue create mode 100644 src/plugins/fast-crud.ts diff --git a/package.json b/package.json index 55ed90b..7f50905 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cef4a54..e12a16e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: {} diff --git a/src/common/utils/crud-request.ts b/src/common/utils/crud-request.ts new file mode 100644 index 0000000..1b9c8ad --- /dev/null +++ b/src/common/utils/crud-request.ts @@ -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 = { + ...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>({ 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 + })) +} diff --git a/src/main.ts b/src/main.ts index b43cfa4..5b97d31 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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" diff --git a/src/pages/demo/fast-crud/crud.ts b/src/pages/demo/fast-crud/crud.ts new file mode 100644 index 0000000..fb203d3 --- /dev/null +++ b/src/pages/demo/fast-crud/crud.ts @@ -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 => { + 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" } + ] + }) + } + } + } + } +} diff --git a/src/pages/demo/fast-crud/index.vue b/src/pages/demo/fast-crud/index.vue new file mode 100644 index 0000000..94fa66a --- /dev/null +++ b/src/pages/demo/fast-crud/index.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/src/plugins/fast-crud.ts b/src/plugins/fast-crud.ts new file mode 100644 index 0000000..65da793 --- /dev/null +++ b/src/plugins/fast-crud.ts @@ -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 + } + } + }) +} diff --git a/src/plugins/index.ts b/src/plugins/index.ts index ac348f6..f3b293d 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -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) } diff --git a/src/router/index.ts b/src/router/index.ts index e737c0a..a032030 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -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"),