Compare commits

...
29 Commits
Author SHA1 Message Date
pixelMax(奇淼 c70fedfb43 fixed: 修复addFunction下前端api.js无法创建的bug。 2024-08-15 18:05:43 +08:00
pixelMax(奇淼 85d7b5fd5d fixed: 修复addFunction下前端api.js无法创建的bug。 2024-08-15 18:04:33 +08:00
pixelMax(奇淼 cbace6f97e feature: 调整全量引入功能对格式化后的代码依然生效 2024-08-15 15:52:35 +08:00
pixelMax(奇淼 bec5dc4476 fixed: 模板中el-image默认添加preview-teleported,用户在预览时候方式弹出层被遮罩。 2024-08-15 15:38:29 +08:00
sliboy f7ddd8bbbb chore(user.vue): Update user.vue, alter userInfo username to userName. (#1855) 2024-08-15 10:49:31 +08:00
马国栋 43d0c291ed feature:swagger 接口请求方式优化 (#1854) 2024-08-15 10:46:41 +08:00
pixelmaxQM 0956edd0d0 feat: 初始化完成后主动获取GVA_ACTIVE_DBNAME。 2024-08-14 20:48:47 +08:00
c9477d37fa V2.7.2版本发布 (#1853)
* feat: 自动化代码增加json导出和导入功能

* feat: 自动化代码前端可见分为Table和Form分别选中

* feature: 调整代码预览为左边栏模式的tabs。

* feat: 增加方法自动添加前端api

* feat: 自动化生成前端支持详情功能

* feat: 增加自动创建可控权限按钮功能

* fixed: 顶栏样式菜单样式细节bug修复

* fixed: 修改视频地址

* fixed: 自动获取表结构和数据库表列结构保持一致

* fixed: casbin 设置空权限无需调用 AddPolicies 方法 (#1850)

* feat:对象存储支持配置Cloudflare R2 (#1849)

* fixed:设为首页和菜单勾选互为必选

---------

Co-authored-by: SliverHorn <503551462@qq.com>
Co-authored-by: 千石 <CN_QianShi@hotmail.com>

* feat: 复杂数据类型的查询将不会生成查询语句,会以string形式接收参数,用户自行实现复杂查询逻辑。

* feat: 创建新角色默认携带字典和长传权限。
禁止删除有首页占用的菜单。
不允许切换至无首页的角色。
自动化代码创建失败将返回错误信息。

* feat: 当package或plugin结构异常时候,阻止创建自动化代码。

---------

Co-authored-by: krank <emosick@qq.com>
Co-authored-by: SliverHorn <503551462@qq.com>
Co-authored-by: 千石 <CN_QianShi@hotmail.com>
2024-08-13 21:55:27 +08:00
千石 3b5c96d7cb feat:对象存储支持配置Cloudflare R2 (#1849) 2024-08-12 22:30:14 +08:00
SliverHorn c3c049b225 fixed: casbin 设置空权限无需调用 AddPolicies 方法 (#1850) 2024-08-12 22:29:50 +08:00
lbbniu 6fedb9773b fix: 代码自动生成选择业务库自动迁移代码生成异常 (#1848) 2024-08-12 15:08:17 +08:00
pixelmaxQM 332a8de2e9 fixed: 修复其他数据库类型下无法获取表列表的bug 2024-08-05 20:05:23 +08:00
PiexlMax(奇淼andkrank 7121968081 2.7.1版本提交 (#1844)
* feature:表格模板自动化生成 (#1841)

* feature:  为导出模板增加自动化功能

* feature:  视频按钮修改

* feature: 发布2.7.1Beta版本,视频集中处理。

---------

Co-authored-by: krank <emosick@qq.com>
2024-08-04 16:06:00 +08:00
pixelmaxQM a44133d0c9 fixed: 修复在同时开启多个窗口的情况下刷新页面导致的死循环情况 2024-08-04 11:12:43 +08:00
pixelmaxQM 3c3d9487d0 fixed:修复复用代码无法改变主键的bug 2024-08-03 15:08:25 +08:00
PiexlMax(奇淼 15b5083d17 Update README.md 2024-08-03 14:55:11 +08:00
pixelMax(奇淼 e25cafe2b4 fixed: 调整重复创建代码的提示 2024-08-02 14:35:16 +08:00
pixelMax(奇淼 b5c44e47ff feature: 调整baseUrl方法 2024-08-01 18:24:11 +08:00
pixelMax(奇淼 5d04b8deea fixed: 调整autoCode标签 2024-08-01 14:20:18 +08:00
pixelMax(奇淼 1207291249 fixed: 修复自动化模板多了}的问题 2024-08-01 14:08:05 +08:00
pixelMax(奇淼 06bf641e7a fixed: 修复自动化代码路径不匹配的bug 2024-07-31 18:09:12 +08:00
pixelMax(奇淼 ce764082b8 feature: 修改vite.config.js注释 2024-07-31 16:23:09 +08:00
pixelMax(奇淼 aebc301b05 feature: swagger自动化模板优化 2024-07-31 15:55:31 +08:00
pixelMax(奇淼 3b93a073c3 feature: swagger 优化 2024-07-31 15:52:23 +08:00
pixelMax(奇淼 a4a7b44da9 feature: Local优化文件删除逻辑 2024-07-31 11:22:56 +08:00
maxwell e65be6ee36 feat: tab支持使用鼠标中键关闭 (#1840)
* fix: 修复注释错误

* feat: tab支持使用鼠标中键进行关闭
2024-07-31 11:20:14 +08:00
LixvYang 2d0b9ecac0 add cloudflare r2 storage (#1835) 2024-07-30 15:31:23 +08:00
pixelmaxQM 5a5b86aeec fixed:新增方法清空已经输入的内容 2024-07-27 22:25:13 +08:00
pixelmaxQM ff41fe0577 fixed:对导出关键字进行处理 2024-07-27 17:59:00 +08:00
80 changed files with 3195 additions and 1288 deletions
+2 -4
View File
@@ -94,11 +94,9 @@ cd server
# 使用 go mod 并安装go依赖包
go generate
# 编译
go build -o server main.go (windows编译命令为go build -o server.exe main.go )
# 运行
go run .
# 运行二进制
./server (windows运行命令为 server.exe)
```
### 2.2 web项目
+2 -2
View File
@@ -66,7 +66,7 @@ func (a *AutoCodeHistoryApi) Delete(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body systemReq.RollBack true "请求参数"
// @Param data body request.SysAutoHistoryRollBack true "请求参数"
// @Success 200 {object} response.Response{msg=string} "回滚自动生成代码"
// @Router /autoCode/rollback [post]
func (a *AutoCodeHistoryApi) RollBack(c *gin.Context) {
@@ -90,7 +90,7 @@ func (a *AutoCodeHistoryApi) RollBack(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body systemReq.SysAutoHistory true "请求参数"
// @Param data body common.PageInfo true "请求参数"
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "查询回滚记录,返回包括列表,总数,页码,每页数量"
// @Router /autoCode/getSysHistory [post]
func (a *AutoCodeHistoryApi) GetList(c *gin.Context) {
+2 -2
View File
@@ -19,7 +19,7 @@ type AutoCodePackageApi struct{}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysAutoCode true "创建package"
// @Param data body request.SysAutoCodePackageCreate true "创建package"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
// @Router /autoCode/createPackage [post]
func (a *AutoCodePackageApi) Create(c *gin.Context) {
@@ -48,7 +48,7 @@ func (a *AutoCodePackageApi) Create(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysAutoCode true "创建package"
// @Param data body common.GetById true "创建package"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "删除package成功"
// @Router /autoCode/delPackage [post]
func (a *AutoCodePackageApi) Delete(c *gin.Context) {
+1 -1
View File
@@ -55,7 +55,7 @@ func (a *AutoCodePluginApi) Install(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.SysAutoCode true "打包插件"
// @Param plugName query string true "插件名称"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "打包插件成功"
// @Router /autoCode/pubPlug [get]
func (a *AutoCodePluginApi) Packaged(c *gin.Context) {
+3 -3
View File
@@ -17,7 +17,7 @@ type AutoCodeTemplateApi struct{}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.AutoCodeStruct true "预览创建代码"
// @Param data body request.AutoCode true "预览创建代码"
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "预览创建后的代码"
// @Router /autoCode/preview [post]
func (a *AutoCodeTemplateApi) Preview(c *gin.Context) {
@@ -53,7 +53,7 @@ func (a *AutoCodeTemplateApi) Preview(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body system.AutoCodeStruct true "创建自动代码"
// @Param data body request.AutoCode true "创建自动代码"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /autoCode/createTemp [post]
func (a *AutoCodeTemplateApi) Create(c *gin.Context) {
@@ -76,7 +76,7 @@ func (a *AutoCodeTemplateApi) Create(c *gin.Context) {
err = autoCodeTemplateService.Create(c.Request.Context(), info)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
response.FailWithMessage(err.Error(), c)
} else {
response.OkWithMessage("创建成功", c)
}
+1 -1
View File
@@ -74,7 +74,7 @@ func (s *SystemApiApi) SyncApi(c *gin.Context) {
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "获取API分组"
// @Router /api/getApiGroups [post]
// @Router /api/getApiGroups [get]
func (s *SystemApiApi) GetApiGroups(c *gin.Context) {
groups, apiGroupMap, err := apiService.GetApiGroups()
if err != nil {
+23 -2
View File
@@ -51,8 +51,19 @@ func (autoApi *AutoCodeApi) GetDB(c *gin.Context) {
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前数据库所有表"
// @Router /autoCode/getTables [get]
func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
dbName := c.DefaultQuery("dbName", global.GVA_CONFIG.Mysql.Dbname)
dbName := c.Query("dbName")
businessDB := c.Query("businessDB")
if dbName == "" {
dbName = *global.GVA_ACTIVE_DBNAME
if businessDB != "" {
for _, db := range global.GVA_CONFIG.DBList {
if db.AliasName == businessDB {
dbName = db.Dbname
}
}
}
}
tables, err := autoCodeService.Database(businessDB).GetTables(businessDB, dbName)
if err != nil {
global.GVA_LOG.Error("查询table失败!", zap.Error(err))
@@ -72,7 +83,17 @@ func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
// @Router /autoCode/getColumn [get]
func (autoApi *AutoCodeApi) GetColumn(c *gin.Context) {
businessDB := c.Query("businessDB")
dbName := c.DefaultQuery("dbName", global.GVA_CONFIG.Mysql.Dbname)
dbName := c.Query("dbName")
if dbName == "" {
dbName = *global.GVA_ACTIVE_DBNAME
if businessDB != "" {
for _, db := range global.GVA_CONFIG.DBList {
if db.AliasName == businessDB {
dbName = db.Dbname
}
}
}
}
tableName := c.Query("tableName")
columns, err := autoCodeService.Database(businessDB).GetColumn(businessDB, tableName, dbName)
if err != nil {
+1 -1
View File
@@ -171,7 +171,7 @@ func (a *AuthorityMenuApi) DeleteBaseMenu(c *gin.Context) {
err = baseMenuService.DeleteBaseMenu(menu.ID)
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
response.FailWithMessage("删除失败:"+err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
+9
View File
@@ -211,6 +211,15 @@ aws-s3:
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
# cloudflare r2 configuration
cloudflare-r2:
bucket: xxxx0bucket
base-url: https://gin.vue.admin.com
path: uploads
account-id: xxx_account_id
access-key-id: xxx_key_id
secret-access-key: xxx_secret_key
# huawei obs configuration
hua-wei-obs:
path: you-path
+7 -6
View File
@@ -18,12 +18,13 @@ type Server struct {
Sqlite Sqlite `mapstructure:"sqlite" json:"sqlite" yaml:"sqlite"`
DBList []SpecializedDB `mapstructure:"db-list" json:"db-list" yaml:"db-list"`
// oss
Local Local `mapstructure:"local" json:"local" yaml:"local"`
Qiniu Qiniu `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"`
AliyunOSS AliyunOSS `mapstructure:"aliyun-oss" json:"aliyun-oss" yaml:"aliyun-oss"`
HuaWeiObs HuaWeiObs `mapstructure:"hua-wei-obs" json:"hua-wei-obs" yaml:"hua-wei-obs"`
TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencent-cos" yaml:"tencent-cos"`
AwsS3 AwsS3 `mapstructure:"aws-s3" json:"aws-s3" yaml:"aws-s3"`
Local Local `mapstructure:"local" json:"local" yaml:"local"`
Qiniu Qiniu `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"`
AliyunOSS AliyunOSS `mapstructure:"aliyun-oss" json:"aliyun-oss" yaml:"aliyun-oss"`
HuaWeiObs HuaWeiObs `mapstructure:"hua-wei-obs" json:"hua-wei-obs" yaml:"hua-wei-obs"`
TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencent-cos" yaml:"tencent-cos"`
AwsS3 AwsS3 `mapstructure:"aws-s3" json:"aws-s3" yaml:"aws-s3"`
CloudflareR2 CloudflareR2 `mapstructure:"cloudflare-r2" json:"cloudflare-r2" yaml:"cloudflare-r2"`
Excel Excel `mapstructure:"excel" json:"excel" yaml:"excel"`
+10
View File
@@ -0,0 +1,10 @@
package config
type CloudflareR2 struct {
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
Path string `mapstructure:"path" json:"path" yaml:"path"`
AccountID string `mapstructure:"account-id" json:"account-id" yaml:"account-id"`
AccessKeyID string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
SecretAccessKey string `mapstructure:"secret-access-key" json:"secret-access-key" yaml:"secret-access-key"`
}
+1 -1
View File
@@ -38,7 +38,7 @@ func RunWindowsServer() {
fmt.Printf(`
欢迎使用 gin-vue-admin
当前版本:v2.7.0
当前版本:v2.7.2
加群方式:微信号:shouzi_1994 QQ群:470239250
项目地址:https://github.com/flipped-aurora/gin-vue-admin
插件市场:https://plugin.gin-vue-admin.com
+805 -335
View File
File diff suppressed because it is too large Load Diff
+805 -335
View File
File diff suppressed because it is too large Load Diff
+479 -235
View File
@@ -16,34 +16,16 @@ definitions:
type: object
config.Autocode:
properties:
ai-path:
type: string
module:
type: string
root:
type: string
server:
type: string
server-api:
type: string
server-initialize:
type: string
server-model:
type: string
server-plug:
type: string
server-request:
type: string
server-router:
type: string
server-service:
type: string
transfer-restart:
type: boolean
web:
type: string
web-api:
type: string
web-form:
type: string
web-table:
type: string
type: object
config.AwsS3:
properties:
@@ -106,6 +88,21 @@ definitions:
description: 防爆破验证码超时时间,单位:s(秒)
type: integer
type: object
config.CloudflareR2:
properties:
access-key-id:
type: string
account-id:
type: string
base-url:
type: string
bucket:
type: string
path:
type: string
secret-access-key:
type: string
type: object
config.DiskList:
properties:
mount-point:
@@ -431,6 +428,8 @@ definitions:
$ref: '#/definitions/config.AwsS3'
captcha:
$ref: '#/definitions/config.Captcha'
cloudflare-r2:
$ref: '#/definitions/config.CloudflareR2'
cors:
allOf:
- $ref: '#/definitions/config.CORS'
@@ -764,6 +763,32 @@ definitions:
description: 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用
type: string
type: object
model.Info:
properties:
ID:
description: 主键ID
type: integer
attachments:
description: 附件
items:
type: object
type: array
content:
description: 内容
type: string
createdAt:
description: 创建时间
type: string
title:
description: 标题
type: string
updatedAt:
description: 更新时间
type: string
userID:
description: 作者
type: integer
type: object
request.AddMenuAuthorityInfo:
properties:
authorityId:
@@ -774,6 +799,8 @@ definitions:
$ref: '#/definitions/system.SysBaseMenu'
type: array
type: object
request.AutoCode:
type: object
request.CasbinInReceive:
properties:
authorityId:
@@ -909,21 +936,6 @@ definitions:
example: 用户名
type: string
type: object
request.RollBack:
properties:
deleteApi:
description: 是否删除接口
type: boolean
deleteMenu:
description: 是否删除菜单
type: boolean
deleteTable:
description: 是否删除表
type: boolean
id:
description: 主键ID
type: integer
type: object
request.SearchApiParams:
properties:
ID:
@@ -990,16 +1002,34 @@ definitions:
type: integer
type: array
type: object
request.SysAutoHistory:
request.SysAutoCodePackageCreate:
properties:
keyword:
description: 关键字
desc:
example: 描述
type: string
page:
description: 页码
type: integer
pageSize:
description: 每页大小
label:
example: 展示名
type: string
packageName:
example: 包名
type: string
template:
example: 模版
type: string
type: object
request.SysAutoHistoryRollBack:
properties:
deleteApi:
description: 是否删除接口
type: boolean
deleteMenu:
description: 是否删除菜单
type: boolean
deleteTable:
description: 是否删除表
type: boolean
id:
description: 主键ID
type: integer
type: object
response.Email:
@@ -1140,53 +1170,6 @@ definitions:
user:
$ref: '#/definitions/system.SysUser'
type: object
system.AutoCodeStruct:
properties:
abbreviation:
description: Struct简称
type: string
autoCreateApiToSql:
description: 是否自动创建api
type: boolean
autoCreateMenuToSql:
description: 是否自动创建menu
type: boolean
autoCreateResource:
description: 是否自动创建资源标识
type: boolean
autoMigrate:
description: 是否自动迁移表结构
type: boolean
businessDB:
description: 业务数据库
type: string
description:
description: Struct中文名称
type: string
fields:
items:
$ref: '#/definitions/system.Field'
type: array
gvaModel:
description: 是否使用gva默认Model
type: boolean
humpPackageName:
description: go文件名称
type: string
package:
type: string
packageName:
description: 文件名称
type: string
primaryField:
$ref: '#/definitions/system.Field'
structName:
description: Struct名称
type: string
tableName:
description: 表名
type: string
type: object
system.Condition:
properties:
ID:
@@ -1207,82 +1190,6 @@ definitions:
description: 更新时间
type: string
type: object
system.DataSource:
properties:
association:
description: 关联关系 1 一对一 2 一对多
type: integer
label:
type: string
table:
type: string
value:
type: string
type: object
system.Field:
properties:
checkDataSource:
description: 是否检查数据源
type: boolean
clearable:
description: 是否可清空
type: boolean
columnName:
description: 数据库字段
type: string
comment:
description: 数据库字段描述
type: string
dataSource:
allOf:
- $ref: '#/definitions/system.DataSource'
description: 数据源
dataTypeLong:
description: 数据库字段长度
type: string
defaultValue:
description: 是否必填
type: string
dictType:
description: 字典
type: string
errorText:
description: 校验失败文字
type: string
fieldDesc:
description: 中文名
type: string
fieldIndexType:
description: 索引类型
type: string
fieldJson:
description: FieldJson
type: string
fieldName:
description: Field名
type: string
fieldSearchHide:
description: 是否隐藏查询条件
type: boolean
fieldSearchType:
description: 搜索条件
type: string
fieldType:
description: Field数据类型
type: string
front:
description: 是否前端可见
type: boolean
primaryKey:
description: 是否主键
type: boolean
require:
description: 是否必填
type: boolean
sort:
description: 是否增加排序
type: boolean
type: object
system.JoinTemplate:
properties:
ID:
@@ -1382,24 +1289,6 @@ definitions:
description: 更新时间
type: string
type: object
system.SysAutoCode:
properties:
ID:
description: 主键ID
type: integer
createdAt:
description: 创建时间
type: string
desc:
type: string
label:
type: string
packageName:
type: string
updatedAt:
description: 更新时间
type: string
type: object
system.SysBaseMenu:
properties:
ID:
@@ -1745,7 +1634,7 @@ info:
contact: {}
description: 使用gin+vue进行极速开发的全栈开发基础平台
title: Gin-Vue-Admin Swagger API接口文档
version: v2.7.0
version: v2.7.2
paths:
/api/createApi:
post:
@@ -1831,6 +1720,27 @@ paths:
summary: 删除选中Api
tags:
- SysApi
/api/enterSyncApi:
post:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 确认同步API
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 确认同步API
tags:
- SysApi
/api/freshCasbin:
get:
consumes:
@@ -1901,6 +1811,27 @@ paths:
summary: 根据id获取api
tags:
- SysApi
/api/getApiGroups:
post:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 获取API分组
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 获取API分组
tags:
- SysApi
/api/getApiList:
post:
consumes:
@@ -1931,6 +1862,48 @@ paths:
summary: 分页获取API列表
tags:
- SysApi
/api/ignoreApi:
post:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 同步API
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 忽略API
tags:
- IgnoreApi
/api/syncApi:
get:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 同步API
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 同步API
tags:
- SysApi
/api/updateApi:
post:
consumes:
@@ -2214,6 +2187,29 @@ paths:
summary: 设置权限按钮
tags:
- AuthorityBtn
/autoCode/addFunc:
post:
consumes:
- application/json
parameters:
- description: 增加方法
in: body
name: data
required: true
schema:
$ref: '#/definitions/request.AutoCode'
produces:
- application/json
responses:
"200":
description: '{"success":true,"data":{},"msg":"创建成功"}'
schema:
type: string
security:
- ApiKeyAuth: []
summary: 增加方法
tags:
- AddFunc
/autoCode/createPackage:
post:
consumes:
@@ -2224,7 +2220,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/system.SysAutoCode'
$ref: '#/definitions/request.SysAutoCodePackageCreate'
produces:
- application/json
responses:
@@ -2244,38 +2240,7 @@ paths:
- ApiKeyAuth: []
summary: 创建package
tags:
- AutoCode
/autoCode/createPlug:
post:
consumes:
- application/json
parameters:
- description: 创建插件模板
in: body
name: data
required: true
schema:
$ref: '#/definitions/system.SysAutoCode'
produces:
- application/json
responses:
"200":
description: 创建插件模板成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
additionalProperties: true
type: object
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 创建插件模板
tags:
- AutoCode
- AutoCodePackage
/autoCode/createTemp:
post:
consumes:
@@ -2286,7 +2251,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/system.AutoCodeStruct'
$ref: '#/definitions/request.AutoCode'
produces:
- application/json
responses:
@@ -2298,7 +2263,7 @@ paths:
- ApiKeyAuth: []
summary: 自动代码模板
tags:
- AutoCode
- AutoCodeTemplate
/autoCode/delPackage:
post:
consumes:
@@ -2309,7 +2274,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/system.SysAutoCode'
$ref: '#/definitions/request.GetById'
produces:
- application/json
responses:
@@ -2460,7 +2425,7 @@ paths:
- ApiKeyAuth: []
summary: 获取package
tags:
- AutoCode
- AutoCodePackage
/autoCode/getSysHistory:
post:
consumes:
@@ -2471,7 +2436,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/request.SysAutoHistory'
$ref: '#/definitions/request.PageInfo'
produces:
- application/json
responses:
@@ -2515,6 +2480,30 @@ paths:
summary: 获取当前数据库所有表
tags:
- AutoCode
/autoCode/getTemplates:
get:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 创建package成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
additionalProperties: true
type: object
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 获取package
tags:
- AutoCodePackage
/autoCode/installPlugin:
post:
consumes:
@@ -2545,7 +2534,7 @@ paths:
- ApiKeyAuth: []
summary: 安装插件
tags:
- AutoCode
- AutoCodePlugin
/autoCode/preview:
post:
consumes:
@@ -2556,7 +2545,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/system.AutoCodeStruct'
$ref: '#/definitions/request.AutoCode'
produces:
- application/json
responses:
@@ -2576,18 +2565,17 @@ paths:
- ApiKeyAuth: []
summary: 预览创建后的代码
tags:
- AutoCode
- AutoCodeTemplate
/autoCode/pubPlug:
get:
consumes:
- application/json
parameters:
- description: 打包插件
in: body
name: data
- description: 插件名称
in: query
name: plugName
required: true
schema:
$ref: '#/definitions/system.SysAutoCode'
type: string
produces:
- application/json
responses:
@@ -2607,7 +2595,7 @@ paths:
- ApiKeyAuth: []
summary: 打包插件
tags:
- AutoCode
- AutoCodePlugin
/autoCode/rollback:
post:
consumes:
@@ -2618,7 +2606,7 @@ paths:
name: data
required: true
schema:
$ref: '#/definitions/request.RollBack'
$ref: '#/definitions/request.SysAutoHistoryRollBack'
produces:
- application/json
responses:
@@ -3115,6 +3103,262 @@ paths:
summary: 上传文件示例
tags:
- ExaFileUploadAndDownload
/info/createInfo:
post:
consumes:
- application/json
parameters:
- description: 创建公告
in: body
name: data
required: true
schema:
$ref: '#/definitions/model.Info'
produces:
- application/json
responses:
"200":
description: 创建成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 创建公告
tags:
- Info
/info/deleteInfo:
delete:
consumes:
- application/json
parameters:
- description: 删除公告
in: body
name: data
required: true
schema:
$ref: '#/definitions/model.Info'
produces:
- application/json
responses:
"200":
description: 删除成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 删除公告
tags:
- Info
/info/deleteInfoByIds:
delete:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 批量删除成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 批量删除公告
tags:
- Info
/info/findInfo:
get:
consumes:
- application/json
parameters:
- description: 主键ID
in: query
name: ID
type: integer
- description: 内容
in: query
name: content
type: string
- description: 创建时间
in: query
name: createdAt
type: string
- description: 标题
in: query
name: title
type: string
- description: 更新时间
in: query
name: updatedAt
type: string
- description: 作者
in: query
name: userID
type: integer
produces:
- application/json
responses:
"200":
description: 查询成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/model.Info'
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 用id查询公告
tags:
- Info
/info/getInfoDataSource:
get:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 查询成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
type: object
msg:
type: string
type: object
summary: 获取Info的数据源
tags:
- Info
/info/getInfoList:
get:
consumes:
- application/json
parameters:
- in: query
name: endCreatedAt
type: string
- description: 关键字
in: query
name: keyword
type: string
- description: 页码
in: query
name: page
type: integer
- description: 每页大小
in: query
name: pageSize
type: integer
- in: query
name: startCreatedAt
type: string
produces:
- application/json
responses:
"200":
description: 获取成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/response.PageResult'
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 分页获取公告列表
tags:
- Info
/info/getInfoPublic:
get:
consumes:
- application/json
parameters:
- in: query
name: endCreatedAt
type: string
- description: 关键字
in: query
name: keyword
type: string
- description: 页码
in: query
name: page
type: integer
- description: 每页大小
in: query
name: pageSize
type: integer
- in: query
name: startCreatedAt
type: string
produces:
- application/json
responses:
"200":
description: 获取成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
type: object
msg:
type: string
type: object
summary: 不需要鉴权的公告接口
tags:
- Info
/info/updateInfo:
put:
consumes:
- application/json
parameters:
- description: 更新公告
in: body
name: data
required: true
schema:
$ref: '#/definitions/model.Info'
produces:
- application/json
responses:
"200":
description: 更新成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 更新公告
tags:
- Info
/init/checkdb:
post:
produces:
+1
View File
@@ -31,6 +31,7 @@ var (
GVA_Timer timer.Timer = timer.NewTimerTask()
GVA_Concurrency_Control = &singleflight.Group{}
GVA_ROUTERS gin.RoutesInfo
GVA_ACTIVE_DBNAME *string
BlackCache local_cache.Cache
lock sync.RWMutex
)
+6
View File
@@ -14,16 +14,22 @@ import (
func Gorm() *gorm.DB {
switch global.GVA_CONFIG.System.DbType {
case "mysql":
global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Mysql.Dbname
return GormMysql()
case "pgsql":
global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Pgsql.Dbname
return GormPgSql()
case "oracle":
global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Oracle.Dbname
return GormOracle()
case "mssql":
global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Mssql.Dbname
return GormMssql()
case "sqlite":
global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Sqlite.Dbname
return GormSqlite()
default:
global.GVA_ACTIVE_DBNAME = &global.GVA_CONFIG.Mysql.Dbname
return GormMysql()
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ import (
//go:generate go mod download
// @title Gin-Vue-Admin Swagger API接口文档
// @version v2.7.0
// @version v2.7.2
// @description 使用gin+vue进行极速开发的全栈开发基础平台
// @securityDefinitions.apikey ApiKeyAuth
// @in header
+27 -28
View File
@@ -12,21 +12,21 @@ import (
type AutoCode struct {
Package string `json:"package"`
PackageT string `json:"-"`
TableName string `json:"tableName" example:"表名"`
BusinessDB string `json:"businessDB" example:"业务数据库"`
StructName string `json:"structName" example:"Struct名称"`
PackageName string `json:"packageName" example:"文件名称"`
Description string `json:"description" example:"Struct中文名称"`
Abbreviation string `json:"abbreviation" example:"Struct简称"`
HumpPackageName string `json:"humpPackageName" example:"go文件名称"`
GvaModel bool `json:"gvaModel" example:"是否使用gva默认Model"`
AutoMigrate bool `json:"autoMigrate" example:"是否自动迁移表结构"`
AutoCreateResource bool `json:"autoCreateResource" example:"是否自动创建资源标识"`
AutoCreateApiToSql bool `json:"autoCreateApiToSql" example:"是否自动创建api"`
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql" example:"是否自动创建menu"`
TableName string `json:"tableName" example:"表名"` // 表名
BusinessDB string `json:"businessDB" example:"业务数据库"` // 业务数据库
StructName string `json:"structName" example:"Struct名称"` // Struct名称
PackageName string `json:"packageName" example:"文件名称"` // 文件名称
Description string `json:"description" example:"Struct中文名称"` // Struct中文名称
Abbreviation string `json:"abbreviation" example:"Struct简称"` // Struct简称
HumpPackageName string `json:"humpPackageName" example:"go文件名称"` // go文件名称
GvaModel bool `json:"gvaModel" example:"false"` // 是否使用gva默认Model
AutoMigrate bool `json:"autoMigrate" example:"false"` // 是否自动迁移表结构
AutoCreateResource bool `json:"autoCreateResource" example:"false"` // 是否自动创建资源标识
AutoCreateApiToSql bool `json:"autoCreateApiToSql" example:"false"` // 是否自动创建api
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql" example:"false"` // 是否自动创建menu
AutoCreateBtnAuth bool `json:"autoCreateBtnAuth" example:"false"` // 是否自动创建按钮权限
Fields []*AutoCodeField `json:"fields"`
DictTypes []string `json:"-"`
FrontFields []*AutoCodeField `json:"-"`
PrimaryField *AutoCodeField `json:"primaryField"`
DataSourceMap map[string]*DataSource `json:"-"`
HasPic bool `json:"-"`
@@ -114,7 +114,6 @@ func (r *AutoCode) Pretreatment() error {
} // test
length := len(r.Fields)
dict := make(map[string]string, length)
r.FrontFields = make([]*AutoCodeField, 0, length)
r.DataSourceMap = make(map[string]*DataSource, length)
for i := 0; i < length; i++ {
if r.Fields[i].DictType != "" {
@@ -123,9 +122,6 @@ func (r *AutoCode) Pretreatment() error {
if r.Fields[i].Sort {
r.NeedSort = true
}
if r.Fields[i].Front {
r.FrontFields = append(r.FrontFields, r.Fields[i])
}
switch r.Fields[i].FieldType {
case "file":
r.HasFile = true
@@ -204,17 +200,20 @@ func (r *AutoCode) History() SysAutoHistoryCreate {
}
type AutoCodeField struct {
FieldName string `json:"fieldName"` // Field名
FieldDesc string `json:"fieldDesc"` // 中文名
FieldType string `json:"fieldType"` // Field数据类型
FieldJson string `json:"fieldJson"` // FieldJson
DataTypeLong string `json:"dataTypeLong"` // 数据库字段长度
Comment string `json:"comment"` // 数据库字段描述
ColumnName string `json:"columnName"` // 数据库字段
FieldSearchType string `json:"fieldSearchType"` // 搜索条件
FieldSearchHide bool `json:"fieldSearchHide"` // 是否隐藏查询条件
DictType string `json:"dictType"` // 字典
Front bool `json:"front"` // 是否前端可见
FieldName string `json:"fieldName"` // Field名
FieldDesc string `json:"fieldDesc"` // 中文名
FieldType string `json:"fieldType"` // Field数据类型
FieldJson string `json:"fieldJson"` // FieldJson
DataTypeLong string `json:"dataTypeLong"` // 数据库字段长度
Comment string `json:"comment"` // 数据库字段描述
ColumnName string `json:"columnName"` // 数据库字段
FieldSearchType string `json:"fieldSearchType"` // 搜索条件
FieldSearchHide bool `json:"fieldSearchHide"` // 是否隐藏查询条件
DictType string `json:"dictType"` // 字典
//Front bool `json:"front"` // 是否前端可见
Form bool `json:"form"` // 是否前端新建/编辑
Table bool `json:"table"` // 是否前端表格列
Desc bool `json:"desc"` // 是否前端详情
Require bool `json:"require"` // 是否必填
DefaultValue string `json:"defaultValue"` // 是否必填
ErrorText string `json:"errorText"` // 校验失败文字
+3 -2
View File
@@ -17,10 +17,11 @@ func DefaultCasbin() []CasbinInfo {
{Path: "/menu/getMenu", Method: "POST"},
{Path: "/jwt/jsonInBlacklist", Method: "POST"},
{Path: "/base/login", Method: "POST"},
{Path: "/user/admin_register", Method: "POST"},
{Path: "/user/changePassword", Method: "POST"},
{Path: "/user/setUserAuthority", Method: "POST"},
{Path: "/user/setUserInfo", Method: "PUT"},
{Path: "/user/getUserInfo", Method: "GET"},
{Path: "/user/setSelfInfo", Method: "PUT"},
{Path: "/fileUploadAndDownload/upload", Method: "POST"},
{Path: "/sysDictionary/findSysDictionary", Method: "GET"},
}
}
+5 -5
View File
@@ -19,7 +19,7 @@ type info struct{}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body announcement.Info true "创建公告"
// @Param data body model.Info true "创建公告"
// @Success 200 {object} response.Response{msg=string} "创建成功"
// @Router /info/createInfo [post]
func (a *info) CreateInfo(c *gin.Context) {
@@ -44,7 +44,7 @@ func (a *info) CreateInfo(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body announcement.Info true "删除公告"
// @Param data body model.Info true "删除公告"
// @Success 200 {object} response.Response{msg=string} "删除成功"
// @Router /info/deleteInfo [delete]
func (a *info) DeleteInfo(c *gin.Context) {
@@ -82,7 +82,7 @@ func (a *info) DeleteInfoByIds(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body announcement.Info true "更新公告"
// @Param data body model.Info true "更新公告"
// @Success 200 {object} response.Response{msg=string} "更新成功"
// @Router /info/updateInfo [put]
func (a *info) UpdateInfo(c *gin.Context) {
@@ -107,8 +107,8 @@ func (a *info) UpdateInfo(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query announcement.Info true "用id查询公告"
// @Success 200 {object} response.Response{data=object{reinfo=announcement.Info},msg=string} "查询成功"
// @Param data query model.Info true "用id查询公告"
// @Success 200 {object} response.Response{data=model.Info,msg=string} "查询成功"
// @Router /info/findInfo [get]
func (a *info) FindInfo(c *gin.Context) {
ID := c.Query("ID")
+34
View File
@@ -0,0 +1,34 @@
{{if .IsPlugin}}
// {{.FuncName}} 等待开发的的{{.Description}}接口
// @Tags {{.StructName}}
// @Summary 等待开发的的{{.Description}}接口
// @accept application/json
// @Produce application/json
// @Param data query request.{{.StructName}}Search true "分页获取{{.Description}}列表"
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
// @Router /{{.Abbreviation}}/{{.Router}} [{{.Method}}]
export const {{.Router}} = () => {
return service({
url: '/{{.Abbreviation}}/{{.Router}}',
method: '{{.Method}}'
})
}
{{- else -}}
// {{.FuncName}} 等待开发的的{{.Description}}接口
// @Tags {{.StructName}}
// @Summary 等待开发的的{{.Description}}接口
// @accept application/json
// @Produce application/json
// @Param data query {{.Package}}Req.{{.StructName}}Search true "成功"
// @Success 200 {object} response.Response{data=object,msg=string} "成功"
// @Router /{{.Abbreviation}}/{{.Router}} [{{.Method}}]
export const {{.Router}} = () => {
return service({
url: '/{{.Abbreviation}}/{{.Router}}',
method: '{{.Method}}'
})
}
{{- end -}}
@@ -36,7 +36,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Create{{.StructName}}(c *gin.Con
err = {{.Abbreviation}}Service.Create{{.StructName}}(&{{.Abbreviation}})
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
response.FailWithMessage("创建失败:" + err.Error(), c)
return
}
response.OkWithMessage("创建成功", c)
@@ -59,7 +59,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Delete{{.StructName}}(c *gin.Con
err := {{.Abbreviation}}Service.Delete{{.StructName}}({{.PrimaryField.FieldJson}} {{- if .AutoCreateResource -}},userID{{- end -}})
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
response.FailWithMessage("删除失败:" + err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
@@ -81,7 +81,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Delete{{.StructName}}ByIds(c *gi
err := {{.Abbreviation}}Service.Delete{{.StructName}}ByIds({{.PrimaryField.FieldJson}}s{{- if .AutoCreateResource }},userID{{- end }})
if err != nil {
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
response.FailWithMessage("批量删除失败", c)
response.FailWithMessage("批量删除失败:" + err.Error(), c)
return
}
response.OkWithMessage("批量删除成功", c)
@@ -109,7 +109,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Update{{.StructName}}(c *gin.Con
err = {{.Abbreviation}}Service.Update{{.StructName}}({{.Abbreviation}})
if err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
response.FailWithMessage("更新失败:" + err.Error(), c)
return
}
response.OkWithMessage("更新成功", c)
@@ -122,14 +122,14 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Update{{.StructName}}(c *gin.Con
// @accept application/json
// @Produce application/json
// @Param data query {{.Package}}.{{.StructName}} true "用id查询{{.Description}}"
// @Success 200 {object} response.Response{data=object{re{{.Abbreviation}}={{.Package}}.{{.StructName}}},msg=string} "查询成功"
// @Success 200 {object} response.Response{data={{.Package}}.{{.StructName}},msg=string} "查询成功"
// @Router /{{.Abbreviation}}/find{{.StructName}} [get]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Find{{.StructName}}(c *gin.Context) {
{{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}")
re{{.Abbreviation}}, err := {{.Abbreviation}}Service.Get{{.StructName}}({{.PrimaryField.FieldJson}})
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
response.FailWithMessage("查询失败:" + err.Error(), c)
return
}
response.OkWithData(re{{.Abbreviation}}, c)
@@ -154,7 +154,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}List(c *gin.Co
list, total, err := {{.Abbreviation}}Service.Get{{.StructName}}InfoList(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
response.FailWithMessage("获取失败:" + err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
@@ -178,7 +178,7 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}DataSource(c *
dataSource, err := {{.Abbreviation}}Service.Get{{.StructName}}DataSource()
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
response.FailWithMessage("查询失败:" + err.Error(), c)
return
}
response.OkWithData(dataSource, c)
@@ -16,14 +16,8 @@ type {{.StructName}}Search struct{
Start{{.FieldName}} *{{.FieldType}} `json:"start{{.FieldName}}" form:"start{{.FieldName}}"`
End{{.FieldName}} *{{.FieldType}} `json:"end{{.FieldName}}" form:"end{{.FieldName}}"`
{{- else }}
{{- if eq .FieldType "enum" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}"`
{{- else if eq .FieldType "picture" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if eq .FieldType "video" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if eq .FieldType "richtext" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- if or (eq .FieldType "enum") (eq .FieldType "picture") (eq .FieldType "video") (eq .FieldType "richtext") (eq .FieldType "json") }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if ne .FieldType "string" }}
{{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else }}
@@ -93,9 +93,13 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoLis
{{- end }}
{{- range .Fields}}
{{- if .FieldSearchType}}
{{- if or (eq .FieldType "string") (eq .FieldType "enum") (eq .FieldType "picture") (eq .FieldType "video") (eq .FieldType "richtext") }}
{{- if or (eq .FieldType "string") (eq .FieldType "enum") (eq .FieldType "picture") (eq .FieldType "video") (eq .FieldType "richtext") (eq .FieldType "json") }}
if info.{{.FieldName}} != "" {
{{- if or (eq .FieldType "enum") (eq .FieldType "string") }}
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+ {{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }})
{{- else}}
// 数据类型为复杂类型,请根据业务需求自行实现复杂类型的查询业务
{{- end}}
}
{{- else if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
if info.Start{{.FieldName}} != nil && info.End{{.FieldName}} != nil {
@@ -3,6 +3,7 @@
<div class="gva-form-box">
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
{{- range .Fields}}
{{- if .Form }}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}">
{{- if .CheckDataSource}}
<el-select {{if eq .DataSource.Association 2}} multiple {{ end }} v-model="formData.{{.FieldJson}}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
@@ -56,6 +57,7 @@
{{- end }}
{{- end }}
</el-form-item>
{{- end }}
{{- end }}
<el-form-item>
<el-button type="primary" @click="save">保存</el-button>
@@ -106,6 +108,7 @@ const {{ $element }}Options = ref([])
{{- end }}
const formData = ref({
{{- range .Fields}}
{{- if .Form }}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
@@ -142,6 +145,7 @@ const formData = ref({
{{- if eq .FieldType "array" }}
{{.FieldJson}}: [],
{{- end }}
{{- end }}
{{- end }}
})
// 验证规则
+137 -53
View File
@@ -1,3 +1,4 @@
{{- $global := . }}
<template>
<div>
<div class="gva-search-box">
@@ -149,8 +150,8 @@
</div>
<div class="gva-table-box">
<div class="gva-btn-list">
<el-button type="primary" icon="plus" @click="openDialog">新增</el-button>
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="onDelete">删除</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.add"{{ end }} type="primary" icon="plus" @click="openDialog">新增</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.batchDelete"{{ end }} icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="onDelete">删除</el-button>
</div>
<el-table
ref="multipleTable"
@@ -169,7 +170,8 @@
<template #default="scope">{{ "{{ formatDate(scope.row.CreatedAt) }}" }}</template>
</el-table-column>
{{ end }}
{{- range .FrontFields}}
{{- range .Fields}}
{{- if .Table}}
{{- if .CheckDataSource }}
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template #default="scope">
@@ -199,14 +201,14 @@
{{- else if eq .FieldType "picture" }}
<el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="200">
<template #default="scope">
<el-image style="width: 100px; height: 100px" :src="getUrl(scope.row.{{.FieldJson}})" fit="cover"/>
<el-image preview-teleported style="width: 100px; height: 100px" :src="getUrl(scope.row.{{.FieldJson}})" fit="cover"/>
</template>
</el-table-column>
{{- else if eq .FieldType "pictures" }}
<el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="200">
<template #default="scope">
<div class="multiple-img-box">
<el-image v-for="(item,index) in scope.row.{{.FieldJson}}" :key="index" style="width: 80px; height: 80px" :src="getUrl(item)" fit="cover"/>
<el-image preview-teleported v-for="(item,index) in scope.row.{{.FieldJson}}" :key="index" style="width: 80px; height: 80px" :src="getUrl(item)" fit="cover"/>
</div>
</template>
</el-table-column>
@@ -242,14 +244,16 @@
[JSON]
</template>
</el-table-column>
{{- else }}
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120" />
{{- else }}
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120" />
{{- end }}
{{- end }}
{{- end }}
<el-table-column align="left" label="操作" fixed="right" min-width="240">
<template #default="scope">
<el-button type="primary" link icon="edit" class="table-button" @click="update{{.StructName}}Func(scope.row)">变更</el-button>
<el-button type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.info"{{ end }} type="primary" link class="table-button" @click="getDetails(scope.row)"><el-icon style="margin-right: 5px"><InfoFilled /></el-icon>查看详情</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.edit"{{ end }} type="primary" link icon="edit" class="table-button" @click="update{{.StructName}}Func(scope.row)">变更</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.delete"{{ end }} type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -277,7 +281,8 @@
</template>
<el-form :model="formData" label-position="top" ref="elFormRef" :rules="rule" label-width="80px">
{{- range .FrontFields}}
{{- range .Fields}}
{{- if .Form}}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}" >
{{- if .CheckDataSource}}
<el-select {{if eq .DataSource.Association 2}} multiple {{ end }} v-model="formData.{{.FieldJson}}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
@@ -347,8 +352,39 @@
{{- end }}
</el-form-item>
{{- end }}
{{- end }}
</el-form>
</el-drawer>
<el-drawer destroy-on-close size="800" v-model="detailShow" :show-close="true" :before-close="closeDetailShow">
<el-descriptions column="1" border>
{{- range .Fields}}
{{- if .Desc }}
<el-descriptions-item label="{{ .FieldDesc }}">
{{- if and (ne .FieldType "picture" ) (ne .FieldType "pictures" ) (ne .FieldType "file" ) }}
{{"{{"}} detailFrom.{{.FieldJson}} {{"}}"}}
{{- else }}
{{- if eq .FieldType "picture" }}
<el-image style="width: 50px; height: 50px" :preview-src-list="ReturnArrImg(detailFrom.{{ .FieldJson }})" :src="getUrl(formData.{{ .FieldJson }})" fit="cover" />
{{- end }}
{{- if eq .FieldType "pictures" }}
<el-image style="width: 50px; height: 50px; margin-right: 10px" :preview-src-list="ReturnArrImg(detailFrom.{{ .FieldJson }})" :initial-index="index" v-for="(item,index) in formData.{{ .FieldJson }}" :key="index" :src="getUrl(item)" fit="cover" />
{{- end }}
{{- if eq .FieldType "file" }}
<div class="fileBtn" v-for="(item,index) in detailFrom.{{ .FieldJson }}" :key="index">
<el-button type="primary" text bg @click="onDownloadFile(item.url)">
<el-icon style="margin-right: 5px"><Download /></el-icon>
{{"{{"}}item.name{{"}}"}}
</el-button>
</div>
{{- end }}
{{- end }}
</el-descriptions-item>
{{- end }}
{{- end }}
</el-descriptions>
</el-drawer>
</div>
</template>
@@ -388,11 +424,20 @@ import SelectFile from '@/components/selectFile/selectFile.vue'
import { getDictFunc, formatDate, formatBoolean, filterDict ,filterDataSource, ReturnArrImg, onDownloadFile } from '@/utils/format'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ref, reactive } from 'vue'
{{- if .AutoCreateBtnAuth }}
// 引入按钮权限标识
import { useBtnAuth } from '@/utils/btnAuth'
{{- end }}
defineOptions({
name: '{{.StructName}}'
})
{{- if .AutoCreateBtnAuth }}
// 按钮权限实例化
const btnAuth = useBtnAuth()
{{- end }}
// 控制更多查询条件显示/隐藏状态
const showAllQuery = ref(false)
@@ -401,43 +446,45 @@ const showAllQuery = ref(false)
const {{ $element }}Options = ref([])
{{- end }}
const formData = ref({
{{- range .FrontFields}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
{{- if eq .FieldType "string" }}
{{.FieldJson}}: '',
{{- end }}
{{- if eq .FieldType "richtext" }}
{{.FieldJson}}: '',
{{- end }}
{{- if eq .FieldType "int" }}
{{.FieldJson}}: {{- if or .DictType .DataSource}} undefined{{ else }} 0{{- end }},
{{- end }}
{{- if eq .FieldType "time.Time" }}
{{.FieldJson}}: new Date(),
{{- end }}
{{- if eq .FieldType "float64" }}
{{.FieldJson}}: 0,
{{- end }}
{{- if eq .FieldType "picture" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "video" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "pictures" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "file" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "json" }}
{{.FieldJson}}: {},
{{- end }}
{{- if eq .FieldType "array" }}
{{.FieldJson}}: [],
{{- end }}
{{- range .Fields}}
{{- if .Form}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
{{- if eq .FieldType "string" }}
{{.FieldJson}}: '',
{{- end }}
{{- if eq .FieldType "richtext" }}
{{.FieldJson}}: '',
{{- end }}
{{- if eq .FieldType "int" }}
{{.FieldJson}}: {{- if or .DictType .DataSource}} undefined{{ else }} 0{{- end }},
{{- end }}
{{- if eq .FieldType "time.Time" }}
{{.FieldJson}}: new Date(),
{{- end }}
{{- if eq .FieldType "float64" }}
{{.FieldJson}}: 0,
{{- end }}
{{- if eq .FieldType "picture" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "video" }}
{{.FieldJson}}: "",
{{- end }}
{{- if eq .FieldType "pictures" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "file" }}
{{.FieldJson}}: [],
{{- end }}
{{- if eq .FieldType "json" }}
{{.FieldJson}}: {},
{{- end }}
{{- if eq .FieldType "array" }}
{{.FieldJson}}: [],
{{- end }}
{{- end }}
{{- end }}
})
@@ -456,7 +503,8 @@ const formData = ref({
// 验证规则
const rule = reactive({
{{- range .FrontFields }}
{{- range .Fields }}
{{- if .Form }}
{{- if eq .Require true }}
{{.FieldJson }} : [{
required: true,
@@ -472,6 +520,7 @@ const rule = reactive({
{{- end }}
],
{{- end }}
{{- end }}
{{- end }}
})
@@ -489,7 +538,7 @@ const searchRule = reactive({
}
}, trigger: 'change' }
],
{{- range .FrontFields }}
{{- range .Fields }}
{{- if .FieldSearchType}}
{{- if eq .FieldType "time.Time" }}
{{.FieldJson }} : [{ validator: (rule, value, callback) => {
@@ -522,12 +571,14 @@ const searchInfo = ref({})
// 排序
const sortChange = ({ prop, order }) => {
const sortMap = {
{{- range .FrontFields}}
{{- range .Fields}}
{{- if .Table}}
{{- if and .Sort}}
{{- if not (eq .ColumnName "")}}
{{.FieldJson}}: '{{.ColumnName}}',
{{- end}}
{{- end}}
{{- end}}
{{- end}}
}
@@ -554,7 +605,7 @@ const onSubmit = () => {
if (!valid) return
page.value = 1
pageSize.value = 10
{{- range .FrontFields}}{{- if eq .FieldType "bool" }}
{{- range .Fields}}{{- if eq .FieldType "bool" }}
if (searchInfo.value.{{.FieldJson}} === ""){
searchInfo.value.{{.FieldJson}}=null
}{{ end }}{{ end }}
@@ -693,7 +744,8 @@ const openDialog = () => {
const closeDialog = () => {
dialogFormVisible.value = false
formData.value = {
{{- range .FrontFields}}
{{- range .Fields}}
{{- if .Form}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
@@ -727,7 +779,8 @@ const closeDialog = () => {
{{- if eq .FieldType "json" }}
{{.FieldJson}}: {},
{{- end }}
{{- end }}
{{- end }}
{{- end }}
}
}
// 弹窗确定
@@ -761,6 +814,37 @@ const downloadFile = (url) => {
window.open(getUrl(url), '_blank')
}
{{end}}
const detailFrom = ref({})
// 查看详情控制标记
const detailShow = ref(false)
// 打开详情弹窗
const openDetailShow = () => {
detailShow.value = true
}
// 打开详情
const getDetails = async (row) => {
// 打开弹窗
const res = await find{{.StructName}}({ {{.PrimaryField.FieldJson}}: row.{{.PrimaryField.FieldJson}} })
if (res.code === 0) {
detailFrom.value = res.data
openDetailShow()
}
}
// 关闭详情弹窗
const closeDetailShow = () => {
detailShow.value = false
detailFrom.value = {}
}
</script>
<style>
@@ -22,7 +22,7 @@ type {{.Abbreviation}} struct {}
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body {{.Package}}.{{.StructName}} true "创建{{.Description}}"
// @Param data body model.{{.StructName}} true "创建{{.Description}}"
// @Success 200 {object} response.Response{msg=string} "创建成功"
// @Router /{{.Abbreviation}}/create{{.StructName}} [post]
func (a *{{.Abbreviation}}) Create{{.StructName}}(c *gin.Context) {
@@ -38,7 +38,7 @@ func (a *{{.Abbreviation}}) Create{{.StructName}}(c *gin.Context) {
err = service{{ .StructName }}.Create{{.StructName}}(&info)
if err != nil {
global.GVA_LOG.Error("创建失败!", zap.Error(err))
response.FailWithMessage("创建失败", c)
response.FailWithMessage("创建失败:" + err.Error(), c)
return
}
response.OkWithMessage("创建成功", c)
@@ -50,7 +50,7 @@ func (a *{{.Abbreviation}}) Create{{.StructName}}(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body {{.Package}}.{{.StructName}} true "删除{{.Description}}"
// @Param data body model.{{.StructName}} true "删除{{.Description}}"
// @Success 200 {object} response.Response{msg=string} "删除成功"
// @Router /{{.Abbreviation}}/delete{{.StructName}} [delete]
func (a *{{.Abbreviation}}) Delete{{.StructName}}(c *gin.Context) {
@@ -61,7 +61,7 @@ func (a *{{.Abbreviation}}) Delete{{.StructName}}(c *gin.Context) {
err := service{{ .StructName }}.Delete{{.StructName}}({{.PrimaryField.FieldJson}} {{- if .AutoCreateResource -}},userID{{- end -}})
if err != nil {
global.GVA_LOG.Error("删除失败!", zap.Error(err))
response.FailWithMessage("删除失败", c)
response.FailWithMessage("删除失败:" + err.Error(), c)
return
}
response.OkWithMessage("删除成功", c)
@@ -83,7 +83,7 @@ func (a *{{.Abbreviation}}) Delete{{.StructName}}ByIds(c *gin.Context) {
err := service{{ .StructName }}.Delete{{.StructName}}ByIds({{.PrimaryField.FieldJson}}s{{- if .AutoCreateResource }},userID{{- end }})
if err != nil {
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
response.FailWithMessage("批量删除失败", c)
response.FailWithMessage("批量删除失败:" + err.Error(), c)
return
}
response.OkWithMessage("批量删除成功", c)
@@ -95,7 +95,7 @@ func (a *{{.Abbreviation}}) Delete{{.StructName}}ByIds(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body {{.Package}}.{{.StructName}} true "更新{{.Description}}"
// @Param data body model.{{.StructName}} true "更新{{.Description}}"
// @Success 200 {object} response.Response{msg=string} "更新成功"
// @Router /{{.Abbreviation}}/update{{.StructName}} [put]
func (a *{{.Abbreviation}}) Update{{.StructName}}(c *gin.Context) {
@@ -111,7 +111,7 @@ func (a *{{.Abbreviation}}) Update{{.StructName}}(c *gin.Context) {
err = service{{ .StructName }}.Update{{.StructName}}(info)
if err != nil {
global.GVA_LOG.Error("更新失败!", zap.Error(err))
response.FailWithMessage("更新失败", c)
response.FailWithMessage("更新失败:" + err.Error(), c)
return
}
response.OkWithMessage("更新成功", c)
@@ -123,15 +123,15 @@ func (a *{{.Abbreviation}}) Update{{.StructName}}(c *gin.Context) {
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query {{.Package}}.{{.StructName}} true "用id查询{{.Description}}"
// @Success 200 {object} response.Response{data=object{re{{.Abbreviation}}={{.Package}}.{{.StructName}}},msg=string} "查询成功"
// @Param data query model.{{.StructName}} true "用id查询{{.Description}}"
// @Success 200 {object} response.Response{data=model.{{.StructName}},msg=string} "查询成功"
// @Router /{{.Abbreviation}}/find{{.StructName}} [get]
func (a *{{.Abbreviation}}) Find{{.StructName}}(c *gin.Context) {
{{.PrimaryField.FieldJson}} := c.Query("{{.PrimaryField.FieldJson}}")
re{{.Abbreviation}}, err := service{{ .StructName }}.Get{{.StructName}}({{.PrimaryField.FieldJson}})
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
response.FailWithMessage("查询失败:" + err.Error(), c)
return
}
response.OkWithData(re{{.Abbreviation}}, c)
@@ -156,7 +156,7 @@ func (a *{{.Abbreviation}}) Get{{.StructName}}List(c *gin.Context) {
list, total, err := service{{ .StructName }}.Get{{.StructName}}InfoList(pageInfo)
if err != nil {
global.GVA_LOG.Error("获取失败!", zap.Error(err))
response.FailWithMessage("获取失败", c)
response.FailWithMessage("获取失败:" + err.Error(), c)
return
}
response.OkWithDetailed(response.PageResult{
@@ -180,7 +180,7 @@ func (a *{{.Abbreviation}}) Get{{.StructName}}DataSource(c *gin.Context) {
dataSource, err := service{{ .StructName }}.Get{{.StructName}}DataSource()
if err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
response.FailWithMessage("查询失败:" + err.Error(), c)
return
}
response.OkWithData(dataSource, c)
@@ -16,13 +16,7 @@ type {{.StructName}}Search struct{
Start{{.FieldName}} *{{.FieldType}} `json:"start{{.FieldName}}" form:"start{{.FieldName}}"`
End{{.FieldName}} *{{.FieldType}} `json:"end{{.FieldName}}" form:"end{{.FieldName}}"`
{{- else }}
{{- if eq .FieldType "enum" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}"`
{{- else if eq .FieldType "picture" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if eq .FieldType "video" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if eq .FieldType "richtext" }}
{{- if or (eq .FieldType "enum") (eq .FieldType "picture") (eq .FieldType "video") (eq .FieldType "richtext") (eq .FieldType "json") }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
{{- else if ne .FieldType "string" }}
{{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" `
@@ -95,10 +95,14 @@ func (s *{{.Abbreviation}}) Get{{.StructName}}InfoList(info request.{{.StructNam
{{- end }}
{{- range .Fields}}
{{- if .FieldSearchType}}
{{- if or (eq .FieldType "string") (eq .FieldType "enum") (eq .FieldType "picture") (eq .FieldType "video") (eq .FieldType "richtext") }}
{{- if or (eq .FieldType "string") (eq .FieldType "enum") (eq .FieldType "picture") (eq .FieldType "video") (eq .FieldType "richtext") (eq .FieldType "json") }}
if info.{{.FieldName}} != "" {
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+ {{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }})
}
{{- if or (eq .FieldType "enum") (eq .FieldType "string") }}
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ?",{{if eq .FieldSearchType "LIKE"}}"%"+ {{ end }}info.{{.FieldName}}{{if eq .FieldSearchType "LIKE"}}+"%"{{ end }})
{{- else}}
// 数据类型为复杂类型,请根据业务需求自行实现复杂类型的查询业务
{{- end}}
}
{{- else if eq .FieldSearchType "BETWEEN" "NOT BETWEEN"}}
if info.Start{{.FieldName}} != nil && info.End{{.FieldName}} != nil {
db = db.Where("{{.ColumnName}} {{.FieldSearchType}} ? AND ? ",info.Start{{.FieldName}},info.End{{.FieldName}})
@@ -3,6 +3,7 @@
<div class="gva-form-box">
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
{{- range .Fields}}
{{- if .Form }}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}">
{{- if .CheckDataSource}}
<el-select {{if eq .DataSource.Association 2}} multiple {{ end }} v-model="formData.{{.FieldJson}}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
@@ -56,6 +57,7 @@
{{- end }}
{{- end }}
</el-form-item>
{{- end }}
{{- end }}
<el-form-item>
<el-button type="primary" @click="save">保存</el-button>
@@ -106,6 +108,7 @@ const {{ $element }}Options = ref([])
{{- end }}
const formData = ref({
{{- range .Fields}}
{{- if .Form }}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
@@ -142,11 +145,13 @@ const formData = ref({
{{- if eq .FieldType "array" }}
{{.FieldJson}}: [],
{{- end }}
{{- end }}
{{- end }}
})
// 验证规则
const rule = reactive({
{{- range .Fields }}
{{- if .Form }}
{{- if eq .Require true }}
{{.FieldJson }} : [{
required: true,
@@ -154,6 +159,7 @@ const rule = reactive({
trigger: ['input','blur'],
}],
{{- end }}
{{- end }}
{{- end }}
})
+107 -18
View File
@@ -1,3 +1,4 @@
{{- $global := . }}
<template>
<div>
<div class="gva-search-box">
@@ -149,8 +150,8 @@
</div>
<div class="gva-table-box">
<div class="gva-btn-list">
<el-button type="primary" icon="plus" @click="openDialog">新增</el-button>
<el-button icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="onDelete">删除</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.add"{{ end }} type="primary" icon="plus" @click="openDialog">新增</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.batchDelete"{{ end }} icon="delete" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="onDelete">删除</el-button>
</div>
<el-table
ref="multipleTable"
@@ -169,7 +170,8 @@
<template #default="scope">{{ "{{ formatDate(scope.row.CreatedAt) }}" }}</template>
</el-table-column>
{{ end }}
{{- range .FrontFields}}
{{- range .Fields}}
{{- if .Table}}
{{- if .CheckDataSource }}
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template #default="scope">
@@ -199,14 +201,14 @@
{{- else if eq .FieldType "picture" }}
<el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="200">
<template #default="scope">
<el-image style="width: 100px; height: 100px" :src="getUrl(scope.row.{{.FieldJson}})" fit="cover"/>
<el-image preview-teleported style="width: 100px; height: 100px" :src="getUrl(scope.row.{{.FieldJson}})" fit="cover"/>
</template>
</el-table-column>
{{- else if eq .FieldType "pictures" }}
<el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="200">
<template #default="scope">
<div class="multiple-img-box">
<el-image v-for="(item,index) in scope.row.{{.FieldJson}}" :key="index" style="width: 80px; height: 80px" :src="getUrl(item)" fit="cover"/>
<el-image preview-teleported v-for="(item,index) in scope.row.{{.FieldJson}}" :key="index" style="width: 80px; height: 80px" :src="getUrl(item)" fit="cover"/>
</div>
</template>
</el-table-column>
@@ -246,10 +248,12 @@
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120" />
{{- end }}
{{- end }}
{{- end }}
<el-table-column align="left" label="操作" fixed="right" min-width="240">
<template #default="scope">
<el-button type="primary" link icon="edit" class="table-button" @click="update{{.StructName}}Func(scope.row)">变更</el-button>
<el-button type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.info"{{ end }} type="primary" link class="table-button" @click="getDetails(scope.row)"><el-icon style="margin-right: 5px"><InfoFilled /></el-icon>查看详情</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.edit"{{ end }} type="primary" link icon="edit" class="table-button" @click="update{{.StructName}}Func(scope.row)">变更</el-button>
<el-button {{ if $global.AutoCreateBtnAuth }}v-auth="btnAuth.delete"{{ end }} type="primary" link icon="delete" @click="deleteRow(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -277,7 +281,8 @@
</template>
<el-form :model="formData" label-position="top" ref="elFormRef" :rules="rule" label-width="80px">
{{- range .FrontFields}}
{{- range .Fields}}
{{- if .Form}}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}" >
{{- if .CheckDataSource}}
<el-select {{if eq .DataSource.Association 2}} multiple {{ end }} v-model="formData.{{.FieldJson}}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
@@ -347,8 +352,43 @@
{{- end }}
</el-form-item>
{{- end }}
{{- end }}
</el-form>
</el-drawer>
<el-drawer destroy-on-close size="800" v-model="detailShow" :show-close="true" :before-close="closeDetailShow">
<el-descriptions column="1" border>
{{- range .Fields}}
{{- if .Desc }}
<el-descriptions-item label="{{ .FieldDesc }}">
{{- if and (ne .FieldType "picture" ) (ne .FieldType "pictures" ) (ne .FieldType "file" ) }}
{{"{{"}} detailFrom.{{.FieldJson}} {{"}}"}}
{{- else }}
{{- if eq .FieldType "picture" }}
<el-image style="width: 50px; height: 50px" :preview-src-list="ReturnArrImg(detailFrom.{{ .FieldJson }})" :src="getUrl(formData.{{ .FieldJson }})" fit="cover" />
{{- end }}
{{- if eq .FieldType "pictures" }}
<el-image style="width: 50px; height: 50px; margin-right: 10px" :preview-src-list="ReturnArrImg(detailFrom.{{ .FieldJson }})" :initial-index="index" v-for="(item,index) in formData.{{ .FieldJson }}" :key="index" :src="getUrl(item)" fit="cover" />
{{- end }}
{{- if eq .FieldType "file" }}
<div class="fileBtn" v-for="(item,index) in detailFrom.{{ .FieldJson }}" :key="index">
<el-button type="primary" text bg @click="onDownloadFile(item.url)">
<el-icon style="margin-right: 5px"><Download /></el-icon>
{{"{{"}}item.name{{"}}"}}
</el-button>
</div>
{{- end }}
{{- end }}
</el-descriptions-item>
{{- end }}
{{- end }}
</el-descriptions>
</el-drawer>
</div>
</template>
@@ -388,11 +428,20 @@ import SelectFile from '@/components/selectFile/selectFile.vue'
import { getDictFunc, formatDate, formatBoolean, filterDict ,filterDataSource, ReturnArrImg, onDownloadFile } from '@/utils/format'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ref, reactive } from 'vue'
{{- if .AutoCreateBtnAuth }}
// 引入按钮权限标识
import { useBtnAuth } from '@/utils/btnAuth'
{{- end }}
defineOptions({
name: '{{.StructName}}'
})
{{- if .AutoCreateBtnAuth }}
// 按钮权限实例化
const btnAuth = useBtnAuth()
{{- end }}
// 控制更多查询条件显示/隐藏状态
const showAllQuery = ref(false)
@@ -401,7 +450,8 @@ const showAllQuery = ref(false)
const {{ $element }}Options = ref([])
{{- end }}
const formData = ref({
{{- range .FrontFields}}
{{- range .Fields}}
{{- if .Form}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
@@ -439,6 +489,7 @@ const formData = ref({
{{.FieldJson}}: [],
{{- end }}
{{- end }}
{{- end }}
})
{{- if .HasDataSource }}
@@ -456,7 +507,8 @@ const formData = ref({
// 验证规则
const rule = reactive({
{{- range .FrontFields }}
{{- range .Fields }}
{{- if .Form }}
{{- if eq .Require true }}
{{.FieldJson }} : [{
required: true,
@@ -472,6 +524,7 @@ const rule = reactive({
{{- end }}
],
{{- end }}
{{- end }}
{{- end }}
})
@@ -489,7 +542,7 @@ const searchRule = reactive({
}
}, trigger: 'change' }
],
{{- range .FrontFields }}
{{- range .Fields }}
{{- if .FieldSearchType}}
{{- if eq .FieldType "time.Time" }}
{{.FieldJson }} : [{ validator: (rule, value, callback) => {
@@ -522,10 +575,12 @@ const searchInfo = ref({})
// 排序
const sortChange = ({ prop, order }) => {
const sortMap = {
{{- range .FrontFields}}
{{- if and .Sort}}
{{- if not (eq .ColumnName "")}}
{{.FieldJson}}: '{{.ColumnName}}',
{{- range .Fields}}
{{- if .Table}}
{{- if and .Sort}}
{{- if not (eq .ColumnName "")}}
{{.FieldJson}}: '{{.ColumnName}}',
{{- end}}
{{- end}}
{{- end}}
{{- end}}
@@ -554,7 +609,7 @@ const onSubmit = () => {
if (!valid) return
page.value = 1
pageSize.value = 10
{{- range .FrontFields}}{{- if eq .FieldType "bool" }}
{{- range .Fields}}{{- if eq .FieldType "bool" }}
if (searchInfo.value.{{.FieldJson}} === ""){
searchInfo.value.{{.FieldJson}}=null
}{{ end }}{{ end }}
@@ -693,7 +748,8 @@ const openDialog = () => {
const closeDialog = () => {
dialogFormVisible.value = false
formData.value = {
{{- range .FrontFields}}
{{- range .Fields}}
{{- if .Form}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
@@ -727,7 +783,8 @@ const closeDialog = () => {
{{- if eq .FieldType "json" }}
{{.FieldJson}}: {},
{{- end }}
{{- end }}
{{- end }}
{{- end }}
}
}
// 弹窗确定
@@ -761,6 +818,38 @@ const downloadFile = (url) => {
window.open(getUrl(url), '_blank')
}
{{end}}
const detailFrom = ref({})
// 查看详情控制标记
const detailShow = ref(false)
// 打开详情弹窗
const openDetailShow = () => {
detailShow.value = true
}
// 打开详情
const getDetails = async (row) => {
// 打开弹窗
const res = await find{{.StructName}}({ {{.PrimaryField.FieldJson}}: row.{{.PrimaryField.FieldJson}} })
if (res.code === 0) {
detailFrom.value = res.data
openDetailShow()
}
}
// 关闭详情弹窗
const closeDetailShow = () => {
detailShow.value = false
detailFrom.value = {}
}
</script>
<style>
+1 -1
View File
@@ -564,7 +564,7 @@ func (s *autoCodePackage) templates(ctx context.Context, entity model.SysAutoCod
code[four] = create
continue
}
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), "plugin", entity.PackageName, secondDirs[j].Name(), info.Abbreviation+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
create := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.WebRoot(), "plugin", entity.PackageName, secondDirs[j].Name(), info.PackageName+filepath.Ext(strings.TrimSuffix(threeDirs[k].Name(), ext)))
code[four] = create
}
default:
+56 -8
View File
@@ -24,6 +24,34 @@ var AutoCodeTemplate = new(autoCodeTemplate)
type autoCodeTemplate struct{}
func (s *autoCodeTemplate) checkPackage(Pkg string, template string) (err error) {
switch template {
case "package":
apiEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", Pkg, "enter.go")
_, err = os.Stat(apiEnter)
if err != nil {
return fmt.Errorf("package结构异常,缺少api/v1/%s/enter.go", Pkg)
}
serviceEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", Pkg, "enter.go")
_, err = os.Stat(serviceEnter)
if err != nil {
return fmt.Errorf("package结构异常,缺少service/%s/enter.go", Pkg)
}
routerEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "router", Pkg, "enter.go")
_, err = os.Stat(routerEnter)
if err != nil {
return fmt.Errorf("package结构异常,缺少router/%s/enter.go", Pkg)
}
case "plugin":
pluginEnter := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", Pkg, "plugin.go")
_, err = os.Stat(pluginEnter)
if err != nil {
return fmt.Errorf("plugin结构异常,缺少plugin/%s/plugin.go", Pkg)
}
}
return nil
}
// Create 创建生成自动化代码
func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) error {
history := info.History()
@@ -32,7 +60,10 @@ func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) er
if err != nil {
return errors.Wrap(err, "查询包失败!")
}
err = s.checkPackage(info.Package, autoPkg.Template)
if err != nil {
return err
}
// 增加判断: 重复创建struct
if AutocodeHistory.Repeat(info.BusinessDB, info.StructName, info.Package) {
return errors.New("已经创建过此数据结构,请勿重复创建!")
@@ -87,6 +118,15 @@ func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) er
id = entity.ID
} else {
entity = info.Menu(autoPkg.Template)
if info.AutoCreateBtnAuth {
entity.MenuBtn = []model.SysBaseMenuBtn{
{SysBaseMenuID: entity.ID, Name: "add", Desc: "新增"},
{SysBaseMenuID: entity.ID, Name: "batchDelete", Desc: "批量删除"},
{SysBaseMenuID: entity.ID, Name: "delete", Desc: "删除"},
{SysBaseMenuID: entity.ID, Name: "edit", Desc: "编辑"},
{SysBaseMenuID: entity.ID, Name: "info", Desc: "详情"},
}
}
err = global.GVA_DB.WithContext(ctx).Create(&entity).Error
id = entity.ID
if err != nil {
@@ -190,11 +230,15 @@ func (s *autoCodeTemplate) AddFunc(info request.AutoFunc) error {
if autoPkg.Template != "package" {
info.IsPlugin = true
}
err = s.addTemplateToFile("api", info)
err = s.addTemplateToFile("api.go", info)
if err != nil {
return err
}
err = s.addTemplateToFile("server", info)
err = s.addTemplateToFile("server.go", info)
if err != nil {
return err
}
err = s.addTemplateToFile("api.js", info)
if err != nil {
return err
}
@@ -203,7 +247,7 @@ func (s *autoCodeTemplate) AddFunc(info request.AutoFunc) error {
}
func (s *autoCodeTemplate) getTemplateStr(t string, info request.AutoFunc) (string, error) {
tempPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "function", t+".go.tpl")
tempPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "resource", "function", t+".tpl")
files, err := template.ParseFiles(tempPath)
if err != nil {
return "", errors.Wrapf(err, "[filepath:%s]读取模版文件失败!", tempPath)
@@ -267,17 +311,21 @@ func (s *autoCodeTemplate) addTemplateToFile(t string, info request.AutoFunc) er
var target string
switch t {
case "api":
case "api.go":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "api", "v1", info.Package, info.HumpPackageName+".go")
case "server":
case "server.go":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "service", info.Package, info.HumpPackageName+".go")
case "api.js":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "api", info.Package, info.PackageName+".js")
}
if info.IsPlugin {
switch t {
case "api":
case "api.go":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "api", info.HumpPackageName+".go")
case "server":
case "server.go":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", info.Package, "service", info.HumpPackageName+".go")
case "api.js":
target = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, "plugin", info.Package, "api", info.PackageName+".js")
}
}
+4 -1
View File
@@ -53,7 +53,8 @@ SELECT
CASE
WHEN pk.object_id IS NOT NULL THEN 1
ELSE 0
END AS primary_key
END AS primary_key,
sc.column_id
FROM
%s.sys.columns sc
JOIN
@@ -68,6 +69,8 @@ LEFT JOIN
%s.sys.key_constraints pk ON pk.object_id = si.object_id
WHERE
st.is_user_defined=0 AND sc.object_id = so.object_id
ORDER BY
sc.column_id
`, dbName, dbName, tableName, dbName, dbName, dbName)
if businessDB == "" {
+5 -2
View File
@@ -57,7 +57,8 @@ func (s *autoCodeMysql) GetColumn(businessDB string, tableName string, dbName st
ELSE ''
END AS data_type_long,
c.COLUMN_COMMENT column_comment,
CASE WHEN kcu.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS primary_key
CASE WHEN kcu.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END AS primary_key,
c.ORDINAL_POSITION
FROM
INFORMATION_SCHEMA.COLUMNS c
LEFT JOIN
@@ -69,7 +70,9 @@ ON
AND kcu.CONSTRAINT_NAME = 'PRIMARY'
WHERE
c.TABLE_NAME = ?
AND c.TABLE_SCHEMA = ?;`
AND c.TABLE_SCHEMA = ?
ORDER BY
c.ORDINAL_POSITION;`
if businessDB == "" {
err = global.GVA_DB.Raw(sql, tableName, dbName).Scan(&entities).Error
} else {
@@ -36,12 +36,13 @@ func (s *autoCodeOracle) GetTables(businessDB string, dbName string) (data []res
func (s *autoCodeOracle) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) {
var entities []response.Column
sql := `
SELECT
SELECT
lower(a.COLUMN_NAME) as "column_name",
(CASE WHEN a.DATA_TYPE = 'NUMBER' AND a.DATA_SCALE=0 THEN 'int' else lower(a.DATA_TYPE) end) as "data_type",
(CASE WHEN a.DATA_TYPE = 'NUMBER' THEN a.DATA_PRECISION else a.DATA_LENGTH end) as "data_type_long",
b.COMMENTS as "column_comment",
(CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END) as "primary_key"
(CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END) as "primary_key",
a.COLUMN_ID
FROM
all_tab_columns a
JOIN
@@ -61,7 +62,9 @@ LEFT JOIN
) pk ON a.OWNER = pk.OWNER AND a.TABLE_NAME = pk.TABLE_NAME AND a.COLUMN_NAME = pk.COLUMN_NAME
WHERE
lower(a.table_name) = ?
AND lower(a.OWNER) = ?;
AND lower(a.OWNER) = ?
ORDER BY
a.COLUMN_ID;
`
err = global.GVA_DBList[businessDB].Raw(sql, tableName, dbName).Scan(&entities).Error
+5 -2
View File
@@ -111,13 +111,16 @@ SELECT
attrelid = conrelid
AND attname = psc.column_name
)]
) > 0 AS primary_key
) > 0 AS primary_key,
psc.ordinal_position
FROM
INFORMATION_SCHEMA.COLUMNS psc
WHERE
table_catalog = ?
AND table_schema = 'public'
AND TABLE_NAME = ?;
AND TABLE_NAME = ?
ORDER BY
psc.ordinal_position;
`
var entities []response.Column
//sql = strings.ReplaceAll(sql, "@table_catalog", dbName)
+39 -29
View File
@@ -20,36 +20,46 @@ var BaseMenuServiceApp = new(BaseMenuService)
func (baseMenuService *BaseMenuService) DeleteBaseMenu(id int) (err error) {
err = global.GVA_DB.First(&system.SysBaseMenu{}, "parent_id = ?", id).Error
if err != nil {
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
err = tx.Delete(&system.SysBaseMenu{}, "id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysAuthorityMenu{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
return nil
})
if err == nil {
return errors.New("此菜单存在子菜单不可删除")
}
return errors.New("此菜单存在子菜单不可删除")
var menu system.SysBaseMenu
err = global.GVA_DB.First(&menu, id).Error
if err != nil {
return errors.New("记录不存在")
}
err = global.GVA_DB.First(&system.SysAuthority{}, "default_router = ?", menu.Name).Error
if err == nil {
return errors.New("此菜单有角色正在作为首页,不可删除")
}
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
err = tx.Delete(&system.SysBaseMenu{}, "id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error
if err != nil {
return err
}
err = tx.Delete(&system.SysAuthorityMenu{}, "sys_base_menu_id = ?", id).Error
if err != nil {
return err
}
return nil
})
}
//@author: [piexlmax](https://github.com/piexlmax)
+3
View File
@@ -39,6 +39,9 @@ func (casbinService *CasbinService) UpdateCasbin(AuthorityID uint, casbinInfos [
rules = append(rules, []string{authorityId, v.Path, v.Method})
}
}
if len(rules) == 0 {
return nil
} // 设置空权限无需调用 AddPolicies 方法
e := casbinService.Casbin()
success, _ := e.AddPolicies(rules)
if !success {
+4 -1
View File
@@ -153,10 +153,13 @@ func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID
return nil, "", err
}
var tableTitle []string
var selectKeyFmt []string
for _, key := range columns {
selectKeyFmt = append(selectKeyFmt, fmt.Sprintf("`%s`", key))
tableTitle = append(tableTitle, templateInfoMap[key])
}
selects := strings.Join(columns, ", ")
selects := strings.Join(selectKeyFmt, ", ")
var tableMap []map[string]interface{}
db := global.GVA_DB
if template.DBName != "" {
@@ -33,6 +33,7 @@ func (h MssqlInitHandler) WriteConfig(ctx context.Context) error {
for k, v := range cs {
global.GVA_VP.Set(k, v)
}
global.GVA_ACTIVE_DBNAME = &c.Dbname
return global.GVA_VP.WriteConfig()
}
@@ -37,6 +37,7 @@ func (h MysqlInitHandler) WriteConfig(ctx context.Context) error {
for k, v := range cs {
global.GVA_VP.Set(k, v)
}
global.GVA_ACTIVE_DBNAME = &c.Dbname
return global.GVA_VP.WriteConfig()
}
@@ -37,6 +37,7 @@ func (h PgsqlInitHandler) WriteConfig(ctx context.Context) error {
for k, v := range cs {
global.GVA_VP.Set(k, v)
}
global.GVA_ACTIVE_DBNAME = &c.Dbname
return global.GVA_VP.WriteConfig()
}
@@ -34,6 +34,7 @@ func (h SqliteInitHandler) WriteConfig(ctx context.Context) error {
for k, v := range cs {
global.GVA_VP.Set(k, v)
}
global.GVA_ACTIVE_DBNAME = &c.Dbname
return global.GVA_VP.WriteConfig()
}
+34
View File
@@ -104,10 +104,44 @@ func (userService *UserService) GetUserInfoList(info request.PageInfo) (list int
//@return: err error
func (userService *UserService) SetUserAuthority(id uint, authorityId uint) (err error) {
assignErr := global.GVA_DB.Where("sys_user_id = ? AND sys_authority_authority_id = ?", id, authorityId).First(&system.SysUserAuthority{}).Error
if errors.Is(assignErr, gorm.ErrRecordNotFound) {
return errors.New("该用户无此角色")
}
var authority system.SysAuthority
err = global.GVA_DB.Where("authority_id = ?", authorityId).First(&authority).Error
if err != nil {
return err
}
var authorityMenu []system.SysAuthorityMenu
var authorityMenuIDs []string
err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityId).Find(&authorityMenu).Error
if err != nil {
return err
}
for i := range authorityMenu {
authorityMenuIDs = append(authorityMenuIDs, authorityMenu[i].MenuId)
}
var authorityMenus []system.SysBaseMenu
err = global.GVA_DB.Preload("Parameters").Where("id in (?)", authorityMenuIDs).Find(&authorityMenus).Error
if err != nil {
return err
}
hasMenu := false
for i := range authorityMenus {
if authorityMenus[i].Name == authority.DefaultRouter {
hasMenu = true
break
}
}
if !hasMenu {
return errors.New("找不到默认路由,无法切换本角色")
}
err = global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", id).Update("authority_id", authorityId).Error
return err
}
+2 -2
View File
@@ -93,7 +93,7 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) {
{ApiGroup: "分片上传", Method: "POST", Path: "/fileUploadAndDownload/breakpointContinueFinish", Description: "断点续传完成"},
{ApiGroup: "分片上传", Method: "POST", Path: "/fileUploadAndDownload/removeChunk", Description: "上传完成移除文件"},
{ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/upload", Description: "文件上传示例"},
{ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/upload", Description: "文件上传(建议选择)"},
{ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/deleteFile", Description: "删除文件"},
{ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/editFileName", Description: "文件名或者备注编辑"},
{ApiGroup: "文件上传与下载", Method: "POST", Path: "/fileUploadAndDownload/getFileList", Description: "获取上传文件列表"},
@@ -136,7 +136,7 @@ func (i *initApi) InitializeData(ctx context.Context) (context.Context, error) {
{ApiGroup: "系统字典", Method: "POST", Path: "/sysDictionary/createSysDictionary", Description: "新增字典"},
{ApiGroup: "系统字典", Method: "DELETE", Path: "/sysDictionary/deleteSysDictionary", Description: "删除字典"},
{ApiGroup: "系统字典", Method: "PUT", Path: "/sysDictionary/updateSysDictionary", Description: "更新字典"},
{ApiGroup: "系统字典", Method: "GET", Path: "/sysDictionary/findSysDictionary", Description: "根据ID获取字典"},
{ApiGroup: "系统字典", Method: "GET", Path: "/sysDictionary/findSysDictionary", Description: "根据ID获取字典(建议选择)"},
{ApiGroup: "系统字典", Method: "GET", Path: "/sysDictionary/getSysDictionaryList", Description: "获取字典列表"},
{ApiGroup: "操作记录", Method: "POST", Path: "/sysOperationRecord/createSysOperationRecord", Description: "新增操作记录"},
+1 -1
View File
@@ -142,7 +142,7 @@ func (a *PackageInitializeGorm) addDbVar(astBody *ast.BlockStmt) {
for i := range astBody.List {
if assignStmt, ok := astBody.List[i].(*ast.AssignStmt); ok {
if ident, ok := assignStmt.Lhs[0].(*ast.Ident); ok {
if ident.Name == "db" || ident.Name == a.Business+"Db" {
if (a.Business == "" && ident.Name == "db") || ident.Name == a.Business+"Db" {
return
}
}
+85
View File
@@ -0,0 +1,85 @@
package upload
import (
"errors"
"fmt"
"mime/multipart"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"go.uber.org/zap"
)
type CloudflareR2 struct{}
func (c *CloudflareR2) UploadFile(file *multipart.FileHeader) (fileUrl string, fileName string, err error) {
session := c.newSession()
client := s3manager.NewUploader(session)
fileKey := fmt.Sprintf("%d_%s", time.Now().Unix(), file.Filename)
fileName = fmt.Sprintf("%s/%s", global.GVA_CONFIG.CloudflareR2.Path, fileKey)
f, openError := file.Open()
if openError != nil {
global.GVA_LOG.Error("function file.Open() failed", zap.Any("err", openError.Error()))
return "", "", errors.New("function file.Open() failed, err:" + openError.Error())
}
defer f.Close() // 创建文件 defer 关闭
input := &s3manager.UploadInput{
Bucket: aws.String(global.GVA_CONFIG.CloudflareR2.Bucket),
Key: aws.String(fileName),
Body: f,
}
_, err = client.Upload(input)
if err != nil {
global.GVA_LOG.Error("function uploader.Upload() failed", zap.Any("err", err.Error()))
return "", "", err
}
return fmt.Sprintf("%s/%s", global.GVA_CONFIG.CloudflareR2.BaseURL,
fileName),
fileKey,
nil
}
func (c *CloudflareR2) DeleteFile(key string) error {
session := newSession()
svc := s3.New(session)
filename := global.GVA_CONFIG.CloudflareR2.Path + "/" + key
bucket := global.GVA_CONFIG.CloudflareR2.Bucket
_, err := svc.DeleteObject(&s3.DeleteObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(filename),
})
if err != nil {
global.GVA_LOG.Error("function svc.DeleteObject() failed", zap.Any("err", err.Error()))
return errors.New("function svc.DeleteObject() failed, err:" + err.Error())
}
_ = svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(filename),
})
return nil
}
func (*CloudflareR2) newSession() *session.Session {
endpoint := fmt.Sprintf("%s.r2.cloudflarestorage.com", global.GVA_CONFIG.CloudflareR2.AccountID)
return session.Must(session.NewSession(&aws.Config{
Region: aws.String("auto"),
Endpoint: aws.String(endpoint),
Credentials: credentials.NewStaticCredentials(
global.GVA_CONFIG.CloudflareR2.AccessKeyID,
global.GVA_CONFIG.CloudflareR2.SecretAccessKey,
"",
),
}))
}
+28 -5
View File
@@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
@@ -14,6 +15,8 @@ import (
"go.uber.org/zap"
)
var mu sync.Mutex
type Local struct{}
//@author: [piexlmax](https://github.com/piexlmax)
@@ -76,11 +79,31 @@ func (*Local) UploadFile(file *multipart.FileHeader) (string, string, error) {
//@return: error
func (*Local) DeleteFile(key string) error {
p := global.GVA_CONFIG.Local.StorePath + "/" + key
if strings.Contains(p, global.GVA_CONFIG.Local.StorePath) {
if err := os.Remove(p); err != nil {
return errors.New("本地文件删除失败, err:" + err.Error())
}
// 检查 key 是否为空
if key == "" {
return errors.New("key不能为空")
}
// 验证 key 是否包含非法字符或尝试访问存储路径之外的文件
if strings.Contains(key, "..") || strings.ContainsAny(key, `\/:*?"<>|`) {
return errors.New("非法的key")
}
p := filepath.Join(global.GVA_CONFIG.Local.StorePath, key)
// 检查文件是否存在
if _, err := os.Stat(p); os.IsNotExist(err) {
return errors.New("文件不存在")
}
// 使用文件锁防止并发删除
mu.Lock()
defer mu.Unlock()
err := os.Remove(p)
if err != nil {
return errors.New("文件删除失败: " + err.Error())
}
return nil
}
+2
View File
@@ -31,6 +31,8 @@ func NewOss() OSS {
return HuaWeiObs
case "aws-s3":
return &AwsS3{}
case "cloudflare-r2":
return &CloudflareR2{}
default:
return &Local{}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gin-vue-admin",
"version": "2.7.0",
"version": "2.7.2",
"private": true,
"scripts": {
"serve": "node openDocument.js && vite --host --mode development",
+1 -2
View File
@@ -12,8 +12,7 @@ export const createTemp = (data) => {
return service({
url: '/autoCode/createTemp',
method: 'post',
data,
responseType: 'blob'
data
})
}
+1 -1
View File
@@ -20,7 +20,7 @@ export const viteLogo = (env) => {
)
console.log(
chalk.green(
`> 当前版本:v2.7.0`
`> 当前版本:v2.7.2`
)
)
console.log(
+1 -1
View File
@@ -10,7 +10,7 @@ export default {
register(app)
console.log(`
欢迎使用 Gin-Vue-Admin
当前版本:v2.7.0
当前版本:v2.7.2
加群方式:微信:shouzi_1994 QQ群:622360840
项目地址:https://github.com/flipped-aurora/gin-vue-admin
插件市场:https://plugin.gin-vue-admin.com
+4
View File
@@ -124,6 +124,10 @@
justify-content: center;
}
.el-menu--horizontal .el-menu .el-sub-menu__title{
justify-content: flex-start;
}
html.dark {
/* 自定义深色背景颜色 */
--el-bg-color: rgb(30 ,41 ,59);
+1 -1
View File
@@ -47,7 +47,7 @@
.el-dropdown{
outline: none;
div{
*{
outline: none;
}
}
+1 -1
View File
@@ -121,5 +121,5 @@ export const setBodyPrimaryColor = ( primaryColor, darkMode ) =>{
const baseUrl = ref(import.meta.env.VITE_BASE_API)
export const getBaseUrl = () => {
return baseUrl.value === "/" ? "" : baseUrl.value + "/"
return baseUrl.value === "/" ? "" : baseUrl.value
}
@@ -61,7 +61,7 @@ const sideHeight = computed(() => {
<style lang="scss">
.gva-sub-menu{
.el-sub-menu__title{
height: v-bind('sideHeight');
height: v-bind('sideHeight') !important;
}
}
</style>
@@ -1,10 +1,10 @@
<template>
<div class="h-full">
<div v-if="mode==='head'" class="bg-white text-slate-700 dark:text-slate-300 mx-2 dark:bg-slate-900 flex items-center w-full overflow-auto">
<div v-if="mode==='head'" class="bg-white h-[calc(100%-4px)] text-slate-700 dark:text-slate-300 mx-2 dark:bg-slate-900 flex items-center w-[calc(100vw-600px)] overflow-auto">
<el-menu
:default-active="routerStore.topActive"
mode="horizontal"
class="border-r-0 border-b-0 w-[calc(100%-100px)] flex gap-1 items-center box-border h-[calc(100%-1px)]"
class="border-r-0 border-b-0 w-full flex gap-1 items-center box-border h-[calc(100%-1px)]"
unique-opened
@select="(index, _, ele)=>selectMenuItem(index, _, ele,true)"
>
@@ -85,7 +85,6 @@ const router = useRouter();
const routerStore = useRouterStore();
const isCollapse = ref(false);
const active = ref("");
const topActive = ref("");
const layoutSideWidth = computed(() => {
if (!isCollapse.value) {
return config.value.layout_side_width;
+2 -2
View File
@@ -1,9 +1,9 @@
<template>
<div class="bg-white h-[calc(100%-4px)] text-slate-700 dark:text-slate-300 mx-2 dark:bg-slate-900 flex items-center w-full overflow-auto">
<div class="bg-white h-[calc(100%-4px)] text-slate-700 dark:text-slate-300 mx-2 dark:bg-slate-900 flex items-center w-[calc(100vw-600px)] overflow-auto">
<el-menu
:default-active="active"
mode="horizontal"
class="border-r-0 w-[calc(100%-100px)] flex gap-1 items-center box-border h-[calc(100%-1px)]"
class="border-r-0 w-full flex gap-1 items-center box-border h-[calc(100%-1px)]"
unique-opened
@select="selectMenuItem"
>
+1 -1
View File
@@ -8,7 +8,7 @@
<div
class="flex items-center cursor-pointer flex-1"
>
<div class="flex items-center cursor-pointer" @click="router.push({ path: '/' })">
<div class="flex items-center cursor-pointer min-w-48" @click="router.push({ path: '/' })">
<img
alt
class="h-12 bg-white rounded-full"
+78
View File
@@ -5,6 +5,25 @@
<template>
<div class="flex items-center mx-4 gap-4">
<el-tooltip
class=""
effect="dark"
content="视频教程"
placement="bottom"
>
<el-dropdown @command="toDoc">
<el-icon class="w-8 h-8 shadow rounded-full border border-gray-200 dark:border-gray-600 cursor-pointer border-solid">
<Film />
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in videoList" :key="item.link" :command="item.link">{{ item.title }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-tooltip>
<el-tooltip
class=""
effect="dark"
@@ -68,6 +87,7 @@ import GvaSetting from "@/view/layout/setting/index.vue"
import { ref } from "vue"
import { emitter } from "@/utils/bus.js";
import CommandMenu from "@/components/commandMenu/index.vue";
import {toDoc} from "@/utils/doc";
const appStore = useAppStore()
const showSettingDrawer = ref(false)
@@ -112,6 +132,64 @@ const initPage = () => {
initPage();
const videoList = [
{
title:"1.clone项目和安装依赖",
link:"https://www.bilibili.com/video/BV1jx4y1s7xx",
},
{
title:"2.初始化项目",
link:"https://www.bilibili.com/video/BV1sr421K7sv",
},
{
title:"3.开启调试工具+创建初始化包",
link:"https://www.bilibili.com/video/BV1iH4y1c7Na",
},
{
title:"4.手动使用自动化创建功能",
link:"https://www.bilibili.com/video/BV1UZ421T7fV",
},
{
title:"5.使用已有表格创建业务",
link:"https://www.bilibili.com/video/BV1NE4m1977s",
},
{
title:"6.使用AI创建业务和创建数据源模式的可选项",
link:"https://www.bilibili.com/video/BV17i421a7DE",
},
{
title:"7.创建自己的后端方法",
link:"https://www.bilibili.com/video/BV1Yw4m1k7fg",
},
{
title:"8.新增一个前端页面",
link:"https://www.bilibili.com/video/BV12y411i7oE",
},
{
title:"9.配置一个前端二级页面",
link:"https://www.bilibili.com/video/BV1ZM4m1y7i3",
},
{
title:"10.配置一个前端菜单参数",
link:"https://www.bilibili.com/video/BV1WS42197DZ",
},
{
title:"11.菜单参数实战+动态菜单标题+菜单高亮配置",
link:"https://www.bilibili.com/video/BV1NE4m1979c",
},
{
title:"12.增加菜单可控按钮",
link:"https://www.bilibili.com/video/BV1Sw4m1k746",
},
{
title:"14.新增客户角色和其相关配置教学",
link:"https://www.bilibili.com/video/BV1Ki421a7X2",
},
{
title:"15.发布项目上线",
link:"https://www.bilibili.com/video/BV1Lx4y1s77D",
}
]
</script>
+16
View File
@@ -12,6 +12,7 @@
@contextmenu.prevent="openContextMenu($event)"
@tab-click="changeTab"
@tab-remove="removeTab"
@click.middle.prevent="middleCloseTab($event)"
>
<el-tab-pane
v-for="item in historys"
@@ -343,6 +344,21 @@ onUnmounted(() => {
emitter.off('collapse')
emitter.off('mobile')
})
const middleCloseTab = (e) => {
if (historys.value.length === 1 && route.name === defaultRouter.value) {
return false
}
let id = ''
if (e.srcElement.nodeName === 'SPAN') {
id = e.srcElement.offsetParent.id
} else {
id = e.srcElement.id
}
if (id) {
removeTab(id.substring(4))
}
}
</script>
<style lang="scss" scoped>
+1 -1
View File
@@ -108,7 +108,7 @@
<BottomInfo class="left-0 right-0 absolute bottom-3 mx-auto w-full z-20">
<div class="links items-center justify-center gap-2 hidden md:flex">
<a
href="http://doc.henrongyi.top/"
href="https://www.gin-vue-admin.com/"
target="_blank"
>
<img
-8
View File
@@ -72,12 +72,6 @@
>
新增
</el-button>
<el-icon
class="cursor-pointer"
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT?p=7&vd_source=f2640257c21e3b547a790461ed94875e')"
>
<VideoCameraFilled />
</el-icon>
<el-button
icon="delete"
:disabled="!apis.length"
@@ -483,8 +477,6 @@ import { toSQLLine } from '@/utils/stringFun'
import WarningBar from '@/components/warningBar/warningBar.vue'
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { VideoCameraFilled } from '@element-plus/icons-vue'
import { toDoc } from '@/utils/doc'
import ExportExcel from '@/components/exportExcel/exportExcel.vue'
import ExportTemplate from '@/components/exportExcel/exportTemplate.vue'
import ImportExcel from '@/components/exportExcel/importExcel.vue'
@@ -8,10 +8,6 @@
icon="plus"
@click="addAuthority(0)"
>新增角色</el-button>
<el-icon
class="cursor-pointer"
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT?p=8&vd_source=f2640257c21e3b547a790461ed94875e')"
><VideoCameraFilled /></el-icon>
</div>
<el-table
:data="tableData"
@@ -189,8 +185,7 @@ import WarningBar from '@/components/warningBar/warningBar.vue'
import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { toDoc } from '@/utils/doc'
import { VideoCameraFilled } from '@element-plus/icons-vue'
defineOptions({
name: 'Authority'
@@ -29,13 +29,11 @@
<template #default="{ node , data }">
<span class="custom-tree-node">
<span>{{ node.label }}</span>
<span>
<span v-if="node.checked">
<el-button
type="primary"
link
:style="{color:row.defaultRouter === data.name?'#E6A23C':'#85ce61'}"
:disabled="!node.checked"
@click="() => setDefault(data)"
>
{{ row.defaultRouter === data.name?"首页":"设为首页" }}
@@ -45,7 +43,6 @@
<el-button
type="primary"
link
@click="() => OpenBtn(data)"
>
分配按钮
@@ -124,6 +121,9 @@ const menuDefaultProps = ref({
children: 'children',
label: function(data) {
return data.meta.title
},
disabled: function(data) {
return props.row.defaultRouter === data.name
}
})
@@ -141,6 +141,7 @@ const init = async() => {
}
})
menuTreeIds.value = arr
}
init()
+1 -31
View File
@@ -9,16 +9,6 @@
>
新增根菜单
</el-button>
<el-icon
class="cursor-pointer"
@click="
toDoc(
'https://www.bilibili.com/video/BV1kv4y1g7nT/?p=4&vd_source=f2640257c21e3b547a790461ed94875e'
)
"
>
<VideoCameraFilled />
</el-icon>
</div>
<!-- 由于此处菜单跟左侧列表一一对应所以不需要分页 pageSize默认999 -->
@@ -423,16 +413,6 @@
>
新增菜单参数
</el-button>
<el-icon
class="cursor-pointer"
@click="
toDoc(
'https://www.bilibili.com/video/BV1kv4y1g7nT?p=9&vd_source=f2640257c21e3b547a790461ed94875e'
)
"
>
<VideoCameraFilled />
</el-icon>
</div>
<el-table
:data="form.parameters"
@@ -516,16 +496,6 @@
>
<QuestionFilled />
</el-icon>
<el-icon
class="cursor-pointer"
@click="
toDoc(
'https://www.bilibili.com/video/BV1kv4y1g7nT?p=11&vd_source=f2640257c21e3b547a790461ed94875e'
)
"
>
<VideoCameraFilled />
</el-icon>
</div>
<el-table
@@ -588,7 +558,7 @@ import WarningBar from '@/components/warningBar/warningBar.vue'
import { canRemoveAuthorityBtnApi } from '@/api/authorityBtn'
import { reactive, ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { QuestionFilled, VideoCameraFilled } from '@element-plus/icons-vue'
import { QuestionFilled } from '@element-plus/icons-vue'
import pathInfo from '@/pathInfo.json'
import { toDoc } from '@/utils/doc'
+1 -1
View File
@@ -368,7 +368,7 @@ const deleteUserFunc = async(row) => {
// 弹窗相关
const userInfo = ref({
username: '',
userName: '',
password: '',
nickName: '',
headerImg: '',
@@ -2,7 +2,7 @@
<div>
<warning-bar title="id , created_at , updated_at , deleted_at 会自动生成请勿重复创建。搜索时如果条件为LIKE只支持字符串" />
<el-form
ref="fieldDialogFrom"
ref="fieldDialogForm"
:model="middleDate"
label-width="120px"
label-position="right"
@@ -158,8 +158,14 @@
/>
</el-select>
</el-form-item>
<el-form-item label="前端可见">
<el-switch v-model="middleDate.front" />
<el-form-item label="前端新建/编辑">
<el-switch v-model="middleDate.form" />
</el-form-item>
<el-form-item label="前端表格列">
<el-switch v-model="middleDate.table" />
</el-form-item>
<el-form-item label="前端详情">
<el-switch v-model="middleDate.desc" />
</el-form-item>
<el-form-item label="是否排序">
<el-switch v-model="middleDate.sort" />
@@ -432,6 +438,6 @@ const selectDB = async (val) => {
}
const fieldDialogFrom = ref(null)
defineExpose({ fieldDialogFrom })
const fieldDialogForm = ref(null)
defineExpose({ fieldDialogForm })
</script>
@@ -1,5 +1,5 @@
<template>
<el-tabs v-model="activeName">
<el-tabs v-model="activeName" tab-position="left" class="h-[calc(100vh-110px)]">
<el-tab-pane
v-for="(item, key) in previewCode"
:key="key"
@@ -8,7 +8,7 @@
>
<div
:id="key"
class="h-[calc(100vh-160px)] bg-white px-5 overflow-y-scroll"
class="h-[calc(100vh-110px)] bg-white px-5 overflow-y-scroll"
/>
</el-tab-pane>
</el-tabs>
+104 -11
View File
@@ -358,6 +358,20 @@
<el-checkbox v-model="form.autoMigrate" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item>
<template #label>
<el-tooltip
content="注:会自动产生页面内的按钮权限配置,若不在角色管理中进行按钮分配则按钮不可见"
placement="bottom"
effect="light"
>
<div> 创建按钮权限 <el-icon><QuestionFilled /></el-icon> </div>
</el-tooltip>
</template>
<el-checkbox v-model="form.autoCreateBtnAuth" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item>
<template #label>
@@ -467,11 +481,30 @@
</el-table-column>
<el-table-column
align="left"
prop="front"
label="前端可见"
prop="form"
width="100"
label="新建/编辑"
>
<template #default="{row}">
<el-checkbox v-model="row.front" />
<el-checkbox v-model="row.form" />
</template>
</el-table-column>
<el-table-column
align="left"
prop="table"
label="表格"
>
<template #default="{row}">
<el-checkbox v-model="row.table" />
</template>
</el-table-column>
<el-table-column
align="left"
prop="desc"
label="详情"
>
<template #default="{row}">
<el-checkbox v-model="row.desc" />
</template>
</el-table-column>
<el-table-column
@@ -615,6 +648,20 @@
</div>
<!-- 组件列表 -->
<div class="gva-btn-list justify-end mt-4">
<el-button
type="primary"
@click="exportJson()"
>
导出json
</el-button>
<el-upload
class="flex items-center"
:before-upload="importJson"
show-file-list="false"
accept=".json"
>
<el-button type="primary" class="mx-2">导入json</el-button>
</el-upload>
<el-button
type="primary"
@click="clearCatch()"
@@ -676,7 +723,7 @@
<el-drawer
v-model="previewFlag"
size="60%"
size="80%"
:show-close="false"
>
<template #header>
@@ -758,9 +805,11 @@ const llmAutoFunc = async (mode) =>{
fieldSearchType: '',
fieldIndexType: '',
dictType: '',
front: true,
form: true,
desc: true,
table: true,
dataSource: {
association:1,
association:1,
table: '',
label: '',
value: ''
@@ -912,7 +961,9 @@ const fieldTemplate = {
defaultValue: '',
require: false,
sort: false,
front: true,
form: true,
desc: true,
table: true,
errorText: '',
primaryKey: false,
clearable: true,
@@ -947,6 +998,7 @@ const form = ref({
businessDB: '',
autoCreateApiToSql: true,
autoCreateMenuToSql: true,
autoCreateBtnAuth: false,
autoMigrate: true,
gvaModel: true,
autoCreateResource: false,
@@ -1031,7 +1083,7 @@ const editAndAddField = (item) => {
const fieldDialogNode = ref(null)
const enterDialog = () => {
fieldDialogNode.value.fieldDialogFrom.validate(valid => {
fieldDialogNode.value.fieldDialogForm.validate(valid => {
if (valid) {
dialogMiddle.value.fieldName = toUpperCase(
dialogMiddle.value.fieldName
@@ -1096,6 +1148,15 @@ const enterForm = async(isPreview) => {
return false
}
if (form.value.fields.some(item => !item.fieldType)) {
ElMessage({
type: 'error',
message: '请填写所有字段类型后进行提交'
})
return false
}
autoCodeForm.value.validate(async valid => {
if (valid) {
for (const key in form.value) {
@@ -1116,13 +1177,14 @@ const enterForm = async(isPreview) => {
return false
}
form.value.humpPackageName = toSQLLine(form.value.packageName)
delete form.value.primaryField
if (isPreview) {
const data = await preview(form.value)
preViewCode.value = data.data.autoCode
previewFlag.value = true
} else {
const data = await createTemp(form.value)
if (data.headers?.success === 'false') {
const res = await createTemp(form.value)
if (res.code !== 0) {
return
}
ElMessage({
@@ -1194,7 +1256,9 @@ const getColumnFunc = async() => {
fieldSearchType: '',
fieldIndexType: '',
dictType: '',
front: true,
form: true,
table: true,
desc: true,
dataSource: {
association:1,
table: '',
@@ -1286,6 +1350,7 @@ const clearCatch = async () => {
businessDB: '',
autoCreateApiToSql: true,
autoCreateMenuToSql: true,
autoCreateBtnAuth: false,
autoMigrate: true,
gvaModel: true,
autoCreateResource: false,
@@ -1297,4 +1362,32 @@ const clearCatch = async () => {
getCatch()
const exportJson = () => {
const dataStr = JSON.stringify(form.value, null, 2)
const blob = new Blob([dataStr], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'form_data.json'
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
const importJson = (file) =>{
const reader = new FileReader()
reader.onload = (e) => {
console.log(e)
try {
form.value = JSON.parse(e.target.result)
ElMessage.success('JSON 文件导入成功')
} catch (error) {
ElMessage.error('无效的 JSON 文件')
}
}
reader.readAsText(file)
return false
}
</script>
@@ -248,6 +248,9 @@ const addFuncBtn = (row) => {
autoFunc.value.abbreviation = req.abbreviation
autoFunc.value.humpPackageName = req.humpPackageName
autoFunc.value.businessDB = req.businessDB
autoFunc.value.method = ""
autoFunc.value.funcName = ""
autoFunc.value.router = ""
funcFlag.value = true;
};
+1 -8
View File
@@ -13,12 +13,7 @@
>
新增
</el-button>
<el-icon
class="cursor-pointer"
@click="toDoc('https://www.bilibili.com/video/BV1kv4y1g7nT?p=3&vd_source=f2640257c21e3b547a790461ed94875e')"
>
<VideoCameraFilled />
</el-icon>
</div>
<el-table :data="tableData">
<el-table-column
@@ -153,8 +148,6 @@ import {
import { ref } from 'vue'
import WarningBar from '@/components/warningBar/warningBar.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { toDoc } from '@/utils/doc'
import { VideoCameraFilled } from '@element-plus/icons-vue'
defineOptions({
name: 'AutoPkg',
@@ -122,7 +122,7 @@
width="120"
>
<template #default="scope">
<span>{{ scope.row.dbNname || "GVA库" }}</span>
<span>{{ scope.row.dbName || "GVA库" }}</span>
</template>
</el-table-column>
<el-table-column
@@ -227,16 +227,17 @@
</el-tooltip>
</template>
<el-select
v-model="formData.dbName"
clearable
placeholder="选择业务库"
v-model="formData.dbName"
clearable
@change="dbNameChange"
placeholder="选择业务库"
>
<el-option
v-for="item in dbList"
:key="item.aliasName"
:value="item.aliasName"
:label="item.aliasName"
:disabled="item.disable"
v-for="item in dbList"
:key="item.aliasName"
:value="item.aliasName"
:label="item.aliasName"
:disabled="item.disable"
>
<div>
<span>{{ item.aliasName }}</span>
@@ -258,13 +259,34 @@
</el-form-item>
<el-form-item
label="表名称:"
clearable
prop="tableName"
>
<el-input
<!-- <el-input
v-model="formData.tableName"
:clearable="true"
placeholder="请输入要导出的表名称"
/>
/>-->
<div
class="w-full flex gap-4"
>
<el-select
v-model="formData.tableName"
class="flex-1"
filterable
placeholder="请选择表"
>
<el-option
v-for="item in tableOptions"
:key="item.tableName"
:label="item.tableName"
:value="item.tableName"
/>
</el-select>
<el-button type="primary" @click="getColumnFunc">自动生成模板</el-button>
</div>
</el-form-item>
<el-form-item
label="模板标识:"
@@ -416,7 +438,7 @@ import { formatDate } from '@/utils/format'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ref, reactive } from 'vue'
import WarningBar from '@/components/warningBar/warningBar.vue'
import { getDB } from '@/api/autoCode'
import {getDB, getTable, getColumn} from '@/api/autoCode'
defineOptions({
name: 'ExportTemplate'
@@ -438,6 +460,7 @@ JOINS模式下不支持导入
const formData = ref({
name: '',
tableName: '',
dbName: '',
templateID: '',
templateInfo: '',
limit: 0,
@@ -566,7 +589,9 @@ const total = ref(0)
const pageSize = ref(10)
const tableData = ref([])
const searchInfo = ref({})
const dbList = ref([])
const tableOptions = ref([])
const getDbFunc = async() => {
const res = await getDB()
@@ -577,6 +602,45 @@ const getDbFunc = async() => {
getDbFunc()
const dbNameChange = () => {
formData.value.tableName = ''
formData.value.templateInfo = ''
getTableFunc()
}
const getTableFunc = async() => {
const res = await getTable({ businessDB: formData.value.dbName })
if (res.code === 0) {
tableOptions.value = res.data.tables
}
formData.value.tableName = ''
}
getTableFunc()
const getColumnFunc = async () => {
if(!formData.value.tableName) {
ElMessage({
type: 'error',
message: '请先选择业务库及选择表后再进行操作'
})
return
}
formData.value.templateInfo = ""
const res = await getColumn({
businessDB: formData.value.dbName,
tableName: formData.value.tableName
})
if(res.code === 0) {
// 把返回值的data.columns做尊换,制作一组JSON数据,columnName做key,columnComment做value
const templateInfo = {}
res.data.columns.forEach(item => {
templateInfo[item.columnName] = item.columnComment || item.columnName
})
formData.value.templateInfo = JSON.stringify(templateInfo, null, 2)
}
}
// 重置
const onReset = () => {
searchInfo.value = {}
+73 -50
View File
@@ -1,23 +1,23 @@
<template>
<div class="system">
<el-form
ref="form"
:model="config"
label-width="240px"
ref="form"
:model="config"
label-width="240px"
>
<!-- System start -->
<el-collapse v-model="activeNames">
<el-collapse-item
title="系统配置"
name="1"
title="系统配置"
name="1"
>
<el-form-item label="端口值">
<el-input v-model.number="config.system.addr" />
</el-form-item>
<el-form-item label="数据库类型">
<el-select
v-model="config.system['db-type']"
style="width:100%"
v-model="config.system['db-type']"
style="width:100%"
>
<el-option value="mysql" />
<el-option value="pgsql" />
@@ -25,14 +25,15 @@
</el-form-item>
<el-form-item label="Oss类型">
<el-select
v-model="config.system['oss-type']"
style="width:100%"
v-model="config.system['oss-type']"
style="width:100%"
>
<el-option value="local" />
<el-option value="qiniu" />
<el-option value="tencent-cos" />
<el-option value="aliyun-oss" />
<el-option value="huawei-obs" />
<el-option value="cloudflare-r2" />
</el-select>
</el-form-item>
<el-form-item label="多点登录拦截">
@@ -48,8 +49,8 @@
<el-input-number v-model.number="config.system['iplimit-time']" />
</el-form-item>
<el-tooltip
content="请修改完成后,注意一并修改前端env环境下的VITE_BASE_PATH"
placement="top-start"
content="请修改完成后,注意一并修改前端env环境下的VITE_BASE_PATH"
placement="top-start"
>
<el-form-item label="全局路由前缀">
<el-input v-model="config.system['router-prefix']" />
@@ -57,8 +58,8 @@
</el-tooltip>
</el-collapse-item>
<el-collapse-item
title="jwt签名"
name="2"
title="jwt签名"
name="2"
>
<el-form-item label="jwt签名">
<el-input v-model="config.jwt['signing-key']" />
@@ -74,8 +75,8 @@
</el-form-item>
</el-collapse-item>
<el-collapse-item
title="Zap日志配置"
name="3"
title="Zap日志配置"
name="3"
>
<el-form-item label="级别">
<el-input v-model.number="config.zap.level" />
@@ -106,8 +107,8 @@
</el-form-item>
</el-collapse-item>
<el-collapse-item
title="Redis admin数据库配置"
name="4"
title="Redis admin数据库配置"
name="4"
>
<el-form-item label="库">
<el-input v-model.number="config.redis.db" />
@@ -121,8 +122,8 @@
</el-collapse-item>
<el-collapse-item
title="Mongo 数据库配置"
name="14"
title="Mongo 数据库配置"
name="14"
>
<el-form-item label="collection name(表名,一般不写)">
<el-input v-model="config.mongo.coll" />
@@ -156,29 +157,29 @@
</el-form-item>
<el-form-item label="hosts">
<template v-for="(item,k) in config.mongo.hosts">
<div
v-for="(_,k2) in item"
:key="k2"
>
<el-form-item
:key="k+k2"
:label="k2"
<div
v-for="(_,k2) in item"
:key="k2"
>
<el-input v-model="item[k2]" />
</el-form-item>
</div>
</template>
<el-form-item
:key="k+k2"
:label="k2"
>
<el-input v-model="item[k2]" />
</el-form-item>
</div>
</template>
</el-form-item>
</el-collapse-item>
<el-collapse-item
title="邮箱配置"
name="5"
title="邮箱配置"
name="5"
>
<el-form-item label="接收者邮箱">
<el-input
v-model="config.email.to"
placeholder="可多个,以逗号分隔"
v-model="config.email.to"
placeholder="可多个,以逗号分隔"
/>
</el-form-item>
<el-form-item label="端口">
@@ -201,8 +202,8 @@
</el-form-item>
</el-collapse-item>
<el-collapse-item
title="验证码配置"
name="7"
title="验证码配置"
name="7"
>
<el-form-item label="字符长度">
<el-input v-model.number="config.captcha['key-long']" />
@@ -215,8 +216,8 @@
</el-form-item>
</el-collapse-item>
<el-collapse-item
title="数据库配置"
name="9"
title="数据库配置"
name="9"
>
<template v-if="config.system['db-type'] === 'mysql'">
<el-form-item label="用户名">
@@ -291,8 +292,8 @@
</el-collapse-item>
<el-collapse-item
title="oss配置"
name="10"
title="oss配置"
name="10"
>
<template v-if="config.system['oss-type'] === 'local'">
<h2>本地文件配置</h2>
@@ -384,12 +385,33 @@
<el-input v-model="config['hua-wei-obs']['secret-key']" />
</el-form-item>
</template>
<template v-if="config.system['oss-type'] === 'cloudflare-r2'">
<h2>Cloudflare R2上传配置</h2>
<el-form-item label="路径">
<el-input v-model="config['cloudflare-r2'].path" />
</el-form-item>
<el-form-item label="存储桶名称">
<el-input v-model="config['cloudflare-r2'].bucket" />
</el-form-item>
<el-form-item label="Base URL">
<el-input v-model="config['cloudflare-r2']['base-url']" />
</el-form-item>
<el-form-item label="Account ID">
<el-input v-model="config['cloudflare-r2']['account-id']" />
</el-form-item>
<el-form-item label="Access Key ID">
<el-input v-model="config['cloudflare-r2']['access-key-id']" />
</el-form-item>
<el-form-item label="Secret Access Key">
<el-input v-model="config['cloudflare-r2']['secret-access-key']" />
</el-form-item>
</template>
</el-collapse-item>
<el-collapse-item
title="Excel上传配置"
name="11"
title="Excel上传配置"
name="11"
>
<el-form-item label="合成目标地址">
<el-input v-model="config.excel.dir" />
@@ -397,16 +419,16 @@
</el-collapse-item>
<el-collapse-item
title="自动化代码配置"
name="12"
title="自动化代码配置"
name="12"
>
<el-form-item label="是否自动重启(linux)">
<el-checkbox v-model="config.autocode['transfer-restart']" />
</el-form-item>
<el-form-item label="root(项目根路径)">
<el-input
v-model="config.autocode.root"
disabled
v-model="config.autocode.root"
disabled
/>
</el-form-item>
<el-form-item label="Server(后端代码地址)">
@@ -447,12 +469,12 @@
</el-form>
<div class="mt-4">
<el-button
type="primary"
@click="update"
type="primary"
@click="update"
>立即更新</el-button>
<el-button
type="primary"
@click="reload"
type="primary"
@click="reload"
>重启服务(开发中)</el-button>
</div>
</div>
@@ -502,6 +524,7 @@ const config = ref({
'tencent-cos': {},
'aliyun-oss': {},
'hua-wei-obs': {},
'cloudflare-r2': {},
captcha: {},
zap: {},
local: {},
+2 -2
View File
@@ -52,8 +52,8 @@ export default ({
}
const config = {
base: '/', // index.html文件所在位置
root: './', // js导入的资源路径,src
base: '/', // 编译后js导入的资源路径
root: './', // index.html文件所在位置
publicDir: 'public', // 静态资源文件夹
resolve: {
alias,
+5 -1
View File
@@ -63,7 +63,11 @@ const vueFilePathPlugin = (outputFilePath) => {
return html.replace(
'</body>',
`<script>
const isWindowActive = () => !document.hidden;
window.addEventListener('beforeunload', function () {
if (!isWindowActive()) {
return;
}
fetch('/generate-path-name-map');
});
</script></body>`
@@ -97,4 +101,4 @@ const vueFilePathPlugin = (outputFilePath) => {
};
}
export default vueFilePathPlugin
export default vueFilePathPlugin
+3 -3
View File
@@ -12,9 +12,9 @@ export default function fullImportPlugin() {
if (sourcePath === targetPath) {
const name = 'ElementPlus'
// 引入 ElementPlus 和 样式
code = code.replace(`import { createApp } from 'vue'`, ($1) => $1 + `\nimport ${name} from 'element-plus'`)
code = code.replace(`import './style/element_visiable.scss'`, ($1) => $1 + `\nimport 'element-plus/theme-chalk/src/index.scss'`)
code = code.replace('.mount(', ($1) => `.use(${name})` + $1)
code = code.replace(/import\s*{\s*createApp\s*}\s*from\s*['"]vue['"]/, ($1) => $1 + `\nimport ${name} from 'element-plus'`)
code = code.replace(/import\s*['"]\.\/style\/element_visiable\.scss['"]/, ($1) => $1 + `\nimport 'element-plus/theme-chalk/src/index.scss'`)
code = code.replace(/\.mount\(/, ($1) => `.use(${name})` + $1)
return code
}
return code