Compare commits

..
51 Commits
Author SHA1 Message Date
pixelMax(奇淼 0f9e519b0e feature:调整侧边栏暗夜模式的hover错误 2024-05-10 15:17:04 +08:00
PiexlMax(奇淼 0d17a2351f Update ci.yaml 2024-05-10 14:34:21 +08:00
pixelMax(奇淼 1b388664cb feature:水印密集度调整 2024-05-10 13:16:06 +08:00
pixelMax(奇淼 1edb0c529f feature:调整bottomInfo组件位置 2024-05-10 13:05:36 +08:00
pixelMax(奇淼 f07a58bff4 feature:readme版权相关文案调整 2024-05-10 11:18:48 +08:00
pixelMax(奇淼 c054840aec feature:上线前细节数据调整 2024-05-10 11:15:38 +08:00
PiexlMax(奇淼 e259418398 Update README.md 2024-05-09 21:42:11 +08:00
PiexlMax(奇淼andbypanghu df1d68a724 新版前端UI,预发布2.6.4版本。 (#1735)
* fix: 修改 layout 中相关 emit 和 重复声明的 theme ,采用最新的vue api

* fix:修改 eslintrc 的配置文件,删除大多数无用配置

* fix: 修复 layout 切换手机样式模糊层的 bug

* feature: 重构 layout , 添加暗黑模式 , 删除侧边栏颜色

* fix:细节调整,activeColor摘除。

* feature:右侧滚动模式调整

* feature:无用代码剔除

* fix: 修复暗黑模式相关细节

* fix: custome config layout

* feature:样式细节调整

* fix: 增加前端样式配置文件

* feature:调整基础配置,增加表格边框

* feature:buttomInfo取消底色,a标签更改为活跃色

* feature:调整阴影颜色和配置同步

* feature:版本调整2.6.3==>2.6.4

---------

Co-authored-by: bypanghu <bypanghu@163.com>
2024-05-09 21:35:39 +08:00
Wallandwall-js 9d58390e11 fix(bug): [api.go.tpl]自动化代码swag路径冲突 (#1732)
Co-authored-by: wall-js <liujun@grectronics.cn>
2024-05-09 11:48:15 +08:00
pixelMax(奇淼 db4c7a8bda fixed:修复富文本无法编辑的bug 2024-05-09 10:12:20 +08:00
PiexlMax(奇淼 ab163fc0ef Update ci.yaml 2024-05-06 21:20:20 +08:00
pixelmaxQM 0265801830 fixed:表格模板删除查看详情功能。 2024-05-06 21:15:37 +08:00
pixelmaxQM 5fa7bbbc0f fixed:导出模板顺序一致性调整 2024-05-06 21:10:01 +08:00
PiexlMax(奇淼 e15351e1c5 Update README.md 2024-04-29 17:10:22 +08:00
pixelmaxQM 5eb0133b5c feature:升级后端依赖,前端代码调优 2024-04-28 22:05:50 +08:00
pixelmaxQM ff8344d671 feature:自动化代码增加数据源配置功能 2024-04-27 21:48:24 +08:00
pixelmaxQM d9a43ea1d6 feature:自动化代码增加数据源配置功能 2024-04-27 21:35:29 +08:00
pixelmaxQM 6c62d73afb feature:v2.6.3发布 2024-04-27 17:16:42 +08:00
pixelmaxQM d23d6c342a feature:自动化增加前端可控显隐。 2024-04-25 21:13:55 +08:00
pixelmaxQM 90549a09e1 feature:自动化增加前端可控显隐和自定义class。 2024-04-25 21:02:43 +08:00
pixelmaxQM 17d3639443 feature:优化api代码逻辑,调整pgsql下 more than once的bug。 2024-04-23 21:13:09 +08:00
pixelmaxQM e8a096b8fe feature:调整Update方法,不再使用save全量更新。 2024-04-23 20:56:30 +08:00
pixelmaxQM fea136e513 fixed:修改agent的数据库存储方式,防止日志过长存储失败。 2024-04-23 20:43:50 +08:00
pixelMax(奇淼 f3a4095462 增加丢失的vue-router 2024-04-23 14:45:56 +08:00
pixelmaxQM d7d25cf69b feature:调整抽屉的title属性,消除warning 2024-04-22 22:14:25 +08:00
pixelmaxQM 3b33fe83e6 feature:代码生成器可拖拽行,修复pgsql从库创建代码的bug,增加默认值属性。 2024-04-22 22:04:29 +08:00
pixelmaxQM 56f4d223ab fixed:适配pgsql编辑菜单触发 more than once的bug 2024-04-22 20:59:15 +08:00
pixelMax(奇淼 c07317a619 调整openDocument 2024-04-17 10:39:55 +08:00
Qing Liang 4798f7688b 点击跳转代码插件修复websotrm bug (#1715) 2024-04-17 10:35:32 +08:00
pixelmaxQM 24db0eb5aa fixed:切换角色功能适配新版gorm 2024-04-14 23:43:21 +08:00
Haoran Wu 1ece13c43d sys_export_template.go: 修复导入Excel时前几条为空数据的问题 (#1713) 2024-04-14 21:03:44 +08:00
pixelmaxQM 48268309b9 fixed:回滚时候自动清理掉router下空的作用域 2024-04-14 21:02:35 +08:00
4074a52b1b 修复pgsql和数据库导入bug,增加默认mod中的datatypes (#1712)
* 多文件: fix error specified more than once in PG

* Update sys_authority.go

* Update sys_authority.go

* sys_export_template.go: 导入Excel时使用批量插入提高性能

* 添加gorm.io/datatypes

---------

Co-authored-by: crazyrunsnail <fjyuan1991@163.com>
Co-authored-by: XiMo-210 <2831802697@qq.com>
2024-04-13 18:46:58 +08:00
pixelMax(奇淼 61dd524c26 fixed:修复根目录为O的错误 2024-04-12 14:54:28 +08:00
pixelMax(奇淼 61a678a318 调整无法创建根菜单的bug 2024-04-12 14:47:16 +08:00
pixelmaxQM 8ecec6a14f Merge branch 'fixed_menu'
# Conflicts:
#	server/service/system/sys_base_menu.go
#	web/src/view/superAdmin/menu/menu.vue
2024-04-11 21:27:44 +08:00
PiexlMax(奇淼andcrazyrunsnail e3f8b8a394 fixed:菜单删除功能细节调优 (#1705)
* 多文件: 菜单parentId由string改成uint

* sys_base_menu.go: fix table name "sys_base_menus" specified more than once (SQLSTATE 42712) (#1704)

---------

Co-authored-by: crazyrunsnail <fjyuan1991@163.com>
2024-04-11 21:17:49 +08:00
pixelmaxQM eebddbafcc feature:调整菜单删除逻辑 2024-04-11 21:15:35 +08:00
PiexlMax(奇淼 50cbce0f7a Merge pull request #1702 from crazyrunsnail/fix-model-idtype
多文件: 菜单parentId由string改成uint
2024-04-11 20:56:07 +08:00
fakedavid f9974a8971 sys_base_menu.go: fix table name "sys_base_menus" specified more than once (SQLSTATE 42712) (#1704) 2024-04-11 20:55:46 +08:00
crazyrunsnail baa48a1679 多文件: 菜单parentId由string改成uint 2024-04-11 16:38:09 +08:00
pixelmaxQM 032910f87a feature:插件自动化添加自动注册菜单和api的模板供给修改 2024-04-10 21:48:20 +08:00
pixelmaxQM 773ef95b70 feature:增加插件自动化的前端功能页面 后端api生成自动添加一个开放的接口示例 2024-04-10 21:24:46 +08:00
pixelmaxQM 18c2f84771 feature:表格导出功能增加join支持多表关联导出 2024-04-09 22:05:12 +08:00
pixelmaxQM ea721a71b5 feature:防止自动化代码中的路径穿越 2024-04-09 21:09:01 +08:00
pixelMax(奇淼 b1b7427c6e fixed:修复GHSA-gv3w-m57p-3wc4 2024-04-09 18:09:09 +08:00
pixelmaxQM 7bc7c30510 fixed:#1695 对资源属性进行检测 2024-04-07 21:35:40 +08:00
pixelmaxQM d1e055430d feature:前端自动化增加分包 2024-04-07 21:20:27 +08:00
pixelmaxQM 736534379a fixed:字典限制仅字符串模式可选择 2024-04-07 20:40:02 +08:00
pixelMax(奇淼 f6b71e157c fixed:升级postgres的驱动到1.5.7 2024-04-07 10:16:51 +08:00
ShuBo6 53e9f931f2 fix: 应该调用filepath.Ext的地方调用了path.Ext (#1696)
* fix: 应该调用filepath.Ext的地方调用了path.Ext

* fix: typo
2024-04-03 16:25:09 +08:00
128 changed files with 3939 additions and 3531 deletions
+3 -3
View File
@@ -48,7 +48,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go-version: [1.21]
go-version: [1.22]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
@@ -84,7 +84,7 @@ jobs:
strategy:
matrix:
node-version: [16.8.0]
go-version: [1.21]
go-version: [1.22]
steps:
- name: Check out branch
uses: actions/checkout@v2
@@ -195,7 +195,7 @@ jobs:
strategy:
matrix:
node-version: ['16.x']
go-version: ['1.21']
go-version: ['1.22']
steps:
- uses: actions/checkout@v2
- name: tag major and minor versions
+5 -3
View File
@@ -42,9 +42,11 @@
3.您完全可以通过我们的教程和文档完成一切操作,因此我们不再提供免费的技术服务,如需服务请进行[付费支持](https://www.gin-vue-admin.com/coffee/payment.html)
4.如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。您需保留如下版权声明信息,其余信息功能不做任何限制。如需剔除请[购买授权](https://www.gin-vue-admin.com/empower/index.html)
4.如果您将此项目用于商业用途,请遵守Apache2.0协议并保留作者技术支持声明。您需保留如下版权声明信息,以及日志和代码中所包含的版权声明信息。所需保留信息均为文案性质,不会影响任何业务内容,如决定商用且必须剔除请[购买授权](https://www.gin-vue-admin.com/empower/index.html)
<img src="https://qmplusimg.henrongyi.top/%E6%8E%88%E6%9D%83.png" width="1000">
<img src="https://qmplusimg.henrongyi.top/openSource/login.jpg" width="1000">
<img src="https://qmplusimg.henrongyi.top/openSource/dashboard.jpg" width="1000">
## 1. 基本介绍
@@ -83,7 +85,7 @@ Gin-vue-admin 的成长离不开大家的支持,如果你愿意为 gin-vue-adm
```
- node版本 > v16.8.3
- golang版本 >= v1.16
- golang版本 >= v1.22
- IDE推荐:Goland
```
+11
View File
@@ -188,6 +188,12 @@ func (autoApi *AutoCodeApi) CreatePackage(c *gin.Context) {
response.FailWithMessage(err.Error(), c)
return
}
// PackageName可能导致路径穿越的问题 / 和 \ 都要防止
if strings.Contains(a.PackageName, "\\") || strings.Contains(a.PackageName, "/") || strings.Contains(a.PackageName, "..") {
response.FailWithMessage("包名不合法", c)
return
}
err := autoCodeService.CreateAutoCode(&a)
if err != nil {
@@ -253,6 +259,11 @@ func (autoApi *AutoCodeApi) AutoPlug(c *gin.Context) {
response.FailWithMessage(err.Error(), c)
return
}
if strings.Contains(a.PlugName, "\\") || strings.Contains(a.PlugName, "/") || strings.Contains(a.PlugName, "..") {
response.FailWithMessage("插件名称不合法", c)
return
}
a.Snake = strings.ToLower(a.PlugName)
a.NeedModel = a.HasRequest || a.HasResponse
err = autoCodeService.CreatePlug(a)
+1 -1
View File
@@ -44,7 +44,7 @@ func (b *BaseApi) Captcha(c *gin.Context) {
driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80)
// cp := base64Captcha.NewCaptcha(driver, store.UseWithCtx(c)) // v8下使用redis
cp := base64Captcha.NewCaptcha(driver, store)
id, b64s, err := cp.Generate()
id, b64s, _, err := cp.Generate()
if err != nil {
global.GVA_LOG.Error("验证码获取失败!", zap.Error(err))
response.FailWithMessage("验证码获取失败", c)
-1
View File
@@ -6,7 +6,6 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
systemRes "github.com/flipped-aurora/gin-vue-admin/server/model/system/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
+6 -2
View File
@@ -38,13 +38,17 @@ func RunWindowsServer() {
fmt.Printf(`
欢迎使用 gin-vue-admin
当前版本:v2.6.2
当前版本:v2.6.4
加群方式:微信号:shouzi_1994 QQ群:470239250
项目地址:https://github.com/flipped-aurora/gin-vue-admin
插件市场:https://plugin.gin-vue-admin.com
GVA讨论社区:https://support.qq.com/products/371961
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
默认前端文件运行地址:http://127.0.0.1:8080
如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/coffee/index.html
--------------------------------------版权声明--------------------------------------
** 版权所有方:flipped-aurora开源团队 **
** 版权持有公司:北京翻转极光科技有限责任公司 **
** 剔除授权标识需购买商用授权:https://gin-vue-admin.com/empower/index.html **
`, address)
global.GVA_LOG.Error(s.ListenAndServe().Error())
}
+1 -5
View File
@@ -6918,10 +6918,6 @@ const docTemplate = `{
"description": "主键ID",
"type": "integer"
},
"activeColor": {
"description": "活跃颜色",
"type": "string"
},
"authorities": {
"type": "array",
"items": {
@@ -7001,7 +6997,7 @@ const docTemplate = `{
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "v2.6.2",
Version: "v2.6.4",
Host: "",
BasePath: "",
Schemes: []string{},
+1 -5
View File
@@ -4,7 +4,7 @@
"description": "使用gin+vue进行极速开发的全栈开发基础平台",
"title": "Gin-Vue-Admin Swagger API接口文档",
"contact": {},
"version": "v2.6.2"
"version": "v2.6.4"
},
"paths": {
"/api/createApi": {
@@ -6910,10 +6910,6 @@
"description": "主键ID",
"type": "integer"
},
"activeColor": {
"description": "活跃颜色",
"type": "string"
},
"authorities": {
"type": "array",
"items": {
+1 -4
View File
@@ -1530,9 +1530,6 @@ definitions:
ID:
description: 主键ID
type: integer
activeColor:
description: 活跃颜色
type: string
authorities:
items:
$ref: '#/definitions/system.SysAuthority'
@@ -1585,7 +1582,7 @@ info:
contact: {}
description: 使用gin+vue进行极速开发的全栈开发基础平台
title: Gin-Vue-Admin Swagger API接口文档
version: v2.6.2
version: v2.6.4
paths:
/api/createApi:
post:
+14 -13
View File
@@ -1,11 +1,11 @@
module github.com/flipped-aurora/gin-vue-admin/server
go 1.20
go 1.22
require (
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible
github.com/aws/aws-sdk-go v1.44.307
github.com/casbin/casbin/v2 v2.71.1
github.com/casbin/casbin/v2 v2.87.1
github.com/casbin/gorm-adapter/v3 v3.18.0
github.com/flipped-aurora/ws v1.0.2
github.com/fsnotify/fsnotify v1.6.0
@@ -18,7 +18,7 @@ require (
github.com/gookit/color v1.5.4
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.8+incompatible
github.com/jordan-wright/email v0.0.0-20200824153738-3f5bafa1cd84
github.com/mojocn/base64Captcha v1.3.5
github.com/mojocn/base64Captcha v1.3.6
github.com/otiai10/copy v1.7.0
github.com/pkg/errors v0.9.1
github.com/qiniu/api.v7/v7 v7.4.1
@@ -38,20 +38,20 @@ require (
go.mongodb.org/mongo-driver v1.12.1
go.uber.org/automaxprocs v1.5.3
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.16.0
golang.org/x/crypto v0.22.0
golang.org/x/sync v0.5.0
golang.org/x/text v0.14.0
gorm.io/driver/mysql v1.5.6
gorm.io/driver/postgres v1.5.2
gorm.io/driver/postgres v1.5.7
gorm.io/driver/sqlserver v1.5.1
gorm.io/gorm v1.25.7
gorm.io/gorm v1.25.9
nhooyr.io/websocket v1.8.7
)
require (
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/casbin/govaluate v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
@@ -78,8 +78,9 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.3.1 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -126,12 +127,12 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/image v0.11.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/image v0.15.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.16.1 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/plugin/dbresolver v1.4.1 // indirect
+45 -26
View File
@@ -42,8 +42,6 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aov
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
@@ -52,15 +50,21 @@ github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible/go.mod h1:T/Aws4fEfogEE9
github.com/aws/aws-sdk-go v1.44.307 h1:2R0/EPgpZcFSUwZhYImq/srjaOrOfLv5MNRzrFyAM38=
github.com/aws/aws-sdk-go v1.44.307/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/casbin/casbin/v2 v2.71.1 h1:LRHyqM0S1LzM/K59PmfUIN0ZJfLgcOjL4OhOQI/FNXU=
github.com/casbin/casbin/v2 v2.71.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/casbin/v2 v2.87.1 h1:7H+ENAfYt3HmZJVw++tJsxx/ko7WEHsfNzpOdYTkpYo=
github.com/casbin/casbin/v2 v2.87.1/go.mod h1:jX8uoN4veP85O/n2674r2qtfSXI6myvxW85f6TH50fw=
github.com/casbin/gorm-adapter/v3 v3.18.0 h1:0td7v030eK3H5ftXRHx1d5wVPbuYEJP2ObMSUHtA0Ek=
github.com/casbin/gorm-adapter/v3 v3.18.0/go.mod h1:ekufPNBgVIQvv9JffVGsg7KUv4DjnevTh6AQnBNkoK8=
github.com/casbin/govaluate v1.1.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/casbin/govaluate v1.1.1 h1:J1rFKIBhiC5xr0APd5HP6rDL+xt+BRoyq1pa4o2i/5c=
github.com/casbin/govaluate v1.1.1/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -94,6 +98,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/flipped-aurora/ws v1.0.2 h1:oEUz7sgrbPENvgli7Q4QpC0NIEbJucgR4yjcDMg/AjY=
github.com/flipped-aurora/ws v1.0.2/go.mod h1:RdyM2Fnvxx7f7A6WSmU1aAhDrQIAVW7LS/0LsAUE5mE=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc=
@@ -101,6 +106,7 @@ github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yH
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
@@ -125,6 +131,7 @@ github.com/go-openapi/swag v0.22.5 h1:fVS63IE3M0lsuWRzuom3RLwUMVI2peDH01s6M70ugy
github.com/go-openapi/swag v0.22.5/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
@@ -184,8 +191,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -199,7 +206,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -220,6 +226,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -246,10 +253,12 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
@@ -284,14 +293,17 @@ github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
@@ -315,8 +327,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw=
github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
@@ -341,6 +353,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/qiniu/api.v7/v7 v7.4.1 h1:BnNUBimLk6nrA/mIwsww9yJRupmViSsb1ndLMC7a9OY=
github.com/qiniu/api.v7/v7 v7.4.1/go.mod h1:VE5oC5rkE1xul0u1S2N0b2Uxq9/6hZzhyqjgK25XDcM=
@@ -360,6 +373,7 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/shirou/gopsutil/v3 v3.23.6 h1:5y46WPI9QBKBbK7EEccUPNXpJpNrvPuTD0O2zHEHT08=
github.com/shirou/gopsutil/v3 v3.23.6/go.mod h1:j7QX50DrXYggrpN30W0Mo+I4/8U2UUIQrnrhqUeWrAU=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
@@ -461,6 +475,7 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
@@ -484,8 +499,8 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -497,10 +512,11 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -525,6 +541,7 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -564,8 +581,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -640,8 +657,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -663,6 +680,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -815,12 +833,12 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -838,15 +856,16 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=
gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=
gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM=
gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
gorm.io/driver/sqlserver v1.5.1 h1:wpyW/pR26U94uaujltiFGXY7fd2Jw5hC9PB1ZF/Y5s4=
gorm.io/driver/sqlserver v1.5.1/go.mod h1:AYHzzte2msKTmYBYsSIq8ZUsznLJwBdkB2wpI+kt0nM=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/plugin/dbresolver v1.4.1 h1:Ug4LcoPhrvqq71UhxtF346f+skTYoCa/nEsdjvHwEzk=
gorm.io/plugin/dbresolver v1.4.1/go.mod h1:CTbCtMWhsjXSiJqiW2R8POvJ2cq18RVOl4WGyT5nhNc=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+2
View File
@@ -50,6 +50,7 @@ func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error
sysModel.SysAutoCode{},
sysModel.SysExportTemplate{},
sysModel.Condition{},
sysModel.JoinTemplate{},
adapter.CasbinRule{},
@@ -87,6 +88,7 @@ func (e *ensureTables) TableCreated(ctx context.Context) bool {
sysModel.SysAutoCode{},
sysModel.SysExportTemplate{},
sysModel.Condition{},
sysModel.JoinTemplate{},
adapter.CasbinRule{},
+1
View File
@@ -47,6 +47,7 @@ func RegisterTables() {
system.SysAutoCode{},
system.SysExportTemplate{},
system.Condition{},
system.JoinTemplate{},
example.ExaFile{},
example.ExaCustomer{},
+1 -1
View File
@@ -15,7 +15,7 @@ import (
//go:generate go mod download
// @title Gin-Vue-Admin Swagger API接口文档
// @version v2.6.2
// @version v2.6.4
// @description 使用gin+vue进行极速开发的全栈开发基础平台
// @securityDefinitions.apikey ApiKeyAuth
// @in header
+1 -1
View File
@@ -14,7 +14,7 @@ type AddMenuAuthorityInfo struct {
func DefaultMenu() []system.SysBaseMenu {
return []system.SysBaseMenu{{
GVA_MODEL: global.GVA_MODEL{ID: 1},
ParentId: "0",
ParentId: 0,
Path: "dashboard",
Name: "dashboard",
Component: "view/dashboard/index.vue",
+1 -1
View File
@@ -2,7 +2,7 @@ package system
type SysMenu struct {
SysBaseMenu
MenuId string `json:"menuId" gorm:"comment:菜单ID"`
MenuId uint `json:"menuId" gorm:"comment:菜单ID"`
AuthorityId uint `json:"-" gorm:"comment:角色ID"`
Children []SysMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters" gorm:"foreignKey:SysBaseMenuID;references:MenuId"`
+51 -38
View File
@@ -10,30 +10,39 @@ import (
// AutoCodeStruct 初始版本自动化代码工具
type AutoCodeStruct struct {
StructName string `json:"structName"` // Struct名称
TableName string `json:"tableName"` // 表名
PackageName string `json:"packageName"` // 文件名称
HumpPackageName string `json:"humpPackageName"` // go文件名称
Abbreviation string `json:"abbreviation"` // Struct简称
Description string `json:"description"` // Struct中文名称
AutoCreateApiToSql bool `json:"autoCreateApiToSql"` // 是否自动创建api
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql"` // 是否自动创建menu
AutoCreateResource bool `json:"autoCreateResource"` // 是否自动创建资源标识
AutoMoveFile bool `json:"autoMoveFile"` // 是否自动移动文件
BusinessDB string `json:"businessDB"` // 业务数据库
GvaModel bool `json:"gvaModel"` // 是否使用gva默认Model
Fields []*Field `json:"fields"`
PrimaryField *Field `json:"primaryField"`
HasTimer bool `json:"-"`
HasSearchTimer bool `json:"-"`
DictTypes []string `json:"-"`
Package string `json:"package"`
PackageT string `json:"-"`
NeedSort bool `json:"-"`
HasPic bool `json:"-"`
HasRichText bool `json:"-"`
HasFile bool `json:"-"`
NeedJSON bool `json:"-"`
StructName string `json:"structName"` // Struct名称
TableName string `json:"tableName"` // 表名
PackageName string `json:"packageName"` // 文件名称
HumpPackageName string `json:"humpPackageName"` // go文件名称
Abbreviation string `json:"abbreviation"` // Struct简称
Description string `json:"description"` // Struct中文名称
AutoCreateApiToSql bool `json:"autoCreateApiToSql"` // 是否自动创建api
AutoCreateMenuToSql bool `json:"autoCreateMenuToSql"` // 是否自动创建menu
AutoCreateResource bool `json:"autoCreateResource"` // 是否自动创建资源标识
AutoMoveFile bool `json:"autoMoveFile"` // 是否自动移动文件
BusinessDB string `json:"businessDB"` // 业务数据库
GvaModel bool `json:"gvaModel"` // 是否使用gva默认Model
Fields []*Field `json:"fields"`
PrimaryField *Field `json:"primaryField"`
HasTimer bool `json:"-"`
HasSearchTimer bool `json:"-"`
DictTypes []string `json:"-"`
Package string `json:"package"`
PackageT string `json:"-"`
NeedSort bool `json:"-"`
HasPic bool `json:"-"`
HasRichText bool `json:"-"`
HasFile bool `json:"-"`
NeedJSON bool `json:"-"`
FrontFields []*Field `json:"-"`
HasDataSource bool `json:"-"`
DataSourceMap map[string]*DataSource `json:"-"`
}
type DataSource struct {
Table string `json:"table"`
Label string `json:"label"`
Value string `json:"value"`
}
func (a *AutoCodeStruct) Pretreatment() {
@@ -58,20 +67,24 @@ func (a *AutoCodeStruct) SuffixTest() {
}
type Field 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"` // 搜索条件
DictType string `json:"dictType"` // 字典
Require bool `json:"require"` // 是否必填
ErrorText string `json:"errorText"` // 校验失败文字
Clearable bool `json:"clearable"` // 是否可清空
Sort bool `json:"sort"` // 是否增加排序
PrimaryKey bool `json:"primaryKey"` // 是否主键
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"` // 搜索条件
DictType string `json:"dictType"` // 字典
Front bool `json:"front"` // 是否前端可见
Require bool `json:"require"` // 是否必填
DefaultValue string `json:"defaultValue"` // 是否必填
ErrorText string `json:"errorText"` // 校验失败文字
Clearable bool `json:"clearable"` // 是否可清空
Sort bool `json:"sort"` // 是否增加排序
PrimaryKey bool `json:"primaryKey"` // 是否主键
DataSource *DataSource `json:"dataSource"` // 数据源
CheckDataSource bool `json:"checkDataSource"` // 是否检查数据源
}
var ErrAutoMove error = errors.New("创建代码成功并移动文件成功")
+17 -17
View File
@@ -6,34 +6,34 @@ import (
type SysBaseMenu struct {
global.GVA_MODEL
MenuLevel uint `json:"-"`
ParentId string `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID
Path string `json:"path" gorm:"comment:路由path"` // 路由path
Name string `json:"name" gorm:"comment:路由name"` // 路由name
Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏
Component string `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径
Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记
Meta `json:"meta" gorm:"embedded;comment:附加属性"` // 附加属性
SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"`
Children []SysBaseMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters"`
MenuBtn []SysBaseMenuBtn `json:"menuBtn"`
MenuLevel uint `json:"-"`
ParentId uint `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID
Path string `json:"path" gorm:"comment:路由path"` // 路由path
Name string `json:"name" gorm:"comment:路由name"` // 路由name
Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏
Component string `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径
Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记
Meta `json:"meta" gorm:"embedded;comment:附加属性"` // 附加属性
SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"`
Children []SysBaseMenu `json:"children" gorm:"-"`
Parameters []SysBaseMenuParameter `json:"parameters"`
MenuBtn []SysBaseMenuBtn `json:"menuBtn"`
}
type Meta struct {
ActiveName string `json:"activeName" gorm:"comment:高亮菜单"`
KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存
KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存
DefaultMenu bool `json:"defaultMenu" gorm:"comment:是否是基础路由(开发中)"` // 是否是基础路由(开发中)
Title string `json:"title" gorm:"comment:菜单名"` // 菜单名
Icon string `json:"icon" gorm:"comment:菜单图标"` // 菜单图标
CloseTab bool `json:"closeTab" gorm:"comment:自动关闭tab"` // 自动关闭tab
Title string `json:"title" gorm:"comment:菜单名"` // 菜单名
Icon string `json:"icon" gorm:"comment:菜单图标"` // 菜单图标
CloseTab bool `json:"closeTab" gorm:"comment:自动关闭tab"` // 自动关闭tab
}
type SysBaseMenuParameter struct {
global.GVA_MODEL
SysBaseMenuID uint
Type string `json:"type" gorm:"comment:地址栏携带参数为params还是query"` // 地址栏携带参数为params还是query
Key string `json:"key" gorm:"comment:地址栏携带参数的key"` // 地址栏携带参数的key
Key string `json:"key" gorm:"comment:地址栏携带参数的key"` // 地址栏携带参数的key
Value string `json:"value" gorm:"comment:地址栏携带参数的值"` // 地址栏携带参数的值
}
+21 -8
View File
@@ -8,14 +8,27 @@ import (
// 导出模板 结构体 SysExportTemplate
type SysExportTemplate struct {
global.GVA_MODEL
DBName string `json:"dbName" form:"dbName" gorm:"column:db_name;comment:数据库名称;"` //数据库名称
Name string `json:"name" form:"name" gorm:"column:name;comment:模板名称;"` //模板名称
TableName string `json:"tableName" form:"tableName" gorm:"column:table_name;comment:表名称;"` //表名称
TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识;"` //模板标识
TemplateInfo string `json:"templateInfo" form:"templateInfo" gorm:"column:template_info;type:text;"` //模板信息
Limit int `json:"limit" form:"limit" gorm:"column:limit;comment:导出限制"`
Order string `json:"order" form:"order" gorm:"column:order;comment:排序"`
Conditions []Condition `json:"conditions" form:"conditions" gorm:"foreignKey:TemplateID;references:TemplateID;comment:条件"`
DBName string `json:"dbName" form:"dbName" gorm:"column:db_name;comment:数据库名称;"` //数据库名称
Name string `json:"name" form:"name" gorm:"column:name;comment:模板名称;"` //模板名称
TableName string `json:"tableName" form:"tableName" gorm:"column:table_name;comment:表名称;"` //表名称
TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识;"` //模板标识
TemplateInfo string `json:"templateInfo" form:"templateInfo" gorm:"column:template_info;type:text;"` //模板信息
Limit int `json:"limit" form:"limit" gorm:"column:limit;comment:导出限制"`
Order string `json:"order" form:"order" gorm:"column:order;comment:排序"`
Conditions []Condition `json:"conditions" form:"conditions" gorm:"foreignKey:TemplateID;references:TemplateID;comment:条件"`
JoinTemplate []JoinTemplate `json:"joinTemplate" form:"joinTemplate" gorm:"foreignKey:TemplateID;references:TemplateID;comment:关联"`
}
type JoinTemplate struct {
global.GVA_MODEL
TemplateID string `json:"templateID" form:"templateID" gorm:"column:template_id;comment:模板标识"`
JOINS string `json:"joins" form:"joins" gorm:"column:joins;comment:关联"`
Table string `json:"table" form:"table" gorm:"column:table;comment:关联表"`
ON string `json:"on" form:"on" gorm:"column:on;comment:关联条件"`
}
func (JoinTemplate) TableName() string {
return "sys_export_template_join"
}
type Condition struct {
+1 -1
View File
@@ -15,7 +15,7 @@ type SysOperationRecord struct {
Path string `json:"path" form:"path" gorm:"column:path;comment:请求路径"` // 请求路径
Status int `json:"status" form:"status" gorm:"column:status;comment:请求状态"` // 请求状态
Latency time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟
Agent string `json:"agent" form:"agent" gorm:"column:agent;comment:代理"` // 代理
Agent string `json:"agent" form:"agent" gorm:"type:text;column:agent;comment:代理"` // 代理
ErrorMessage string `json:"error_message" form:"error_message" gorm:"column:error_message;comment:错误信息"` // 错误信息
Body string `json:"body" form:"body" gorm:"type:text;column:body;comment:请求Body"` // 请求Body
Resp string `json:"resp" form:"resp" gorm:"type:text;column:resp;comment:响应Body"` // 响应Body
-1
View File
@@ -14,7 +14,6 @@ type SysUser struct {
SideMode string `json:"sideMode" gorm:"default:dark;comment:用户侧边主题"` // 用户侧边主题
HeaderImg string `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像
BaseColor string `json:"baseColor" gorm:"default:#fff;comment:基础颜色"` // 基础颜色
ActiveColor string `json:"activeColor" gorm:"default:#1890ff;comment:活跃颜色"` // 活跃颜色
AuthorityId uint `json:"authorityId" gorm:"default:888;comment:用户角色ID"` // 用户角色ID
Authority SysAuthority `json:"authority" gorm:"foreignKey:AuthorityId;references:AuthorityId;comment:用户角色"`
Authorities []SysAuthority `json:"authorities" gorm:"many2many:sys_user_authority;"`
+3 -3
View File
@@ -2,9 +2,9 @@ package utils
import (
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"strconv"
)
func RegisterApis(apis ...system.SysApi) {
@@ -37,13 +37,13 @@ func RegisterMenus(menus ...system.SysBaseMenu) {
fmt.Println("插件已安装或存在同名菜单")
return
}
parentMenu.ParentId = "0"
parentMenu.ParentId = 0
err := global.GVA_DB.Create(&parentMenu).Error
if err != nil {
fmt.Println(err)
}
for i := range otherMenus {
pid := strconv.Itoa(int(parentMenu.ID))
pid := parentMenu.ID
otherMenus[i].ParentId = pid
}
err = global.GVA_DB.Create(&otherMenus).Error
@@ -165,3 +165,38 @@ func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}List(c *gin.Co
}, "获取成功", c)
}
}
{{- if .HasDataSource }}
// Get{{.StructName}}DataSource 获取{{.StructName}}的数据源
// @Tags {{.StructName}}
// @Summary 获取{{.StructName}}的数据源
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /{{.Abbreviation}}/get{{.StructName}}DataSource [get]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}DataSource(c *gin.Context) {
// 此接口为获取数据源定义的数据
if dataSource, err := {{.Abbreviation}}Service.Get{{.StructName}}DataSource(); err != nil {
global.GVA_LOG.Error("查询失败!", zap.Error(err))
response.FailWithMessage("查询失败", c)
} else {
response.OkWithData(dataSource, c)
}
}
{{- end }}
// Get{{.StructName}}Public 不需要鉴权的{{.Description}}接口
// @Tags {{.StructName}}
// @Summary 不需要鉴权的{{.Description}}接口
// @accept application/json
// @Produce application/json
// @Param data query {{.Package}}Req.{{.StructName}}Search true "分页获取{{.Description}}列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /{{.Abbreviation}}/get{{.StructName}}Public [get]
func ({{.Abbreviation}}Api *{{.StructName}}Api) Get{{.StructName}}Public(c *gin.Context) {
// 此接口不需要鉴权
// 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑
response.OkWithDetailed(gin.H{
"info": "不需要鉴权的{{.Description}}接口信息",
}, "获取成功", c)
}
@@ -12,23 +12,23 @@ type {{.StructName}} struct {
{{ if .GvaModel }} global.GVA_MODEL {{ end }}
{{- range .Fields}}
{{- if eq .FieldType "enum" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}column:{{.ColumnName}};type:enum({{.DataTypeLong}});comment:{{.Comment}};" {{- if .Require }} binding:"required"{{- end -}}`
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};type:enum({{.DataTypeLong}});comment:{{.Comment}};" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "picture" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "video" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "file" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "pictures" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "richtext" }}
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}}`
{{.FieldName}} string `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if eq .FieldType "json" }}
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}}`
{{.FieldName}} datatypes.JSON `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}type:text;" {{- if .Require }} binding:"required"{{- end -}}`
{{- else if ne .FieldType "string" }}
{{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- else }}
{{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"{{- if .PrimaryKey -}}primarykey;{{- end -}}{{- if .DefaultValue -}}default:{{ .DefaultValue }};{{- end -}}column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}" {{- if .Require }} binding:"required"{{- end -}}`
{{- end }} {{ if .FieldDesc }}//{{.FieldDesc}} {{ end }} {{- end }}
{{- if .AutoCreateResource }}
CreatedBy uint `gorm:"column:created_by;comment:创建者"`
@@ -10,9 +10,11 @@ type {{.StructName}}Router struct {
}
// Init{{.StructName}}Router 初始化 {{.Description}} 路由信息
func (s *{{.StructName}}Router) Init{{.StructName}}Router(Router *gin.RouterGroup) {
func (s *{{.StructName}}Router) Init{{.StructName}}Router(Router *gin.RouterGroup,PublicRouter *gin.RouterGroup) {
{{.Abbreviation}}Router := Router.Group("{{.Abbreviation}}").Use(middleware.OperationRecord())
{{.Abbreviation}}RouterWithoutRecord := Router.Group("{{.Abbreviation}}")
{{.Abbreviation}}RouterWithoutAuth := PublicRouter.Group("{{.Abbreviation}}")
var {{.Abbreviation}}Api = v1.ApiGroupApp.{{.PackageT}}ApiGroup.{{.StructName}}Api
{
{{.Abbreviation}}Router.POST("create{{.StructName}}", {{.Abbreviation}}Api.Create{{.StructName}}) // 新建{{.Description}}
@@ -24,4 +26,10 @@ func (s *{{.StructName}}Router) Init{{.StructName}}Router(Router *gin.RouterGrou
{{.Abbreviation}}RouterWithoutRecord.GET("find{{.StructName}}", {{.Abbreviation}}Api.Find{{.StructName}}) // 根据ID获取{{.Description}}
{{.Abbreviation}}RouterWithoutRecord.GET("get{{.StructName}}List", {{.Abbreviation}}Api.Get{{.StructName}}List) // 获取{{.Description}}列表
}
{
{{- if .HasDataSource}}
{{.Abbreviation}}RouterWithoutAuth.GET("get{{.StructName}}DataSource", {{.Abbreviation}}Api.Get{{.StructName}}DataSource) // 获取{{.Description}}数据源
{{- end}}
{{.Abbreviation}}RouterWithoutAuth.GET("get{{.StructName}}Public", {{.Abbreviation}}Api.Get{{.StructName}}Public) // 获取{{.Description}}列表
}
}
@@ -67,7 +67,7 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Delete{{.StructName}}ById
// Update{{.StructName}} 更新{{.Description}}记录
// Author [piexlmax](https://github.com/piexlmax)
func ({{.Abbreviation}}Service *{{.StructName}}Service)Update{{.StructName}}({{.Abbreviation}} {{.Package}}.{{.StructName}}) (err error) {
err = {{$db}}.Save(&{{.Abbreviation}}).Error
err = {{$db}}.Model(&{{.Package}}.{{.StructName}}{}).Where("{{.PrimaryField.ColumnName}} = ?",{{.Abbreviation}}.{{.PrimaryField.FieldName}}).Updates(&{{.Abbreviation}}).Error
return err
}
@@ -137,3 +137,15 @@ func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}InfoLis
err = db.Find(&{{.Abbreviation}}s).Error
return {{.Abbreviation}}s, total, err
}
{{- if .HasDataSource }}
func ({{.Abbreviation}}Service *{{.StructName}}Service)Get{{.StructName}}DataSource() (res map[string][]map[string]any, err error) {
res = make(map[string][]map[string]any)
{{range $key, $value := .DataSourceMap}}
{{$key}} := make([]map[string]any, 0)
{{$db}}.Table("{{$value.Table}}").Select("{{$value.Label}} as label,{{$value.Value}} as value").Scan(&{{$key}})
res["{{$key}}"] = {{$key}}
{{- end }}
return
}
{{- end }}
@@ -95,3 +95,19 @@ export const get{{.StructName}}List = (params) => {
params
})
}
{{- if .HasDataSource}}
// @Tags {{.StructName}}
// @Summary 获取数据源
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /{{.Abbreviation}}/find{{.StructName}}DataSource [get]
export const get{{.StructName}}DataSource = () => {
return service({
url: '/{{.Abbreviation}}/get{{.StructName}}DataSource',
method: 'get',
})
}
{{- end}}
@@ -4,6 +4,11 @@
<el-form :model="formData" ref="elFormRef" label-position="right" :rules="rule" label-width="80px">
{{- range .Fields}}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}">
{{- if .CheckDataSource}}
<el-select v-model="formData.{{.FieldJson}}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
<el-option v-for="(item,key) in dataSource.{{.FieldJson}}" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else }}
{{- if eq .FieldType "bool" }}
<el-switch v-model="formData.{{.FieldJson}}" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
{{- end }}
@@ -20,14 +25,8 @@
<RichEdit v-model="formData.{{.FieldJson}}"/>
{{- end }}
{{- if eq .FieldType "int" }}
{{- if .DictType }}
<el-select v-model="formData.{{ .FieldJson }}" placeholder="请选择" :clearable="{{.Clearable}}">
<el-option v-for="(item,key) in {{ .DictType }}Options" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else }}
<el-input v-model.number="formData.{{ .FieldJson }}" :clearable="{{.Clearable}}" placeholder="请输入" />
{{- end }}
{{- end }}
{{- if eq .FieldType "time.Time" }}
<el-date-picker v-model="formData.{{ .FieldJson }}" type="date" placeholder="选择日期" :clearable="{{.Clearable}}"></el-date-picker>
{{- end }}
@@ -55,6 +54,7 @@
// 此字段为json结构,可以前端自行控制展示和数据绑定模式 需绑定json的key为 formData.{{.FieldJson}} 后端会按照json的类型进行存取
{{"{{"}} formData.{{.FieldJson}} {{"}}"}}
{{- end }}
{{- end }}
</el-form-item>
{{- end }}
<el-form-item>
@@ -68,10 +68,13 @@
<script setup>
import {
{{- if .HasDataSource }}
get{{.StructName}}DataSource,
{{- end }}
create{{.StructName}},
update{{.StructName}},
find{{.StructName}}
} from '@/api/{{.PackageName}}'
} from '@/api/{{.Package}}/{{.PackageName}}'
defineOptions({
name: '{{.StructName}}Form'
@@ -153,6 +156,17 @@ const rule = reactive({
const elFormRef = ref()
{{- if .HasDataSource }}
const dataSource = ref([])
const getDataSourceFunc = async()=>{
const res = await get{{.StructName}}DataSource()
if (res.code === 0) {
dataSource.value = res.data
}
}
getDataSourceFunc()
{{- end }}
// 初始化方法
const init = async () => {
// 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
@@ -103,8 +103,14 @@
<template #default="scope">{{ "{{ formatDate(scope.row.CreatedAt) }}" }}</template>
</el-table-column>
{{ end }}
{{- range .Fields}}
{{- if .DictType}}
{{- range .FrontFields}}
{{- if .CheckDataSource }}
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template #default="scope">
{{"{{"}} filterDataSource(dataSource.{{.FieldJson}},scope.row.{{.FieldJson}}) {{"}}"}}
</template>
</el-table-column>
{{- else if .DictType}}
<el-table-column {{- if .Sort}} sortable{{- end}} align="left" label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template #default="scope">
{{"{{"}} filterDict(scope.row.{{.FieldJson}},{{.DictType}}Options) {{"}}"}}
@@ -170,10 +176,6 @@
{{- end }}
<el-table-column align="left" label="操作" fixed="right" min-width="240">
<template #default="scope">
<el-button type="primary" link class="table-button" @click="getDetails(scope.row)">
<el-icon style="margin-right: 5px"><InfoFilled /></el-icon>
查看详情
</el-button>
<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>
</template>
@@ -191,8 +193,8 @@
/>
</div>
</div>
<el-drawer size="800" v-model="dialogFormVisible" :show-close="false" :before-close="closeDialog">
<template #title>
<el-drawer destroy-on-close size="800" v-model="dialogFormVisible" :show-close="false" :before-close="closeDialog">
<template #header>
<div class="flex justify-between items-center">
<span class="text-lg">{{"{{"}}type==='create'?'添加':'修改'{{"}}"}}</span>
<div>
@@ -203,8 +205,13 @@
</template>
<el-form :model="formData" label-position="top" ref="elFormRef" :rules="rule" label-width="80px">
{{- range .Fields}}
{{- range .FrontFields}}
<el-form-item label="{{.FieldDesc}}:" prop="{{.FieldJson}}" >
{{- if .CheckDataSource}}
<el-select v-model="formData.{{.FieldJson}}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
<el-option v-for="(item,key) in dataSource.{{.FieldJson}}" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else }}
{{- if eq .FieldType "bool" }}
<el-switch v-model="formData.{{.FieldJson}}" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
{{- end }}
@@ -225,14 +232,8 @@
{{"{{"}} formData.{{.FieldJson}} {{"}}"}}
{{- end }}
{{- if eq .FieldType "int" }}
{{- if .DictType}}
<el-select v-model="formData.{{ .FieldJson }}" placeholder="请选择{{.FieldDesc}}" style="width:100%" :clearable="{{.Clearable}}" >
<el-option v-for="(item,key) in {{ .DictType }}Options" :key="key" :label="item.label" :value="item.value" />
</el-select>
{{- else }}
<el-input v-model.number="formData.{{ .FieldJson }}" :clearable="{{.Clearable}}" placeholder="请输入{{.FieldDesc}}" />
{{- end }}
{{- end }}
{{- if eq .FieldType "time.Time" }}
<el-date-picker v-model="formData.{{ .FieldJson }}" type="date" style="width:100%" placeholder="选择日期" :clearable="{{.Clearable}}" />
{{- end }}
@@ -265,69 +266,27 @@
{{- end }}
{{- if eq .FieldType "file" }}
<SelectFile v-model="formData.{{ .FieldJson }}" />
{{- end }}
{{- end }}
</el-form-item>
{{- end }}
</el-form>
</el-drawer>
<el-drawer size="800" v-model="detailShow" :before-close="closeDetailShow" title="查看详情" destroy-on-close>
<template #title>
<div class="flex justify-between items-center">
<span class="text-lg">查看详情</span>
</div>
</template>
<el-descriptions :column="1" border>
{{- range .Fields}}
<el-descriptions-item label="{{ .FieldDesc }}">
{{- if .DictType}}
{{"{{"}} filterDict(formData.{{.FieldJson}},{{.DictType}}Options) {{"}}"}}
{{- else if eq .FieldType "picture" }}
<el-image style="width: 50px; height: 50px" :preview-src-list="ReturnArrImg(formData.{{ .FieldJson }})" :src="getUrl(formData.{{ .FieldJson }})" fit="cover" />
{{- else if eq .FieldType "video" }}
<video
style="width: 50px; height: 50px"
muted
preload="metadata"
>
<source :src="getUrl(formData.{{ .FieldJson }}) + '#t=1'">
</video>
{{- else if eq .FieldType "pictures" }}
<el-image style="width: 50px; height: 50px; margin-right: 10px" :preview-src-list="ReturnArrImg(formData.{{ .FieldJson }})" :initial-index="index" v-for="(item,index) in formData.{{ .FieldJson }}" :key="index" :src="getUrl(item)" fit="cover" />
{{- else if eq .FieldType "file" }}
<div class="fileBtn" v-for="(item,index) in formData.{{ .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>
{{- else if eq .FieldType "bool" }}
{{"{{"}} formatBoolean(formData.{{.FieldJson}}) {{"}}"}}
{{- else if eq .FieldType "time.Time" }}
{{"{{"}} formatDate(formData.{{.FieldJson}}) {{"}}"}}
{{- else if eq .FieldType "richtext" }}
[富文本内容]
{{- else if eq .FieldType "json" }}
[JSON]
{{- else}}
{{"{{"}} formData.{{.FieldJson}} {{"}}"}}
{{- end }}
</el-descriptions-item>
{{- end }}
</el-descriptions>
</el-drawer>
</div>
</template>
<script setup>
import {
{{- if .HasDataSource }}
get{{.StructName}}DataSource,
{{- end }}
create{{.StructName}},
delete{{.StructName}},
delete{{.StructName}}ByIds,
update{{.StructName}},
find{{.StructName}},
get{{.StructName}}List
} from '@/api/{{.PackageName}}'
} from '@/api/{{.Package}}/{{.PackageName}}'
{{- if or .HasPic .HasFile}}
import { getUrl } from '@/utils/image'
@@ -349,7 +308,7 @@ import SelectFile from '@/components/selectFile/selectFile.vue'
{{- end }}
// 全量引入格式化工具 请按需保留
import { getDictFunc, formatDate, formatBoolean, filterDict, ReturnArrImg, onDownloadFile } from '@/utils/format'
import { getDictFunc, formatDate, formatBoolean, filterDict,filterDataSource, ReturnArrImg, onDownloadFile } from '@/utils/format'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ref, reactive } from 'vue'
@@ -362,7 +321,7 @@ defineOptions({
const {{ $element }}Options = ref([])
{{- end }}
const formData = ref({
{{- range .Fields}}
{{- range .FrontFields}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
@@ -399,10 +358,22 @@ const formData = ref({
{{- end }}
})
{{- if .HasDataSource }}
const dataSource = ref([])
const getDataSourceFunc = async()=>{
const res = await get{{.StructName}}DataSource()
if (res.code === 0) {
dataSource.value = res.data
}
}
getDataSourceFunc()
{{- end }}
// 验证规则
const rule = reactive({
{{- range .Fields }}
{{- range .FrontFields }}
{{- if eq .Require true }}
{{.FieldJson }} : [{
required: true,
@@ -435,7 +406,7 @@ const searchRule = reactive({
}
}, trigger: 'change' }
],
{{- range .Fields }}
{{- range .FrontFields }}
{{- if .FieldSearchType}}
{{- if eq .FieldType "time.Time" }}
{{.FieldJson }} : [{ validator: (rule, value, callback) => {
@@ -468,7 +439,7 @@ const searchInfo = ref({})
// 排序
const sortChange = ({ prop, order }) => {
const sortMap = {
{{- range .Fields}}
{{- range .FrontFields}}
{{- if and .Sort}}
{{- if not (eq .ColumnName "")}}
{{.FieldJson}}: '{{.ColumnName}}',
@@ -500,7 +471,7 @@ const onSubmit = () => {
if (!valid) return
page.value = 1
pageSize.value = 10
{{- range .Fields}}{{- if eq .FieldType "bool" }}
{{- range .FrontFields}}{{- if eq .FieldType "bool" }}
if (searchInfo.value.{{.FieldJson}} === ""){
searchInfo.value.{{.FieldJson}}=null
}{{ end }}{{ end }}
@@ -629,53 +600,6 @@ const delete{{.StructName}}Func = async (row) => {
// 弹窗控制标记
const dialogFormVisible = ref(false)
// 查看详情控制标记
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) {
formData.value = res.data.re{{.Abbreviation}}
openDetailShow()
}
}
// 关闭详情弹窗
const closeDetailShow = () => {
detailShow.value = false
formData.value = {
{{- range .Fields}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
{{- if eq .FieldType "string" }}
{{.FieldJson}}: '',
{{- end }}
{{- if eq .FieldType "int" }}
{{.FieldJson}}: {{- if .DictType }} undefined{{ else }} 0{{- end }},
{{- end }}
{{- if eq .FieldType "time.Time" }}
{{.FieldJson}}: new Date(),
{{- end }}
{{- if eq .FieldType "float64" }}
{{.FieldJson}}: 0,
{{- end }}
{{- end }}
}
}
// 打开弹窗
const openDialog = () => {
type.value = 'create'
@@ -686,7 +610,7 @@ const openDialog = () => {
const closeDialog = () => {
dialogFormVisible.value = false
formData.value = {
{{- range .Fields}}
{{- range .FrontFields}}
{{- if eq .FieldType "bool" }}
{{.FieldJson}}: false,
{{- end }}
-29
View File
@@ -1,29 +0,0 @@
package {{ .Snake}}
import (
{{- if .HasGlobal }}
"github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Snake}}/global"
{{- end }}
"github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Snake}}/router"
"github.com/gin-gonic/gin"
)
type {{ .PlugName}}Plugin struct {
}
func Create{{ .PlugName}}Plug({{- range .Global}} {{.Key}} {{.Type}}, {{- end }})*{{ .PlugName}}Plugin {
{{- if .HasGlobal }}
{{- range .Global}}
global.GlobalConfig.{{.Key}} = {{.Key}}
{{- end }}
{{ end }}
return &{{ .PlugName}}Plugin{}
}
func (*{{ .PlugName}}Plugin) Register(group *gin.RouterGroup) {
router.RouterGroupApp.Init{{ .PlugName}}Router(group)
}
func (*{{ .PlugName}}Plugin) RouterPath() string {
return "{{ .RouterGroup}}"
}
@@ -0,0 +1,72 @@
package {{ .Snake}}
import (
gvaGlobal "github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils"
{{- if .HasGlobal }}
"github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Snake}}/global"
{{- end }}
"github.com/flipped-aurora/gin-vue-admin/server/plugin/{{ .Snake}}/router"
"github.com/gin-gonic/gin"
)
type {{ .PlugName}}Plugin struct {
}
func Create{{ .PlugName}}Plug({{- range .Global}} {{.Key}} {{.Type}}, {{- end }})*{{ .PlugName }}Plugin {
{{- if .HasGlobal }}
{{- range .Global}}
global.GlobalConfig.{{.Key}} = {{.Key}}
{{- end }}
{{ end }}
gvaGlobal.GVA_DB.AutoMigrate() // 此处可以把插件依赖的数据库结构体自动创建表 需要填写对应的结构体
// 下方会自动注册菜单 第一个参数为菜单一级路由信息一般为定义好的组名 第二个参数为真实使用的web页面路由信息
// 具体值请根据实际情况修改
utils.RegisterMenus(
system.SysBaseMenu{
Path: "{{ .Snake }}Group",
Name: "{{ .Snake }}Group",
Hidden: false,
Component: "view/routerHolder.vue",
Sort: 1000,
Meta: system.Meta{
Title: "{{ .PlugName }}组",
Icon: "school",
},
},
system.SysBaseMenu{
Path: "{{ .Snake }}",
Name: "{{ .Snake }}",
Hidden: false,
Component: "plugin/{{ .Snake }}/view/index.vue",
Sort: 0,
Meta: system.Meta{
Title: "{{ .PlugName }}",
Icon: "school",
},
},
)
// 下方会自动注册api 以下格式为示例格式,请按照实际情况修改
utils.RegisterApis(
system.SysApi{
Path: "/{{ .RouterGroup }}/routerName",
Description: "{{ .PlugName }}接口",
ApiGroup: "{{ .RouterGroup }}",
Method: "POST",
},
)
return &{{ .PlugName}}Plugin{}
}
func (*{{ .PlugName}}Plugin) Register(group *gin.RouterGroup) {
router.RouterGroupApp.Init{{ .PlugName}}Router(group)
}
func (*{{ .PlugName}}Plugin) RouterPath() string {
return "{{ .RouterGroup}}"
}
@@ -0,0 +1,10 @@
import service from '@/utils/request'
// @Router /{{ .RouterGroup }}/routerName
export const routerName = (data) => {
return service({
url: '/{{ .RouterGroup }}/routerName',
method: 'post',
data
})
}
@@ -0,0 +1,30 @@
<template>
<div class="{{ .Snake }}">
在此处书写页面代码
</div>
</template>
<script setup>
import { ref } from 'vue'
import { routerName } from '@/plugin/{{ .Snake }}/api/api.js'
const data = ref({})
defineOptions({
name: '{{ .PlugName }}'
})
const useApi = async() =>{
const res = await routerName(data.value)
if(res.code === 0){
console.log(res.data)
}
}
</script>
<style lang="scss" scoped>
.{{ .Snake }} {
}
</style>
+45 -52
View File
@@ -35,8 +35,8 @@ func (apiService *ApiService) CreateApi(api system.SysApi) (err error) {
func (apiService *ApiService) DeleteApi(api system.SysApi) (err error) {
var entity system.SysApi
err = global.GVA_DB.Where("id = ?", api.ID).First(&entity).Error // 根据id查询api记录
if errors.Is(err, gorm.ErrRecordNotFound) { // api记录不存在
err = global.GVA_DB.First(&entity, "id = ?", api.ID).Error // 根据id查询api记录
if errors.Is(err, gorm.ErrRecordNotFound) { // api记录不存在
return err
}
err = global.GVA_DB.Delete(&entity).Error
@@ -82,34 +82,27 @@ func (apiService *ApiService) GetAPIInfoList(api system.SysApi, info request.Pag
if err != nil {
return apiList, total, err
} else {
db = db.Limit(limit).Offset(offset)
if order != "" {
var OrderStr string
// 设置有效排序key 防止sql注入
// 感谢 Tom4t0 提交漏洞信息
orderMap := make(map[string]bool, 5)
orderMap["id"] = true
orderMap["path"] = true
orderMap["api_group"] = true
orderMap["description"] = true
orderMap["method"] = true
if orderMap[order] {
if desc {
OrderStr = order + " desc"
} else {
OrderStr = order
}
} else { // didn't match any order key in `orderMap`
err = fmt.Errorf("非法的排序字段: %v", order)
return apiList, total, err
}
}
err = db.Order(OrderStr).Find(&apiList).Error
} else {
err = db.Order("id desc").Find(&apiList).Error
db = db.Limit(limit).Offset(offset)
OrderStr := "id desc"
if order != "" {
orderMap := make(map[string]bool, 5)
orderMap["id"] = true
orderMap["path"] = true
orderMap["api_group"] = true
orderMap["description"] = true
orderMap["method"] = true
if !orderMap[order] {
err = fmt.Errorf("非法的排序字段: %v", order)
return apiList, total, err
}
OrderStr = order
if desc {
OrderStr = order + " desc"
}
}
err = db.Order(OrderStr).Find(&apiList).Error
return apiList, total, err
}
@@ -130,7 +123,7 @@ func (apiService *ApiService) GetAllApis() (apis []system.SysApi, err error) {
//@return: api model.SysApi, err error
func (apiService *ApiService) GetApiById(id int) (api system.SysApi, err error) {
err = global.GVA_DB.Where("id = ?", id).First(&api).Error
err = global.GVA_DB.First(&api, "id = ?", id).Error
return
}
@@ -142,30 +135,28 @@ func (apiService *ApiService) GetApiById(id int) (api system.SysApi, err error)
func (apiService *ApiService) UpdateApi(api system.SysApi) (err error) {
var oldA system.SysApi
err = global.GVA_DB.Where("id = ?", api.ID).First(&oldA).Error
err = global.GVA_DB.First(&oldA, "id = ?", api.ID).Error
if oldA.Path != api.Path || oldA.Method != api.Method {
var duplicateApi system.SysApi
if err := global.GVA_DB.Where("path = ? AND method = ?", api.Path, api.Method).First(&duplicateApi).Error; err != nil {
if err := global.GVA_DB.First(&duplicateApi, "path = ? AND method = ?", api.Path, api.Method).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
} else {
if duplicateApi.ID != api.ID {
return errors.New("存在相同api路径")
}
}
if duplicateApi.ID != api.ID {
return errors.New("存在相同api路径")
}
}
if err != nil {
return err
} else {
err = CasbinServiceApp.UpdateCasbinApi(oldA.Path, api.Path, oldA.Method, api.Method)
if err != nil {
return err
} else {
err = global.GVA_DB.Save(&api).Error
}
}
return err
err = CasbinServiceApp.UpdateCasbinApi(oldA.Path, api.Path, oldA.Method, api.Method)
if err != nil {
return err
}
return global.GVA_DB.Save(&api).Error
}
//@author: [piexlmax](https://github.com/piexlmax)
@@ -175,17 +166,19 @@ func (apiService *ApiService) UpdateApi(api system.SysApi) (err error) {
//@return: err error
func (apiService *ApiService) DeleteApisByIds(ids request.IdsReq) (err error) {
var apis []system.SysApi
err = global.GVA_DB.Find(&apis, "id in ?", ids.Ids).Delete(&apis).Error
if err != nil {
return err
} else {
for _, sysApi := range apis {
CasbinServiceApp.ClearCasbin(1, sysApi.Path, sysApi.Method)
}
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
var apis []system.SysApi
err = tx.Find(&apis, "id in ?", ids.Ids).Error
if err != nil {
return err
}
}
return err
err = tx.Delete(&[]system.SysApi{}, "id in ?", ids.Ids).Error
if err != nil {
return err
}
for _, sysApi := range apis {
CasbinServiceApp.ClearCasbin(1, sysApi.Path, sysApi.Method)
}
return err
})
}
+10 -3
View File
@@ -2,9 +2,10 @@ package system
import (
"errors"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"strconv"
systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
@@ -70,7 +71,7 @@ func (authorityService *AuthorityService) CopyAuthority(copyInfo response.SysAut
}
var baseMenu []system.SysBaseMenu
for _, v := range menus {
intNum, _ := strconv.Atoi(v.MenuId)
intNum := v.MenuId
v.SysBaseMenu.ID = uint(intNum)
baseMenu = append(baseMenu, v.SysBaseMenu)
}
@@ -111,7 +112,13 @@ func (authorityService *AuthorityService) CopyAuthority(copyInfo response.SysAut
//@return: authority system.SysAuthority, err error
func (authorityService *AuthorityService) UpdateAuthority(auth system.SysAuthority) (authority system.SysAuthority, err error) {
err = global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&system.SysAuthority{}).Updates(&auth).Error
var oldAuthority system.SysAuthority
err = global.GVA_DB.Where("authority_id = ?", auth.AuthorityId).First(&oldAuthority).Error
if err != nil {
global.GVA_LOG.Debug(err.Error())
return system.SysAuthority{}, errors.New("查询角色数据失败")
}
err = global.GVA_DB.Model(&oldAuthority).Updates(&auth).Error
return auth, err
}
+134 -97
View File
@@ -29,7 +29,8 @@ import (
const (
autoPath = "autocode_template/"
autocodePath = "resource/autocode_template"
plugPath = "resource/plug_template"
plugServerPath = "resource/plug_template/server"
plugWebPath = "resource/plug_template/web"
packageService = "service/%s/enter.go"
packageServiceName = "service"
packageRouter = "router/%s/enter.go"
@@ -132,50 +133,7 @@ var AutoCodeServiceApp = new(AutoCodeService)
// @return: map[string]string, error
func (autoCodeService *AutoCodeService) PreviewTemp(autoCode system.AutoCodeStruct) (map[string]string, error) {
makeDictTypes(&autoCode)
for i := range autoCode.Fields {
if autoCode.Fields[i].FieldType == "time.Time" {
autoCode.HasTimer = true
if autoCode.Fields[i].FieldSearchType != "" {
autoCode.HasSearchTimer = true
}
}
if autoCode.Fields[i].Sort {
autoCode.NeedSort = true
}
if autoCode.Fields[i].FieldType == "picture" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "video" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "richtext" {
autoCode.HasRichText = true
}
if autoCode.Fields[i].FieldType == "pictures" {
autoCode.HasPic = true
autoCode.NeedJSON = true
}
if autoCode.Fields[i].FieldType == "file" {
autoCode.HasFile = true
autoCode.NeedJSON = true
}
if autoCode.GvaModel {
autoCode.PrimaryField = &system.Field{
FieldName: "ID",
FieldType: "uint",
FieldDesc: "ID",
FieldJson: "ID",
DataTypeLong: "20",
Comment: "主键ID",
ColumnName: "id",
}
}
if !autoCode.GvaModel && autoCode.PrimaryField == nil && autoCode.Fields[i].PrimaryKey {
autoCode.PrimaryField = autoCode.Fields[i]
}
}
fmtField(&autoCode)
dataList, _, needMkdir, err := autoCodeService.getNeedList(&autoCode)
if err != nil {
return nil, err
@@ -254,49 +212,7 @@ func makeDictTypes(autoCode *system.AutoCodeStruct) {
// @return: err error
func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruct, menuID uint, ids ...uint) (err error) {
makeDictTypes(&autoCode)
for i := range autoCode.Fields {
if autoCode.Fields[i].FieldType == "time.Time" {
autoCode.HasTimer = true
if autoCode.Fields[i].FieldSearchType != "" {
autoCode.HasSearchTimer = true
}
}
if autoCode.Fields[i].Sort {
autoCode.NeedSort = true
}
if autoCode.Fields[i].FieldType == "picture" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "video" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "richtext" {
autoCode.HasRichText = true
}
if autoCode.Fields[i].FieldType == "pictures" {
autoCode.NeedJSON = true
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "file" {
autoCode.NeedJSON = true
autoCode.HasFile = true
}
if autoCode.GvaModel {
autoCode.PrimaryField = &system.Field{
FieldName: "ID",
FieldType: "uint",
FieldDesc: "ID",
FieldJson: "ID",
DataTypeLong: "20",
Comment: "主键ID",
ColumnName: "id",
}
}
if !autoCode.GvaModel && autoCode.PrimaryField == nil && autoCode.Fields[i].PrimaryKey {
autoCode.PrimaryField = autoCode.Fields[i]
}
}
fmtField(&autoCode)
// 增加判断: 重复创建struct
if autoCode.AutoMoveFile && AutoCodeHistoryServiceApp.Repeat(autoCode.BusinessDB, autoCode.StructName, autoCode.Package) {
return RepeatErr
@@ -498,13 +414,13 @@ func (autoCodeService *AutoCodeService) addAutoMoveFile(data *tplData) {
} else if strings.Contains(fileSlice[1], "web") {
if strings.Contains(fileSlice[n-1], "js") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WApi, base)
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WApi, data.autoPackage, base)
} else if strings.Contains(fileSlice[n-2], "form") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WForm, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), strings.TrimSuffix(base, filepath.Ext(base))+"Form.vue")
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WForm, data.autoPackage, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), strings.TrimSuffix(base, filepath.Ext(base))+"Form.vue")
} else if strings.Contains(fileSlice[n-2], "table") {
data.autoMoveFilePath = filepath.Join(global.GVA_CONFIG.AutoCode.Root,
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WTable, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), base)
global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WTable, data.autoPackage, filepath.Base(filepath.Dir(filepath.Dir(data.autoCodePath))), base)
}
}
}
@@ -577,11 +493,11 @@ func (autoCodeService *AutoCodeService) AutoCreateMenu(a *system.AutoCodeStruct)
if err == nil {
return 0, errors.New("存在相同的菜单路由,请关闭自动创建菜单功能")
}
menu.ParentId = "0"
menu.ParentId = 0
menu.Name = a.Abbreviation
menu.Path = a.Abbreviation
menu.Meta.Title = a.Description
menu.Component = fmt.Sprintf("view/%s/%s.vue", a.PackageName, a.PackageName)
menu.Component = fmt.Sprintf("view/%s/%s/%s.vue", a.Package, a.PackageName, a.PackageName)
err = global.GVA_DB.Create(&menu).Error
return menu.ID, err
}
@@ -694,6 +610,19 @@ func (autoCodeService *AutoCodeService) CreatePackageTemp(packageName string) er
name: packageAPIName,
temp: string(subcontract.API),
}}
webTemp := []string{
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WApi),
filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, global.GVA_CONFIG.AutoCode.WForm),
}
for _, s := range webTemp {
err := os.MkdirAll(filepath.Join(s, packageName), 0755)
if err != nil {
return err
}
}
for i, s := range pendingTemp {
pendingTemp[i].path = filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, filepath.Clean(fmt.Sprintf(s.path, packageName)))
}
@@ -736,7 +665,19 @@ func (autoCodeService *AutoCodeService) CreatePackageTemp(packageName string) er
func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) error {
// 检查列表参数是否有效
plug.CheckList()
tplFileList, _ := autoCodeService.GetAllTplFile(plugPath, nil)
err := autoCodeService.createPluginServer(plug)
if err != nil {
return err
}
err = autoCodeService.createPluginWeb(plug)
if err != nil {
return err
}
return nil
}
func (autoCodeService *AutoCodeService) createPluginServer(plug system.AutoPlugReq) error {
tplFileList, _ := autoCodeService.GetAllTplFile(plugServerPath, nil)
for _, tpl := range tplFileList {
temp, err := template.ParseFiles(tpl)
if err != nil {
@@ -744,11 +685,41 @@ func (autoCodeService *AutoCodeService) CreatePlug(plug system.AutoPlugReq) erro
return err
}
pathArr := strings.SplitAfter(tpl, "/")
if strings.Index(pathArr[2], "tpl") < 0 {
dirPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+pathArr[2]))
if strings.Index(pathArr[3], "tpl") < 0 {
dirPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+pathArr[3]))
os.MkdirAll(dirPath, 0755)
}
file := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+tpl[len(plugPath):len(tpl)-4]))
file := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+tpl[len(plugServerPath):len(tpl)-4]))
f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
zap.L().Error("open file", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
return err
}
defer f.Close()
err = temp.Execute(f, plug)
if err != nil {
zap.L().Error("exec err", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
return err
}
}
return nil
}
func (autoCodeService *AutoCodeService) createPluginWeb(plug system.AutoPlugReq) error {
tplFileList, _ := autoCodeService.GetAllTplFile(plugWebPath, nil)
for _, tpl := range tplFileList {
temp, err := template.ParseFiles(tpl)
if err != nil {
zap.L().Error("parse err", zap.String("tpl", tpl), zap.Error(err))
return err
}
pathArr := strings.SplitAfter(tpl, "/")
if strings.Index(pathArr[3], "tpl") < 0 {
dirPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+pathArr[3]))
os.MkdirAll(dirPath, 0755)
}
file := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Web, fmt.Sprintf(global.GVA_CONFIG.AutoCode.SPlug, plug.Snake+"/"+tpl[len(plugWebPath):len(tpl)-4]))
f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
zap.L().Error("open file", zap.String("tpl", tpl), zap.Error(err), zap.Any("plug", plug))
@@ -974,3 +945,69 @@ func (autoCodeService *AutoCodeService) doZip(zipWriter *zip.Writer, serverPath,
})
return err
}
func fmtField(autoCode *system.AutoCodeStruct) {
makeDictTypes(autoCode)
autoCode.DataSourceMap = make(map[string]*system.DataSource)
for i := range autoCode.Fields {
if autoCode.Fields[i].Front {
autoCode.FrontFields = append(autoCode.FrontFields, autoCode.Fields[i])
}
if autoCode.Fields[i].FieldType == "time.Time" {
autoCode.HasTimer = true
if autoCode.Fields[i].FieldSearchType != "" {
autoCode.HasSearchTimer = true
}
}
if autoCode.Fields[i].Sort {
autoCode.NeedSort = true
}
if autoCode.Fields[i].FieldType == "picture" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "video" {
autoCode.HasPic = true
}
if autoCode.Fields[i].FieldType == "richtext" {
autoCode.HasRichText = true
}
if autoCode.Fields[i].FieldType == "pictures" {
autoCode.HasPic = true
autoCode.NeedJSON = true
}
if autoCode.Fields[i].FieldType == "json" {
autoCode.NeedJSON = true
}
if autoCode.Fields[i].FieldType == "file" {
autoCode.HasFile = true
autoCode.NeedJSON = true
}
if autoCode.Fields[i].DataSource != nil &&
autoCode.Fields[i].DataSource.Table != "" &&
autoCode.Fields[i].DataSource.Label != "" &&
autoCode.Fields[i].DataSource.Value != "" {
autoCode.HasDataSource = true
autoCode.DataSourceMap[autoCode.Fields[i].FieldJson] = autoCode.Fields[i].DataSource
autoCode.Fields[i].CheckDataSource = true
}
if autoCode.GvaModel {
autoCode.PrimaryField = &system.Field{
FieldName: "ID",
FieldType: "uint",
FieldDesc: "ID",
FieldJson: "ID",
DataTypeLong: "20",
Comment: "主键ID",
ColumnName: "id",
}
}
if !autoCode.GvaModel && autoCode.PrimaryField == nil && autoCode.Fields[i].PrimaryKey {
autoCode.PrimaryField = autoCode.Fields[i]
}
}
}
+61 -61
View File
@@ -46,74 +46,74 @@ func (a *autoCodePgsql) GetTables(businessDB string, dbName string) (data []resp
func (a *autoCodePgsql) GetColumn(businessDB string, tableName string, dbName string) (data []response.Column, err error) {
// todo 数据获取不全, 待完善sql
sql := `
SELECT
psc.COLUMN_NAME AS COLUMN_NAME,
psc.udt_name AS data_type,
CASE
psc.udt_name
WHEN 'text' THEN
concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH )
WHEN 'varchar' THEN
concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH )
WHEN 'smallint' THEN
concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE )
WHEN 'decimal' THEN
concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE )
WHEN 'integer' THEN
concat_ws ( '', '', psc.NUMERIC_PRECISION )
WHEN 'int4' THEN
concat_ws ( '', '', psc.NUMERIC_PRECISION )
WHEN 'int8' THEN
concat_ws ( '', '', psc.NUMERIC_PRECISION )
WHEN 'bigint' THEN
concat_ws ( '', '', psc.NUMERIC_PRECISION )
WHEN 'timestamp' THEN
concat_ws ( '', '', psc.datetime_precision )
ELSE ''
END AS data_type_long,
(
SELECT
pd.description
FROM
pg_description pd
WHERE
(pd.objoid,pd.objsubid) in (
SELECT pa.attrelid,pa.attnum
FROM
pg_attribute pa
WHERE pa.attrelid = ( SELECT oid FROM pg_class pc WHERE
pc.relname = psc.table_name
)
and attname = psc.column_name
)
) AS column_comment,
(
SELECT
COUNT(*)
FROM
pg_constraint
WHERE
contype = 'p'
AND conrelid = (
SELECT
psc.COLUMN_NAME AS COLUMN_NAME,
psc.udt_name AS data_type,
CASE
psc.udt_name
WHEN 'text' THEN
concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH )
WHEN 'varchar' THEN
concat_ws ( '', '', psc.CHARACTER_MAXIMUM_LENGTH )
WHEN 'smallint' THEN
concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE )
WHEN 'decimal' THEN
concat_ws ( ',', psc.NUMERIC_PRECISION, psc.NUMERIC_SCALE )
WHEN 'integer' THEN
concat_ws ( '', '', psc.NUMERIC_PRECISION )
WHEN 'int4' THEN
concat_ws ( '', '', psc.NUMERIC_PRECISION )
WHEN 'int8' THEN
concat_ws ( '', '', psc.NUMERIC_PRECISION )
WHEN 'bigint' THEN
concat_ws ( '', '', psc.NUMERIC_PRECISION )
WHEN 'timestamp' THEN
concat_ws ( '', '', psc.datetime_precision )
ELSE ''
END AS data_type_long,
(
SELECT
oid
pd.description
FROM
pg_class
pg_description pd
WHERE
relname = psc.table_name
)
AND conkey::int[] @> ARRAY[(
(pd.objoid,pd.objsubid) in (
SELECT pa.attrelid,pa.attnum
FROM
pg_attribute pa
WHERE pa.attrelid = ( SELECT oid FROM pg_class pc WHERE
pc.relname = psc.table_name
)
and attname = psc.column_name
)
) AS column_comment,
(
SELECT
attnum
COUNT(*)
FROM
pg_attribute
pg_constraint
WHERE
attrelid = conrelid
AND attname = psc.column_name
)]
) > 0 AS primary_key
contype = 'p'
AND conrelid = (
SELECT
oid
FROM
pg_class
WHERE
relname = psc.table_name
)
AND conkey::int[] @> ARRAY[(
SELECT
attnum::integer
FROM
pg_attribute
WHERE
attrelid = conrelid
AND attname = psc.column_name
)]
) > 0 AS primary_key
FROM
INFORMATION_SCHEMA.COLUMNS psc
INFORMATION_SCHEMA.COLUMNS psc
WHERE
table_catalog = ?
AND table_schema = 'public'
@@ -41,6 +41,7 @@ func (autoCodeHistoryService *AutoCodeHistoryService) CreateAutoCodeHistory(meta
TableName: tableName,
ApiIDs: apiIds,
BusinessDB: BusinessDB,
MenuID: menuID,
}).Error
}
+29 -20
View File
@@ -19,28 +19,37 @@ type BaseMenuService struct{}
var BaseMenuServiceApp = new(BaseMenuService)
func (baseMenuService *BaseMenuService) DeleteBaseMenu(id int) (err error) {
err = global.GVA_DB.Preload("MenuBtn").Preload("Parameters").Where("parent_id = ?", id).First(&system.SysBaseMenu{}).Error
err = global.GVA_DB.First(&system.SysBaseMenu{}, "parent_id = ?", id).Error
if err != nil {
var menu system.SysBaseMenu
db := global.GVA_DB.Preload("SysAuthoritys").Where("id = ?", id).First(&menu).Delete(&menu)
err = global.GVA_DB.Delete(&system.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error
err = global.GVA_DB.Delete(&system.SysBaseMenuBtn{}, "sys_base_menu_id = ?", id).Error
err = global.GVA_DB.Delete(&system.SysAuthorityBtn{}, "sys_menu_id = ?", id).Error
if err != nil {
return err
}
if len(menu.SysAuthoritys) > 0 {
err = global.GVA_DB.Model(&menu).Association("SysAuthoritys").Delete(&menu.SysAuthoritys)
} else {
err = db.Error
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
err = tx.Delete(&system.SysBaseMenu{}, "id = ?", id).Error
if err != nil {
return
return err
}
}
} else {
return errors.New("此菜单存在子菜单不可删除")
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
})
}
return err
return errors.New("此菜单存在子菜单不可删除")
}
//@author: [piexlmax](https://github.com/piexlmax)
@@ -66,7 +75,7 @@ func (baseMenuService *BaseMenuService) UpdateBaseMenu(menu system.SysBaseMenu)
upDateMap["sort"] = menu.Sort
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
db := tx.Where("id = ?", menu.ID).Find(&oldMenu)
tx.Where("id = ?", menu.ID).Find(&oldMenu)
if oldMenu.Name != menu.Name {
if !errors.Is(tx.Where("id <> ? AND name = ?", menu.ID, menu.Name).First(&system.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) {
global.GVA_LOG.Debug("存在相同name修改失败")
@@ -105,7 +114,7 @@ func (baseMenuService *BaseMenuService) UpdateBaseMenu(menu system.SysBaseMenu)
}
}
txErr = db.Updates(upDateMap).Error
txErr = tx.Model(&oldMenu).Updates(upDateMap).Error
if txErr != nil {
global.GVA_LOG.Debug(txErr.Error())
return txErr
+6 -2
View File
@@ -63,13 +63,17 @@ func (dictionaryService *DictionaryService) UpdateSysDictionary(sysDictionary *s
"Status": sysDictionary.Status,
"Desc": sysDictionary.Desc,
}
db := global.GVA_DB.Where("id = ?", sysDictionary.ID).First(&dict)
err = global.GVA_DB.Where("id = ?", sysDictionary.ID).First(&dict).Error
if err != nil {
global.GVA_LOG.Debug(err.Error())
return errors.New("查询字典数据失败")
}
if dict.Type != sysDictionary.Type {
if !errors.Is(global.GVA_DB.First(&system.SysDictionary{}, "type = ?", sysDictionary.Type).Error, gorm.ErrRecordNotFound) {
return errors.New("存在相同的type,不允许创建")
}
}
err = db.Updates(sysDictionaryMap).Error
err = global.GVA_DB.Model(&dict).Updates(sysDictionaryMap).Error
return err
}
+39 -9
View File
@@ -51,6 +51,14 @@ func (sysExportTemplateService *SysExportTemplateService) UpdateSysExportTemplat
return e
}
sysExportTemplate.Conditions = nil
joins := sysExportTemplate.JoinTemplate
e = tx.Delete(&[]system.JoinTemplate{}, "template_id = ?", sysExportTemplate.TemplateID).Error
if e != nil {
return e
}
sysExportTemplate.JoinTemplate = nil
e = tx.Updates(&sysExportTemplate).Error
if e != nil {
return e
@@ -61,6 +69,12 @@ func (sysExportTemplateService *SysExportTemplateService) UpdateSysExportTemplat
}
e = tx.Create(&conditions).Error
}
if len(joins) > 0 {
for i := range joins {
joins[i].ID = 0
}
e = tx.Create(&joins).Error
}
return e
})
}
@@ -68,7 +82,7 @@ func (sysExportTemplateService *SysExportTemplateService) UpdateSysExportTemplat
// GetSysExportTemplate 根据id获取导出模板记录
// Author [piexlmax](https://github.com/piexlmax)
func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplate(id uint) (sysExportTemplate system.SysExportTemplate, err error) {
err = global.GVA_DB.Where("id = ?", id).Preload("Conditions").First(&sysExportTemplate).Error
err = global.GVA_DB.Where("id = ?", id).Preload("JoinTemplate").Preload("Conditions").First(&sysExportTemplate).Error
return
}
@@ -110,7 +124,7 @@ func (sysExportTemplateService *SysExportTemplateService) GetSysExportTemplateIn
// Author [piexlmax](https://github.com/piexlmax)
func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID string, values url.Values) (file *bytes.Buffer, name string, err error) {
var template system.SysExportTemplate
err = global.GVA_DB.Preload("Conditions").First(&template, "template_id = ?", templateID).Error
err = global.GVA_DB.Preload("Conditions").Preload("JoinTemplate").First(&template, "template_id = ?", templateID).Error
if err != nil {
return nil, "", err
}
@@ -145,6 +159,13 @@ func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID
if template.DBName != "" {
db = global.MustGetGlobalDBByDBName(template.DBName)
}
if len(template.JoinTemplate) > 0 {
for _, join := range template.JoinTemplate {
db = db.Joins(join.JOINS + "`" + join.Table + "`" + " ON " + join.ON)
}
}
db = db.Select(selects).Table(template.TableName)
if len(template.Conditions) > 0 {
@@ -191,7 +212,7 @@ func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID
db = db.Order(template.Order)
}
err = db.Find(&tableMap).Error
err = db.Debug().Find(&tableMap).Error
if err != nil {
return nil, "", err
}
@@ -200,6 +221,12 @@ func (sysExportTemplateService *SysExportTemplateService) ExportExcel(templateID
for _, table := range tableMap {
var row []string
for _, column := range columns {
if len(template.JoinTemplate) > 0 {
columnArr := strings.Split(column, ".")
if len(columnArr) > 1 {
column = strings.Split(column, ".")[1]
}
}
row = append(row, fmt.Sprintf("%v", table[column]))
}
rows = append(rows, row)
@@ -242,14 +269,18 @@ func (sysExportTemplateService *SysExportTemplateService) ExportTemplate(templat
return
}
var templateInfoMap = make(map[string]string)
columns, err := utils.GetJSONKeys(template.TemplateInfo)
err = json.Unmarshal([]byte(template.TemplateInfo), &templateInfoMap)
if err != nil {
return nil, "", err
}
var tableTitle []string
for key := range templateInfoMap {
for _, key := range columns {
tableTitle = append(tableTitle, templateInfoMap[key])
}
for i := range tableTitle {
fErr := f.SetCellValue("Sheet1", fmt.Sprintf("%s%d", getColumnName(i+1), 1), tableTitle[i])
if fErr != nil {
@@ -309,6 +340,7 @@ func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID
return db.Transaction(func(tx *gorm.DB) error {
excelTitle := rows[0]
values := rows[1:]
items := make([]map[string]interface{}, 0, len(values))
for _, row := range values {
var item = make(map[string]interface{})
for ii, value := range row {
@@ -327,12 +359,10 @@ func (sysExportTemplateService *SysExportTemplateService) ImportExcel(templateID
// item["updated_at"] = time.Now()
//}
cErr := tx.Table(template.TableName).Create(&item).Error
if cErr != nil {
return cErr
}
items = append(items, item)
}
return nil
cErr := tx.Table(template.TableName).CreateInBatches(&items, 1000).Error
return cErr
})
}
+13 -13
View File
@@ -2,7 +2,6 @@ package system
import (
"errors"
"strconv"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
@@ -20,11 +19,11 @@ type MenuService struct{}
var MenuServiceApp = new(MenuService)
func (menuService *MenuService) getMenuTreeMap(authorityId uint) (treeMap map[string][]system.SysMenu, err error) {
func (menuService *MenuService) getMenuTreeMap(authorityId uint) (treeMap map[uint][]system.SysMenu, err error) {
var allMenus []system.SysMenu
var baseMenu []system.SysBaseMenu
var btns []system.SysAuthorityBtn
treeMap = make(map[string][]system.SysMenu)
treeMap = make(map[uint][]system.SysMenu)
var SysAuthorityMenus []system.SysAuthorityMenu
err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityId).Find(&SysAuthorityMenus).Error
@@ -47,7 +46,7 @@ func (menuService *MenuService) getMenuTreeMap(authorityId uint) (treeMap map[st
allMenus = append(allMenus, system.SysMenu{
SysBaseMenu: baseMenu[i],
AuthorityId: authorityId,
MenuId: strconv.Itoa(int(baseMenu[i].ID)),
MenuId: baseMenu[i].ID,
Parameters: baseMenu[i].Parameters,
})
}
@@ -78,7 +77,7 @@ func (menuService *MenuService) getMenuTreeMap(authorityId uint) (treeMap map[st
func (menuService *MenuService) GetMenuTree(authorityId uint) (menus []system.SysMenu, err error) {
menuTree, err := menuService.getMenuTreeMap(authorityId)
menus = menuTree["0"]
menus = menuTree[0]
for i := 0; i < len(menus); i++ {
err = menuService.getChildrenList(&menus[i], menuTree)
}
@@ -91,7 +90,7 @@ func (menuService *MenuService) GetMenuTree(authorityId uint) (menus []system.Sy
//@param: menu *model.SysMenu, treeMap map[string][]model.SysMenu
//@return: err error
func (menuService *MenuService) getChildrenList(menu *system.SysMenu, treeMap map[string][]system.SysMenu) (err error) {
func (menuService *MenuService) getChildrenList(menu *system.SysMenu, treeMap map[uint][]system.SysMenu) (err error) {
menu.Children = treeMap[menu.MenuId]
for i := 0; i < len(menu.Children); i++ {
err = menuService.getChildrenList(&menu.Children[i], treeMap)
@@ -107,7 +106,7 @@ func (menuService *MenuService) getChildrenList(menu *system.SysMenu, treeMap ma
func (menuService *MenuService) GetInfoList() (list interface{}, total int64, err error) {
var menuList []system.SysBaseMenu
treeMap, err := menuService.getBaseMenuTreeMap()
menuList = treeMap["0"]
menuList = treeMap[0]
for i := 0; i < len(menuList); i++ {
err = menuService.getBaseChildrenList(&menuList[i], treeMap)
}
@@ -120,8 +119,8 @@ func (menuService *MenuService) GetInfoList() (list interface{}, total int64, er
//@param: menu *model.SysBaseMenu, treeMap map[string][]model.SysBaseMenu
//@return: err error
func (menuService *MenuService) getBaseChildrenList(menu *system.SysBaseMenu, treeMap map[string][]system.SysBaseMenu) (err error) {
menu.Children = treeMap[strconv.Itoa(int(menu.ID))]
func (menuService *MenuService) getBaseChildrenList(menu *system.SysBaseMenu, treeMap map[uint][]system.SysBaseMenu) (err error) {
menu.Children = treeMap[menu.ID]
for i := 0; i < len(menu.Children); i++ {
err = menuService.getBaseChildrenList(&menu.Children[i], treeMap)
}
@@ -146,9 +145,9 @@ func (menuService *MenuService) AddBaseMenu(menu system.SysBaseMenu) error {
//@description: 获取路由总树map
//@return: treeMap map[string][]system.SysBaseMenu, err error
func (menuService *MenuService) getBaseMenuTreeMap() (treeMap map[string][]system.SysBaseMenu, err error) {
func (menuService *MenuService) getBaseMenuTreeMap() (treeMap map[uint][]system.SysBaseMenu, err error) {
var allMenus []system.SysBaseMenu
treeMap = make(map[string][]system.SysBaseMenu)
treeMap = make(map[uint][]system.SysBaseMenu)
err = global.GVA_DB.Order("sort").Preload("MenuBtn").Preload("Parameters").Find(&allMenus).Error
for _, v := range allMenus {
treeMap[v.ParentId] = append(treeMap[v.ParentId], v)
@@ -163,7 +162,7 @@ func (menuService *MenuService) getBaseMenuTreeMap() (treeMap map[string][]syste
func (menuService *MenuService) GetBaseMenuTree() (menus []system.SysBaseMenu, err error) {
treeMap, err := menuService.getBaseMenuTreeMap()
menus = treeMap["0"]
menus = treeMap[0]
for i := 0; i < len(menus); i++ {
err = menuService.getBaseChildrenList(&menus[i], treeMap)
}
@@ -210,7 +209,7 @@ func (menuService *MenuService) GetMenuAuthority(info *request.GetAuthorityId) (
menus = append(menus, system.SysMenu{
SysBaseMenu: baseMenu[i],
AuthorityId: info.AuthorityId,
MenuId: strconv.Itoa(int(baseMenu[i].ID)),
MenuId: baseMenu[i].ID,
Parameters: baseMenu[i].Parameters,
})
}
@@ -218,6 +217,7 @@ func (menuService *MenuService) GetMenuAuthority(info *request.GetAuthorityId) (
}
// UserAuthorityDefaultRouter 用户角色默认路由检查
//
// Author [SliverHorn](https://github.com/SliverHorn)
func (menuService *MenuService) UserAuthorityDefaultRouter(user *system.SysUser) {
var menuIds []string
+9 -3
View File
@@ -106,7 +106,7 @@ func (userService *UserService) SetUserAuthority(id uint, authorityId uint) (err
if errors.Is(assignErr, gorm.ErrRecordNotFound) {
return errors.New("该用户无此角色")
}
err = global.GVA_DB.Where("id = ?", id).First(&system.SysUser{}).Update("authority_id", authorityId).Error
err = global.GVA_DB.Model(&system.SysUser{}).Where("id = ?", id).Update("authority_id", authorityId).Error
return err
}
@@ -118,7 +118,13 @@ func (userService *UserService) SetUserAuthority(id uint, authorityId uint) (err
func (userService *UserService) SetUserAuthorities(id uint, authorityIds []uint) (err error) {
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
TxErr := tx.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error
var user system.SysUser
TxErr := tx.Where("id = ?", id).First(&user).Error
if TxErr != nil {
global.GVA_LOG.Debug(TxErr.Error())
return errors.New("查询用户数据失败")
}
TxErr = tx.Delete(&[]system.SysUserAuthority{}, "sys_user_id = ?", id).Error
if TxErr != nil {
return TxErr
}
@@ -132,7 +138,7 @@ func (userService *UserService) SetUserAuthorities(id uint, authorityIds []uint)
if TxErr != nil {
return TxErr
}
TxErr = tx.Where("id = ?", id).First(&system.SysUser{}).Update("authority_id", authorityIds[0]).Error
TxErr = tx.Model(&user).Update("authority_id", authorityIds[0]).Error
if TxErr != nil {
return TxErr
}
+31 -30
View File
@@ -2,6 +2,7 @@ package system
import (
"context"
. "github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/service/system"
"github.com/pkg/errors"
@@ -50,36 +51,36 @@ func (i *initMenu) InitializeData(ctx context.Context) (next context.Context, er
return ctx, system.ErrMissingDBContext
}
entities := []SysBaseMenu{
{MenuLevel: 0, Hidden: false, ParentId: "0", Path: "dashboard", Name: "dashboard", Component: "view/dashboard/index.vue", Sort: 1, Meta: Meta{Title: "仪表盘", Icon: "odometer"}},
{MenuLevel: 0, Hidden: false, ParentId: "0", Path: "about", Name: "about", Component: "view/about/index.vue", Sort: 9, Meta: Meta{Title: "关于我们", Icon: "info-filled"}},
{MenuLevel: 0, Hidden: false, ParentId: "0", Path: "admin", Name: "superAdmin", Component: "view/superAdmin/index.vue", Sort: 3, Meta: Meta{Title: "超级管理员", Icon: "user"}},
{MenuLevel: 0, Hidden: false, ParentId: "3", Path: "authority", Name: "authority", Component: "view/superAdmin/authority/authority.vue", Sort: 1, Meta: Meta{Title: "角色管理", Icon: "avatar"}},
{MenuLevel: 0, Hidden: false, ParentId: "3", Path: "menu", Name: "menu", Component: "view/superAdmin/menu/menu.vue", Sort: 2, Meta: Meta{Title: "菜单管理", Icon: "tickets", KeepAlive: true}},
{MenuLevel: 0, Hidden: false, ParentId: "3", Path: "api", Name: "api", Component: "view/superAdmin/api/api.vue", Sort: 3, Meta: Meta{Title: "api管理", Icon: "platform", KeepAlive: true}},
{MenuLevel: 0, Hidden: false, ParentId: "3", Path: "user", Name: "user", Component: "view/superAdmin/user/user.vue", Sort: 4, Meta: Meta{Title: "用户管理", Icon: "coordinate"}},
{MenuLevel: 0, Hidden: false, ParentId: "3", Path: "dictionary", Name: "dictionary", Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: Meta{Title: "字典管理", Icon: "notebook"}},
{MenuLevel: 0, Hidden: false, ParentId: "3", Path: "operation", Name: "operation", Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: Meta{Title: "操作历史", Icon: "pie-chart"}},
{MenuLevel: 0, Hidden: true, ParentId: "0", Path: "person", Name: "person", Component: "view/person/person.vue", Sort: 4, Meta: Meta{Title: "个人信息", Icon: "message"}},
{MenuLevel: 0, Hidden: false, ParentId: "0", Path: "example", Name: "example", Component: "view/example/index.vue", Sort: 7, Meta: Meta{Title: "示例文件", Icon: "management"}},
{MenuLevel: 0, Hidden: false, ParentId: "11", Path: "upload", Name: "upload", Component: "view/example/upload/upload.vue", Sort: 5, Meta: Meta{Title: "媒体库(上传下载)", Icon: "upload"}},
{MenuLevel: 0, Hidden: false, ParentId: "11", Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 6, Meta: Meta{Title: "断点续传", Icon: "upload-filled"}},
{MenuLevel: 0, Hidden: false, ParentId: "11", Path: "customer", Name: "customer", Component: "view/example/customer/customer.vue", Sort: 7, Meta: Meta{Title: "客户列表(资源示例)", Icon: "avatar"}},
{MenuLevel: 0, Hidden: false, ParentId: "0", Path: "systemTools", Name: "systemTools", Component: "view/systemTools/index.vue", Sort: 5, Meta: Meta{Title: "系统工具", Icon: "tools"}},
{MenuLevel: 0, Hidden: false, ParentId: "15", Path: "autoCode", Name: "autoCode", Component: "view/systemTools/autoCode/index.vue", Sort: 1, Meta: Meta{Title: "代码生成器", Icon: "cpu", KeepAlive: true}},
{MenuLevel: 0, Hidden: false, ParentId: "15", Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 2, Meta: Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}},
{MenuLevel: 0, Hidden: false, ParentId: "15", Path: "system", Name: "system", Component: "view/systemTools/system/system.vue", Sort: 3, Meta: Meta{Title: "系统配置", Icon: "operation"}},
{MenuLevel: 0, Hidden: false, ParentId: "15", Path: "autoCodeAdmin", Name: "autoCodeAdmin", Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 1, Meta: Meta{Title: "自动化代码管理", Icon: "magic-stick"}},
{MenuLevel: 0, Hidden: true, ParentId: "15", Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: Meta{Title: "自动化代码-${id}", Icon: "magic-stick"}},
{MenuLevel: 0, Hidden: false, ParentId: "15", Path: "autoPkg", Name: "autoPkg", Component: "view/systemTools/autoPkg/autoPkg.vue", Sort: 0, Meta: Meta{Title: "自动化package", Icon: "folder"}},
{MenuLevel: 0, Hidden: false, ParentId: "0", Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}},
{MenuLevel: 0, Hidden: false, ParentId: "0", Path: "state", Name: "state", Component: "view/system/state.vue", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "cloudy"}},
{MenuLevel: 0, Hidden: false, ParentId: "0", Path: "plugin", Name: "plugin", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "插件系统", Icon: "cherry"}},
{MenuLevel: 0, Hidden: false, ParentId: "24", Path: "https://plugin.gin-vue-admin.com/", Name: "https://plugin.gin-vue-admin.com/", Component: "https://plugin.gin-vue-admin.com/", Sort: 0, Meta: Meta{Title: "插件市场", Icon: "shop"}},
{MenuLevel: 0, Hidden: false, ParentId: "24", Path: "installPlugin", Name: "installPlugin", Component: "view/systemTools/installPlugin/index.vue", Sort: 1, Meta: Meta{Title: "插件安装", Icon: "box"}},
{MenuLevel: 0, Hidden: false, ParentId: "24", Path: "autoPlug", Name: "autoPlug", Component: "view/systemTools/autoPlug/autoPlug.vue", Sort: 2, Meta: Meta{Title: "插件模板", Icon: "folder"}},
{MenuLevel: 0, Hidden: false, ParentId: "24", Path: "pubPlug", Name: "pubPlug", Component: "view/systemTools/pubPlug/pubPlug.vue", Sort: 3, Meta: Meta{Title: "打包插件", Icon: "files"}},
{MenuLevel: 0, Hidden: false, ParentId: "24", Path: "plugin-email", Name: "plugin-email", Component: "plugin/email/view/index.vue", Sort: 4, Meta: Meta{Title: "邮件插件", Icon: "message"}},
{MenuLevel: 0, Hidden: false, ParentId: "15", Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 10, Meta: Meta{Title: "表格模板", Icon: "reading"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "dashboard", Name: "dashboard", Component: "view/dashboard/index.vue", Sort: 1, Meta: Meta{Title: "仪表盘", Icon: "odometer"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "about", Name: "about", Component: "view/about/index.vue", Sort: 9, Meta: Meta{Title: "关于我们", Icon: "info-filled"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "admin", Name: "superAdmin", Component: "view/superAdmin/index.vue", Sort: 3, Meta: Meta{Title: "超级管理员", Icon: "user"}},
{MenuLevel: 0, Hidden: false, ParentId: 3, Path: "authority", Name: "authority", Component: "view/superAdmin/authority/authority.vue", Sort: 1, Meta: Meta{Title: "角色管理", Icon: "avatar"}},
{MenuLevel: 0, Hidden: false, ParentId: 3, Path: "menu", Name: "menu", Component: "view/superAdmin/menu/menu.vue", Sort: 2, Meta: Meta{Title: "菜单管理", Icon: "tickets", KeepAlive: true}},
{MenuLevel: 0, Hidden: false, ParentId: 3, Path: "api", Name: "api", Component: "view/superAdmin/api/api.vue", Sort: 3, Meta: Meta{Title: "api管理", Icon: "platform", KeepAlive: true}},
{MenuLevel: 0, Hidden: false, ParentId: 3, Path: "user", Name: "user", Component: "view/superAdmin/user/user.vue", Sort: 4, Meta: Meta{Title: "用户管理", Icon: "coordinate"}},
{MenuLevel: 0, Hidden: false, ParentId: 3, Path: "dictionary", Name: "dictionary", Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: Meta{Title: "字典管理", Icon: "notebook"}},
{MenuLevel: 0, Hidden: false, ParentId: 3, Path: "operation", Name: "operation", Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: Meta{Title: "操作历史", Icon: "pie-chart"}},
{MenuLevel: 0, Hidden: true, ParentId: 0, Path: "person", Name: "person", Component: "view/person/person.vue", Sort: 4, Meta: Meta{Title: "个人信息", Icon: "message"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "example", Name: "example", Component: "view/example/index.vue", Sort: 7, Meta: Meta{Title: "示例文件", Icon: "management"}},
{MenuLevel: 0, Hidden: false, ParentId: 11, Path: "upload", Name: "upload", Component: "view/example/upload/upload.vue", Sort: 5, Meta: Meta{Title: "媒体库(上传下载)", Icon: "upload"}},
{MenuLevel: 0, Hidden: false, ParentId: 11, Path: "breakpoint", Name: "breakpoint", Component: "view/example/breakpoint/breakpoint.vue", Sort: 6, Meta: Meta{Title: "断点续传", Icon: "upload-filled"}},
{MenuLevel: 0, Hidden: false, ParentId: 11, Path: "customer", Name: "customer", Component: "view/example/customer/customer.vue", Sort: 7, Meta: Meta{Title: "客户列表(资源示例)", Icon: "avatar"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "systemTools", Name: "systemTools", Component: "view/systemTools/index.vue", Sort: 5, Meta: Meta{Title: "系统工具", Icon: "tools"}},
{MenuLevel: 0, Hidden: false, ParentId: 15, Path: "autoCode", Name: "autoCode", Component: "view/systemTools/autoCode/index.vue", Sort: 1, Meta: Meta{Title: "代码生成器", Icon: "cpu", KeepAlive: true}},
{MenuLevel: 0, Hidden: false, ParentId: 15, Path: "formCreate", Name: "formCreate", Component: "view/systemTools/formCreate/index.vue", Sort: 2, Meta: Meta{Title: "表单生成器", Icon: "magic-stick", KeepAlive: true}},
{MenuLevel: 0, Hidden: false, ParentId: 15, Path: "system", Name: "system", Component: "view/systemTools/system/system.vue", Sort: 3, Meta: Meta{Title: "系统配置", Icon: "operation"}},
{MenuLevel: 0, Hidden: false, ParentId: 15, Path: "autoCodeAdmin", Name: "autoCodeAdmin", Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 1, Meta: Meta{Title: "自动化代码管理", Icon: "magic-stick"}},
{MenuLevel: 0, Hidden: true, ParentId: 15, Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: Meta{Title: "自动化代码-${id}", Icon: "magic-stick"}},
{MenuLevel: 0, Hidden: false, ParentId: 15, Path: "autoPkg", Name: "autoPkg", Component: "view/systemTools/autoPkg/autoPkg.vue", Sort: 0, Meta: Meta{Title: "自动化package", Icon: "folder"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Component: "/", Sort: 0, Meta: Meta{Title: "官方网站", Icon: "customer-gva"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "state", Name: "state", Component: "view/system/state.vue", Sort: 8, Meta: Meta{Title: "服务器状态", Icon: "cloudy"}},
{MenuLevel: 0, Hidden: false, ParentId: 0, Path: "plugin", Name: "plugin", Component: "view/routerHolder.vue", Sort: 6, Meta: Meta{Title: "插件系统", Icon: "cherry"}},
{MenuLevel: 0, Hidden: false, ParentId: 24, Path: "https://plugin.gin-vue-admin.com/", Name: "https://plugin.gin-vue-admin.com/", Component: "https://plugin.gin-vue-admin.com/", Sort: 0, Meta: Meta{Title: "插件市场", Icon: "shop"}},
{MenuLevel: 0, Hidden: false, ParentId: 24, Path: "installPlugin", Name: "installPlugin", Component: "view/systemTools/installPlugin/index.vue", Sort: 1, Meta: Meta{Title: "插件安装", Icon: "box"}},
{MenuLevel: 0, Hidden: false, ParentId: 24, Path: "autoPlug", Name: "autoPlug", Component: "view/systemTools/autoPlug/autoPlug.vue", Sort: 2, Meta: Meta{Title: "插件模板", Icon: "folder"}},
{MenuLevel: 0, Hidden: false, ParentId: 24, Path: "pubPlug", Name: "pubPlug", Component: "view/systemTools/pubPlug/pubPlug.vue", Sort: 3, Meta: Meta{Title: "打包插件", Icon: "files"}},
{MenuLevel: 0, Hidden: false, ParentId: 24, Path: "plugin-email", Name: "plugin-email", Component: "plugin/email/view/index.vue", Sort: 4, Meta: Meta{Title: "邮件插件", Icon: "message"}},
{MenuLevel: 0, Hidden: false, ParentId: 15, Path: "exportTemplate", Name: "exportTemplate", Component: "view/systemTools/exportTemplate/exportTemplate.vue", Sort: 10, Meta: Meta{Title: "表格模板", Icon: "reading"}},
}
if err = db.Create(&entities).Error; err != nil {
return ctx, errors.Wrap(err, SysBaseMenu{}.TableName()+"表数据初始化失败!")
+17 -1
View File
@@ -111,7 +111,15 @@ func RollRouterBack(pk, model string) {
}
var block *ast.BlockStmt
var routerStmt *ast.FuncDecl
ast.Inspect(astFile, func(node ast.Node) bool {
if n, ok := node.(*ast.FuncDecl); ok {
if n.Name.Name == "Routers" {
routerStmt = n
}
}
if n, ok := node.(*ast.BlockStmt); ok {
ast.Inspect(n, func(bNode ast.Node) bool {
if in, ok := bNode.(*ast.Ident); ok {
@@ -146,7 +154,15 @@ func RollRouterBack(pk, model string) {
if len(block.List) == 1 {
// 说明这个块就没任何意义了
block.List = nil
// TODO 删除空的{}
}
for i, n := range routerStmt.Body.List {
if n, ok := n.(*ast.BlockStmt); ok {
if n.List == nil {
routerStmt.Body.List = append(append([]ast.Stmt{}, routerStmt.Body.List[:i]...), routerStmt.Body.List[i+1:]...)
i--
}
}
}
var out []byte
+3
View File
@@ -79,6 +79,9 @@ func AddRouterCode(path, funcName, pk, model string) {
&ast.Ident{
Name: "PrivateGroup",
},
&ast.Ident{
Name: "PublicGroup",
},
},
},
})
+4 -4
View File
@@ -169,19 +169,19 @@ func (t *timer) FindCronList() map[string]*taskManager {
}
// StartCron 开始任务
func (t *timer) StartCron(cromName string) {
func (t *timer) StartCron(cronName string) {
t.Lock()
defer t.Unlock()
if v, ok := t.cronList[cromName]; ok {
if v, ok := t.cronList[cronName]; ok {
v.corn.Start()
}
}
// StopCron 停止任务
func (t *timer) StopCron(cromName string) {
func (t *timer) StopCron(cronName string) {
t.Lock()
defer t.Unlock()
if v, ok := t.cronList[cromName]; ok {
if v, ok := t.cronList[cronName]; ok {
v.corn.Stop()
}
}
+2 -2
View File
@@ -5,7 +5,7 @@ import (
"io"
"mime/multipart"
"os"
"path"
"path/filepath"
"strings"
"time"
@@ -27,7 +27,7 @@ type Local struct{}
func (*Local) UploadFile(file *multipart.FileHeader) (string, string, error) {
// 读取文件后缀
ext := path.Ext(file.Filename)
ext := filepath.Ext(file.Filename)
// 读取文件名并加密
name := strings.TrimSuffix(file.Filename, ext)
name = utils.MD5V([]byte(name))
+1 -1
View File
@@ -3,7 +3,7 @@ package utils
var (
IdVerify = Rules{"ID": []string{NotEmpty()}}
ApiVerify = Rules{"Path": {NotEmpty()}, "Description": {NotEmpty()}, "ApiGroup": {NotEmpty()}, "Method": {NotEmpty()}}
MenuVerify = Rules{"Path": {NotEmpty()}, "ParentId": {NotEmpty()}, "Name": {NotEmpty()}, "Component": {NotEmpty()}, "Sort": {Ge("0")}}
MenuVerify = Rules{"Path": {NotEmpty()}, "Name": {NotEmpty()}, "Component": {NotEmpty()}, "Sort": {Ge("0")}}
MenuMetaVerify = Rules{"Title": {NotEmpty()}}
LoginVerify = Rules{"CaptchaId": {NotEmpty()}, "Username": {NotEmpty()}, "Password": {NotEmpty()}}
RegisterVerify = Rules{"Username": {NotEmpty()}, "NickName": {NotEmpty()}, "Password": {NotEmpty()}, "AuthorityId": {NotEmpty()}}
+2 -241
View File
@@ -11,246 +11,7 @@ module.exports = {
},
extends: ['plugin:vue/recommended', 'eslint:recommended'],
rules: {
'vue/no-v-model-argument': 0,
'vue/max-attributes-per-line': 2,
'vue/singleline-html-element-content-newline': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'off',
'accessor-pairs': 2,
'arrow-spacing': [
2,
{
before: true,
after: true
}
],
'block-spacing': [2, 'always'],
'brace-style': [
2,
'1tbs',
{
allowSingleLine: true
}
],
camelcase: [
0,
{
properties: 'always'
}
],
'comma-dangle': [2, 'only-multiline'],
'comma-spacing': [
2,
{
before: false,
after: true
}
],
'comma-style': [2, 'last'],
'constructor-super': 2,
curly: [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
eqeqeq: ['error', 'always', { null: 'ignore' }],
'generator-star-spacing': [
2,
{
before: true,
after: true
}
],
'handle-callback-err': [2, '^(err|error)$'],
indent: [
2,
2,
{
SwitchCase: 1
}
],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [
2,
{
beforeColon: false,
afterColon: true
}
],
'keyword-spacing': [
2,
{
before: true,
after: true
}
],
'new-cap': [
2,
{
newIsCap: true,
capIsNew: false
}
],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [
2,
{
allowLoop: false,
allowSwitch: false
}
],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [
2,
{
max: 1
}
],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 'off',
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [
2,
{
defaultAssignment: false
}
],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [
2,
{
vars: 'all',
args: 'none'
}
],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [
2,
{
initialized: 'never'
}
],
'operator-linebreak': [
2,
'after',
{
overrides: {
'?': 'before',
':': 'before'
}
}
],
'padded-blocks': [2, 'never'],
quotes: [
2,
'single',
{
avoidEscape: true,
allowTemplateLiterals: true
}
],
semi: [2, 'never'],
'semi-spacing': [
2,
{
before: false,
after: true
}
],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [
2,
{
words: true,
nonwords: false
}
],
'spaced-comment': [
2,
'always',
{
markers: ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}
],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
yoda: [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [
2,
'always',
{
objectsInObjects: false
}
],
'array-bracket-spacing': [2, 'never']
"vue/max-attributes-per-line" : 0,
"vue/no-v-model-argument" : 0
}
}
+9 -2
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="zh-cn">
<html lang="zh-cn" class="transition-colors">
<head>
<meta charset="utf-8">
@@ -8,6 +8,13 @@
<meta content="Gin,Vue,Admin.Gin-Vue-Admin,GVA,gin-vue-admin,后台管理框架,vue后台管理框架,gin-vue-admin文档,gin-vue-admin首页,gin-vue-admin" name="keywords" />
<link rel="icon" href="favicon.ico">
<title></title>
<style>
.transition-colors{
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
</style>
</head>
<body>
@@ -15,4 +22,4 @@
<script type="module" src="./src/main.js"></script>
</body>
</html>
</html>
+2 -4
View File
@@ -1,8 +1,6 @@
/*
商用代码公司自用产品无需授权
若作为代码出售的产品(任何涉及代码交付第三方作为后续开发)必须保留此脚本
或标注原作者信息
否则将依法维权
此文件受版权保护,未经授权禁止修改!如果您尚未获得授权,请通过微信(shouzi_1994)联系我们以购买授权。在未授权状态下,只需保留此代码,不会影响任何正常使用。
未经授权的商用使用可能会被我们的资产搜索引擎爬取,并可能导致后续索赔。索赔金额将不低于高级授权费的十倍。请您遵守版权法律法规,尊重知识产权。
*/
var child_process = require('child_process')
+6 -3
View File
@@ -1,6 +1,6 @@
{
"name": "gin-vue-admin",
"version": "2.6.2",
"version": "2.6.4",
"private": true,
"scripts": {
"serve": "node openDocument.js && vite --host --mode development",
@@ -20,7 +20,7 @@
"axios": "^1.4.0",
"core-js": "^3.31.1",
"echarts": "5.4.3",
"element-plus": "^2.3.8",
"element-plus": "^2.7.0",
"highlight.js": "^11.8.0",
"js-cookie": "^3.0.5",
"marked": "4.3.0",
@@ -30,10 +30,13 @@
"pinia": "^2.1.4",
"qs": "^6.11.2",
"screenfull": "^6.0.2",
"sortablejs": "^1.15.2",
"spark-md5": "^3.0.2",
"tailwindcss": "^3.3.3",
"vue": "^3.4.21",
"vue-router": "^4.2.4"
"vue-echarts": "^6.7.2",
"vue-router": "^4.3.2",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.22.9",
+16 -6
View File
@@ -1,5 +1,5 @@
<template>
<div id="app">
<div id="app" class="bg-gray-50 text-slate-700 dark:text-slate-500 dark:bg-slate-800">
<el-config-provider :locale="zhCn">
<router-view />
</el-config-provider>
@@ -10,19 +10,17 @@
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
// element 2.3.8之前使用下面的语句
// import zhCn from 'element-plus/lib/locale/lang/zh-cn'
import {useAppStore} from "@/pinia";
useAppStore()
defineOptions({
name: 'App'
})
</script>
<style lang="scss">
@tailwind base;
@tailwind components;
@tailwind utilities;
// 引入初始化样式
#app {
background: #eee;
height: 100vh;
overflow: hidden;
font-weight: 400 !important;
@@ -30,4 +28,16 @@ defineOptions({
.el-button{
font-weight: 400 !important;
}
.gva-body-h{
min-height: calc(100% - 3rem);
}
.gva-container{
height: calc(100% - 2.5rem);
}
.gva-container2{
height: calc(100% - 4.5rem);
}
</style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@@ -0,0 +1,41 @@
<!--
此文件受版权保护,未经授权禁止修改!如果您尚未获得授权,请通过微信(shouzi_1994)联系我们以购买授权。在未授权状态下,只需保留此代码,不会影响任何正常使用。
未经授权的商用使用可能会被我们的资产搜索引擎爬取,并可能导致后续索赔。索赔金额将不低于高级授权费的十倍。请您遵守版权法律法规,尊重知识产权。
-->
<template>
<div class="flex flex-col md:flex-row gap-2 items-center text-sm text-slate-700 dark:text-slate-500 justify-center py-2">
<div class="text-center">
<span class="mr-1">Powered by</span>
<span>
<a
class="font-bold text-active"
href="https://github.com/flipped-aurora/gin-vue-admin"
>Gin-Vue-Admin</a>
</span>
</div>
<slot />
<div class="text-center">
<span class="mr-1">Copyright</span>
<span>
<a
class="font-bold text-active"
href="https://github.com/flipped-aurora"
>flipped-aurora团队</a>
</span>
</div>
</div>
</template>
<script setup>
defineOptions({
name: 'BottomInfo'
})
console.log(
`%c powered by %c flipped-aurorae %c`,
'background:#0081ff; padding: 1px; border-radius: 3px 0 0 3px; color: #fff',
'background:#354855; padding: 1px 5px; border-radius: 0 3px 3px 0; color: #fff; font-weight: bold;',
'background:transparent'
)
</script>
+56
View File
@@ -0,0 +1,56 @@
<!--
本组件参考 arco-pro 的实现
https://github.com/arco-design/arco-design-pro-vue/blob/main/arco-design-pro-vite/src/components/chart/index.vue
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<VCharts
v-if="renderChart"
:option="options"
:autoresize="autoResize"
:style="{ width, height }"
/>
</template>
<script setup>
import { ref, nextTick } from 'vue';
import VCharts from 'vue-echarts';
import { useWindowResize } from '@/hooks/use-windows-resize'
defineProps({
options: {
type: Object,
default() {
return {};
},
},
autoResize: {
type: Boolean,
default: true,
},
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '100%',
},
});
const renderChart = ref(false);
nextTick(() => {
renderChart.value = true;
});
useWindowResize(()=>{
renderChart.value = false;
nextTick(() => {
renderChart.value = true;
});
})
</script>
<style scoped lang="less"></style>
-222
View File
@@ -1,222 +0,0 @@
<template>
<el-drawer
v-model="drawer"
title="媒体库"
size="650px"
>
<warning-bar
title="点击“文件名/备注”可以编辑文件名或者备注内容。"
/>
<div class="gva-btn-list">
<upload-common
:image-common="imageCommon"
class="upload-btn-media-library"
@on-success="open"
/>
<upload-image
:image-url="imageUrl"
:file-size="512"
:max-w-h="1080"
class="upload-btn-media-library"
@on-success="open"
/>
<el-form
ref="searchForm"
:inline="true"
:model="search"
>
<el-form-item label="">
<el-input
v-model="search.keyword"
class="keyword"
placeholder="请输入文件名或备注"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="search"
@click="open"
>查询</el-button>
</el-form-item>
</el-form>
</div>
<div class="media">
<div
v-for="(item,key) in picList"
:key="key"
class="media-box"
>
<div class="header-img-box-list">
<el-image
:key="key"
:src="getUrl(item.url)"
@click="chooseImg(item.url,target,targetKey)"
>
<template #error>
<div class="header-img-box-list">
<el-icon>
<picture />
</el-icon>
</div>
</template>
</el-image>
</div>
<div
class="img-title"
@click="editFileNameFunc(item)"
>{{ item.name }}</div>
</div>
</div>
<el-pagination
:current-page="page"
:page-size="pageSize"
:total="total"
:style="{'justify-content':'center'}"
layout="total, prev, pager, next, jumper"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</el-drawer>
</template>
<script setup>
import { getUrl } from '@/utils/image'
import { ref } from 'vue'
import { getFileList, editFileName } from '@/api/fileUploadAndDownload'
import UploadImage from '@/components/upload/image.vue'
import UploadCommon from '@/components/upload/common.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import WarningBar from '@/components/warningBar/warningBar.vue'
const imageUrl = ref('')
const imageCommon = ref('')
const search = ref({})
const page = ref(1)
const total = ref(0)
const pageSize = ref(20)
// 分页
const handleSizeChange = (val) => {
pageSize.value = val
open()
}
const handleCurrentChange = (val) => {
page.value = val
open()
}
const emit = defineEmits(['enterImg'])
defineProps({
target: {
type: Object,
default: null,
},
targetKey: {
type: String,
default: '',
},
})
const drawer = ref(false)
const picList = ref([])
const chooseImg = (url, target, targetKey) => {
if (target && targetKey) {
target[targetKey] = url
}
emit('enterImg', url)
drawer.value = false
}
const open = async() => {
const res = await getFileList({ page: page.value, pageSize: pageSize.value, ...search.value })
if (res.code === 0) {
picList.value = res.data.list
total.value = res.data.total
page.value = res.data.page
pageSize.value = res.data.pageSize
drawer.value = true
}
}
/**
* 编辑文件名或者备注
* @param row
* @returns {Promise<void>}
*/
const editFileNameFunc = async(row) => {
ElMessageBox.prompt('请输入文件名或者备注', '编辑', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /\S/,
inputErrorMessage: '不能为空',
inputValue: row.name
}).then(async({ value }) => {
row.name = value
// console.log(row)
const res = await editFileName(row)
if (res.code === 0) {
ElMessage({
type: 'success',
message: '编辑成功!',
})
open()
}
}).catch(() => {
ElMessage({
type: 'info',
message: '取消修改'
})
})
}
defineExpose({ open })
</script>
<style lang="scss">
.upload-btn-media-library {
margin-left: 20px;
}
.media {
display: flex;
flex-wrap: wrap;
.media-box {
width: 120px;
margin-left: 20px;
.img-title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 36px;
text-align: center;
cursor: pointer;
}
.header-img-box-list {
width: 120px;
height: 120px;
border: 1px dashed #ccc;
border-radius: 8px;
text-align: center;
line-height: 120px;
cursor: pointer;
overflow: hidden;
.el-image__inner {
max-width: 120px;
max-height: 120px;
vertical-align: middle;
width: unset;
height: unset;
}
}
}
}
</style>
+8 -7
View File
@@ -41,17 +41,17 @@
<script setup>
import { reactive, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useRouter } from 'vue-router'
import { useRouterStore } from '@/pinia/modules/router'
import { useUserStore } from '@/pinia/modules/user'
import { useAppStore,useUserStore } from '@/pinia'
defineOptions({
name: 'CommandMenu',
})
const appStore = useAppStore()
const userStore = useUserStore()
const router = useRouter()
const route = useRouter()
const userStore = useUserStore()
const routerStore = useRouterStore()
const dialogVisible = ref(false)
const searchInput = ref('')
@@ -134,10 +134,10 @@ const changeRouter = (e) => {
const changeMode = (e) => {
if (e === null) {
userStore.changeSideMode('dark')
appStore.toggleTheme(false )
return
}
userStore.changeSideMode(e)
appStore.toggleTheme(true )
}
const close = () => {
@@ -172,6 +172,7 @@ watch(searchInput, () => {
color: #666;
}
.quick-input{
@apply bg-gray-50 dark:bg-gray-800;
color: #666;
border-radius: 4px 4px 0 0;
border:none;
@@ -186,8 +187,8 @@ watch(searchInput, () => {
padding: 8px;
margin: 4px 0;
&:hover{
@apply bg-gray-200 dark:bg-slate-500;
cursor: pointer;
background: #eee;
border-radius: 4px;
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<span class="headerAvatar">
<span class="headerAvatar ">
<template v-if="picType === 'avatar'">
<el-avatar
v-if="userStore.userInfo.headerImg"
+12 -31
View File
@@ -110,38 +110,25 @@
<div class="gva-btn-list">
<upload-common
:image-common="imageCommon"
class="upload-btn-media-library"
@on-success="getImageList"
/>
<upload-image
:image-url="imageUrl"
:file-size="512"
:max-w-h="1080"
class="upload-btn-media-library"
@on-success="getImageList"
/>
<el-form
ref="searchForm"
:inline="true"
:model="search"
>
<el-form-item label="">
<el-input
v-model="search.keyword"
class="keyword"
placeholder="请输入文件名或备注"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="search"
@click="getImageList"
>查询
</el-button>
</el-form-item>
</el-form>
<el-input
v-model="search.keyword"
class="keyword"
placeholder="请输入文件名或备注"
/>
<el-button
type="primary"
icon="search"
@click="getImageList"
>查询
</el-button>
</div>
<div class="media">
<div
@@ -424,18 +411,12 @@ const getImageList = async() => {
}
}
.upload-btn-media-library {
margin-left: 20px;
}
.media {
display: flex;
flex-wrap: wrap;
gap: 12px;
.media-box {
width: 120px;
margin-left: 20px;
.img-title {
white-space: nowrap;
overflow: hidden;
+1 -1
View File
@@ -1,6 +1,6 @@
<template>
<div
class="px-1.5 py-2 flex items-center bg-amber-50 rounded gap-2 mb-3 text-amber-500"
class="px-1.5 py-2 flex items-center bg-amber-50 gap-2 mb-3 text-amber-500 dark:bg-gray-900 dark:text-gray-200"
:class="href&&'cursor-pointer'"
@click="open"
>
+9
View File
@@ -0,0 +1,9 @@
{"weakness":false,
"grey":false,
"primaryColor":"#3b82f6",
"showTabs":true,
"darkMode":"auto",
"layout_side_width":200,
"layout_side_collapsed_width":60,
"layout_side_item_height":44,
"show_watermark":true}
+28 -3
View File
@@ -1,6 +1,7 @@
/**
* 网站配置文件
*/
import chalk from "chalk";
const config = {
appName: 'Gin-Vue-Admin',
@@ -19,7 +20,7 @@ export const viteLogo = (env) => {
)
console.log(
chalk.green(
`> 当前版本:v2.6.2`
`> 当前版本:v2.6.4`
)
)
console.log(
@@ -29,7 +30,7 @@ export const viteLogo = (env) => {
)
console.log(
chalk.green(
`> GVA讨论社区:https://support.qq.com/products/371961`
`> 项目地址:https://github.com/flipped-aurora/gin-vue-admin`
)
)
console.log(
@@ -37,6 +38,11 @@ export const viteLogo = (env) => {
`> 插件市场:https://plugin.gin-vue-admin.com`
)
)
console.log(
chalk.green(
`> GVA讨论社区:https://support.qq.com/products/371961`
)
)
console.log(
chalk.green(
`> 默认自动化文档地址:http://127.0.0.1:${env.VITE_SERVER_PORT}/swagger/index.html`
@@ -47,9 +53,28 @@ export const viteLogo = (env) => {
`> 默认前端文件运行地址:http://127.0.0.1:${env.VITE_CLI_PORT}`
)
)
console.log(
chalk.green(
`--------------------------------------版权声明--------------------------------------`
)
)
console.log(
chalk.green(
`** 版权所有方:flipped-aurora开源团队 **`
)
)
console.log(
chalk.green(
`** 版权持有公司:北京翻转极光科技有限责任公司 **`
)
)
console.log(
chalk.green(
`> 如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/coffee/index.html`
`** 剔除授权标识需购买商用授权:https://gin-vue-admin.com/empower/index.html **`
)
)
console.log('\n')
+7 -2
View File
@@ -10,13 +10,18 @@ export default {
register(app)
console.log(`
欢迎使用 Gin-Vue-Admin
当前版本:v2.6.2
当前版本:v2.6.4
加群方式:微信:shouzi_1994 QQ群:622360840
GVA讨论社区:https://support.qq.com/products/371961
项目地址:https://github.com/flipped-aurora/gin-vue-admin
插件市场:https://plugin.gin-vue-admin.com
GVA讨论社区:https://support.qq.com/products/371961
默认自动化文档地址:http://127.0.0.1:${import.meta.env.VITE_SERVER_PORT}/swagger/index.html
默认前端文件运行地址:http://127.0.0.1:${import.meta.env.VITE_CLI_PORT}
如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/coffee/index.html
--------------------------------------版权声明--------------------------------------
** 版权所有方:flipped-aurora开源团队 **
** 版权持有公司:北京翻转极光科技有限责任公司 **
** 剔除授权标识需购买商用授权:https://gin-vue-admin.com/empower/index.html **
`)
}
}
+19
View File
@@ -0,0 +1,19 @@
// 本组件参考 arco-pro 的实现
// https://github.com/arco-design/arco-design-pro-vue/blob/main/arco-design-pro-vite/src/hooks/chart-option.ts
import { computed } from 'vue';
import { useAppStore } from '@/pinia';
export default function useChartOption(sourceOption) {
const appStore = useAppStore();
const isDark = computed(() => {
return appStore.theme === 'dark';
});
const chartOption = computed(() => {
return sourceOption(isDark.value);
});
return {
chartOption,
};
}
+35
View File
@@ -0,0 +1,35 @@
// 本组件参考 arco-pro 的实现
// https://github.com/arco-design/arco-design-pro-vue/blob/main/arco-design-pro-vite/src/hooks/responsive.ts
import { onMounted, onBeforeMount, onBeforeUnmount } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import { useAppStore } from '@/pinia';
import { addEventListen, removeEventListen } from '@/utils/event';
const WIDTH = 992;
function queryDevice() {
const rect = document.body.getBoundingClientRect();
return rect.width - 1 < WIDTH;
}
export default function useResponsive(immediate) {
const appStore = useAppStore();
function resizeHandler() {
if (!document.hidden) {
const isMobile = queryDevice();
appStore.toggleDevice(isMobile ? 'mobile' : 'desktop');
// appStore.toggleDevice(isMobile);
}
}
const debounceFn = useDebounceFn(resizeHandler, 100);
onMounted(() => {
if (immediate) debounceFn();
});
onBeforeMount(() => {
addEventListen(window, 'resize', debounceFn);
});
onBeforeUnmount(() => {
removeEventListen(window, 'resize', debounceFn);
});
}
+2
View File
@@ -2,7 +2,9 @@ import 'element-plus/es/components/message/style/css'
import 'element-plus/es/components/loading/style/css'
import 'element-plus/es/components/notification/style/css'
import 'element-plus/es/components/message-box/style/css'
import 'element-plus/theme-chalk/dark/css-vars.css'
import './style/element_visiable.scss'
import { createApp } from 'vue'
// 引入gin-vue-admin前端初始化相关内容
import './core/gin-vue-admin'
+7 -1
View File
@@ -1,7 +1,13 @@
import { createPinia } from 'pinia'
import { useAppStore } from '@/pinia/modules/app'
import { useUserStore } from '@/pinia/modules/user'
import { useDictionaryStore } from '@/pinia/modules/dictionary'
const store = createPinia()
export {
store
store,
useAppStore,
useUserStore,
useDictionaryStore
}
+144
View File
@@ -0,0 +1,144 @@
import { defineStore } from 'pinia'
import { ref, watchEffect, reactive } from 'vue'
import originSetting from "@/config.json"
import { setBodyPrimaryColor } from '@/utils/format'
export const useAppStore = defineStore('app', () => {
const theme = ref(localStorage.getItem('theme') || 'light')
const device = ref("")
const config = reactive({
weakness: false,
grey: false,
primaryColor: '#79B6E7',
showTabs: true,
darkMode: 'light',
layout_side_width : 256,
layout_side_collapsed_width : 80,
layout_side_item_height : 48,
show_watermark: false,
})
// 初始化配置
Object.keys(originSetting).forEach(key => {
config[key] = originSetting[key]
if(key === 'primaryColor'){
setBodyPrimaryColor(originSetting[key])
}
})
if (localStorage.getItem('darkMode')) {
config.darkMode = localStorage.getItem('darkMode')
}
watchEffect(() =>{
if (theme.value === 'dark'){
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
localStorage.setItem('theme', 'dark');
}else{
document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
}
})
const toggleTheme = (dark) => {
if (dark) {
theme.value = 'dark';
} else {
theme.value = 'light';
}
}
const toggleWeakness = (e) => {
config.weakness = e;
if(e) {
document.documentElement.classList.add('html-weakenss');
}else{
document.documentElement.classList.remove('html-weakenss');
}
}
const toggleGrey = (e) => {
config.grey = e;
if(e) {
document.documentElement.classList.add('html-grey');
}else{
document.documentElement.classList.remove('html-grey');
}
}
const togglePrimaryColor = (e) => {
config.primaryColor = e;
setBodyPrimaryColor(e)
}
const toggleTabs = (e) => {
config.showTabs = e;
}
const toggleDevice = (e) => {
device.value = e;
}
const toggleDarkMode = (e) => {
config.darkMode = e
localStorage.setItem('darkMode', e)
if(e === 'dark'){
toggleTheme(true)
}else{
toggleTheme(false)
}
}
const toggleDarkModeAuto = () =>{
// 处理浏览器主题
const darkQuery = window.matchMedia('(prefers-color-scheme: dark)')
const dark = darkQuery.matches
toggleTheme(dark)
darkQuery.addEventListener('change', (e) => {
toggleTheme(e.matches)
})
}
const toggleConfigSideWidth = (e) => {
config.layout_side_width = e;
}
const toggleConfigSideCollapsedWidth = (e) => {
config.layout_side_collapsed_width = e;
}
const toggleConfigSideItemHeight = (e) => {
config.layout_side_item_height = e;
}
const toggleConfigWatermark = (e) => {
config.show_watermark = e;
}
if(config.darkMode === 'auto'){
toggleDarkModeAuto()
}
return {
theme,
device,
config,
toggleTheme,
toggleDevice,
toggleWeakness,
toggleGrey,
togglePrimaryColor,
toggleTabs,
toggleDarkMode,
toggleConfigSideWidth,
toggleConfigSideCollapsedWidth,
toggleConfigSideItemHeight,
toggleConfigWatermark
}
})
-5
View File
@@ -16,7 +16,6 @@ export const useUserStore = defineStore('user', () => {
headerImg: '',
authority: {},
sideMode: 'dark',
activeColor: 'var(--el-color-primary)',
baseColor: '#fff'
})
const token = ref(window.localStorage.getItem('token') || cookie.get('x-token') || '')
@@ -134,9 +133,6 @@ export const useUserStore = defineStore('user', () => {
return userInfo.value.baseColor
}
})
const activeColor = computed(() => {
return 'var(--el-color-primary)'
})
watch(() => token.value, () => {
window.localStorage.setItem('token', token.value)
@@ -155,7 +151,6 @@ export const useUserStore = defineStore('user', () => {
sideMode,
setToken,
baseColor,
activeColor,
loadingInstance,
ClearStorage
}
+47 -131
View File
@@ -1,4 +1,8 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import '@/style/main.scss';
@import "@/style/reset";
.el-button {
font-weight: 400;
@@ -42,26 +46,30 @@
}
.admin-box {
@apply min-h-[calc(100vh-200px)] px-3 py-4 mt-28 mb-4 mx-1;
.el-table {
.el-table {
tr{
th {
@apply px-0 py-2;
@apply dark:bg-slate-900;
.cell {
@apply leading-[40px] text-gray-700;
@apply leading-[36px] text-gray-700 dark:text-gray-200;
}
}
}
.el-table__row {
td {
@apply px-0 py-2;
@apply dark:bg-slate-900;
.cell {
@apply leading-[40px] text-gray-600;
@apply leading-[32px] text-gray-600 dark:text-gray-300;
}
}
.is-leaf {
@apply border-b border-t-0 border-l-0 border-solid border-gray-50;
border-right:var(--el-table-border);
background: #F7FBFF !important;
}
tr{
th{
&.is-leaf {
@apply dark:bg-slate-900;
}
}
}
}
@@ -72,136 +80,44 @@
@apply mt-8;
.btn-prev,
.btn-next {
@apply border border-solid border-gray-300 rounded;
@apply border border-solid border-gray-300 dark:border-gray-700 rounded;
}
.el-pager {
li {
@apply border border-solid border-gray-300 rounded text-gray-600 text-sm mx-1;
@apply border border-solid border-gray-300 dark:border-gray-600 rounded text-gray-600 text-sm mx-1;
}
}
}
// 导航
#app {
.el-container {
@apply relative h-full w-full;
.el-menu{
li{
@apply my-1;
}
.el-container.mobile.openside {
@apply fixed top-0;
}
.gva-aside {
@apply fixed top-0 left-0 z-[1001] overflow-hidden;
.el-menu {
@apply border-r-0;
}
}
.aside {
.el-menu--collapse {
>.el-menu-item {
display: flex;
justify-content: center;
}
}
.el-sub-menu {
.el-menu {
.is-active {
// 关闭三级菜单二级菜单样式
ul {
border: none;
}
}
// 关闭三级菜单二级菜单样式
.is-active.is-opened {
ul {
border: none;
}
}
}
}
}
.hideside {
.aside {
@apply w-[54px]
}
}
.mobile {
.gva-aside {
@apply w-[54px];
}
}
.hideside {
.main-cont.el-main {
@apply ml-[54px];
}
}
.mobile {
.main-cont.el-main {
@apply ml-0;
}
}
.el-menu-item{
border-radius: 2px;
&.is-active{
background-color: var(--el-color-primary) !important;
color: #fff !important;
}
}
.el-container.layout-cont {
.header-cont {
@apply px-4 h-16 bg-white;
}
.el-tabs__header{
margin: 0 0 1px !important;
}
.main-cont {
@apply h-screen overflow-visible;
&.el-main {
@apply min-h-full ml-[220px] bg-main p-0 overflow-auto;
}
.breadcrumb {
@apply h-16 flex items-center p-0 ml-12 text-lg;
.el-breadcrumb__item {
.el-breadcrumb__inner {
@apply text-gray-600;
}
}
.el-breadcrumb__item:nth-last-child(1) {
.el-breadcrumb__inner {
@apply text-gray-600;
}
}
}
.router-history {
@apply bg-white p-0 border-t border-l-0 border-r-0 border-b-0 border-solid border-gray-100;
.el-tabs__header {
@apply m-0;
.el-tabs__item{
@apply border-solid border-r border-t-0 border-gray-100 border-b-0 border-l-0;
}
.el-tabs__item.is-active {
@apply bg-blue-500 bg-opacity-5;
}
.el-tabs__nav {
@apply border-0;
}
}
}
.aside {
@apply overflow-auto;
}
.el-menu-vertical {
@apply h-[calc(100vh-60px)];
&:not(.el-menu--collapse) {
@apply w-[220px];
}
}
.el-menu--collapse {
@apply w-[54px];
li {
.el-tooltip,
.el-sub-menu__title {
@apply px-4;
}
}
}
.el-sub-menu.is-active{
.el-sub-menu__title{
color: var(--el-color-primary) !important;
}
}
.el-sub-menu__title.el-tooltip__trigger,
.el-menu-item .el-menu-tooltip__trigger{
justify-content: center;
}
html.dark {
/* 自定义深色背景颜色 */
--el-bg-color: rgb(30 ,41 ,59);
--el-bg-color-overlay: rgba(30 ,41 ,59, 0.8);
}
+11 -507
View File
@@ -1,514 +1,19 @@
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
@import '@/style/iconfont.css';
html {
line-height: 1.15;
/* 1 */
-webkit-text-size-adjust: 100%;
/* 2 */
.html-grey{
filter: grayscale(100%);
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box;
/* 1 */
height: 0;
/* 1 */
overflow: visible;
/* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none;
/* 1 */
text-decoration: underline;
/* 2 */
text-decoration: underline dotted;
/* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
/* 1 */
font-size: 100%;
/* 1 */
line-height: 1.15;
/* 1 */
margin: 0;
/* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box;
/* 1 */
color: inherit;
/* 2 */
display: table;
/* 1 */
max-width: 100%;
/* 1 */
padding: 0;
/* 3 */
white-space: normal;
/* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box;
/* 1 */
padding: 0;
/* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
HTML,
body,
div,
ul,
ol,
dl,
li,
dt,
dd,
p,
blockquote,
pre,
form,
fieldset,
table,
th,
td {
border: none;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-size: 14px;
margin: 0px;
padding: 0px;
}
html,
body {
height: 100%;
width: 100%;
}
address,
caption,
cite,
code,
th,
var {
font-style: normal;
font-weight: normal;
}
a {
text-decoration: none;
}
input::-ms-clear {
display: none;
}
input::-ms-reveal {
display: none;
}
input {
-webkit-appearance: none;
margin: 0;
outline: none;
padding: 0;
}
input::-webkit-input-placeholder {
color: #ccc;
}
input::-ms-input-placeholder {
color: #ccc;
}
input::-moz-placeholder {
color: #ccc;
}
input[type=submit],
input[type=button] {
cursor: pointer;
}
button[disabled],
input[disabled] {
cursor: default;
}
img {
border: none;
}
ul,
ol,
li {
list-style-type: none;
.html-weakenss{
filter: invert(80%);
}
.gva-table-box {
@apply p-6 bg-white rounded;
@apply p-4 bg-white text-slate-700 dark:text-slate-400 dark:bg-slate-900 rounded m-2;
.el-table{
@apply border-x border-t border-b-0 rounded border-table-border border-solid;
}
}
.gva-btn-list {
@@ -523,16 +28,15 @@ li {
@apply w-4 h-4;
}
::-webkit-scrollbar {
@apply hidden;
}
.gva-search-box {
@apply p-6 pb-0.5 bg-white rounded mb-3;
@apply p-4 bg-white text-slate-700 dark:text-slate-400 dark:bg-slate-900 rounded m-2;
}
.gva-form-box {
@apply p-6 bg-white rounded;
@apply p-4 bg-white text-slate-700 dark:text-slate-400 dark:bg-slate-900 rounded m-2;
}
+508
View File
@@ -0,0 +1,508 @@
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
@import '@/style/iconfont.css';
html {
line-height: 1.15;
/* 1 */
-webkit-text-size-adjust: 100%;
/* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box;
/* 1 */
height: 0;
/* 1 */
overflow: visible;
/* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none;
/* 1 */
text-decoration: underline;
/* 2 */
text-decoration: underline dotted;
/* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
/* 1 */
font-size: 100%;
/* 1 */
line-height: 1.15;
/* 1 */
margin: 0;
/* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box;
/* 1 */
color: inherit;
/* 2 */
display: table;
/* 1 */
max-width: 100%;
/* 1 */
padding: 0;
/* 3 */
white-space: normal;
/* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box;
/* 1 */
padding: 0;
/* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
HTML,
body,
div,
ul,
ol,
dl,
li,
dt,
dd,
p,
blockquote,
pre,
form,
fieldset,
table,
th,
td {
border: none;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-size: 14px;
margin: 0px;
padding: 0px;
}
html,
body {
height: 100%;
width: 100%;
}
address,
caption,
cite,
code,
th,
var {
font-style: normal;
font-weight: normal;
}
a {
text-decoration: none;
}
input::-ms-clear {
display: none;
}
input::-ms-reveal {
display: none;
}
input {
-webkit-appearance: none;
margin: 0;
outline: none;
padding: 0;
}
input::-webkit-input-placeholder {
color: #ccc;
}
input::-ms-input-placeholder {
color: #ccc;
}
input::-moz-placeholder {
color: #ccc;
}
input[type=submit],
input[type=button] {
cursor: pointer;
}
button[disabled],
input[disabled] {
cursor: default;
}
img {
border: none;
}
ul,
ol,
li {
list-style-type: none;
}
+29
View File
@@ -0,0 +1,29 @@
export function addEventListen(
target,
event,
handler,
capture = false
) {
if (
target.addEventListener &&
typeof target.addEventListener === 'function'
) {
target.addEventListener(event, handler, capture);
}
}
export function removeEventListen(
target,
event,
handler,
capture = false
) {
if (
target.removeEventListener &&
typeof target.removeEventListener === 'function'
) {
target.removeEventListener(event, handler, capture);
}
}
+43
View File
@@ -22,6 +22,11 @@ export const filterDict = (value, options) => {
return rowLabel && rowLabel[0] && rowLabel[0].label
}
export const filterDataSource = (dataSource, value) => {
const rowLabel = dataSource && dataSource.find(item => item.value === value)
return rowLabel?.label
}
export const getDictFunc = async(type) => {
const dicts = await getDict(type)
return dicts
@@ -51,3 +56,41 @@ export const ReturnArrImg = (arr) => {
export const onDownloadFile = (url) => {
window.open(path + url)
}
const colorToHex = u=>{
let e = u.replace("#", "").match(/../g);
for (let t = 0; t < 3; t++)
e[t] = parseInt(e[t], 16);
return e
}
const hexToColor = (u,e,t)=>{
let a = [u.toString(16), e.toString(16), t.toString(16)];
for (let n = 0; n < 3; n++)
a[n].length === 1 && (a[n] = `0${a[n]}`);
return `#${a.join("")}`
}
const generateAllColors = (u,e)=> {
let t = colorToHex(u);
for (let a = 0; a < 3; a++)
t[a] = Math.floor((255 - t[a]) * e + t[a]);
return hexToColor(t[0], t[1], t[2])
}
function addOpacityToColor(u, opacity) {
let t = colorToHex(u);
return `rgba(${t[0]}, ${t[1]}, ${ t[2]}, ${opacity})`;
}
export const setBodyPrimaryColor = ( primaryColor ) =>{
document.documentElement.style.setProperty('--el-color-primary', primaryColor)
for (let times = 1; times <= 2; times++) {
document.documentElement.style.setProperty(`--el-color-primary-dark-${times}`, generateAllColors(primaryColor, times / 10))
}
for (let times = 1; times <= 10; times++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${times}`, generateAllColors(primaryColor, times / 10))
}
document.documentElement.style.setProperty(`--el-menu-hover-bg-color`, addOpacityToColor(primaryColor, 0.1))
}
+11 -21
View File
@@ -1,5 +1,5 @@
<template>
<div>
<div class="mt-2">
<el-row :gutter="10">
<el-col :span="12">
<el-card>
@@ -71,27 +71,24 @@
</a>
</el-col>
</el-row>
<el-row
style="margin-left: 40px"
:gutter="20"
>
<el-col
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mt-4">
<div
v-for="(item, index) in members"
:key="index"
:span="8"
>
<a :href="item.html_url">
<a :href="item.html_url" class="flex items-center">
<img
class="avatar-img"
class="w-8 h-8 rounded-full"
:src="item.avatar_url"
>
<a
class="author-name"
class=" text-blue-700 ml-2 text-xl font-bold font-sans "
style=""
>{{ item.login }}</a>
</a>
</el-col>
</el-row>
</div>
</div>
</div>
</el-card>
</el-col>
@@ -120,7 +117,9 @@
type="primary"
link
@click="loadMore"
>Load more</el-button>
>
Load more
</el-button>
</el-card>
</el-col>
</el-row>
@@ -191,15 +190,6 @@ loadMembers()
width: 150px;
}
.author-name {
float: left;
line-height: 65px !important;
margin-left: 10px;
color: darkblue;
line-height: 100px;
font-family: "Lucida Sans", "Lucida Sans Regular", "Lucida Grande",
"Lucida Sans Unicode", Geneva, Verdana, sans-serif;
}
.dom-center {
margin-left: 50%;
@@ -0,0 +1,36 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<el-carousel class="-mt-2">
<el-carousel-item class="cursor-pointer lg:h-40" v-for="(item , index) in banners" :key="index" @click="openLink(item.link)">
<el-image class="h-full w-full" :src="item.img" fit="fill"></el-image>
</el-carousel-item>
</el-carousel>
</template>
<script setup>
import banner from "@/assets/banner.jpg"
import banner2 from "@/assets/banner2.jpg"
const openLink = (link) => {
window.open(link, '_blank')
}
const banners = [
{
img: banner,
link: "https://gin-vue-admin.com/empower/index.html"
},
{
img: banner2,
link: "https://plugin.gin-vue-admin.com"
}
]
</script>
<style scoped lang="scss">
</style>
@@ -0,0 +1,51 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div
class="bg-white dark:bg-slate-900 text-gray-800 dark:text-gray-400 rounded shadow" :class="[
customClass || '',
withoutPadding ? 'p-0' : 'p-4'
]"
>
<div v-if="title" class="flex justify-between items-center">
<div class="text-base font-bold">
{{ title }}
</div>
<div v-if="showAction" class="text-sm text-active cursor-pointer">
查看更多
</div>
</div>
<div class="mt-2">
<slot />
</div>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
default: ''
},
showAction: {
type: Boolean,
default: false
},
customClass: {
type: String,
default: ''
},
withoutPadding: {
type: Boolean,
default: false
}
})
</script>
<style scoped lang="scss">
</style>
@@ -0,0 +1,183 @@
<!--
本组件参考 arco-pro 的实现 将 ts 改为 js 写法
https://github.com/arco-design/arco-design-pro-vue/blob/main/arco-design-pro-vite/src/views/dashboard/workplace/components/content-chart.vue
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<Chart :height="height" :option="chartOption" />
</template>
<script setup>
import Chart from "@/components/charts/index.vue";
import useChartOption from '@/hooks/charts';
import { graphic } from 'echarts'
import { computed, ref } from 'vue'
import { useAppStore } from '@/pinia'
import { storeToRefs } from 'pinia'
const appStore = useAppStore()
const { config } = storeToRefs(appStore)
defineProps({
height: {
type: String,
default: '128px',
},
})
const dotColor = computed(() => {
console.log(appStore.theme)
return appStore.theme === 'dark' ? '#333' : '#E5E8EF'
})
const graphicFactory = (side) => {
return {
type: 'text',
bottom: '8',
...side,
style: {
text: '',
textAlign: 'center',
fill: '#4E5969',
fontSize: 12,
},
};
}
const xAxis = ref(["2024-1", "2024-2", "2024-3", "2024-4", "2024-5", "2024-6", "2024-7", "2024-8"]);
const chartsData = ref([12,22,32,45,32,78,89,92]);
const graphicElements = ref([
graphicFactory({ left: '5%' }),
graphicFactory({ right: 0 }),
]);
const { chartOption } = useChartOption(() => {
return {
grid: {
left: '40',
right: '0',
top: '10',
bottom: '30',
},
xAxis: {
type: 'category',
offset: 2,
data: xAxis.value,
boundaryGap: false,
axisLabel: {
color: '#4E5969',
formatter(value, idx) {
if (idx === 0) return '';
if (idx === xAxis.value.length - 1) return '';
return `${value}`;
},
},
axisLine: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: true,
interval: (idx) => {
if (idx === 0) return false;
if (idx === xAxis.value.length - 1) return false;
return true;
},
lineStyle: {
color: dotColor.value,
},
},
axisPointer: {
show: true,
lineStyle: {
color: `${config.value.primaryColor}FF`,
width: 2,
},
},
},
yAxis: {
type: 'value',
axisLine: {
show: false,
},
axisLabel: {
formatter(value, idx) {
if (idx === 0) return value;
return `${value}k`;
},
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
color: dotColor.value,
},
},
},
tooltip: {
trigger: 'axis',
formatter(params) {
const [firstElement] = params
return `<div>
<p class="tooltip-title">${firstElement.axisValueLabel}</p>
<div class="content-panel"><span>总内容量</span><span class="tooltip-value">${(
Number(firstElement.value) * 10000
).toLocaleString()}</span></div>
</div>`;
},
className: 'echarts-tooltip-diy',
},
graphic: {
elements: graphicElements.value,
},
series: [
{
data: chartsData.value,
type: 'line',
smooth: true,
// symbol: 'circle',
symbolSize: 12,
emphasis: {
focus: 'series',
itemStyle: {
borderWidth: 2,
},
},
lineStyle: {
width: 3,
color: new graphic.LinearGradient(0, 0, 1, 0, [
{
offset: 0,
color: `${config.value.primaryColor}80`,
},
{
offset: 0.5,
color: `${config.value.primaryColor}92`,
},
{
offset: 1,
color: `${config.value.primaryColor}FF`,
},
]),
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: `${config.value.primaryColor}20`,
},
{
offset: 1,
color: `${config.value.primaryColor}08`,
},
]),
},
},
],
};
});
</script>
<style scoped lang="scss">
</style>
@@ -0,0 +1,141 @@
<!--
本组件参考 arco-pro 的实现 将 ts 改为 js 写法
https://github.com/arco-design/arco-design-pro-vue/blob/main/arco-design-pro-vite/src/views/dashboard/workplace/components/content-chart.vue
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
@desc: 人数统计图表
!-->
<template>
<Chart :height="height" :option="chartOption" />
</template>
<script setup>
import Chart from "@/components/charts/index.vue";
import useChartOption from '@/hooks/charts';
import { graphic } from 'echarts'
import { ref } from 'vue';
import { storeToRefs } from "pinia"
import { useAppStore } from '@/pinia'
const appStore = useAppStore()
const { config } = storeToRefs(appStore)
const prop = defineProps({
height: {
type: String,
default: '128px',
},
data : {
type : Array,
default : () => []
}
})
const graphicFactory = (side) => {
return {
type: 'text',
bottom: '8',
...side,
style: {
text: '',
textAlign: 'center',
fill: '#4E5969',
fontSize: 12,
},
};
}
const graphicElements = ref([
graphicFactory({ left: '5%' }),
graphicFactory({ right: 0 }),
]);
const { chartOption } = useChartOption(() => {
return {
grid: {
left: '40',
right: '0',
top: '10',
bottom: '30',
},
xAxis: {
type: 'category',
offset: 2,
show : false,
boundaryGap: false,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
},
yAxis: {
type: 'value',
show: false,
axisLine: {
show: false,
},
axisLabel: {
show: false
},
splitLine: {
show: false,
},
},
graphic: {
elements: graphicElements.value,
},
series: [
{
data: prop.data,
type: 'line',
smooth: true,
symbolSize: 12,
emphasis: {
focus: 'series',
itemStyle: {
borderWidth: 2,
},
},
lineStyle: {
width: 3,
color: new graphic.LinearGradient(0, 0, 1, 0, [
{
offset: 0,
color: `${config.value.primaryColor}32`,
},
{
offset: 0.5,
color: `${config.value.primaryColor}64`,
},
{
offset: 1,
color: `${config.value.primaryColor}FF`,
},
]),
},
showSymbol: false,
areaStyle: {
opacity: 0.8,
color: new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: `${config.value.primaryColor}20`,
},
{
offset: 1,
color: `${config.value.primaryColor}08`,
},
]),
},
},
],
};
});
</script>
<style scoped lang="scss">
</style>
@@ -0,0 +1,58 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div class="">
<div class="flex items-center justify-between mb-2">
<div v-if="title" class="font-bold">
{{ title }}
</div>
<slot v-else name="title" />
</div>
<div class=" w-full relative">
<div v-if="type !== 4">
<div class="mt-4 text-gray-600 text-3xl font-mono">
<el-statistic :value="268500" />
</div>
<div class="mt-2 text-green-600 text-sm font-bold font-mono">
+80% <el-icon><TopRight /></el-icon>
</div>
</div>
<div class=" absolute top-0 right-2 w-[50%] h-20">
<charts-people-number v-if="type === 1" :data="data[0]" height="100%" />
<charts-people-number v-if="type === 2" :data="data[1]" height="100%" />
<charts-people-number v-if="type === 3" :data="data[2]" height="100%" />
</div>
<charts-content-number v-if="type === 4" height="14rem" />
</div>
</div>
</template>
<script setup>
import chartsPeopleNumber from "./charts-people-numbers.vue"
import chartsContentNumber from "./charts-content-numbers.vue"
defineProps({
type :{
type : Number,
default : 1
},
title : {
type : String,
default : ""
}
})
const data = [
[12, 22, 32, 45, 32, 78, 89, 92],
[1, 2, 43, 5, 67, 78, 89, 12],
[12, 22, 32, 45, 32, 78, 89, 92],
]
</script>
<style scoped lang="scss">
</style>
@@ -0,0 +1,19 @@
import GvaBanner from "./banner.vue"
import GvaCard from "./card.vue"
import GvaChart from "./charts.vue"
import GvaTable from "./table.vue"
import GvaNotice from "./notice.vue"
import GvaQuickLink from "./quickLinks.vue"
import GvaWiki from "./wiki.vue"
import GvaPluginTable from "./pluginTable.vue"
export {
GvaBanner,
GvaCard,
GvaChart,
GvaTable,
GvaNotice,
GvaQuickLink,
GvaWiki,
GvaPluginTable
}
@@ -0,0 +1,78 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<el-scrollbar>
<div v-for="(item , index) in notices" :key="index" class="flex items-center mb-1.5 gap-3">
<el-tag :type="item.type" size="small">
{{ item.typeTitle }}
</el-tag>
<el-tooltip effect="light" :content="item.title" placement="top">
<div class="text-xs text-gray-700 dark:text-gray-300 line-clamp-1">
{{ item.title }}
</div>
</el-tooltip>
</div>
</el-scrollbar>
</template>
<script setup>
const notices = [
{
type : 'primary',
typeTitle : "公告",
title : "授权费将在从六月一日起结束第一价格梯度,进入第二价格梯度。",
},
{
type : 'success',
typeTitle : "通知",
title : "授权后将进入专属飞书群,获取官方辅助。",
},
{
type : 'warning',
typeTitle : "警告",
title : "授权可获得插件市场极大优惠价格。",
},
{
type : 'danger',
typeTitle : "违规",
title : "未授权商用将有可能被资源采集工具爬取并追责。",
},
{
type : 'info',
typeTitle : "信息",
title : "再次感谢您对开源事业的支持",
},
{
type : 'primary',
typeTitle : "公告",
title : "让创意更有价值。",
},
{
type : 'success',
typeTitle : "通知",
title : "让劳动更有意义。",
},
{
type : 'warning',
typeTitle : "警告",
title : "让思维更有深度。",
},
{
type : 'danger',
typeTitle : "错误",
title : "让生活更有趣味。",
},
{
type : 'info',
typeTitle : "信息",
title : "让公司更有活力。",
}
]
</script>
<style scoped lang="scss">
</style>
@@ -0,0 +1,63 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div>
<el-table :data="tableData" stripe style="width: 100%" @row-click="toPath">
<el-table-column prop="ranking" label="排名" width="80" align="center"/>
<el-table-column prop="title" label="插件标题" show-overflow-tooltip>
<template #default="{ row }">
<a class="text-active" :href="row.link" target="_blank">{{ row.title }}</a>
</template>
</el-table-column>
<el-table-column prop="click_num" label="关注度" width="100" />
<el-table-column prop="hot" label="热度值" width="100" />
</el-table>
</div>
</template>
<script setup>
const tableData = [
{
ranking: 1,
title : "组织管理插件:更方便管理组织,分配资源权限。",
click_num : 523,
hot : 263,
link : "https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=36"
},
{
ranking: 2,
title : "Kubernetes容器管理:,Kubernetes 原生资源管理,提供炫酷的YAML 编辑,Pod 终端,方便运维兄弟管理k8s资源",
click_num : 416,
hot : 223,
link : "https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=42"
},
{
ranking: 3,
title : "定时任务配置化管理:本插件用于对系统内部的定时任务进行配置化管理,可以配置自定义的函数和HTTP,可以配置cron和remark等等",
click_num : 337,
hot : 176,
link : "https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=67"
},
{
ranking: 4,
title : "官网CMS系统:基于Gin-Vue-Admin 和 插件市场客户端开发基座开发的企业官网类(cms)系统",
click_num : 292,
hot : 145,
link : "https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=69"
},
{
ranking: 5,
title : "微信支付插件:提供扫码支付功能(需自行对接业务)",
click_num : 173,
hot : 110,
link : "https://plugin.gin-vue-admin.com/#/layout/newPluginInfo?id=28"
},
]
</script>
<style scoped lang="scss">
</style>
@@ -0,0 +1,91 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div class="mt-8 w-full">
<div class="grid grid-cols-2 md:grid-cols-3 3xl:grid-cols-4">
<div v-for="(item, index ) in shortcuts" :key="index" class="flex flex-col items-center mb-3 group cursor-pointer" @click="toPath(item)">
<div class="w-8 h-8 rounded bg-gray-200 dark:bg-slate-500 flex items-center justify-center group-hover:bg-blue-400 group-hover:text-white">
<el-icon><component :is="item.icon" /></el-icon>
</div>
<div class="text-xs mt-2 text-gray-700 dark:text-gray-300">
{{ item.title }}
</div>
</div>
</div>
<div class="grid grid-cols-2 md:grid-cols-3 3xl:grid-cols-4 mt-8">
<div v-for="(item, index ) in recentVisits" :key="index" class="flex flex-col items-center mb-3 group cursor-pointer" @click="openLink(item)">
<div class="w-8 h-8 rounded bg-gray-200 dark:bg-slate-500 flex items-center justify-center group-hover:bg-blue-400 group-hover:text-white">
<el-icon><component :is="item.icon" /></el-icon>
</div>
<div class="text-xs mt-2 text-gray-700 dark:text-gray-300">
{{ item.title }}
</div>
</div>
</div>
</div>
</template>
<script setup>
import { Menu,Link,User,Service,Document,Reading,Files,Memo } from '@element-plus/icons-vue'
import {useRouter} from "vue-router";
const router = useRouter()
const toPath = (item) => {
router.push({name: item.path})
}
const openLink = (item) => {
window.open(item.path, '_blank')
}
const shortcuts = [
{
icon : Menu,
title : "菜单管理",
path : "menu",
},
{
icon : Link,
title : "API管理",
path : "api",
},
{
icon : Service,
title : "角色管理",
path : "authority",
},
{
icon : User,
title : "用户管理",
path : "user",
},
{
icon : Files,
title : "自动化包",
path: "autoPkg",
},
{
icon : Memo,
title : "自动代码",
path: "autoCode",
}
]
const recentVisits = [
{
icon : Reading,
title : "授权购买",
path: "https://gin-vue-admin.com/empower/index.html",
},
{
icon : Document,
title : "插件市场",
path: "https://plugin.gin-vue-admin.com/#/layout/home",
}
]
</script>
<style scoped lang="scss">
</style>
@@ -0,0 +1,54 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div>
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column prop="ranking" label="排名" width="80" align="center" />
<el-table-column prop="title" label="内容标题" show-overflow-tooltip />
<el-table-column prop="click_num" label="关注度" width="100" />
<el-table-column prop="hot" label="热度值" width="100" />
</el-table>
</div>
</template>
<script setup>
const tableData = [
{
ranking: 1,
title : "更简洁的使用界面,更快速的操作体验",
click_num : 523,
hot : 263
},
{
ranking: 2,
title : "更优质的服务,更便捷的使用体验",
click_num : 416,
hot : 223
},
{
ranking: 3,
title : "更快速的创意实现,更高效的工作效率",
click_num : 337,
hot : 176
},
{
ranking: 4,
title : "更多的创意资源,更多的创意灵感",
click_num : 292,
hot : 145
},
{
ranking: 5,
title : "更合理的代码结构,更清晰的代码逻辑",
click_num : 173,
hot : 110
},
]
</script>
<style scoped lang="scss">
</style>
@@ -0,0 +1,42 @@
<!--
@auther: bypanghu<bypanghu@163.com>
@date: 2024/5/8
!-->
<template>
<div class="grid grid-cols-2 gap-2">
<a v-for="item in wikis" :key="item.url" :href="item.url" class="text-sm text-gray-700 dark:text-gray-300 no-underline hover:text-active " target="_blank">
{{ item.title }}
</a>
</div>
</template>
<script setup>
const wikis = [
{
title: 'Vue3',
url : 'https://v3.cn.vuejs.org/guide/introduction.html'
},
{
title: 'GIN 文档',
url : 'https://gin-gonic.com/'
},
{
title : "GVA 文档",
url : 'https://www.gin-vue-admin.com/'
},
{
title : "插件市场",
url : 'https://plugin.gin-vue-admin.com/'
},
{
title : "github 仓库",
url : 'https://github.com/flipped-aurora/gin-vue-admin'
}
]
</script>
<style scoped lang="scss">
</style>
@@ -1,138 +0,0 @@
<template>
<div class="dashboard-line-box">
<div class="dashboard-line-title">
访问趋势
</div>
<div
ref="echart"
class="dashboard-line"
/>
</div>
</template>
<script setup>
import * as echarts from 'echarts'
import { nextTick, onMounted, onUnmounted, ref } from 'vue'
import { useWindowResize } from '@/hooks/use-windows-resize'
var dataAxis = []
for (var i = 1; i < 13; i++) {
dataAxis.push(`${i}月`)
}
var data = [
220,
182,
191,
234,
290,
330,
310,
123,
442,
321,
90,
149,
]
var yMax = 500
var dataShadow = []
for (let i = 0; i < data.length; i++) {
dataShadow.push(yMax)
}
let chart = null
const echart = ref(null)
useWindowResize(() => {
if (!chart) {
return
}
chart.resize()
})
const initChart = () => {
if (chart) {
chart = null
}
chart = echarts.init(echart.value)
setOptions()
}
const setOptions = () => {
chart.setOption({
grid: {
left: '40',
right: '20',
top: '40',
bottom: '20',
},
xAxis: {
data: dataAxis,
axisTick: {
show: false,
},
axisLine: {
show: false,
},
z: 10,
},
yAxis: {
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
textStyle: {
color: '#999',
},
},
},
dataZoom: [
{
type: 'inside',
},
],
series: [
{
type: 'bar',
barWidth: '40%',
itemStyle: {
borderRadius: [5, 5, 0, 0],
color: '#188df0',
},
emphasis: {
itemStyle: {
color: '#188df0',
},
},
data: data,
},
],
})
}
onMounted(async() => {
await nextTick()
initChart()
})
onUnmounted(() => {
if (!chart) {
return
}
chart.dispose()
chart = null
})
</script>
<style lang="scss" scoped>
.dashboard-line-box {
.dashboard-line {
background-color: #fff;
height: 360px;
width: 100%;
}
.dashboard-line-title {
font-weight: 600;
margin-bottom: 12px;
}
}
</style>
@@ -1,118 +0,0 @@
<template>
<div class="commit-table">
<div class="commit-table-title">
更新日志
</div>
<div class="log">
<div
v-for="(item,key) in dataTimeline"
:key="key"
class="log-item"
>
<div class="flex-1 flex key-box">
<span
class="key"
:class="key<3&&'top'"
>{{ key+1 }}</span>
</div>
<div class="flex-5 flex message">{{ item.message }}</div>
<div class="flex-3 flex form">{{ item.from }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { Commits } from '@/api/github'
import { formatTimeToStr } from '@/utils/date.js'
import { ref } from 'vue'
defineOptions({
name: 'DashboardTable',
})
const loading = ref(true)
const dataTimeline = ref([])
const loadCommits = () => {
Commits(0).then(({ data }) => {
loading.value = false
data.forEach((element, index) => {
if (element.commit.message && index < 10) {
dataTimeline.value.push({
from: formatTimeToStr(element.commit.author.date, 'yyyy-MM-dd'),
title: element.commit.author.name,
showDayAndMonth: true,
message: element.commit.message,
})
}
})
})
}
loadCommits()
</script>
<style lang="scss" scoped>
.commit-table{
background-color: #fff;
height: 400px;
&-title{
font-weight: 600;
margin-bottom: 12px;
}
.log{
&-item{
display: flex;
justify-content: space-between;
margin-top: 14px;
.key-box{
justify-content: center;
}
.key{
&.top{
background: #314659;
color: #FFFFFF;;
}
display: inline-flex;
justify-content: center;
align-items: center;
width: 20px;
height: 20px;
border-radius: 50%;
background: #F0F2F5;
text-align: center;
color:rgba($color: #000000, $alpha: 0.65)
}
.message{
color: rgba(0, 0, 0, 0.65);
}
.form{
color: rgba(0, 0, 0, 0.65);
margin-left: 12px;
}
.flex{
line-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.flex-1{
flex:1;
}
.flex-2{
flex:2;
}
.flex-3{
flex:3;
}
.flex-4{
flex:4;
}
.flex-5{
flex:5;
}
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More