feat: 添加代码生成功能

This commit is contained in:
2025-03-04 00:56:21 +08:00
parent fef47ca7d9
commit 2a4906a66c
11 changed files with 1631 additions and 1 deletions

View File

@@ -32,6 +32,8 @@ buttons:Export: Export
buttons:Import: Import buttons:Import: Import
buttons:DownLoded: DownLoded buttons:DownLoded: DownLoded
buttons:DownLodedTemplate: DownLoded Template buttons:DownLodedTemplate: DownLoded Template
buttons:DownLodedAll: All DownLoded
buttons:Copy: Copy
buttons:Hide: Hide buttons:Hide: Hide
buttons:ConfirmUpload: Confirm Upload buttons:ConfirmUpload: Confirm Upload
buttons:Save: Save buttons:Save: Save
@@ -51,6 +53,10 @@ buttons:RoleAllocation: Role Allocation
buttons:PermissionDetails: Permission Details buttons:PermissionDetails: Permission Details
buttons:ForceToExit: Force Exit buttons:ForceToExit: Force Exit
buttons:DataAdmin: Data Admin buttons:DataAdmin: Data Admin
buttons:TableList: Table List
buttons:UpdateTableColumns: Update Table Columns
buttons:Preview: Preview
buttons:Sync: Synchroniztion
search:Total: Total search:Total: Total
search:History: History search:History: History
search:Collect: Collect search:Collect: Collect
@@ -114,6 +120,7 @@ menus:FourZeroFour: "404"
menus:FourZeroOne: "403" menus:FourZeroOne: "403"
menus:Five: "500" menus:Five: "500"
menus:SystemConfig: System Config menus:SystemConfig: System Config
menus:GenerateCode: Generate Code
status:Load: Loading... status:Load: Loading...
status:Message: Message status:Message: Message
status:Notify: Notify status:Notify: Notify

View File

@@ -31,6 +31,8 @@ buttons:Delete: 删除
buttons:Export: 导出 buttons:Export: 导出
buttons:Import: 导入 buttons:Import: 导入
buttons:DownLoded: 下载 buttons:DownLoded: 下载
buttons:DownLodedAll: 全部下载
buttons:Copy: 复制
buttons:DownLodedTemplate: 下载模版 buttons:DownLodedTemplate: 下载模版
buttons:Hide: 隐藏 buttons:Hide: 隐藏
buttons:ConfirmUpload: 确认上传 buttons:ConfirmUpload: 确认上传
@@ -51,6 +53,10 @@ buttons:PermissionDetails: 权限详情
buttons:ForceToExit: 强制退出 buttons:ForceToExit: 强制退出
buttons:ExitInBatches: 批量强退 buttons:ExitInBatches: 批量强退
buttons:DataAdmin: 数据管理 buttons:DataAdmin: 数据管理
buttons:TableList: 数据表列表
buttons:UpdateTableColumns: 更新数据表列信息
buttons:Preview: 预览
buttons:Sync: 同步
search:Total: search:Total:
search:History: 搜索历史 search:History: 搜索历史
search:Collect: 收藏 search:Collect: 收藏
@@ -114,6 +120,7 @@ menus:FourZeroFour: "404"
menus:FourZeroOne: "403" menus:FourZeroOne: "403"
menus:Five: "500" menus:Five: "500"
menus:SystemConfig: 系统配置 menus:SystemConfig: 系统配置
menus:GenerateCode: 代码生成
status:Load: 加载中... status:Load: 加载中...
status:Message: 消息 status:Message: 消息
status:Notify: 通知 status:Notify: 通知

View File

@@ -60,7 +60,10 @@
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"element-plus": "^2.9.0", "element-plus": "^2.9.0",
"file-saver": "^2.0.5",
"highlight.js": "^11.11.1",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"jszip": "^3.10.1",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",

94
pnpm-lock.yaml generated
View File

@@ -44,9 +44,18 @@ importers:
element-plus: element-plus:
specifier: ^2.9.0 specifier: ^2.9.0
version: 2.9.0(vue@3.5.13(typescript@5.6.3)) version: 2.9.0(vue@3.5.13(typescript@5.6.3))
file-saver:
specifier: ^2.0.5
version: 2.0.5
highlight.js:
specifier: ^11.11.1
version: 11.11.1
js-cookie: js-cookie:
specifier: ^3.0.5 specifier: ^3.0.5
version: 3.0.5 version: 3.0.5
jszip:
specifier: ^3.10.1
version: 3.10.1
localforage: localforage:
specifier: ^1.10.0 specifier: ^1.10.0
version: 1.10.0 version: 1.10.0
@@ -1565,6 +1574,9 @@ packages:
convert-source-map@2.0.0: convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, tarball: https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz}
cosmiconfig-typescript-loader@5.1.0: cosmiconfig-typescript-loader@5.1.0:
resolution: {integrity: sha512-7PtBB+6FdsOvZyJtlF3hEPpACq7RQX6BVGsgC7/lfVXnKMvNCu/XY3ykreqG5w/rBNdu2z8LCIKoF3kpHHdHlA==} resolution: {integrity: sha512-7PtBB+6FdsOvZyJtlF3hEPpACq7RQX6BVGsgC7/lfVXnKMvNCu/XY3ykreqG5w/rBNdu2z8LCIKoF3kpHHdHlA==}
engines: {node: '>=v16'} engines: {node: '>=v16'}
@@ -1960,6 +1972,9 @@ packages:
resolution: {integrity: sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==} resolution: {integrity: sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==}
engines: {node: '>=18'} engines: {node: '>=18'}
file-saver@2.0.5:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==, tarball: https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz}
fill-range@7.1.1: fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -2149,6 +2164,10 @@ packages:
hey-listen@1.0.8: hey-listen@1.0.8:
resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==}
highlight.js@11.11.1:
resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==, tarball: https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz}
engines: {node: '>=12.0.0'}
hookable@5.5.3: hookable@5.5.3:
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
@@ -2177,7 +2196,7 @@ packages:
engines: {node: '>= 4'} engines: {node: '>= 4'}
immediate@3.0.6: immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==, tarball: https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz}
immutable@5.0.3: immutable@5.0.3:
resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==} resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==}
@@ -2196,6 +2215,9 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'} engines: {node: '>=0.8.19'}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, tarball: https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz}
ini@1.3.8: ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
@@ -2266,6 +2288,9 @@ packages:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'} engines: {node: '>=8'}
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==, tarball: https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz}
isexe@2.0.0: isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@@ -2334,6 +2359,9 @@ packages:
resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
engines: {'0': node >= 0.2.0} engines: {'0': node >= 0.2.0}
jszip@3.10.1:
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==, tarball: https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz}
keyv@4.5.4: keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@@ -2361,6 +2389,9 @@ packages:
lie@3.1.1: lie@3.1.1:
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
lie@3.3.0:
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==, tarball: https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz}
lilconfig@3.1.3: lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'} engines: {node: '>=14'}
@@ -2678,6 +2709,9 @@ packages:
package-json-from-dist@1.0.1: package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==, tarball: https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz}
parent-module@1.0.1: parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'} engines: {node: '>=6'}
@@ -3043,6 +3077,9 @@ packages:
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==, tarball: https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz}
proxy-from-env@1.1.0: proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
@@ -3068,6 +3105,9 @@ packages:
read-cache@1.0.0: read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==, tarball: https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz}
readdirp@3.6.0: readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'} engines: {node: '>=8.10.0'}
@@ -3144,6 +3184,9 @@ packages:
run-parallel@1.2.0: run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==, tarball: https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz}
sass@1.82.0: sass@1.82.0:
resolution: {integrity: sha512-j4GMCTa8elGyN9A7x7bEglx0VgSpNUG4W4wNedQ33wSMdnkqQCT8HTwOaVSV4e6yQovcu/3Oc4coJP/l0xhL2Q==} resolution: {integrity: sha512-j4GMCTa8elGyN9A7x7bEglx0VgSpNUG4W4wNedQ33wSMdnkqQCT8HTwOaVSV4e6yQovcu/3Oc4coJP/l0xhL2Q==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
@@ -3168,6 +3211,9 @@ packages:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==, tarball: https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz}
shebang-command@2.0.0: shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -3246,6 +3292,9 @@ packages:
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==, tarball: https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz}
strip-ansi@6.0.1: strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -5159,6 +5208,8 @@ snapshots:
convert-source-map@2.0.0: {} convert-source-map@2.0.0: {}
core-util-is@1.0.3: {}
cosmiconfig-typescript-loader@5.1.0(@types/node@20.17.9)(cosmiconfig@9.0.0(typescript@5.6.3))(typescript@5.6.3): cosmiconfig-typescript-loader@5.1.0(@types/node@20.17.9)(cosmiconfig@9.0.0(typescript@5.6.3))(typescript@5.6.3):
dependencies: dependencies:
'@types/node': 20.17.9 '@types/node': 20.17.9
@@ -5611,6 +5662,8 @@ snapshots:
dependencies: dependencies:
flat-cache: 5.0.0 flat-cache: 5.0.0
file-saver@2.0.5: {}
fill-range@7.1.1: fill-range@7.1.1:
dependencies: dependencies:
to-regex-range: 5.0.1 to-regex-range: 5.0.1
@@ -5818,6 +5871,8 @@ snapshots:
hey-listen@1.0.8: {} hey-listen@1.0.8: {}
highlight.js@11.11.1: {}
hookable@5.5.3: hookable@5.5.3:
optional: true optional: true
@@ -5856,6 +5911,8 @@ snapshots:
imurmurhash@0.1.4: {} imurmurhash@0.1.4: {}
inherits@2.0.4: {}
ini@1.3.8: {} ini@1.3.8: {}
ini@4.1.1: {} ini@4.1.1: {}
@@ -5906,6 +5963,8 @@ snapshots:
dependencies: dependencies:
is-docker: 2.2.1 is-docker: 2.2.1
isarray@1.0.0: {}
isexe@2.0.0: {} isexe@2.0.0: {}
jackspeak@3.4.3: jackspeak@3.4.3:
@@ -5962,6 +6021,13 @@ snapshots:
jsonparse@1.3.1: {} jsonparse@1.3.1: {}
jszip@3.10.1:
dependencies:
lie: 3.3.0
pako: 1.0.11
readable-stream: 2.3.8
setimmediate: 1.0.5
keyv@4.5.4: keyv@4.5.4:
dependencies: dependencies:
json-buffer: 3.0.1 json-buffer: 3.0.1
@@ -5990,6 +6056,10 @@ snapshots:
dependencies: dependencies:
immediate: 3.0.6 immediate: 3.0.6
lie@3.3.0:
dependencies:
immediate: 3.0.6
lilconfig@3.1.3: {} lilconfig@3.1.3: {}
lines-and-columns@1.2.4: {} lines-and-columns@1.2.4: {}
@@ -6288,6 +6358,8 @@ snapshots:
package-json-from-dist@1.0.1: {} package-json-from-dist@1.0.1: {}
pako@1.0.11: {}
parent-module@1.0.1: parent-module@1.0.1:
dependencies: dependencies:
callsites: 3.1.0 callsites: 3.1.0
@@ -6613,6 +6685,8 @@ snapshots:
prettier@3.4.2: {} prettier@3.4.2: {}
process-nextick-args@2.0.1: {}
proxy-from-env@1.1.0: {} proxy-from-env@1.1.0: {}
punycode@2.3.1: {} punycode@2.3.1: {}
@@ -6639,6 +6713,16 @@ snapshots:
dependencies: dependencies:
pify: 2.3.0 pify: 2.3.0
readable-stream@2.3.8:
dependencies:
core-util-is: 1.0.3
inherits: 2.0.4
isarray: 1.0.0
process-nextick-args: 2.0.1
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
readdirp@3.6.0: readdirp@3.6.0:
dependencies: dependencies:
picomatch: 2.3.1 picomatch: 2.3.1
@@ -6725,6 +6809,8 @@ snapshots:
dependencies: dependencies:
queue-microtask: 1.2.3 queue-microtask: 1.2.3
safe-buffer@5.1.2: {}
sass@1.82.0: sass@1.82.0:
dependencies: dependencies:
chokidar: 4.0.1 chokidar: 4.0.1
@@ -6751,6 +6837,8 @@ snapshots:
gopd: 1.2.0 gopd: 1.2.0
has-property-descriptors: 1.0.2 has-property-descriptors: 1.0.2
setimmediate@1.0.5: {}
shebang-command@2.0.0: shebang-command@2.0.0:
dependencies: dependencies:
shebang-regex: 3.0.0 shebang-regex: 3.0.0
@@ -6823,6 +6911,10 @@ snapshots:
get-east-asian-width: 1.3.0 get-east-asian-width: 1.3.0
strip-ansi: 7.1.0 strip-ansi: 7.1.0
string_decoder@1.1.1:
dependencies:
safe-buffer: 5.1.2
strip-ansi@6.0.1: strip-ansi@6.0.1:
dependencies: dependencies:
ansi-regex: 5.0.1 ansi-regex: 5.0.1

110
src/api/generate.ts Normal file
View File

@@ -0,0 +1,110 @@
import { http } from "@/utils/http";
import type {
GenerateCodeResult,
GenerateTableInfo,
TableColumnInfo,
TableInfo
} from "types/generate";
import { filterEmptyObject } from "./utils";
/**获取数据库中所有表信息 */
export const getTableListAPI = () => {
return http.request<QueryListResult<TableInfo>>(
"GET",
"/api/generate/tables"
);
};
/**
* 添加生成表参数
*/
interface AddGenerateTableParams {
/**表名 */
table_name: string;
/**表注释 */
table_comment: string;
/**作者 */
author: string;
/**类名 */
class_name: string;
/**权限ID */
permission_id: string;
/**API前缀 */
prefix: string;
/**备注 */
remark: string;
/**描述 */
description: string;
}
/**
* 添加生成表
*/
export const postAddGenerateTableAPI = (data: AddGenerateTableParams) => {
return http.request<null>("POST", "/api/generate/add", { data });
};
/**删除生成表信息 */
export const deleteGenerateTableAPI = (id: string) => {
return http.request<null>("DELETE", `/api/generate/delete/${id}`);
};
/**批量删除生成表 */
export const deleteGenerateTableListAPI = (ids: string[]) => {
return http.request<null>("DELETE", `/api/generate/delete`, {
data: { ids }
});
};
/**
* 修改生成表参数
*/
interface UpdateGenerateTableParams {
columns: TableColumnInfo[];
}
/**修改生成表 */
export const putUpdateGenerateTableAPI = (
data: AddGenerateTableParams,
id: string
) => {
return http.request<null>("PUT", `/api/generate/update/${id}`, { data });
};
/**修改生成表列信息 */
export const putUpdateGenerateTableColumnAPI = (
data: UpdateGenerateTableParams,
id: string
) => {
return http.request<null>("PUT", `/api/generate/updateColumns/${id}`, {
data
});
};
/**获取生成表信息 */
export const getGenerateTableInfoAPI = (id: string) => {
return http.request<GenerateTableInfo & { columns: TableColumnInfo[] }>(
"GET",
`/api/generate/info/${id}`
);
};
/**获取生成表列表 */
export const getGenerateTableListAPI = (params: {
page: number;
pageSize: number;
table_comment?: string;
permission_id?: string;
}) => {
return http.request<
QueryListResult<GenerateTableInfo & { columns: TableColumnInfo[] }>
>("GET", "/api/generate/list", {
params: filterEmptyObject(params)
});
};
/**
* 获取生成的代码
*/
export const getGenerateCodeAPI = (id: string) => {
return http.request<GenerateCodeResult>("GET", `/api/generate/code/${id}`);
};

View File

@@ -0,0 +1,158 @@
<template>
<el-table
ref="dragTable"
:data="newFormInline.columns"
row-key="id"
:max-height="tableHeight"
border
stripe
>
<el-table-column label="序号" type="index" min-width="5%" align="center" />
<el-table-column
label="字段列名"
prop="column_name"
min-width="10%"
:show-overflow-tooltip="true"
align="center"
/>
<el-table-column
label="字段描述"
min-width="12%"
align="center"
:show-overflow-tooltip="true"
>
<template #default="scope">
<el-input v-model="scope.row.column_comment" />
</template>
</el-table-column>
<el-table-column
label="字段类型"
prop="column_type"
min-width="10%"
align="center"
:show-overflow-tooltip="true"
/>
<el-table-column label="python类型" min-width="8%" align="center">
<template #default="scope">
<el-select v-model="scope.row.python_type">
<el-option label="int" value="int" />
<el-option label="str" value="str" />
<el-option label="datetime" value="datetime" />
<el-option label="float" value="float" />
<el-option label="bytes" value="bytes" />
<el-option label="dict" value="dict" />
</el-select>
</template>
</el-table-column>
<el-table-column label="python字段名" min-width="10%" align="center">
<template #default="scope">
<el-input v-model="scope.row.python_name" />
</template>
</el-table-column>
<el-table-column label="插入" min-width="5%" align="center">
<template #default="scope">
<el-checkbox
v-model="scope.row.is_insert"
:true-label="1"
:false-label="0"
/>
</template>
</el-table-column>
<el-table-column label="编辑" min-width="5%" align="center">
<template #default="scope">
<el-checkbox
v-model="scope.row.is_edit"
:true-label="1"
:false-label="0"
/>
</template>
</el-table-column>
<el-table-column label="列表" min-width="5%" align="center">
<template #default="scope">
<el-checkbox
v-model="scope.row.is_list"
:true-label="1"
:false-label="0"
/>
</template>
</el-table-column>
<el-table-column label="查询" min-width="5%" align="center">
<template #default="scope">
<el-checkbox
v-model="scope.row.is_query"
:true-label="1"
:false-label="0"
/>
</template>
</el-table-column>
<el-table-column label="必填" min-width="5%" align="center">
<template #default="scope">
<el-checkbox
v-model="scope.row.is_required"
:true-label="1"
:false-label="0"
/>
</template>
</el-table-column>
<el-table-column label="隐藏" min-width="5%" align="center">
<template #default="scope">
<el-checkbox
v-model="scope.row.is_hide"
:true-label="1"
:false-label="0"
/>
</template>
</el-table-column>
<el-table-column label="查询方式" min-width="10%" align="center">
<template #default="scope">
<el-select v-model="scope.row.query_way">
<el-option label="=" value="" />
<el-option label="!=" value="__not" />
<el-option label=">" value="__gt" />
<el-option label=">=" value="__gte" />
<el-option label="<" value="__lt" />
<el-option label="<=" value="__lte" />
<el-option label="LIKE" value="__icontains" />
<el-option label="BETWEEN" value="__range" />
</el-select>
</template>
</el-table-column>
<el-table-column label="显示类型" min-width="10%" align="center">
<template #default="scope">
<el-select v-model="scope.row.show_type">
<el-option label="文本框" value="input" />
<el-option label="文本域" value="textarea" />
<el-option label="下拉框" value="select" />
<el-option label="单选框" value="radio" />
<el-option label="复选框" value="checkbox" />
<el-option label="日期控件" value="datetime" />
<el-option label="图片上传" value="imageUpload" />
<el-option label="文件上传" value="fileUpload" />
<el-option label="富文本控件" value="editor" />
</el-select>
</template>
</el-table-column>
</el-table>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { TableColumnInfo } from "types/generate";
interface FormItemProps {
/**列表 */
columns: TableColumnInfo[];
}
interface FormProps {
formInline: FormItemProps;
}
const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({
/**列表 */
columns: []
})
});
const newFormInline = ref<FormItemProps>(props.formInline);
defineExpose({ newFormInline });
const tableHeight = ref(document.documentElement.scrollHeight - 245 + "px");
</script>

View File

@@ -0,0 +1,216 @@
<template>
<el-form ref="ruleFormRef" :model="newFormInline" label-width="82px">
<el-row :gutter="30">
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="数据表描述" prop="table_comment">
<el-select
v-model="newFormInline.table_comment"
clearable
placeholder="请输入数据表描述"
@change="handleTableChange"
>
<el-option
v-for="item in newFormInline.tables"
:key="item.table_name"
:value="item.table_comment"
:label="item.table_comment"
/>
</el-select>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="数据表名称" prop="table_name">
<el-input
v-model="newFormInline.table_name"
clearable
disabled
placeholder="请输入数据表名称~"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="类名" prop="class_name">
<el-input
v-model="newFormInline.class_name"
clearable
placeholder="请输入类名~"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="类描述" prop="description">
<el-input
v-model="newFormInline.description"
clearable
placeholder="请输入类描述~"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="API前缀" prop="prefix">
<el-input
v-model="newFormInline.prefix"
clearable
placeholder="请输入API前缀~"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="作者" prop="author">
<el-input
v-model="newFormInline.author"
clearable
placeholder="请输入作者名称~"
/>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="权限ID" prop="permission_id">
<el-cascader
v-model="newFormInline.permission_id"
class="w-full"
:options="newFormInline.permissions"
:props="{
value: 'id',
label: 'name',
emitPath: false,
checkStrictly: true
}"
clearable
filterable
placeholder="请选择所属权限~"
>
<template #default="{ node, data }">
<span>{{ t(data.name) }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</re-col>
<re-col :value="24" :xm="24" :sm="24">
<el-form-item label="备注" prop="remark">
<el-input
v-model="newFormInline.remark"
type="textarea"
clearable
placeholder="请输入内容"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way === '修改'" :value="24" :xm="24" :sm="24">
<el-form-item label="开始时间" prop="create_time">
<el-input
:value="
newFormInline.create_time
? dayjs(newFormInline.create_time).format('YYYY-MM-DD HH:mm:ss')
: ''
"
clearable
disabled
placeholder="请输入创建时间~"
/>
</el-form-item>
</re-col>
<re-col v-if="newFormInline.way === '修改'" :value="24" :xm="24" :sm="24">
<el-form-item label="修改时间" prop="update_time">
<el-input
:value="
newFormInline.update_time
? dayjs(newFormInline.update_time).format('YYYY-MM-DD HH:mm:ss')
: ''
"
clearable
disabled
placeholder="请输入修改时间~"
/>
</el-form-item>
</re-col>
</el-row>
</el-form>
</template>
<script setup lang="ts">
import dayjs from "dayjs";
import { ref } from "vue";
import ReCol from "@/components/ReCol";
import { TableInfo } from "types/generate";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
interface FormItemProps {
/**方式 */
way: string;
/**数据表列表 */
tables: TableInfo[];
/**权限列表 */
permissions: any[];
/**数据ID */
id: string;
/**作者 */
author: string;
/**类名 */
class_name: string;
/**权限ID */
permission_id: string;
/**API前缀 */
prefix: string;
/**备注 */
remark: string;
/**描述 */
description: string;
/**表名 */
table_name: string;
/**表注释 */
table_comment: string;
/**创建时间 */
create_time: string;
/**修改时间 */
update_time: string;
}
interface FormProps {
formInline: FormItemProps;
}
const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({
/**方式 */
way: "",
/**数据表列表 */
tables: [],
/**权限列表 */
permissions: [],
/**数据ID */
id: "",
/**作者 */
author: "",
/**类名 */
class_name: "",
/**权限ID */
permission_id: "",
/**API前缀 */
prefix: "",
/**备注 */
remark: "",
/**描述 */
description: "",
/**表名 */
table_name: "",
/**表注释 */
table_comment: "",
/**创建时间 */
create_time: "",
/**修改时间 */
update_time: ""
})
});
const newFormInline = ref<FormItemProps>(props.formInline);
defineExpose({ newFormInline });
/**表选择变化 */
const handleTableChange = (value: string) => {
const table = newFormInline.value.tables.find(
item => item.table_comment === value
);
if (table) {
newFormInline.value.table_name = table.table_name;
}
};
</script>

View File

@@ -0,0 +1,214 @@
<template>
<el-card shadow="never">
<Segmented
value="`backend`"
:resize="true"
block
:options="codeTypeOptions"
@change="handleClick"
/>
<el-tabs v-model="activeName">
<el-tab-pane
v-for="item in activeNameOptions"
:key="item.label"
:name="item.label"
:label="item.label"
>
<div class="code-header">
<span class="lang">{{ item.type }}</span>
<div>
<el-button
text
class="copy-btn"
:icon="useRenderIcon(Copy)"
@click="copyCode(item.value)"
>
{{ t("buttons:Copy") }}
</el-button>
<el-button
text
class="download-btn"
:icon="useRenderIcon(Download)"
@click="downloadFile(item.label, item.value)"
>
{{ t("buttons:DownLoded") }}
</el-button>
</div>
</div>
<el-scrollbar :height="maxHeight">
<!-- 代码高亮显示 -->
<pre><code class="hljs" v-html="highlightedCode(item.value, item.type)"/></pre>
</el-scrollbar>
</el-tab-pane>
</el-tabs>
<!-- 下载全部代码按钮 -->
<el-button
type="primary"
class="zip-download-btn"
:icon="useRenderIcon(Download)"
@click="downloadZip"
>
{{ t("buttons:DownLodedAll") }}
</el-button>
</el-card>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from "vue";
import { message } from "@/utils/message";
import Segmented, { OptionsType } from "@/components/ReSegmented";
import hljs from "highlight.js";
import "highlight.js/styles/github.css";
import JSZip from "jszip";
import { saveAs } from "file-saver";
import Copy from "@iconify-icons/ri/file-copy-2-line";
import Download from "@iconify-icons/ri/download-line";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { GenerateCodeInfo, GenerateCodeResult } from "types/generate";
import { getGenerateCodeAPI } from "@/api/generate";
import { cloneDeep } from "@pureadmin/utils";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
interface FormItemProps {
id: string;
table_name: string;
}
interface FormProps {
formInline: FormItemProps;
}
const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({ id: "", table_name: "" })
});
const newFormInline = ref<FormItemProps>(props.formInline);
defineExpose({ newFormInline });
const codeTypeOptions: Array<OptionsType> = [
{ label: "后端代码", value: "backend" },
{ label: "前端代码", value: "frontend" },
{ label: "SQL代码", value: "sql" }
];
const activeName = ref<string>("");
const activeNameOptions = ref<GenerateCodeInfo[]>([]);
const dataList = ref<GenerateCodeResult>();
/** 处理tab点击 */
const handleClick = (item: {
index: number;
option: { value: string; label: string };
}) => {
activeNameOptions.value = dataList.value[item.option.value];
activeName.value = activeNameOptions.value[0].label;
};
/** 获取代码数据 */
const getGenerateCode = async () => {
const res = await getGenerateCodeAPI(newFormInline.value.id);
if (res.success) {
dataList.value = cloneDeep(res.data);
activeNameOptions.value = dataList.value.backend;
activeName.value = activeNameOptions.value[0].label;
}
};
const maxHeight = ref(document.documentElement.scrollHeight - 380 + "px");
/** 代码高亮 */
const highlightedCode = (code: string, lang: string) => {
try {
return hljs.highlight(code, { language: lang }).value;
} catch (e) {
return hljs.highlightAuto(code).value;
}
};
/** 复制代码 */
const copyCode = async (text: string) => {
try {
await navigator.clipboard.writeText(text);
message("复制成功!", { type: "success" });
} catch (err) {
message("复制失败,请手动复制!", { type: "error" });
}
};
/** 下载单个文件 */
const downloadFile = (filename: string, content: string) => {
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
saveAs(blob, filename);
};
/** 下载 ZIP 文件,按 path 组织目录 */
const downloadZip = () => {
const zip = new JSZip();
Object.values(dataList.value).forEach(items => {
items.forEach(({ path, value }) => {
if (path) {
zip.file(path, value); // 直接使用 path 作为文件路径
}
});
});
zip.generateAsync({ type: "blob" }).then(blob => {
saveAs(blob, `${newFormInline.value.table_name}_code.zip`);
});
};
onMounted(async () => {
nextTick(() => {
document.querySelectorAll("pre code").forEach(block => {
hljs.highlightElement(block as HTMLElement);
});
});
await getGenerateCode();
});
</script>
<style scoped>
.el-menu-top {
margin-bottom: 16px;
}
.code-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 12px;
font-size: 14px;
font-weight: bold;
background: #f6f8fa;
border-radius: 6px 6px 0 0;
}
.lang {
color: #0366d6;
}
.copy-btn,
.download-btn {
margin-left: 8px;
font-size: 12px;
cursor: pointer;
}
.zip-download-btn {
width: 100%;
margin-top: 16px;
}
pre {
padding: 10px;
overflow-x: auto;
background: #fff;
border: 1px solid #e1e4e8;
border-radius: 0 0 6px 6px;
}
</style>

View File

@@ -0,0 +1,244 @@
<template>
<div class="main">
<el-form
ref="formRef"
:inline="true"
:model="form"
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
>
<el-form-item label="数据表描述" prop="table_comment">
<el-select
v-model="form.table_comment"
clearable
placeholder="请输入数据表描述"
class="!w-[200px]"
>
<el-option
v-for="item in tableList"
:key="item.table_name"
:value="item.table_comment"
:label="item.table_comment"
/>
</el-select>
</el-form-item>
<el-form-item label="权限ID" prop="permission_id">
<el-cascader
v-model="form.permission_id"
class="w-full"
:options="permissions"
:props="{
value: 'id',
label: 'name',
emitPath: false,
checkStrictly: true
}"
clearable
filterable
placeholder="请选择所属权限~"
>
<template #default="{ node, data }">
<span>{{ t(data.name) }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
<el-form-item>
<el-button
type="primary"
:icon="useRenderIcon('ri:search-line')"
:loading="loading"
@click="onSearch"
>
{{ t("buttons:Search") }}
</el-button>
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
{{ t("buttons:Reset") }}
</el-button>
</el-form-item>
</el-form>
<PureTableBar title="生成表管理" :columns="columns" @refresh="onSearch">
<template #buttons>
<el-button
v-if="hasAuth('generate:btn:add')"
type="primary"
:icon="useRenderIcon(AddFill)"
@click="handleEdit('新增')"
>
{{ t("buttons:Add") }}
</el-button>
</template>
<template v-slot="{ size, dynamicColumns }">
<div
v-if="selectedNum > 0"
v-motion-fade
class="bg-[var(--el-fill-color-light)] w-full h-[46px] mb-2 pl-4 flex items-center"
>
<div class="flex-auto">
<span
style="font-size: var(--el-font-size-base)"
class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]"
>
已选 {{ selectedNum }}
</span>
<el-button type="primary" text @click="onSelectionCancel">
{{ t("buttons:Deselect") }}
</el-button>
</div>
<el-popconfirm
v-if="hasAuth('generate:btn:delete')"
title="是否确认删除?"
@confirm="onbatchDel"
>
<template #reference>
<el-button type="danger" text class="mr-1">
{{ t("buttons:DeleteInBatches") }}
</el-button>
</template>
</el-popconfirm>
</div>
<pure-table
ref="tableRef"
row-key="id"
adaptive
border
stripe
:adaptiveConfig="{ offsetBottom: 45 }"
align-whole="center"
table-layout="auto"
:loading="loading"
:size="size"
:data="dataList"
:columns="dynamicColumns"
:pagination="pagination"
:paginationSmall="size === 'small' ? true : false"
:header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}"
@selection-change="handleSelectionChange"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
>
<template #operation="{ row }">
<el-button
class="reset-margin"
link
type="primary"
:size="size"
:disabled="!hasAuth('generate:btn:code')"
:icon="useRenderIcon(View)"
@click="handlePreview(row)"
>
{{ t("buttons:Preview") }}
</el-button>
<el-button
class="reset-margin"
link
type="primary"
:size="size"
:disabled="!hasAuth('generate:btn:update')"
:icon="useRenderIcon(EditPen)"
@click="handleEdit('修改', row)"
>
{{ t("buttons:Update") }}
</el-button>
<el-button
class="reset-margin"
link
type="primary"
:size="size"
:disabled="!hasAuth('generate:btn:updateColumns')"
:icon="useRenderIcon(Refresh)"
@click="handleColumn(row)"
>
{{ t("buttons:Sync") }}
</el-button>
<el-popconfirm
:title="`是否确认删除配置名为 ${row.table_name} 的这条数据`"
@confirm="handleDelete(row)"
>
<template #reference>
<el-button
class="reset-margin"
link
:disabled="!hasAuth('generate:btn:delete')"
type="danger"
:size="size"
:icon="useRenderIcon(Delete)"
>
{{ t("buttons:Delete") }}
</el-button>
</template>
</el-popconfirm>
</template>
</pure-table>
</template>
</PureTableBar>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: "GenerateIndex"
});
import { ref } from "vue";
import { useGenerate } from "./utils/hook";
import { useI18n } from "vue-i18n";
import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Delete from "@iconify-icons/ep/delete";
import EditPen from "@iconify-icons/ep/edit-pen";
import Refresh from "@iconify-icons/ep/refresh";
import AddFill from "@iconify-icons/ri/add-circle-line";
import View from "@iconify-icons/ri/eye-line";
import Downdload from "@iconify-icons/ri/download-2-line";
const { t } = useI18n();
import { hasAuth } from "@/utils/auth";
/**
* 表格Ref
*/
const tableRef = ref();
const formRef = ref();
const {
form,
dataList,
loading,
pagination,
columns,
selectedNum,
tableList,
permissions,
onSearch,
resetForm,
handleDelete,
handleSizeChange,
handleEdit,
handleCurrentChange,
handleSelectionChange,
onSelectionCancel,
onbatchDel,
handleColumn,
handlePreview
} = useGenerate(tableRef);
</script>
<style scoped lang="scss">
:deep(.el-dropdown-menu__item i) {
margin: 0;
}
:deep(.el-button:focus-visible) {
outline: none;
}
.main-content {
margin: 24px 24px 0 !important;
}
.search-form {
:deep(.el-form-item) {
margin-bottom: 12px;
}
}
</style>

View File

@@ -0,0 +1,472 @@
import dayjs from "dayjs";
import InfoForm from "../components/info.vue";
import editForm from "../components/form.vue";
import previewForm from "../components/preview.vue";
import { message } from "@/utils/message";
import { type Ref, ref, reactive, onMounted, h, toRaw } from "vue";
import type { PaginationProps } from "@pureadmin/table";
import { addDialog } from "@/components/ReDialog";
import type {
GenerateTableInfo,
TableColumnInfo,
TableInfo
} from "types/generate";
import {
deleteGenerateTableAPI,
deleteGenerateTableListAPI,
getGenerateTableListAPI,
getTableListAPI,
postAddGenerateTableAPI,
putUpdateGenerateTableAPI,
putUpdateGenerateTableColumnAPI
} from "@/api/generate";
import { cloneDeep, getKeyList, handleTree } from "@pureadmin/utils";
import type { PermissionInfo } from "types/system";
import { getPermissionListAPI } from "@/api/system";
export const useGenerate = (tableRef: Ref) => {
/**
* 查询表单
*/
const form = reactive({
table_comment: "",
permission_id: ""
});
/**
* 表单Ref
*/
const formRef = ref(null);
/**生成表类型 */
type DataInfo = GenerateTableInfo & { columns: TableColumnInfo[] };
/**
* 数据列表
*/
const dataList = ref<DataInfo[]>([]);
/**
* 加载状态
*/
const loading = ref(true);
/**数据表列表 */
const tableList = ref<TableInfo[]>([]);
/**
* 已选数量
*/
const selectedNum = ref<number>(0);
/**
* 分页参数
*/
const pagination = reactive<PaginationProps>({
total: 0,
pageSize: 10,
currentPage: 1,
background: true,
pageSizes: [10, 20, 30, 40, 50]
});
/**
* 表格列设置
*/
const columns: TableColumnList = [
{
label: "勾选列", // 如果需要表格多选此处label必须设置
type: "selection",
fixed: "left",
reserveSelection: true // 数据刷新后保留选项
},
{
label: "数据表名称",
prop: "table_name"
},
{
label: "数据表注释",
prop: "table_comment"
},
{
label: "描述",
prop: "description"
},
{
label: "备注",
prop: "remark"
},
{
label: "创建时间",
prop: "create_time",
formatter: ({ create_time }) =>
create_time ? dayjs(create_time).format("YYYY-MM-DD HH:mm:ss") : ""
},
{
label: "更新时间",
prop: "update_time",
formatter: ({ update_time }) =>
update_time ? dayjs(update_time).format("YYYY-MM-DD HH:mm:ss") : ""
},
{
label: "操作",
fixed: "right",
width: 380,
slot: "operation"
}
];
/**
* 初次查询
*/
const onSearch = async () => {
loading.value = true;
const res = await getGenerateTableListAPI({
page: pagination.currentPage,
pageSize: pagination.pageSize,
...toRaw(form)
});
if (res.success) {
dataList.value = res.data.result;
pagination.total = res.data.total;
pagination.currentPage = res.data.page;
pagination.pageSize = res.data.pageSize;
}
message(res.msg, {
type: res.success ? "success" : "error"
});
loading.value = false;
};
/**
* 重置表单
* @param formEl 表单ref
* @returns
*/
const resetForm = async (formEl: any) => {
if (!formEl) return;
formEl.resetFields();
await onSearch();
};
/**
* 处理删除
* @param row
*/
const handleDelete = async (row: DataInfo) => {
const res = await deleteGenerateTableAPI(row.id);
if (res.success) {
onSearch();
}
message(res.msg, {
type: res.success ? "success" : "error"
});
};
/**
* 处理每页数量变化
*/
const handleSizeChange = async (val: number) => {
loading.value = true;
const res = await getGenerateTableListAPI({
page: pagination.currentPage,
pageSize: val,
...toRaw(form)
});
if (res.success) {
dataList.value = res.data.result;
pagination.total = res.data.total;
pagination.currentPage = res.data.page;
pagination.pageSize = res.data.pageSize;
}
message(res.msg, {
type: res.success ? "success" : "error"
});
loading.value = false;
};
/**
* 处理页码变化
* @param val
*/
const handleCurrentChange = async (val: number) => {
loading.value = true;
const res = await getGenerateTableListAPI({
page: val,
pageSize: pagination.pageSize,
...toRaw(form)
});
if (res.success) {
dataList.value = res.data.result;
pagination.total = res.data.total;
pagination.currentPage = res.data.page;
pagination.pageSize = res.data.pageSize;
}
message(res.msg, {
type: res.success ? "success" : "error"
});
loading.value = false;
};
/** 当CheckBox选择项发生变化时会触发该事件 */
const handleSelectionChange = async (val: any) => {
selectedNum.value = val.length;
// 重置表格高度
tableRef.value.setAdaptive();
};
/** 取消选择 */
const onSelectionCancel = async () => {
selectedNum.value = 0;
// 用于多选表格,清空用户的选择
tableRef.value.getTableRef().clearSelection();
};
/**
* 批量删除
*/
const onbatchDel = async () => {
// 返回当前选中的行
const curSelected = tableRef.value.getTableRef().getSelectionRows();
const res = await deleteGenerateTableListAPI(getKeyList(curSelected, "id"));
if (res.success) {
message(res.msg, {
type: "success"
});
tableRef.value.getTableRef().clearSelection();
onSearch();
} else {
message(res.msg, { type: "error", duration: 5000 });
}
};
/**处理添加修改 */
const handleEdit = async (way: string = "新增", row?: DataInfo) => {
addDialog({
title: `${way}表信息`,
props: {
formInline: {
/**方式 */
way: way,
/**数据表列表 */
tables: tableList.value ?? [],
/**权限列表 */
permissions: permissions.value ?? [],
/**数据ID */
id: row?.id ?? "",
/**作者 */
author: row?.author ?? "",
/**类名 */
class_name: row?.class_name ?? "",
/**权限ID */
permission_id: row?.permission_id ?? "",
/**API前缀 */
prefix: row?.prefix ?? "",
/**备注 */
remark: row?.remark ?? "",
/**表名 */
table_name: row?.table_name ?? "",
/**表注释 */
table_comment: row?.table_comment ?? "",
/**创建时间 */
create_time: row?.create_time ?? "",
/**修改时间 */
update_time: row?.update_time ?? "",
/**描述 */
description: row?.description ?? ""
}
},
width: "45%",
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () =>
h(InfoForm, {
formInline: {
/**方式 */
way: way,
/**数据表列表 */
tables: tableList.value ?? [],
/**权限列表 */
permissions: permissions.value ?? [],
/**数据ID */
id: row?.id ?? "",
/**作者 */
author: row?.author ?? "",
/**类名 */
class_name: row?.class_name ?? "",
/**权限ID */
permission_id: row?.permission_id ?? "",
/**API前缀 */
prefix: row?.prefix ?? "",
/**备注 */
remark: row?.remark ?? "",
/**表名 */
table_name: row?.table_name ?? "",
/**表注释 */
table_comment: row?.table_comment ?? "",
/**创建时间 */
create_time: row?.create_time ?? "",
/**修改时间 */
update_time: row?.update_time ?? "",
/**描述 */
description: row?.description ?? ""
},
ref: formRef
}),
beforeSure: async (done, {}) => {
const FormData = formRef.value.newFormInline;
let form = {
/**表名 */
table_name: FormData.table_name ?? "",
/**表注释 */
table_comment: FormData.table_comment ?? "",
/**作者 */
author: FormData.author ?? "",
/**类名 */
class_name: FormData.class_name ?? "",
/**权限ID */
permission_id: FormData.permission_id ?? "",
/**API前缀 */
prefix: FormData.prefix ?? "",
/**备注 */
remark: FormData.remark ?? "",
/**描述 */
description: FormData.description ?? ""
};
if (way === "新增") {
const res = await postAddGenerateTableAPI(form);
if (res.success) {
done();
await onSearch();
}
message(res.msg, { type: res.success ? "success" : "error" });
} else {
const res = await putUpdateGenerateTableAPI(form, row.id);
if (res.success) {
done();
await onSearch();
}
message(res.msg, { type: res.success ? "success" : "error" });
}
}
});
};
/**获取数据表列表 */
const getTableList = async () => {
const res = await getTableListAPI();
if (res.success) {
tableList.value = res.data.result;
}
message(res.msg, {
type: res.success ? "success" : "error"
});
};
/**权限列表 */
const permissions = ref<PermissionInfo[]>([]);
const formatHigherOptions = treeList => {
// 根据返回数据的status字段值判断追加是否禁用disabled字段返回处理后的树结构用于上级部门级联选择器的展示实际开发中也是如此不可能前端需要的每个字段后端都会返回这时需要前端自行根据后端返回的某些字段做逻辑处理
if (!treeList || !treeList.length) return;
const newTreeList = [];
for (let i = 0; i < treeList.length; i++) {
treeList[i].disabled = treeList[i].status === 0 ? true : false;
formatHigherOptions(treeList[i].children);
newTreeList.push(treeList[i]);
}
return newTreeList;
};
/**获取权限列表 */
const getPermissions = async () => {
const res = await getPermissionListAPI({ page: 1, pageSize: 9999 });
if (res.success) {
let data = cloneDeep(res.data.result).map(item => {
return {
id: item.id,
parent_id: item.parent_id,
name: item.title
};
});
permissions.value = formatHigherOptions(
handleTree(data, "id", "parent_id")
);
}
message(res.msg, {
type: res.success ? "success" : "error"
});
};
/**处理字段设置 */
const handleColumn = async (row: DataInfo) => {
addDialog({
title: `配置信息`,
props: {
formInline: {
columns: row.columns ?? []
}
},
width: "90%",
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () =>
h(editForm, {
formInline: {
columns: row.columns ?? []
},
ref: formRef
}),
beforeSure: async (done, {}) => {
const FormData = formRef.value.newFormInline;
const res = await putUpdateGenerateTableColumnAPI(
{ columns: FormData.columns },
row.id
);
if (res.success) {
done();
}
message(res.msg, { type: res.success ? "success" : "error" });
}
});
};
/**处理代码预览 */
const handlePreview = async (row: DataInfo) => {
addDialog({
title: `代码预览`,
props: {
formInline: {
id: row?.id ?? "",
table_name: row?.table_name ?? ""
}
},
width: "65%",
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () =>
h(previewForm, {
formInline: {
id: row?.id ?? "",
table_name: row?.table_name ?? ""
},
ref: formRef
}),
beforeSure: async (done, {}) => {
done();
}
});
};
/**
* 页面加载执行
*/
onMounted(async () => {
await onSearch();
await getTableList();
await getPermissions();
});
return {
form,
dataList,
loading,
pagination,
columns,
selectedNum,
tableList,
permissions,
onSearch,
resetForm,
handleDelete,
handleSizeChange,
handleEdit,
handleCurrentChange,
handleSelectionChange,
onSelectionCancel,
onbatchDel,
handleColumn,
handlePreview
};
};

107
types/generate.d.ts vendored Normal file
View File

@@ -0,0 +1,107 @@
/**生成表基本信息 */
export interface TableInfo {
/**数据ID*/
id: string;
/**表名 */
table_name: string;
/**表注释 */
table_comment: string;
/**数据表行数 */
table_rows: number;
/**数据表大小 */
data_length: string;
/**索引大小 */
index_length: string;
/**创建时间 */
create_time: string;
/**修改时间 */
update_time: string;
}
/**生成表信息 */
export interface GenerateTableInfo {
/**数据ID */
id: string;
/**作者 */
author: string;
/**类名 */
class_name: string;
/**权限ID */
permission_id: string;
/**API前缀 */
prefix: string;
/**备注 */
remark: string;
/**描述 */
description: string;
/**表名 */
table_name: string;
/**表注释 */
table_comment: string;
/**创建时间 */
create_time: string;
/**修改时间 */
update_time: string;
}
/**生成表字段信息 */
export interface TableColumnInfo {
/**数据ID */
id: string;
/**表ID */
table_id: string;
/**表名 */
table_name: string;
/**字段名 */
column_name: string;
/**字段注释 */
column_comment: string;
/**字段类型 */
column_type: string;
/**python 类型 */
python_type: string;
/**python字段名 */
python_name: string;
/**是否插入 */
is_insert: boolean;
/**是否编辑 */
is_edit: boolean;
/**是否查询 */
is_query: boolean;
/**是否列表显示 */
is_list: boolean;
/**是否隐藏 */
is_hide: boolean;
/**是否必填 */
is_required: boolean;
/**查询方式 */
query_way: string;
/**展示类型 */
show_type: string;
/**索引 */
index: number;
/**创建时间 */
create_time: string;
/**修改时间 */
update_time: string;
}
/**
* 代码生成信息
*/
export type GenerateCodeInfo = {
label: string;
value: string;
type: string;
path: string;
};
/**代码生成结果 */
export type GenerateCodeResult = {
/**后端代码 */
backend: GenerateCodeInfo[];
/**前端代码 */
frontend: GenerateCodeInfo[];
/**SQL代码*/
sql: GenerateCodeInfo[];
};