Browse Source

Merge branch 'gva_gormv2_dev' of https://github.com/songzhibin97/gin-vue-admin into gva_gormv2_dev

songzhibin97 3 years ago
parent
commit
2fb22c6921
100 changed files with 1541 additions and 3479 deletions
  1. 0 2
      server/api/v1/example/enter.go
  2. 0 95
      server/api/v1/example/exa_simple_uploader.go
  3. 1 2
      server/api/v1/system/sys_auto_code.go
  4. 0 1
      server/initialize/gorm.go
  5. 7 1
      server/initialize/plugin.go
  6. 0 1
      server/initialize/router.go
  7. 0 13
      server/model/example/exa_simple_uploader.go
  8. 26 2
      server/plugin/email/README.MD
  9. 19 0
      server/plugin/email/api/sys_email.go
  10. 7 0
      server/plugin/email/model/response/email.go
  11. 3 1
      server/plugin/email/router/sys_email.go
  12. 13 0
      server/plugin/email/service/sys_email.go
  13. 74 0
      server/plugin/notify/README.MD
  14. 67 0
      server/plugin/notify/api/api.go
  15. 7 0
      server/plugin/notify/api/enter.go
  16. 7 0
      server/plugin/notify/config/dingding.go
  17. 5 0
      server/plugin/notify/global/global.go
  18. 28 0
      server/plugin/notify/main.go
  19. 21 0
      server/plugin/notify/model/response/notify.go
  20. 7 0
      server/plugin/notify/router/enter.go
  21. 18 0
      server/plugin/notify/router/router.go
  22. 7 0
      server/plugin/notify/service/enter.go
  23. 157 0
      server/plugin/notify/service/notify.go
  24. 1 0
      server/plugin/notify/utils/utils.go
  25. 0 0
      server/resource/page/js/index.94d8e405.js
  26. 0 2
      server/resource/template/readme.txt.tpl
  27. 2 2
      server/resource/template/web/form.vue.tpl
  28. 17 31
      server/resource/template/web/table.vue.tpl
  29. 0 1
      server/router/example/enter.go
  30. 0 18
      server/router/example/exa_simple_uploader.go
  31. 0 1
      server/service/example/enter.go
  32. 0 98
      server/service/example/exa_simple_uploader.go
  33. 1 1
      server/service/system/sys_auto_code.go
  34. 0 1
      server/service/system/sys_initdb.go
  35. 0 1
      server/source/authorities_menus.go
  36. 1 2
      server/source/menu.go
  37. 1 0
      web/.eslintrc.js
  38. 1 0
      web/.gitignore
  39. 1 7
      web/babel.config.js
  40. 340 1477
      web/package-lock.json
  41. 18 45
      web/package.json
  42. BIN
      web/public/favicon.ico
  43. 7 17
      web/public/index.html
  44. 0 6
      web/public/js/project.js
  45. 2 2
      web/src/api/excel.js
  46. 2 2
      web/src/api/github.js
  47. 0 34
      web/src/api/simpleUploader.js
  48. 7 5
      web/src/components/chooseImg/index.vue
  49. 0 120
      web/src/core/element_lazy.js
  50. 16 21
      web/src/core/gin-vue-admin.js
  51. 2 2
      web/src/directive/auth.js
  52. 14 12
      web/src/main.js
  53. 17 0
      web/src/mixins/infoList.js
  54. 8 11
      web/src/permission.js
  55. 17 31
      web/src/router/index.js
  56. 3 4
      web/src/store/index.js
  57. 3 3
      web/src/store/module/dictionary.js
  58. 8 11
      web/src/store/module/router.js
  59. 8 7
      web/src/store/module/user.js
  60. 49 140
      web/src/style/main.scss
  61. 3 1
      web/src/style/newLogin.scss
  62. 5 17
      web/src/utils/bus.js
  63. 12 11
      web/src/utils/request.js
  64. 17 13
      web/src/view/about/index.vue
  65. 0 44
      web/src/view/dashboard/component/musicPlayer.vue
  66. 0 81
      web/src/view/dashboard/component/todoList/Todo.vue
  67. 0 320
      web/src/view/dashboard/component/todoList/index.scss
  68. 0 122
      web/src/view/dashboard/component/todoList/index.vue
  69. 69 80
      web/src/view/dashboard/index.vue
  70. 15 20
      web/src/view/example/customer/customer.vue
  71. 2 1
      web/src/view/example/excel/excel.vue
  72. 12 4
      web/src/view/example/index.vue
  73. 0 161
      web/src/view/example/simpleUploader/simpleUploader.vue
  74. 8 17
      web/src/view/example/upload/upload.vue
  75. 0 1
      web/src/view/init/index.vue
  76. 4 4
      web/src/view/layout/aside/asideComponent/asyncSubmenu.vue
  77. 3 1
      web/src/view/layout/aside/asideComponent/menuItem.vue
  78. 13 10
      web/src/view/layout/aside/historyComponent/history.vue
  79. 7 5
      web/src/view/layout/aside/index.vue
  80. 75 62
      web/src/view/layout/index.vue
  81. 4 2
      web/src/view/layout/search/search.vue
  82. 1 1
      web/src/view/layout/setting/index.vue
  83. 10 10
      web/src/view/login/index.vue
  84. 7 5
      web/src/view/person/person.vue
  85. 12 4
      web/src/view/routerHolder.vue
  86. 23 23
      web/src/view/superAdmin/api/api.vue
  87. 15 10
      web/src/view/superAdmin/authority/authority.vue
  88. 5 5
      web/src/view/superAdmin/authority/components/datas.vue
  89. 16 14
      web/src/view/superAdmin/authority/components/menus.vue
  90. 14 28
      web/src/view/superAdmin/dictionary/sysDictionary.vue
  91. 14 28
      web/src/view/superAdmin/dictionary/sysDictionaryDetail.vue
  92. 12 4
      web/src/view/superAdmin/index.vue
  93. 6 2
      web/src/view/superAdmin/menu/icon.vue
  94. 25 21
      web/src/view/superAdmin/menu/menu.vue
  95. 20 30
      web/src/view/superAdmin/operation/sysOperationRecord.vue
  96. 17 12
      web/src/view/superAdmin/user/user.vue
  97. 22 16
      web/src/view/system/state.vue
  98. 22 20
      web/src/view/systemTools/autoCode/component/fieldDialog.vue
  99. 28 15
      web/src/view/systemTools/autoCode/index.vue
  100. 3 21
      web/src/view/systemTools/autoCodeAdmin/index.vue

+ 0 - 2
server/api/v1/example/enter.go

@@ -6,10 +6,8 @@ type ApiGroup struct {
 	CustomerApi
 	ExcelApi
 	FileUploadAndDownloadApi
-	SimpleUploaderApi
 }
 
 var fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
 var customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
 var excelService = service.ServiceGroupApp.ExampleServiceGroup.ExcelService
-var simpleUploaderService = service.ServiceGroupApp.ExampleServiceGroup.SimpleUploaderService

+ 0 - 95
server/api/v1/example/exa_simple_uploader.go

@@ -1,95 +0,0 @@
-package example
-
-import (
-	"github.com/flipped-aurora/gin-vue-admin/server/global"
-	"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
-	"github.com/flipped-aurora/gin-vue-admin/server/model/example"
-	"github.com/flipped-aurora/gin-vue-admin/server/utils"
-	"github.com/gin-gonic/gin"
-	"go.uber.org/zap"
-)
-
-type SimpleUploaderApi struct {
-}
-
-// @Tags SimpleUploader
-// @Summary 断点续传插件版示例
-// @Security ApiKeyAuth
-// @accept multipart/form-data
-// @Produce  application/json
-// @Param file formData file true "断点续传插件版示例"
-// @Success 200 {string} string "{"success":true,"data":{},"msg":"切片创建成功"}"
-// @Router /SimpleUploaderApi/upload [post]
-func (s *SimpleUploaderApi) SimpleUploaderUpload(c *gin.Context) {
-	var chunk example.ExaSimpleUploader
-	_, header, err := c.Request.FormFile("file")
-	chunk.Filename = c.PostForm("filename")
-	chunk.ChunkNumber = c.PostForm("chunkNumber")
-	chunk.CurrentChunkSize = c.PostForm("currentChunkSize")
-	chunk.Identifier = c.PostForm("identifier")
-	chunk.TotalSize = c.PostForm("totalSize")
-	chunk.TotalChunks = c.PostForm("totalChunks")
-	var chunkDir = "./chunk/" + chunk.Identifier + "/"
-	hasDir, _ := utils.PathExists(chunkDir)
-	if !hasDir {
-		if err := utils.CreateDir(chunkDir); err != nil {
-			global.GVA_LOG.Error("创建目录失败!", zap.Any("err", err))
-		}
-	}
-	chunkPath := chunkDir + chunk.Filename + chunk.ChunkNumber
-	err = c.SaveUploadedFile(header, chunkPath)
-	if err != nil {
-		global.GVA_LOG.Error("切片创建失败!", zap.Any("err", err))
-		response.FailWithMessage("切片创建失败", c)
-		return
-	}
-	chunk.CurrentChunkPath = chunkPath
-	err = simpleUploaderService.SaveChunk(chunk)
-	if err != nil {
-		global.GVA_LOG.Error("切片创建失败!", zap.Any("err", err))
-		response.FailWithMessage("切片创建失败", c)
-		return
-	} else {
-		response.OkWithMessage("切片创建成功", c)
-	}
-}
-
-// @Tags SimpleUploader
-// @Summary 断点续传插件版示例
-// @Security ApiKeyAuth
-// @Produce  application/json
-// @Param md5 query string true "md5"
-// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
-// @Router /SimpleUploaderApi/checkFileMd5 [get]
-func (s *SimpleUploaderApi) CheckFileMd5(c *gin.Context) {
-	md5 := c.Query("md5")
-	err, chunks, isDone := simpleUploaderService.CheckFileMd5(md5)
-	if err != nil {
-		global.GVA_LOG.Error("md5读取失败!", zap.Any("err", err))
-		response.FailWithMessage("md5读取失败", c)
-	} else {
-		response.OkWithDetailed(gin.H{
-			"chunks": chunks,
-			"isDone": isDone,
-		}, "查询成功", c)
-	}
-}
-
-// @Tags SimpleUploader
-// @Summary 合并文件
-// @Security ApiKeyAuth
-// @Produce  application/json
-// @Param md5 query string true "md5"
-// @Success 200 {string} string "{"success":true,"data":{},"msg":"合并成功"}"
-// @Router /SimpleUploaderApi/mergeFileMd5 [get]
-func (s *SimpleUploaderApi) MergeFileMd5(c *gin.Context) {
-	md5 := c.Query("md5")
-	fileName := c.Query("fileName")
-	err := simpleUploaderService.MergeFileMd5(md5, fileName)
-	if err != nil {
-		global.GVA_LOG.Error("md5读取失败!", zap.Any("err", err))
-		response.FailWithMessage("md5读取失败", c)
-	} else {
-		response.OkWithMessage("合并成功", c)
-	}
-}

+ 1 - 2
server/api/v1/system/sys_auto_code.go

@@ -155,8 +155,7 @@ func (autoApi *AutoCodeApi) CreateTemp(c *gin.Context) {
 	err := autoCodeService.CreateTemp(a, apiIds...)
 	if err != nil {
 		if errors.Is(err, system.AutoMoveErr) {
-			c.Writer.Header().Add("success", "false")
-			c.Writer.Header().Add("msgtype", "success")
+			c.Writer.Header().Add("success", "true")
 			c.Writer.Header().Add("msg", url.QueryEscape(err.Error()))
 		} else {
 			c.Writer.Header().Add("success", "false")

+ 0 - 1
server/initialize/gorm.go

@@ -48,7 +48,6 @@ func MysqlTables(db *gorm.DB) {
 		example.ExaFileUploadAndDownload{},
 		example.ExaFile{},
 		example.ExaFileChunk{},
-		example.ExaSimpleUploader{},
 		example.ExaCustomer{},
 		system.SysOperationRecord{},
 		system.SysAutoCodeHistory{},

+ 7 - 1
server/initialize/plugin.go

@@ -4,7 +4,7 @@ import (
 	"github.com/flipped-aurora/gin-vue-admin/server/global"
 	"github.com/flipped-aurora/gin-vue-admin/server/plugin/ws"
 	"go.uber.org/zap"
-
+	"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify"
 	//email "github.com/flipped-aurora/gva-plug-email"   // 在线仓库模式
 	"github.com/flipped-aurora/gin-vue-admin/server/plugin/email" // 本地插件仓库地址模式
 	"github.com/flipped-aurora/gin-vue-admin/server/plugin/example_plugin"
@@ -34,4 +34,10 @@ func InstallPlugin(PublicGroup *gin.RouterGroup, PrivateGroup *gin.RouterGroup)
 		global.GVA_CONFIG.Email.Port,
 		global.GVA_CONFIG.Email.IsSSL,
 	))
+
+	//  钉钉通知,暂时开放权限
+	PluginInit(PublicGroup, notify.CreateDDPlug(
+		"https://oapi.dingtalk.com/robot/send",
+		"8ded23f91917dc4f6275f44ba5ef243e6ed1d2cc74de83f01a6f5f5f39905671",
+		"SECaecf452bd6e671ab0d47469c3ad933e32fcc47b335333049a1b8961606192f38"))
 }

+ 0 - 1
server/initialize/router.go

@@ -51,7 +51,6 @@ func Routers() *gin.Engine {
 		systemRouter.InitSysDictionaryDetailRouter(PrivateGroup)    // 字典详情管理
 		exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
 		exampleRouter.InitExcelRouter(PrivateGroup)                 // 表格导入导出
-		exampleRouter.InitSimpleUploaderRouter(PrivateGroup)        // 断点续传(插件版)
 		exampleRouter.InitCustomerRouter(PrivateGroup)              // 客户路由
 
 		// Code generated by github.com/flipped-aurora/gin-vue-admin/server Begin; DO NOT EDIT.

+ 0 - 13
server/model/example/exa_simple_uploader.go

@@ -1,13 +0,0 @@
-package example
-
-type ExaSimpleUploader struct {
-	ChunkNumber      string `json:"chunkNumber" gorm:"comment:当前切片标记"`
-	CurrentChunkSize string `json:"currentChunkSize" gorm:"comment:当前切片容量"`
-	CurrentChunkPath string `json:"currentChunkPath" gorm:"comment:切片本地路径"`
-	TotalSize        string `json:"totalSize" gorm:"comment:总容量"`
-	Identifier       string `json:"identifier" gorm:"comment:文件标识(md5)"`
-	Filename         string `json:"filename" gorm:"comment:文件名"`
-	TotalChunks      string `json:"totalChunks" gorm:"comment:切片总数"`
-	IsDone           bool   `json:"isDone" gorm:"comment:是否上传完成"`
-	FilePath         string `json:"filePath" gorm:"comment:文件本地路径"`
-}

+ 26 - 2
server/plugin/email/README.MD

@@ -1,4 +1,5 @@
 ## GVA 邮件发送功能插件
+#### 开发者:GIN-VUE-ADMIN 官方
 
 ### 使用步骤
 
@@ -27,8 +28,9 @@
     true,
     ))
 
-#### 2. 配置说明
+### 2. 配置说明
 
+#### 2-1 全局配置结构体说明
     //其中 Form 和 Secret 通常来说就是用户名和密码
 
     type Email struct {
@@ -40,8 +42,17 @@
 	    Port     int     // 端口     请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
 	    IsSSL    bool    // 是否SSL   是否开启SSL
     }
+#### 2-2 入参结构说明
+    //其中 Form 和 Secret 通常来说就是用户名和密码
+
+    type Email struct {
+        To      string `json:"to"`      // 邮件发送给谁
+        Subject string `json:"subject"` // 邮件标题
+        Body    string `json:"body"`    // 邮件内容
+    }
+
 
-### 方法API
+### 3. 方法API
 
     utils.EmailTest(邮件标题,邮件主体) 发送测试邮件
     例:utils.EmailTest("测试邮件","测试邮件")
@@ -49,3 +60,16 @@
     例:utils.ErrorToEmail("测试邮件","测试邮件")
     utils.Email(目标邮箱多个的话用逗号分隔,邮件标题,邮件主体) 发送测试邮件
     例:utils.Email(”a.qq.com,b.qq.com“,"测试邮件","测试邮件")
+
+### 4. 可直接调用的接口
+
+    测试接口: /email/emailTest [post] 已配置swagger
+
+    发送邮件接口接口: /email/emailSend [post] 已配置swagger
+    入参:
+    type Email struct {
+        To      string `json:"to"`      // 邮件发送给谁
+        Subject string `json:"subject"` // 邮件标题
+        Body    string `json:"body"`    // 邮件内容
+    }
+   

+ 19 - 0
server/plugin/email/api/sys_email.go

@@ -3,6 +3,7 @@ package api
 import (
 	"github.com/flipped-aurora/gin-vue-admin/server/global"
 	"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
+	email_response "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/model/response"
 	"github.com/flipped-aurora/gin-vue-admin/server/plugin/email/service"
 	"github.com/gin-gonic/gin"
 	"go.uber.org/zap"
@@ -25,3 +26,21 @@ func (s *EmailApi) EmailTest(c *gin.Context) {
 		response.OkWithData("发送成功", c)
 	}
 }
+
+// @Tags System
+// @Summary 发送邮件
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body email_response.Email true "发送邮件必须的参数"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
+// @Router /email/sendEmail [post]
+func (s *EmailApi) SendEmail(c *gin.Context) {
+	var email email_response.Email
+	_ = c.ShouldBindJSON(&email)
+	if err := service.ServiceGroupApp.SendEmail(email.To, email.Subject, email.Body); err != nil {
+		global.GVA_LOG.Error("发送失败!", zap.Any("err", err))
+		response.FailWithMessage("发送失败", c)
+	} else {
+		response.OkWithData("发送成功", c)
+	}
+}

+ 7 - 0
server/plugin/email/model/response/email.go

@@ -0,0 +1,7 @@
+package response
+
+type Email struct {
+	To      string `json:"to"`      // 邮件发送给谁
+	Subject string `json:"subject"` // 邮件标题
+	Body    string `json:"body"`    // 邮件内容
+}

+ 3 - 1
server/plugin/email/router/sys_email.go

@@ -12,7 +12,9 @@ type EmailRouter struct {
 func (s *EmailRouter) InitEmailRouter(Router *gin.RouterGroup) {
 	emailRouter := Router.Use(middleware.OperationRecord())
 	var EmailApi = api.ApiGroupApp.EmailApi.EmailTest
+	var SendEmail = api.ApiGroupApp.EmailApi.SendEmail
 	{
-		emailRouter.POST("emailTest", EmailApi) // 发送测试邮件
+		emailRouter.POST("emailTest", EmailApi)  // 发送测试邮件
+		emailRouter.POST("sendEmail", SendEmail) // 发送邮件
 	}
 }

+ 13 - 0
server/plugin/email/service/sys_email.go

@@ -18,3 +18,16 @@ func (e *EmailService) EmailTest() (err error) {
 	err = utils.EmailTest(subject, body)
 	return err
 }
+
+//@author: [maplepie](https://github.com/maplepie)
+//@function: EmailTest
+//@description: 发送邮件测试
+//@return: err error
+//@params to string 	 收件人
+//@params subject string   标题(主题)
+//@params body  string 	 邮件内容
+
+func (e *EmailService) SendEmail(to, subject, body string) (err error) {
+	err = utils.Email(to, subject, body)
+	return err
+}

+ 74 - 0
server/plugin/notify/README.MD

@@ -0,0 +1,74 @@
+## GVA 钉钉群通知插件
+
+本插件用于向钉钉群推送消息
+
+### 1. 使用场景
+
+- 当服务运行异常时,可以向钉钉推送异常信息,便于及时发现解决问题
+- 推送一些关键业务的运行日志等
+
+### 2. 配置说明
+
+钉钉 token 等相关信息的获取,请参考 [钉钉官网](https://developers.dingtalk.com/document/robots/custom-robot-access?spm=ding_open_doc.document.0.0.7f8710afbfzduV#topic-2026027)
+
+在`plugin/notify/global/global.go` 文件中配置钉钉通知的URL ,Token 等
+
+```go
+	//  在gin-vue-admin 主程序的initialize中的plugin的InstallPlugin 函数中写入如下代码
+    PluginInit(PublicGroup, notify.CreateDDPlug(
+        URL,
+        Token,
+        密钥))
+}
+```
+
+### 3 参数说明
+#### 3-1 全局参数说明
+
+```go
+	Url    string `mapstructure:"url" json:"url" yaml:"url"`          // Url
+	Token  string `mapstructure:"token" json:"token" yaml:"token"`    // access_token
+	Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥
+```
+#### 3-2 请求入参说明
+```go
+
+
+```
+
+### 3方法API(可调用方法)
+```go
+
+//content 发送的内容
+//atMobiles 需要艾特的人的手机号 
+//isAtAll 是否艾特全体
+SendTextMessage(content string,atMobiles []string,isAtAll bool)
+
+//content 发送的内容
+//title 内容标题
+//picUrl 配图
+//messageUrl 点击跳转路径
+SendLinkMessage(content,title,picUrl,messageUrl string)
+
+//content 发送的内容(markdown语法)
+//title 内容标题
+//atMobiles 需要艾特的人的手机号 
+//isAtAll 是否艾特全体
+SendMarkdownMessage(content,title string,atMobiles []string,isAtAll bool)
+
+```
+
+### 4. 可直接调用接口
+
+    发送文字消息接口: /notify/sendTextMessage [post] 已配置swagger
+    发送图文链接消息接口: /notify/sendLinkMessage [post] 已配置swagger
+    发送markdown消息接口: /notify/sendMarkdownMessage [post] 已配置swagger
+
+    入参:
+    type Email struct {
+        To      string `json:"to"`      // 邮件发送给谁
+        Subject string `json:"subject"` // 邮件标题
+        Body    string `json:"body"`    // 邮件内容
+    }
+
+

+ 67 - 0
server/plugin/notify/api/api.go

@@ -0,0 +1,67 @@
+package api
+
+import (
+	"github.com/flipped-aurora/gin-vue-admin/server/global"
+	"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
+	notify_response "github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/model/response"
+	"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/service"
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type Api struct {
+}
+
+// @Tags Notify
+// @Summary 发送文字消息接口
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body notify_response.TextNotify true "发送文字消息的参数"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
+// @Router /notify/sendTextMessage [post]
+func (s *Api) SendTextMessage(c *gin.Context) {
+	var textNotify notify_response.TextNotify
+	_ = c.ShouldBindJSON(&textNotify)
+	if err := service.ServiceGroupApp.SendTextMessage(textNotify.Content, textNotify.AtMobiles, textNotify.IsAtAll); err != nil {
+		global.GVA_LOG.Error("发送失败!", zap.Any("err", err))
+		response.FailWithMessage("发送失败", c)
+	} else {
+		response.OkWithData("发送成功", c)
+	}
+}
+
+// @Tags Notify
+// @Summary 发送图文链接消息接口
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body notify_response.LinkNotify true "发送图文链接消息的参数"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
+// @Router /notify/sendLinkMessage [post]
+func (s *Api) SendLinkMessage(c *gin.Context) {
+	var linkNotify notify_response.LinkNotify
+	_ = c.ShouldBindJSON(&linkNotify)
+	if err := service.ServiceGroupApp.SendLinkMessage(linkNotify.Content, linkNotify.Title, linkNotify.PicUrl, linkNotify.MessageUrl); err != nil {
+		global.GVA_LOG.Error("发送失败!", zap.Any("err", err))
+		response.FailWithMessage("发送失败", c)
+	} else {
+		response.OkWithData("发送成功", c)
+	}
+}
+
+// @Tags Notify
+// @Summary 发送markdown消息接口
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body notify_response.MarkdownNotify true "发送markdown消息的参数"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
+// @Router /notify/sendMarkdownMessage [post]
+func (s *Api) SendMarkdownMessage(c *gin.Context) {
+	var markdownNotify notify_response.MarkdownNotify
+	_ = c.ShouldBindJSON(&markdownNotify)
+	if err := service.ServiceGroupApp.SendMarkdownMessage(markdownNotify.Content, markdownNotify.Title, markdownNotify.AtMobiles, markdownNotify.IsAtAll); err != nil {
+		global.GVA_LOG.Error("发送失败!", zap.Any("err", err))
+		response.FailWithMessage("发送失败", c)
+	} else {
+		response.OkWithData("发送成功", c)
+	}
+}

+ 7 - 0
server/plugin/notify/api/enter.go

@@ -0,0 +1,7 @@
+package api
+
+type ApiGroup struct {
+	Api
+}
+
+var ApiGroupApp = new(ApiGroup)

+ 7 - 0
server/plugin/notify/config/dingding.go

@@ -0,0 +1,7 @@
+package config
+
+type DingDing struct {
+	Url    string `mapstructure:"url" json:"url" yaml:"url"`          // Url
+	Token  string `mapstructure:"token" json:"token" yaml:"token"`    // access_token
+	Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥
+}

+ 5 - 0
server/plugin/notify/global/global.go

@@ -0,0 +1,5 @@
+package global
+
+import "github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/config"
+
+var GlobalConfig_ = &config.DingDing{}

+ 28 - 0
server/plugin/notify/main.go

@@ -0,0 +1,28 @@
+package notify
+
+import (
+	"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/global"
+	"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/router"
+	"github.com/gin-gonic/gin"
+)
+
+type ddPlugin struct {
+	Secret string
+	Token  string
+	Url    string
+}
+
+func CreateDDPlug(url, Token, Secret string) *ddPlugin {
+	global.GlobalConfig_.Url = url
+	global.GlobalConfig_.Token = Token
+	global.GlobalConfig_.Secret = Secret
+	return &ddPlugin{}
+}
+
+func (*ddPlugin) Register(group *gin.RouterGroup) {
+	router.RouterGroupApp.InitRouter(group)
+}
+
+func (*ddPlugin) RouterPath() string {
+	return "notify"
+}

+ 21 - 0
server/plugin/notify/model/response/notify.go

@@ -0,0 +1,21 @@
+package response
+
+type TextNotify struct { // 文字信息
+	Content   string   `json:"content"`   // 发送的内容
+	AtMobiles []string `json:"atMobiles"` // 需要艾特的人的手机号
+	IsAtAll   bool     `json:"isAtAll"`   // 是否艾特全体
+}
+
+type LinkNotify struct { // 图文链接信息
+	Content    string `json:"content"`    // 发送的内容
+	Title      string `json:"title"`      // 内容标题
+	PicUrl     string `json:"picUrl"`     // 配图
+	MessageUrl string `json:"messageUrl"` // 点击跳转路径
+}
+
+type MarkdownNotify struct { // markdown信息
+	Title     string   `json:"title"`     // 内容标题
+	Content   string   `json:"content"`   // 发送的内容
+	AtMobiles []string `json:"atMobiles"` // 需要艾特的人的手机号
+	IsAtAll   bool     `json:"isAtAll"`   // 是否艾特全体
+}

+ 7 - 0
server/plugin/notify/router/enter.go

@@ -0,0 +1,7 @@
+package router
+
+type RouterGroup struct {
+	NotifyRouter
+}
+
+var RouterGroupApp = new(RouterGroup)

+ 18 - 0
server/plugin/notify/router/router.go

@@ -0,0 +1,18 @@
+package router
+
+import (
+	"github.com/flipped-aurora/gin-vue-admin/server/middleware"
+	"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/api"
+	"github.com/gin-gonic/gin"
+)
+
+type NotifyRouter struct {
+}
+
+func (s *NotifyRouter) InitRouter(Router *gin.RouterGroup) {
+	router := Router.Use(middleware.OperationRecord())
+	var SendTextMessage = api.ApiGroupApp.Api.SendTextMessage
+	{
+		router.POST("sendTextMessage", SendTextMessage)
+	}
+}

+ 7 - 0
server/plugin/notify/service/enter.go

@@ -0,0 +1,7 @@
+package service
+
+type ServiceGroup struct {
+	NotifyService
+}
+
+var ServiceGroupApp = new(ServiceGroup)

+ 157 - 0
server/plugin/notify/service/notify.go

@@ -0,0 +1,157 @@
+package service
+
+import (
+	"bytes"
+	"crypto/hmac"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/global"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"time"
+)
+
+type NotifyService struct {
+}
+
+//@author: [Espoir](https://github.com/nightsimon)
+//@function: SendTextMessage
+//@description: 发送钉钉文字信息
+//@params content string发送的文字内容
+//@params atMobiles []string 艾特的手机号
+//@params isAtAll bool 是否艾特全体
+//@return: err error
+
+func (e *NotifyService) SendTextMessage(content string, atMobiles []string, isAtAll bool) (err error) {
+	msg := map[string]interface{}{
+		"msgtype": "text",
+		"text": map[string]string{
+			"content": content,
+		},
+		"at": map[string]interface{}{
+			"atMobiles": atMobiles,
+			"isAtAll":   isAtAll,
+		},
+	}
+	return SendMessage(msg)
+}
+
+//@author: [Espoir](https://github.com/nightsimon)
+//@function: SendLinkMessage
+//@description: 发送钉钉图文链接信息
+//@params content string 发送的文字内容
+//@params title string 发送的标题
+//@params picUrl string 艾特的手机号
+//@params messageUrl string 是否艾特全体
+//@return: err error
+
+func (e *NotifyService) SendLinkMessage(content, title, picUrl, messageUrl string) (err error) {
+	msg := map[string]interface{}{
+		"msgtype": "link",
+		"link": map[string]string{
+			"text":       content,
+			"title":      title,
+			"picUrl":     picUrl,
+			"messageUrl": messageUrl,
+		},
+	}
+	return SendMessage(msg)
+}
+
+//@author: [Espoir](https://github.com/nightsimon)
+//@function: SendMarkdownMessage
+//@description: 发送钉钉Markdown信息
+//@params content 发送的文字内容
+//@params title 发送的标题
+//@params atMobiles []string 艾特的手机号
+//@params isAtAll bool 是否艾特全体
+//@return: err error
+
+func (e *NotifyService) SendMarkdownMessage(content, title string, atMobiles []string, isAtAll bool) (err error) {
+	msg := map[string]interface{}{
+		"msgtype": "markdown",
+		"markdown": map[string]string{
+			"text":  content,
+			"title": title,
+		},
+		"at": map[string]interface{}{
+			"atMobiles": atMobiles,
+			"isAtAll":   isAtAll,
+		},
+	}
+	return SendMessage(msg)
+}
+
+func SendMessage(msg interface{}) error {
+	body := bytes.NewBuffer(nil)
+	err := json.NewEncoder(body).Encode(msg)
+	if err != nil {
+		return fmt.Errorf("msg json failed, msg: %v, err: %v", msg, err.Error())
+	}
+
+	value := url.Values{}
+	value.Set("access_token", global.GlobalConfig_.Token)
+	if global.GlobalConfig_.Secret != "" {
+		t := time.Now().UnixNano() / 1e6
+		value.Set("timestamp", fmt.Sprintf("%d", t))
+		value.Set("sign", sign(t, global.GlobalConfig_.Secret))
+	}
+
+	request, err := http.NewRequest(http.MethodPost, global.GlobalConfig_.Url, body)
+	if err != nil {
+		return fmt.Errorf("error request: %v", err.Error())
+	}
+	request.URL.RawQuery = value.Encode()
+	request.Header.Add("Content-Type", "application/json")
+	res, err := (&http.Client{}).Do(request)
+	if err != nil {
+		return fmt.Errorf("send dingTalk message failed, error: %v", err.Error())
+	}
+	defer func() { _ = res.Body.Close() }()
+	result, err := ioutil.ReadAll(res.Body)
+
+	if res.StatusCode != 200 {
+		return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, "http code is not 200"))
+	}
+	if err != nil {
+		return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, err.Error()))
+	}
+
+	type response struct {
+		ErrCode int `json:"errcode"`
+	}
+	var ret response
+
+	if err := json.Unmarshal(result, &ret); err != nil {
+		return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, err.Error()))
+	}
+
+	if ret.ErrCode != 0 {
+		return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, "errcode is not 0"))
+	}
+
+	return nil
+}
+
+func httpError(request *http.Request, response *http.Response, body []byte, error string) string {
+	return fmt.Sprintf(
+		"http request failure, error: %s, status code: %d, %s %s, body:\n%s",
+		error,
+		response.StatusCode,
+		request.Method,
+		request.URL.String(),
+		string(body),
+	)
+}
+func sign(t int64, secret string) string {
+	strToHash := fmt.Sprintf("%d\n%s", t, secret)
+	hmac256 := hmac.New(sha256.New, []byte(secret))
+	hmac256.Write([]byte(strToHash))
+	data := hmac256.Sum(nil)
+	return base64.StdEncoding.EncodeToString(data)
+}
+
+//	其余方法请参考 https://developers.dingtalk.com/document/robots/custom-robot-access?spm=ding_open_doc.document.0.0.7f8710afbfzduV#topic-2026027

+ 1 - 0
server/plugin/notify/utils/utils.go

@@ -0,0 +1 @@
+package utils

File diff suppressed because it is too large
+ 0 - 0
server/resource/page/js/index.94d8e405.js


+ 0 - 2
server/resource/template/readme.txt.tpl

@@ -5,5 +5,3 @@
 项目github:"https://github.com/piexlmax/github.com/flipped-aurora/gin-vue-admin/server"
 
 希望大家给个star多多鼓励
-
-暂时不保存大家生成的结构体 只为方便一次性使用

+ 2 - 2
server/resource/template/web/form.vue.tpl

@@ -4,7 +4,7 @@
     {{- range .Fields}}
       <el-form-item label="{{.FieldDesc}}:">
     {{- if eq .FieldType "bool" }}
-        <el-switch active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" v-model="formData.{{.FieldJson}}" clearable ></el-switch>
+        <el-switch v-model="formData.{{.FieldJson}}" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
     {{- end }}
     {{- if eq .FieldType "string" }}
         <el-input v-model="formData.{{.FieldJson}}" clearable placeholder="请输入" />
@@ -19,7 +19,7 @@
     {{- end }}
     {{- end }}
     {{- if eq .FieldType "time.Time" }}
-        <el-date-picker type="date" placeholder="选择日期" v-model="formData.{{ .FieldJson }}" clearable></el-date-picker>
+        <el-date-picker v-model="formData.{{ .FieldJson }}" type="date" placeholder="选择日期" clearable></el-date-picker>
     {{- end }}
     {{- if eq .FieldType "float64" }}
         <el-input-number v-model="formData.{{ .FieldJson }}" :precision="2" clearable></el-input-number>

+ 17 - 31
server/resource/template/web/table.vue.tpl

@@ -24,13 +24,15 @@
         <el-form-item>
           <el-button size="mini" type="primary" icon="el-icon-search" @click="onSubmit">查询</el-button>
           <el-button size="mini" type="primary" icon="el-icon-plus" @click="openDialog">新增</el-button>
-          <el-popover v-model="deleteVisible" placement="top" width="160">
+          <el-popover v-model:visible="deleteVisible" placement="top" width="160">
             <p>确定要删除吗?</p>
             <div style="text-align: right; margin: 0">
               <el-button size="mini" type="text" @click="deleteVisible = false">取消</el-button>
               <el-button size="mini" type="primary" @click="onDelete">确定</el-button>
             </div>
-            <el-button slot="reference" icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
+            <template #reference>
+              <el-button icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
+            </template>
           </el-popover>
         </el-form-item>
       </el-form>
@@ -46,24 +48,24 @@
     >
       <el-table-column type="selection" width="55" />
       <el-table-column label="日期" width="180">
-        <template slot-scope="scope">{{ "{{ scope.row.CreatedAt|formatDate }}" }}</template>
+        <template #default="scope">{{ "{{ formatDate(scope.row.CreatedAt) }}" }}</template>
       </el-table-column>
       {{- range .Fields}}
       {{- if .DictType}}
       <el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
-        <template slot-scope="scope">
+        <template #default="scope">
           {{"{{"}} filterDict(scope.row.{{.FieldJson}},"{{.DictType}}") {{"}}"}}
         </template>
       </el-table-column>
       {{- else if eq .FieldType "bool" }}
       <el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
-        <template slot-scope="scope">{{ "{{scope.row."}}{{.FieldJson}}{{"|formatBoolean}}" }}</template>
+        <template #default="scope">{{"{{"}} formatBoolean(scope.row.{{.FieldJson}}) {{"}}"}}</template>
       </el-table-column> {{- else }}
       <el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120" />
       {{- end }}
       {{- end }}
       <el-table-column label="按钮组">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-button size="small" type="primary" icon="el-icon-edit" class="table-button" @click="update{{.StructName}}(scope.row)">变更</el-button>
           <el-button type="danger" icon="el-icon-delete" size="mini" @click="deleteRow(scope.row)">删除</el-button>
         </template>
@@ -79,12 +81,12 @@
       @current-change="handleCurrentChange"
       @size-change="handleSizeChange"
     />
-    <el-dialog :before-close="closeDialog" :visible.sync="dialogFormVisible" title="弹窗操作">
+    <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作">
       <el-form :model="formData" label-position="right" label-width="80px">
     {{- range .Fields}}
         <el-form-item label="{{.FieldDesc}}:">
       {{- if eq .FieldType "bool" }}
-          <el-switch active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" v-model="formData.{{.FieldJson}}" clearable ></el-switch>
+          <el-switch v-model="formData.{{.FieldJson}}" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
       {{- end }}
       {{- if eq .FieldType "string" }}
           <el-input v-model="formData.{{.FieldJson}}" clearable placeholder="请输入" />
@@ -99,7 +101,7 @@
       {{- end }}
       {{- end }}
       {{- if eq .FieldType "time.Time" }}
-          <el-date-picker type="date" placeholder="选择日期" v-model="formData.{{ .FieldJson }}" clearable />
+          <el-date-picker v-model="formData.{{ .FieldJson }}" type="date" placeholder="选择日期" clearable />
       {{- end }}
       {{- if eq .FieldType "float64" }}
           <el-input-number v-model="formData.{{ .FieldJson }}" :precision="2" clearable />
@@ -107,10 +109,12 @@
         </el-form-item>
       {{- end }}
       </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="closeDialog">取 消</el-button>
-        <el-button type="primary" @click="enterDialog">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDialog">取 消</el-button>
+          <el-button type="primary" @click="enterDialog">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
   </div>
 </template>
@@ -124,27 +128,9 @@ import {
   find{{.StructName}},
   get{{.StructName}}List
 } from '@/api/{{.PackageName}}' //  此处请自行替换地址
-import { formatTimeToStr } from '@/utils/date'
 import infoList from '@/mixins/infoList'
 export default {
   name: '{{.StructName}}',
-  filters: {
-    formatDate: function(time) {
-      if (time !== null && time !== '') {
-        var date = new Date(time)
-        return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
-      } else {
-        return ''
-      }
-    },
-    formatBoolean: function(bool) {
-      if (bool != null) {
-        return bool ? '是' : '否'
-      } else {
-        return ''
-      }
-    }
-  },
   mixins: [infoList],
   data() {
     return {

+ 0 - 1
server/router/example/enter.go

@@ -4,5 +4,4 @@ type RouterGroup struct {
 	CustomerRouter
 	ExcelRouter
 	FileUploadAndDownloadRouter
-	SimpleUploaderRouter
 }

+ 0 - 18
server/router/example/exa_simple_uploader.go

@@ -1,18 +0,0 @@
-package example
-
-import (
-	v1 "github.com/flipped-aurora/gin-vue-admin/server/api/v1"
-	"github.com/gin-gonic/gin"
-)
-
-type SimpleUploaderRouter struct{}
-
-func (e *SimpleUploaderRouter) InitSimpleUploaderRouter(Router *gin.RouterGroup) {
-	simpleUploaderRouter := Router.Group("simpleUploader")
-	var simpleUploaderApi = v1.ApiGroupApp.ExampleApiGroup.SimpleUploaderApi
-	{
-		simpleUploaderRouter.POST("upload", simpleUploaderApi.SimpleUploaderUpload) // 上传功能
-		simpleUploaderRouter.GET("checkFileMd5", simpleUploaderApi.CheckFileMd5)    // 文件完整度验证
-		simpleUploaderRouter.GET("mergeFileMd5", simpleUploaderApi.MergeFileMd5)    // 合并文件
-	}
-}

+ 0 - 1
server/service/example/enter.go

@@ -4,5 +4,4 @@ type ServiceGroup struct {
 	FileUploadAndDownloadService
 	CustomerService
 	ExcelService
-	SimpleUploaderService
 }

+ 0 - 98
server/service/example/exa_simple_uploader.go

@@ -1,98 +0,0 @@
-package example
-
-import (
-	"errors"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"strconv"
-
-	"github.com/flipped-aurora/gin-vue-admin/server/global"
-	"github.com/flipped-aurora/gin-vue-admin/server/model/example"
-	"gorm.io/gorm"
-)
-
-type SimpleUploaderService struct {
-}
-
-//@author: [piexlmax](https://github.com/piexlmax)
-//@function: SaveChunk
-//@description: 保存文件切片路径
-//@param: uploader model.ExaSimpleUploader
-//@return: err error
-
-func (exa *SimpleUploaderService) SaveChunk(uploader example.ExaSimpleUploader) (err error) {
-	return global.GVA_DB.Create(uploader).Error
-}
-
-//@author: [piexlmax](https://github.com/piexlmax)
-//@function: CheckFileMd5
-//@description: 检查文件是否已经上传过
-//@param: md5 string
-//@return: err error, uploads []model.ExaSimpleUploader, isDone bool
-
-func (exa *SimpleUploaderService) CheckFileMd5(md5 string) (err error, uploads []example.ExaSimpleUploader, isDone bool) {
-	err = global.GVA_DB.Find(&uploads, "identifier = ? AND is_done = ?", md5, false).Error
-	isDone = errors.Is(global.GVA_DB.First(&example.ExaSimpleUploader{}, "identifier = ? AND is_done = ?", md5, true).Error, gorm.ErrRecordNotFound)
-	return err, uploads, !isDone
-}
-
-//@author: [piexlmax](https://github.com/piexlmax)
-//@function: MergeFileMd5
-//@description: 合并文件
-//@param: md5 string, fileName string
-//@return: err error
-
-func (exa *SimpleUploaderService) MergeFileMd5(md5 string, fileName string) (err error) {
-	finishDir := "./finish/"
-	dir := "./chunk/" + md5
-	// 如果文件上传成功 不做后续操作 通知成功即可
-	if !errors.Is(global.GVA_DB.First(&example.ExaSimpleUploader{}, "identifier = ? AND is_done = ?", md5, true).Error, gorm.ErrRecordNotFound) {
-		return nil
-	}
-
-	// 打开切片文件夹
-	rd, err := ioutil.ReadDir(dir)
-	_ = os.MkdirAll(finishDir, os.ModePerm)
-	// 创建目标文件
-	fd, err := os.OpenFile(finishDir+fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
-	if err != nil {
-		return
-	}
-	// 关闭文件
-	defer fd.Close()
-	// 将切片文件按照顺序写入
-	for k := range rd {
-		content, _ := ioutil.ReadFile(dir + "/" + fileName + strconv.Itoa(k+1))
-		_, err = fd.Write(content)
-		if err != nil {
-			_ = os.Remove(finishDir + fileName)
-		}
-	}
-
-	if err != nil {
-		return err
-	}
-	err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
-		// 删除切片信息
-		if err = tx.Delete(&example.ExaSimpleUploader{}, "identifier = ? AND is_done = ?", md5, false).Error; err != nil {
-			fmt.Println(err)
-			return err
-		}
-		data := example.ExaSimpleUploader{
-			Identifier: md5,
-			IsDone:     true,
-			FilePath:   finishDir + fileName,
-			Filename:   fileName,
-		}
-		// 添加文件信息
-		if err = tx.Create(&data).Error; err != nil {
-			fmt.Println(err)
-			return err
-		}
-		return nil
-	})
-
-	err = os.RemoveAll(dir) // 清除切片
-	return err
-}

+ 1 - 1
server/service/system/sys_auto_code.go

@@ -247,7 +247,7 @@ func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruc
 		return err
 	}
 	if autoCode.AutoMoveFile {
-		return errors.New("创建代码成功并移动文件成功")
+		return system.AutoMoveErr
 	}
 	return nil
 

+ 0 - 1
server/service/system/sys_initdb.go

@@ -133,7 +133,6 @@ func (initDBService *InitDBService) InitDB(conf request.InitDB) error {
 		example.ExaFileUploadAndDownload{},
 		example.ExaFile{},
 		example.ExaFileChunk{},
-		example.ExaSimpleUploader{},
 		example.ExaCustomer{},
 		system.SysOperationRecord{},
 		system.SysAutoCodeHistory{},

+ 0 - 1
server/source/authorities_menus.go

@@ -36,7 +36,6 @@ var authorityMenus = []AuthorityMenus{
 	{"888", 18},
 	{"888", 19},
 	{"888", 20},
-	{"888", 21},
 	{"888", 22},
 	{"888", 23},
 	{"888", 24},

+ 1 - 2
server/source/menu.go

@@ -35,8 +35,7 @@ var menus = []system.SysBaseMenu{
 	{GVA_MODEL: global.GVA_MODEL{ID: 18, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "3", Path: "dictionary", Name: "dictionary", Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: system.Meta{Title: "字典管理", Icon: "notebook-2"}},
 	{GVA_MODEL: global.GVA_MODEL{ID: 19, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: true, ParentId: "3", Path: "dictionaryDetail/:id", Name: "dictionaryDetail", Component: "view/superAdmin/dictionary/sysDictionaryDetail.vue", Sort: 1, Meta: system.Meta{Title: "字典详情", Icon: "s-order"}},
 	{GVA_MODEL: global.GVA_MODEL{ID: 20, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "3", Path: "operation", Name: "operation", Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: system.Meta{Title: "操作历史", Icon: "time"}},
-	{GVA_MODEL: global.GVA_MODEL{ID: 21, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "9", Path: "simpleUploader", Name: "simpleUploader", Component: "view/example/simpleUploader/simpleUploader", Sort: 6, Meta: system.Meta{Title: "断点续传(插件版)", Icon: "upload"}},
-	{GVA_MODEL: global.GVA_MODEL{ID: 22, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "https://www.github.com/flipped-aurora/gin-vue-admin/server.com", Name: "https://www.github.com/flipped-aurora/gin-vue-admin/server.com", Hidden: false, Component: "/", Sort: 0, Meta: system.Meta{Title: "官方网站", Icon: "s-home"}},
+	{GVA_MODEL: global.GVA_MODEL{ID: 22, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Hidden: false, Component: "/", Sort: 0, Meta: system.Meta{Title: "官方网站", Icon: "s-home"}},
 	{GVA_MODEL: global.GVA_MODEL{ID: 23, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "state", Name: "state", Hidden: false, Component: "view/system/state.vue", Sort: 6, Meta: system.Meta{Title: "服务器状态", Icon: "cloudy"}},
 	{GVA_MODEL: global.GVA_MODEL{ID: 24, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "14", Path: "autoCodeAdmin", Name: "autoCodeAdmin", Hidden: false, Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 1, Meta: system.Meta{Title: "自动化代码管理", Icon: "s-finance"}},
 	{GVA_MODEL: global.GVA_MODEL{ID: 25, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "14", Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Hidden: true, Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: system.Meta{Title: "自动化代码(复用)", Icon: "s-finance"}},

+ 1 - 0
web/.eslintrc.js

@@ -16,6 +16,7 @@ module.exports = {
   // add your custom rules here
   // it is base on https://github.com/vuejs/eslint-config-vue
   rules: {
+    'vue/no-v-model-argument':'off',
     'vue/max-attributes-per-line': [
       2,
       {

+ 1 - 0
web/.gitignore

@@ -0,0 +1 @@
+node_modules/*

+ 1 - 7
web/babel.config.js

@@ -3,12 +3,6 @@ module.exports = {
     '@vue/cli-plugin-babel/preset'
   ],
   'plugins': [
-    [
-      'component',
-      {
-        'libraryName': 'element-ui',
-        'styleLibraryName': 'theme-chalk'
-      }
-    ]
+
   ]
 }

File diff suppressed because it is too large
+ 340 - 1477
web/package-lock.json


+ 18 - 45
web/package.json

@@ -1,5 +1,5 @@
 {
-  "name": "gin-vue-admin",
+  "name": "gvav3",
   "version": "0.1.0",
   "private": true,
   "scripts": {
@@ -8,16 +8,13 @@
     "lint": "vue-cli-service lint"
   },
   "dependencies": {
-    "@antv/dom-util": "2.0.2",
-    "@antv/g-canvas": "^0.4.12",
-    "@antv/g6": "3.5.2",
-    "@antv/matrix-util": "2.0.7",
-    "@antv/util": "~2.0.9",
     "@moefe/vue-aplayer": "^2.0.0-beta.5",
     "axios": "^0.19.2",
-    "element-ui": "^2.12.0",
+    "core-js": "^3.6.5",
+    "element-plus": "^1.1.0-beta.4",
     "highlight.js": "^10.6.0",
     "marked": "^2.0.0",
+    "mitt": "^3.0.0",
     "path": "^0.12.7",
     "qs": "^6.8.0",
     "quill": "^1.3.7",
@@ -26,50 +23,26 @@
     "script-ext-html-webpack-plugin": "^2.1.4",
     "spark-md5": "^3.0.1",
     "timeline-vuejs": "1.1.1",
-    "vue": "^2.6.10",
+    "vue": "^3.0.0",
     "vue-particle-line": "^0.1.4",
-    "vue-router": "^3.1.3",
+    "vue-router": "^4.0.0-0",
     "vue-simple-uploader": "^0.7.4",
     "vuescroll": "^4.14.4",
-    "vuex": "^3.1.1",
+    "vuex": "^4.0.0-0",
     "vuex-persist": "^2.1.0"
   },
   "devDependencies": {
-    "@vue/cli-plugin-babel": "^4.5.6",
-    "@vue/cli-plugin-eslint": "^4.5.6",
-    "@vue/cli-service": "^4.5.6",
+    "@vue/cli-plugin-babel": "~4.5.0",
+    "@vue/cli-plugin-eslint": "~4.5.0",
+    "@vue/cli-plugin-router": "~4.5.0",
+    "@vue/cli-plugin-vuex": "~4.5.0",
+    "@vue/cli-service": "~4.5.0",
+    "@vue/compiler-sfc": "^3.0.0",
     "babel-eslint": "^10.1.0",
-    "babel-plugin-component": "^1.1.1",
-    "babel-preset-es2015": "^6.24.1",
-    "core-js": "^3.3.2",
+    "babel-plugin-import": "^1.13.3",
     "eslint": "^6.7.2",
-    "eslint-plugin-vue": "^6.2.2",
-    "numericjs": "^1.2.6",
-    "raw-loader": "^3.1.0",
-    "sass-loader": "^8.0.0",
-    "vue-template-compiler": "^2.6.10"
-  },
-  "eslintConfig": {
-    "root": true,
-    "env": {
-      "node": true
-    },
-    "extends": [
-      "plugin:vue/essential",
-      "eslint:recommended"
-    ],
-    "rules": {},
-    "parserOptions": {
-      "parser": "babel-eslint"
-    }
-  },
-  "postcss": {
-    "plugins": {
-      "autoprefixer": {}
-    }
-  },
-  "browserslist": [
-    "> 1%",
-    "last 2 versions"
-  ]
+    "eslint-plugin-vue": "^7.0.0",
+    "sass": "^1.26.5",
+    "sass-loader": "^8.0.2"
+  }
 }

BIN
web/public/favicon.ico


+ 7 - 17
web/public/index.html

@@ -1,27 +1,17 @@
 <!DOCTYPE html>
-<html lang="zh-CN">
-
-<head>
+<html lang="">
+  <head>
     <meta charset="utf-8">
-    <meta name="renderer" content="webkit">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
-    <title><%=htmlWebpackPlugin.options.title%></title>
-    <% if(process.env.NODE_ENV!=='development' && htmlWebpackPlugin.options.cdns){ %>
-      <% htmlWebpackPlugin.options.cdns.forEach(function(item){ if(item.js){ %>
-        <script type="text/javascript" src="<%= item.js %>"></script>
-      <% } }) %>
-    <% } %>  
-</head>
-
-<body>
+    <title><%= htmlWebpackPlugin.options.title %></title>
+  </head>
+  <body>
     <noscript>
-      <strong>We're sorry but GIN-VUE-ADMIN doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
     </noscript>
     <div id="app"></div>
     <!-- built files will be auto injected -->
-    <script src="./js/project.js"></script>
-</body>
-
+  </body>
 </html>

+ 0 - 6
web/public/js/project.js

@@ -1,6 +0,0 @@
-// 未经过授权商用请勿删除此文件,将会导致意想不到的bug
-(function(){})(
-    document.write(
-    unescape(`%3Cspan style='display:none' id='cnzz_stat_icon_1279266757'%3E%3C/span%3E%3Cscript src='https://s4.cnzz.com/z_stat.php%3Fid%3D1279266757' type='text/javascript'%3E%3C/script%3E`)
-   )
-)

+ 2 - 2
web/src/api/excel.js

@@ -1,5 +1,5 @@
 import service from '@/utils/request'
-import { Message } from 'element-ui'
+import { ElMessage } from 'element-plus'
 
 const handleFileError = (res, fileName) => {
   if (typeof (res.data) !== 'undefined') {
@@ -7,7 +7,7 @@ const handleFileError = (res, fileName) => {
       const reader = new FileReader()
       reader.onload = function() {
         const message = JSON.parse(reader.result).msg
-        Message({
+        ElMessage({
           showClose: true,
           message: message,
           type: 'error'

+ 2 - 2
web/src/api/github.js

@@ -1,11 +1,11 @@
 import axios from 'axios'
-import { Loading } from 'element-ui'
+import { ElLoading } from 'element-plus'
 
 let loadingInstance
 const service = axios.create()
 
 service.interceptors.request.use((config) => {
-  loadingInstance = Loading.service({ fullscreen: true })
+  loadingInstance = ElLoading.service({ fullscreen: true })
   return config
 })
 

+ 0 - 34
web/src/api/simpleUploader.js

@@ -1,34 +0,0 @@
-
-import service from '@/utils/request'
-
-// @Tags SimpleUploader
-// @Summary 断点续传插件版示例
-// @Security ApiKeyAuth
-
-// @Produce  application/json
-// @Param params md5 get "测试文件是否已经存在和判断已经上传过的切片"
-// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
-// @Router /simpleUploader/checkFileMd5 [get]
-export const checkFileMd5 = (params) => {
-  return service({
-    url: '/simpleUploader/checkFileMd5',
-    method: 'get',
-    params
-  })
-}
-
-// @Tags SimpleUploader
-// @Summary 合并文件
-// @Security ApiKeyAuth
-// @Produce  application/json
-// @Param params md5 get "合并文件"
-// @Success 200 {string} string "{"success":true,"data":{},"msg":"合并成功"}"
-// @Router /simpleUploader/mergeFileMd5 [get]
-export const mergeFileMd5 = (params) => {
-  return service({
-    url: '/simpleUploader/mergeFileMd5',
-    method: 'get',
-    params
-  })
-}
-

+ 7 - 5
web/src/components/chooseImg/index.vue

@@ -1,16 +1,18 @@
 <template>
-  <el-drawer title="媒体库" :visible.sync="drawer">
+  <el-drawer v-model="drawer" title="媒体库">
     <div class="media">
       <el-image
         v-for="(item,key) in picList"
         :key="key"
         class="header-img-box-list"
         :src="(item.url && item.url.slice(0, 4) !== 'http')?path+item.url:item.url"
-        @click.native="chooseImg(item.url,target,targetKey)"
+        @click="chooseImg(item.url,target,targetKey)"
       >
-        <div slot="error" class="header-img-box-list">
-          <i class="el-icon-picture-outline" />
-        </div>
+        <template #error>
+          <div class="header-img-box-list">
+            <i class="el-icon-picture-outline" />
+          </div>
+        </template>
       </el-image>
     </div>
   </el-drawer>

+ 0 - 120
web/src/core/element_lazy.js

@@ -1,120 +0,0 @@
-/*
-*
-* 按需加载element
-*
-*
-* */
-
-import Vue from 'vue'
-//  按需引入element
-import {
-  Button,
-  Select,
-  Dialog,
-  Form,
-  Input,
-  FormItem,
-  Option,
-  Loading,
-  Message,
-  Container,
-  Card,
-  Dropdown,
-  DropdownMenu,
-  DropdownItem,
-  Row,
-  Col,
-  Menu,
-  Submenu,
-  MenuItem,
-  Aside,
-  Main,
-  Badge,
-  Header,
-  Tabs,
-  Breadcrumb,
-  BreadcrumbItem,
-  Scrollbar,
-  Avatar,
-  TabPane,
-  Divider,
-  Table,
-  TableColumn,
-  Cascader,
-  Checkbox,
-  CheckboxGroup,
-  Pagination,
-  Tag,
-  Drawer,
-  Tree,
-  Popover,
-  Switch,
-  Collapse,
-  CollapseItem,
-  Tooltip,
-  DatePicker,
-  InputNumber,
-  Steps,
-  Upload,
-  Progress,
-  MessageBox,
-  Image,
-  ColorPicker
-} from 'element-ui'
-
-Vue.use(Button)
-Vue.use(Select)
-Vue.use(Dialog)
-Vue.use(Form)
-Vue.use(FormItem)
-Vue.use(Input)
-Vue.use(Option)
-Vue.use(Container)
-Vue.use(Card)
-Vue.use(Dropdown)
-Vue.use(DropdownMenu)
-Vue.use(DropdownItem)
-Vue.use(Row)
-Vue.use(Col)
-Vue.use(Menu)
-Vue.use(Submenu)
-Vue.use(MenuItem)
-Vue.use(Aside)
-Vue.use(Main)
-Vue.use(Badge)
-Vue.use(Header)
-Vue.use(Tabs)
-Vue.use(Breadcrumb)
-Vue.use(BreadcrumbItem)
-Vue.use(Avatar)
-Vue.use(TabPane)
-Vue.use(Divider)
-Vue.use(Table)
-Vue.use(TableColumn)
-Vue.use(Checkbox)
-Vue.use(Cascader)
-Vue.use(Tag)
-Vue.use(Pagination)
-Vue.use(Drawer)
-Vue.use(Tree)
-Vue.use(CheckboxGroup)
-Vue.use(Popover)
-Vue.use(InputNumber)
-Vue.use(Switch)
-Vue.use(Collapse)
-Vue.use(CollapseItem)
-Vue.use(Tooltip)
-Vue.use(DatePicker)
-Vue.use(Steps)
-Vue.use(Upload)
-Vue.use(Progress)
-Vue.use(Scrollbar)
-Vue.use(Loading.directive)
-Vue.use(Image)
-Vue.use(ColorPicker)
-Vue.prototype.$loading = Loading.service
-Vue.prototype.$message = Message
-Vue.prototype.$confirm = MessageBox.confirm
-Dialog.props.closeOnClickModal.default = false
-
-console.warn('[GIN-VUE-ADMIN]--按需加载elementUI成功,如出现element-ui组件无法使用问题,请至/src/core/element_lazy 下引入对应组件即可')

+ 16 - 21
web/src/core/gin-vue-admin.js

@@ -2,29 +2,24 @@
 * gin-vue-admin web框架组
 *
 * */
-import Vue from 'vue'
-import './element_lazy' // 按需加载element
-import uploader from 'vue-simple-uploader'
-import APlayer from '@moefe/vue-aplayer'
 // time line css
 import '../../node_modules/timeline-vuejs/dist/timeline-vuejs.css'
-// 路由守卫
-import Bus from '@/utils/bus'
+
 // 加载网站配置文件夹
 import config from './config'
-Vue.prototype.$GIN_VUE_ADMIN = config
-Vue.use(Bus)
-Vue.use(APlayer, {
-  defaultCover: 'https://github.com/u3u.png',
-  productionTip: true
-})
-Vue.use(uploader)
 
-console.log(`
-   欢迎使用 Gin-Vue-Admin
-   当前版本:V2.4.5 alpha
-   加群方式:微信:shouzi_1994 QQ群:622360840
-   默认自动化文档地址:http://127.0.0.1:${process.env.VUE_APP_SERVER_PORT}/swagger/index.html
-   默认前端文件运行地址:http://127.0.0.1:${process.env.VUE_APP_CLI_PORT}
-   如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/docs/coffee
-`)
+export const run = function(app) {
+  app.config.globalProperties.$GIN_VUE_ADMIN = config
+
+  // app.use(uploader)
+
+  console.log(`
+     欢迎使用 Gin-Vue-Admin
+     当前版本:V2.4.5 alpha
+     加群方式:微信:shouzi_1994 QQ群:622360840
+     默认自动化文档地址:http://127.0.0.1:${process.env.VUE_APP_SERVER_PORT}/swagger/index.html
+     默认前端文件运行地址:http://127.0.0.1:${process.env.VUE_APP_CLI_PORT}
+     如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/docs/coffee
+  `)
+}
+

+ 2 - 2
web/src/directive/auth.js

@@ -1,7 +1,7 @@
 // 权限按钮展示指令
 import { store } from '@/store'
-export const auth = (Vue) => {
-  Vue.directive('auth', {
+export const auth = (app) => {
+  app.directive('auth', {
     // 当被绑定的元素插入到 DOM 中时……
     bind: function(el, binding) {
       const userInfo = store.getters['user/userInfo']

+ 14 - 12
web/src/main.js

@@ -1,20 +1,22 @@
-import Vue from 'vue'
-import App from './App.vue'
+import { createApp } from 'vue'
 // 引入gin-vue-admin前端初始化相关内容
 import './core/gin-vue-admin'
+
 // 引入封装的router
 import router from '@/router/index'
+import { run } from '@/core/gin-vue-admin.js'
 import '@/permission'
-import { store } from '@/store'
-Vue.config.productionTip = false
+import { store } from '@/store/index'
 
 import { auth } from '@/directive/auth'
-// 按钮权限指令
-auth(Vue)
-
-export default new Vue({
-  render: h => h(App),
-  router,
-  store
-}).$mount('#app')
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+import zhCn from 'element-plus/es/locale/lang/zh-cn'
+import App from './App.vue'
+const app = createApp(App)
+run(app)
+auth(app)
+app.config.productionTip = false
+app.use(store).use(router).use(ElementPlus, { locale: zhCn }).mount('#app')
 
+export default app

+ 17 - 0
web/src/mixins/infoList.js

@@ -1,4 +1,6 @@
 import { getDict } from '@/utils/dictionary'
+import { formatTimeToStr } from '@/utils/date'
+
 export default {
   data() {
     return {
@@ -10,6 +12,21 @@ export default {
     }
   },
   methods: {
+    formatBoolean: function(bool) {
+      if (bool !== null) {
+        return bool ? '是' : '否'
+      } else {
+        return ''
+      }
+    },
+    formatDate: function(time) {
+      if (time !== null && time !== '') {
+        var date = new Date(time)
+        return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
+      } else {
+        return ''
+      }
+    },
     filterDict(value, type) {
       const rowLabel = this[type + 'Options'] && this[type + 'Options'].filter(item => item.value === value)
       return rowLabel && rowLabel[0] && rowLabel[0].label

+ 8 - 11
web/src/permission.js

@@ -1,18 +1,17 @@
 import router from './router'
-import { store } from '@/store'
+import { store } from '@/store/index'
 import getPageTitle from '@/utils/page'
-
 let asyncRouterFlag = 0
 
 const whiteList = ['Login', 'Init']
+
 router.beforeEach(async(to, from, next) => {
   const token = store.getters['user/token']
   // 在白名单中的判断情况
-  // 修改网页标签名称
   document.title = getPageTitle(to.meta.title)
   if (whiteList.indexOf(to.name) > -1) {
     if (token) {
-      next({ name: store.getters['user/userInfo'].authority.defaultRouter })
+      next({ path: '/layout/dashboard' })
     } else {
       next()
     }
@@ -20,19 +19,17 @@ router.beforeEach(async(to, from, next) => {
     // 不在白名单中并且已经登陆的时候
     if (token) {
       // 添加flag防止多次获取动态路由和栈溢出
-      if (!asyncRouterFlag && store.getters['router/asyncRouters'].length === 0) {
+      if (!asyncRouterFlag) {
         asyncRouterFlag++
         await store.dispatch('router/SetAsyncRouter')
         await store.dispatch('user/GetUserInfo')
         const asyncRouters = store.getters['router/asyncRouters']
-        router.addRoutes(asyncRouters)
+        asyncRouters.map(asyncRouter => {
+          router.addRoute(asyncRouter)
+        })
         next({ ...to, replace: true })
       } else {
-        if (to.matched.length) {
-          next()
-        } else {
-          next({ path: '/layout/404' })
-        }
+        next()
       }
     }
     // 不在白名单中并且未登陆的时候

+ 17 - 31
web/src/router/index.js

@@ -1,38 +1,24 @@
-import Vue from 'vue'
-import Router from 'vue-router'
+import { createRouter, createWebHashHistory } from 'vue-router'
 
-Vue.use(Router)
-
-// 获取原型对象上的push函数
-const originalPush = Router.prototype.push
-// 修改原型对象中的push方法
-Router.prototype.push = function push(location) {
-  return originalPush.call(this, location).catch(err => err)
+const routes = [{
+  path: '/',
+  redirect: '/login'
+},
+{
+  path: '/init',
+  name: 'Init',
+  component: () => import('@/view/init/index')
+},
+{
+  path: '/login',
+  name: 'Login',
+  component: () => import('@/view/login/index')
 }
-
-const baseRouters = [
-  {
-    path: '/',
-    redirect: '/login'
-  },
-  {
-    path: '/init',
-    name: 'Init',
-    component: () => import('@/view/init/index')
-  },
-  {
-    path: '/login',
-    name: 'Login',
-    component: () => import('@/view/login/index')
-  }
 ]
 
-// 需要通过后台数据来生成的组件
-
-const createRouter = () => new Router({
-  routes: baseRouters
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes
 })
 
-const router = createRouter()
-
 export default router

+ 3 - 4
web/src/store/index.js

@@ -1,17 +1,16 @@
-import Vue from 'vue'
-import Vuex from 'vuex'
+import { createStore } from 'vuex'
 import VuexPersistence from 'vuex-persist'
 
 import { user } from '@/store/module/user'
 import { router } from '@/store/module/router'
 import { dictionary } from '@/store/module/dictionary'
-Vue.use(Vuex)
 
 const vuexLocal = new VuexPersistence({
   storage: window.localStorage,
   modules: ['user']
 })
-export const store = new Vuex.Store({
+
+export const store = createStore({
   modules: {
     user,
     router,

+ 3 - 3
web/src/store/module/dictionary.js

@@ -3,18 +3,18 @@ import { findSysDictionary } from '@/api/sysDictionary'
 export const dictionary = {
   namespaced: true,
   state: {
-    dictionaryMap: {}
+    dictionaryMap: {},
   },
   mutations: {
     setDictionaryMap(state, dictionaryMap) {
       state.dictionaryMap = { ...state.dictionaryMap, ...dictionaryMap }
-    }
+    },
 
   },
   actions: {
     // 从后台获取动态路由
     async getDictionary({ commit, state }, type) {
-      if (state.dictionaryMap[type]) {
+      if (state.dictionaryMap[type] && state.dictionaryMap[type].length) {
         return state.dictionaryMap[type]
       } else {
         const res = await findSysDictionary({ type })

+ 8 - 11
web/src/store/module/router.js

@@ -3,11 +3,13 @@ import { asyncRouterHandle } from '@/utils/asyncRouter'
 import { asyncMenu } from '@/api/menu'
 
 const routerList = []
+
 const formatRouter = (routes) => {
   routes && routes.map(item => {
-    if ((!item.children || item.children.every(ch => ch.hidden)) && item.name !== '404') {
+    if ((!item.children || item.children.every(ch => ch.hidden)) && item.name !== '404' && !item.hidden) {
       routerList.push({ label: item.meta.title, value: item.name })
     }
+    item.meta.hidden = item.hidden
     if (item.children && item.children.length > 0) {
       formatRouter(item.children)
     }
@@ -18,7 +20,7 @@ export const router = {
   namespaced: true,
   state: {
     asyncRouters: [],
-    routerList: routerList
+    routerList: routerList,
   },
   mutations: {
     setRouterList(state, routerList) {
@@ -42,24 +44,22 @@ export const router = {
         children: []
       }]
       const asyncRouterRes = await asyncMenu()
-      if (asyncRouterRes.code !== 0) {
-        return
-      }
-      const asyncRouter = asyncRouterRes.data && asyncRouterRes.data.menus
+      const asyncRouter = asyncRouterRes.data.menus
       asyncRouter.push({
         path: '404',
         name: '404',
         hidden: true,
         meta: {
-          title: '迷路了*。*'
+          title: '迷路了*。*',
         },
         component: 'view/error/index.vue'
       })
       formatRouter(asyncRouter)
       baseRouter[0].children = asyncRouter
       baseRouter.push({
-        path: '*',
+        path: '/:catchAll(.*)',
         redirect: '/layout/404'
+
       })
       asyncRouterHandle(baseRouter)
       commit('setAsyncRouter', baseRouter)
@@ -74,9 +74,6 @@ export const router = {
     },
     routerList(state) {
       return state.routerList
-    },
-    defaultRouter(state) {
-      return state.defaultRouter
     }
   }
 }

+ 8 - 7
web/src/store/module/user.js

@@ -2,7 +2,7 @@ import { login, getUserInfo } from '@/api/user'
 import { jsonInBlacklist } from '@/api/jwt'
 import router from '@/router/index'
 import { setUserInfo } from '@/api/user'
-import { Message } from 'element-ui'
+import { ElMessage } from 'element-plus'
 
 export const user = {
   namespaced: true,
@@ -16,7 +16,7 @@ export const user = {
       activeColor: '#1890ff',
       baseColor: '#fff'
     },
-    token: ''
+    token: '',
   },
   mutations: {
     setUserInfo(state, userInfo) {
@@ -70,7 +70,9 @@ export const user = {
         commit('setToken', res.data.token)
         await dispatch('router/SetAsyncRouter', {}, { root: true })
         const asyncRouters = rootGetters['router/asyncRouters']
-        router.addRoutes(asyncRouters)
+        asyncRouters.map(asyncRouter => {
+          router.addRoute(asyncRouter)
+        })
         // const redirect = router.history.current.query.redirect
         // console.log(redirect)
         // if (redirect) {
@@ -91,7 +93,7 @@ export const user = {
       const res = await setUserInfo({ activeColor: data, ID: state.userInfo.ID })
       if (res.code === 0) {
         commit('ChangeActiveColor', data)
-        Message({
+        ElMessage({
           type: 'success',
           message: '设置成功'
         })
@@ -101,7 +103,7 @@ export const user = {
       const res = await setUserInfo({ sideMode: data, ID: state.userInfo.ID })
       if (res.code === 0) {
         commit('ChangeSideMode', data)
-        Message({
+        ElMessage({
           type: 'success',
           message: '设置成功'
         })
@@ -111,7 +113,7 @@ export const user = {
       const res = await setUserInfo({ baseColor: data, ID: state.userInfo.ID })
       if (res.code === 0) {
         commit('ChangeBaseColor', data)
-        Message({
+        ElMessage({
           type: 'success',
           message: '设置成功'
         })
@@ -152,6 +154,5 @@ export const user = {
       }
       return state.userInfo.activeColor
     }
-
   }
 }

+ 49 - 140
web/src/style/main.scss

@@ -8,7 +8,6 @@
  */
 
 @import '@/style/basics.scss';
-
 html {
     line-height: 1.15;
     /* 1 */
@@ -535,17 +534,36 @@ li {
 
 // 导航
 #app {
+    .pd-lr-15 {
+        padding: 0 15px;
+    }
+    .height-full {
+        height: 100%;
+    }
+    .width-full {
+        width: 100%;
+    }
+    .dp-flex {
+        display: flex;
+    }
+    .justify-content-center {
+        justify-content: center;
+    }
+    .align-items {
+        align-items: center;
+    }
+    .pd-0 {
+        padding: 0;
+    }
     .el-container {
         position: relative;
         height: 100%;
         width: 100%;
     }
-
     .el-container.mobile.openside {
         position: fixed;
         top: 0;
     }
-
     .el-aside {
         -webkit-transition: width .2s;
         transition: width .2s;
@@ -559,11 +577,9 @@ li {
         left: 0;
         z-index: 1001;
         overflow: hidden;
-
         .el-menu {
             border-right: none;
         }
-
         .tilte {
             min-height: $height-aside-tilte;
             line-height: $height-aside-tilte;
@@ -578,7 +594,6 @@ li {
                 border-radius: 50%;
                 padding: 3px;
             }
-
             .tit-text {
                 display: inline-block;
                 color: #fff;
@@ -588,15 +603,17 @@ li {
                 padding-left: 10px;
             }
         }
-
     }
-
     .aside {
+        .el-menu-item {
+            >div {
+                padding: 0 15px !important;
+            }
+        }
         .el-menu-vertical {
-            transition: all 0.3s;
             background-color: $bg-aside;
         }
-        .el-submenu {
+        .el-sub-menu {
             background-color: $bg-aside;
             .el-menu {
                 .el-menu-item {
@@ -607,46 +624,43 @@ li {
                 .is-active {
                     background-color: #1890ff;
                     // 关闭三级菜单二级菜单样式
-                    ul{
-                        border:none;
+                    ul {
+                        border: none;
                     }
                 }
                 // 关闭三级菜单二级菜单样式
-                .is-active.is-opened{
+                .is-active.is-opened {
                     background-color: #191a23;
-                    ul{
-                        border:none;
+                    ul {
+                        border: none;
                     }
                 }
             }
         }
-        .el-menu-item:focus, .el-menu-item:hover{
+        .el-menu-item:focus,
+        .el-menu-item:hover {
             background-color: transparent;
         }
         .el-menu-item:hover i,
         .el-menu-item:hover span {
             color: #fff;
         }
-
-        .el-submenu__title:hover {
+        .el-sub-menu__title:hover {
             background-color: $bg-aside;
         }
-
-        .el-submenu__title:hover i,
-        .el-submenu__title:hover span {
+        .el-sub-menu__title:hover i,
+        .el-sub-menu__title:hover span {
             color: #fff;
         }
         .el-menu--inline {
             border-left: 5px solid #2c3b41;
         }
     }
-
     .hideside {
         .aside {
             width: $width-hideside-aside;
         }
     }
-
     .mobile.hideside {
         .el-aside {
             -webkit-transition-duration: .2s;
@@ -655,7 +669,6 @@ li {
             transform: translate3d(-220px, 0, 0);
         }
     }
-
     .mobile {
         .el-aside {
             -webkit-transition: -webkit-transform .28s;
@@ -665,9 +678,6 @@ li {
             width: $width-mobile-aside;
         }
     }
-
-
-
     .main-cont.el-main {
         min-height: 100%;
         -webkit-transition: margin-left .28s;
@@ -675,19 +685,16 @@ li {
         margin-left: $width-aside;
         position: relative;
     }
-
     .hideside {
         .main-cont.el-main {
             margin-left: 54px;
         }
     }
-
     .mobile {
         .main-cont.el-main {
             margin-left: 0px;
         }
     }
-
     .openside.mobile {
         .shadowBg {
             background: #000;
@@ -702,8 +709,6 @@ li {
     }
 }
 
-
-
 //   layout
 .layout-cont {
     .main-cont {
@@ -724,7 +729,6 @@ li {
         height: 30px;
         line-height: 30px;
     }
-
     .el-input__icon {
         line-height: 30px;
     }
@@ -736,65 +740,52 @@ li {
     padding: 14px;
     margin: 114px 14px 20px;
     border-radius: 2px;
-
     .el-table--border {
         border-radius: 4px;
         margin-bottom: 14px;
     }
-
     .el-table {
         thead {
             color: $color-table-thead;
         }
-
         th {
             padding: 5px 0;
-
             .cell {
                 min-height: 34px;
                 line-height: 34px;
             }
         }
-
         td {
             padding: 8px 0;
         }
-
         td,
         th.is-leaf {
             border-bottom: 1px solid #e8e8e8;
         }
     }
-
     .search-term {
         border-left: none;
         border-right: none;
         padding: 0 5px;
-
         .el-form-item {
             margin-bottom: 10px;
         }
     }
-
     .el-pagination {
         padding: 20px 0 0 0;
     }
-
     .upload-demo,
     .upload {
         padding: 0;
     }
-
     .system {
         padding: 0;
     }
-
     .el-form.el-form--inline {
         .el-form-item:last-child {
             margin-bottom: 0;
         }
     }
-
     .edit_container,
     .edit {
         padding: 0;
@@ -819,7 +810,6 @@ li {
     padding: 0 15px;
     border-left: 1px solid #ebeef5;
     border-right: 1px solid #ebeef5;
-
     .demo-form-inline {
         margin-bottom: 10px;
     }
@@ -850,13 +840,11 @@ li {
 }
 
 .el-pagination {
-
     .btn-prev,
     .btn-next {
         border: 1px solid #ddd;
         border-radius: 4px;
     }
-
     .el-pager {
         li {
             color: #666;
@@ -866,13 +854,11 @@ li {
             border-radius: 4px;
         }
     }
-
     padding: 20px 0 !important;
 }
 
 .el-row {
     padding: 10px 0;
-
     .el-col>label {
         line-height: 30px;
         text-align: right;
@@ -880,7 +866,6 @@ li {
         padding-right: 15px;
         display: inline-block;
     }
-
     .line {
         line-height: 30px;
         text-align: center;
@@ -891,7 +876,6 @@ li {
 .edit_container {
     background-color: $white-bg;
     padding: 15px;
-
     .el-button {
         margin: 15px 0;
     }
@@ -900,7 +884,6 @@ li {
 .edit {
     background-color: $white-bg;
     padding: 15px;
-
     .el-button {
         margin: 15px 0;
     }
@@ -911,12 +894,10 @@ li {
 .upload {
     background-color: $white-bg;
     padding: 15px;
-
     .el-upload-list__item-status-label {
         right: 0;
         left: 120px;
     }
-
     .el-upload__tip {
         margin: 10px 0;
     }
@@ -925,33 +906,27 @@ li {
 // system
 .system {
     padding: 15px;
-
     .el-input__inner {
         width: 80%;
     }
 }
 
-
-
 // .el-menu .el-menu--inline {
 //     background: #2c3b41;
 // }
-// .el-submenu .el-submenu {
+// .el-sub-menu .el-sub-menu {
 //     background-color: #000408 !important;
 // }
-// .aside .el-scrollbar .el-scrollbar__view .el-submenu__title:hover {
+// .aside .el-scrollbar .el-scrollbar__view .el-sub-menu__title:hover {
 //     background-color: $bg-aside !important;
 // }
-
 // .el-menu--vertical {
 //     .el-menu {
 //         margin-left: -8px;
 //         background-color: rgb(48, 65, 86);
-
 //         .el-menu-item {
 //             background-color: rgb(48, 65, 86);
 //         }
-
 //         .el-menu-item:focus,
 //         .el-menu-item:hover {
 //             background-color: #263445;
@@ -959,7 +934,6 @@ li {
 //         }
 //     }
 // }
-
 // 导航*****
 // add 5.13
 .el-container {
@@ -967,83 +941,67 @@ li {
     //     padding: 15px;
     //     margin: 115px 15px 20px;
     //     border-radius: 2px;
-
     //     .button-box {
     //         border: none;
     //         padding: 0 0 10px 0px;
     //     }
-
     //     .el-table--border {
     //         border-radius: 4px;
     //         margin-bottom: 15px;
     //     }
-
     //     .el-table {
     //         thead {
     //             color: $color-table-thead;
     //         }
-
     //         th {
     //             padding: 5px 0;
-
     //             .cell {
     //                 min-height: 34px;
     //                 line-height: 34px;
     //             }
     //         }
-
     //         td {
     //             padding: 8px 0;
     //         }
-
     //         td,
     //         th.is-leaf {
     //             border-bottom: 1px solid #e8e8e8;
     //         }
     //     }
-
     //     .search-term {
     //         border-left: none;
     //         border-right: none;
     //         padding: 0 5px;
-
     //         .el-form-item {
     //             margin-bottom: 10px;
     //         }
     //     }
-
     //     .el-pagination {
     //         padding: 20px 0 0 0;
     //     }
-
     //     .upload-demo,
     //     .upload {
     //         padding: 0;
     //     }
-
     //     .system {
     //         padding: 0;
     //     }
-
     //     .el-form.el-form--inline {
     //         .el-form-item:last-child {
     //             margin-bottom: 0;
     //         }
     //     }
-
     //     .edit_container,
     //     .edit {
     //         padding: 0;
     //     }
     // }
-
     // .admin-box:after,
     // .admin-box:before {
     //     content: "";
     //     display: block;
     //     clear: both;
     // }
-
     .tips {
         margin-top: 10px;
         font-size: 14px;
@@ -1053,7 +1011,6 @@ li {
 }
 
 .el-container.layout-cont {
-
     // .header-cont,
     // .breadcrumb {
     //     height: 40px !important;
@@ -1061,13 +1018,11 @@ li {
     // }
     .main-cont.el-main {
         background-color: $bg-main;
-
         .menu-total {
             font-size: 22px;
             color: #838383;
             margin-top: 16px;
         }
-
         // background-color: #f0f2f5;
     }
 }
@@ -1080,21 +1035,17 @@ li {
             padding: 0 6px;
             border-top: 1px solid $border-color;
             padding: 0;
-
             .el-tabs__header {
                 margin: 0px 0 0 0;
-
                 .el-tabs__item {
                     height: $height-nav-scroll;
                     height: $height-nav-scroll;
                     border: none;
                     border-left: 1px solid $border-color;
                 }
-
                 .el-tabs__item.is-active {
                     background-color: rgba(64, 158, 255, .08);
                 }
-
                 .el-tabs__nav {
                     border: none;
                 }
@@ -1107,7 +1058,6 @@ li {
     .el-button.el-button--text.el-button--small {
         position: relative;
     }
-
     // .el-button.el-button--text.el-button--small::after {
     //     content: '';
     //     position: absolute;
@@ -1139,7 +1089,6 @@ li {
             border-right: 1.5px solid #ccc;
             margin-left: 6px;
         }
-
         .el-table__placeholder {
             width: 10px;
         }
@@ -1152,7 +1101,6 @@ li {
             border-right: 1.5px solid #ccc;
             margin-left: 6px;
         }
-
         .el-table__placeholder {
             width: 10px;
         }
@@ -1180,7 +1128,6 @@ li {
 
 $headerHigh: 52px;
 $mainHight: 100vh;
-
 .dropdown-group {
     min-width: 100px;
 }
@@ -1190,18 +1137,14 @@ $mainHight: 100vh;
     top: 0;
     box-sizing: border-box;
     z-index: 999;
-    >.el-row{
+    >.el-row {
         padding: 0;
-        .el-col-lg-14{
+        .el-col-lg-14 {
             height: 60px;
         }
     }
-
-
 }
 
-
-
 .el-scrollbar__wrap {
     padding-bottom: 17px;
 }
@@ -1213,19 +1156,16 @@ $mainHight: 100vh;
         text-align: center;
         vertical-align: middle;
         margin-right: 10px;
-
         img {
             vertical-align: middle;
             border: 1px solid #ccc;
             border-radius: 6px;
         }
     }
-
     .header-cont {
         height: $height-header;
         background: #fff;
     }
-
     .main-cont {
         .breadcrumb {
             height: $height-header;
@@ -1234,21 +1174,17 @@ $mainHight: 100vh;
             background-color: #fff;
             padding: 0;
         }
-
         .fl-right {
             // height: $height-header;
             // line-height: $height-header;
         }
-
         &.el-main {
             overflow: auto;
             background: #fff;
         }
-
         height: $mainHight !important;
         overflow: visible;
         position: relative;
-
         .menu-total {
             float: left;
             margin-top: 10px;
@@ -1257,16 +1193,13 @@ $mainHight: 100vh;
             line-height: 30px;
             font-size: 30px;
         }
-
         .aside {
             overflow: auto;
-
             // background: #fff;
             &::-webkit-scrollbar {
                 display: none;
             }
         }
-
         .el-menu-vertical {
             height: calc(100vh - 64px) !important;
             visibility: auto;
@@ -1274,38 +1207,30 @@ $mainHight: 100vh;
                 width: 220px;
             }
         }
-
         .el-menu--collapse {
             width: 54px;
-
             li {
-
                 .el-tooltip,
-                .el-submenu__title {
+                .el-sub-menu__title {
                     padding: 0px 15px !important;
                 }
             }
         }
-
         &::-webkit-scrollbar {
             display: none;
         }
-
         &.main-left {
             width: auto !important;
         }
-
         &.main-right {
             .admin-title {
                 float: left;
                 font-size: 16px;
                 vertical-align: middle;
                 margin-left: 20px;
-
                 img {
                     vertical-align: middle;
                 }
-
                 &.collapse {
                     width: 53px;
                 }
@@ -1330,16 +1255,13 @@ $mainHight: 100vh;
     height: 60px;
     width: 120px;
     text-align: center;
-
     .el-input__inner {
         border: none;
         border-bottom: 1px solid #606266;
     }
-
     .el-dropdown-link {
         cursor: pointer;
     }
-
     .search-icon {
         font-size: $icon-size;
         margin-right: 14px;
@@ -1348,7 +1270,6 @@ $mainHight: 100vh;
         box-sizing: border-box;
         color: #606266;
     }
-
     .dropdown-group {
         min-width: 100px;
     }
@@ -1377,18 +1298,15 @@ $mainHight: 100vh;
     padding: 20px;
     border-radius: 4px;
     overflow: hidden;
-
     .car-left {
         height: $height-car;
         // width: 70%;
         // float: left;
     }
-
     .car-right {
         height: $height-car;
         // width: 29%;
         // float: left;
-
         .flow,
         .user-number,
         .feedback {
@@ -1401,54 +1319,48 @@ $mainHight: 100vh;
             font-size: 13px;
             margin-right: 5px;
         }
-
         .flow {
             background-color: #fff7e8;
             border-color: #feefd0;
             color: #faad14;
         }
-
         .user-number {
             background-color: #ecf5ff;
             border-color: #d9ecff;
             color: #409eff;
         }
-
         .feedback {
             background-color: #eef9e8;
             border-color: #dcf3d1;
             color: #52c41a;
         }
-
-        .car-item {
+        .card-item {
+            padding-right: 20px;
             text-align: right;
-
+            margin-top: 12px;
             b {
+                margin-top: 6px;
                 display: block;
             }
         }
     }
-
     .card-img {
         width: $height-car;
         height: $height-car;
         display: inline-block;
         float: left;
         overflow: hidden;
-
         img {
             width: 100%;
             height: 100%;
             border-radius: 50%;
         }
     }
-
     .text {
         height: $height-car;
         margin-left: 10px;
         float: left;
         margin-top: 14px;
-
         h4 {
             font-size: 20px;
             color: #262626;
@@ -1457,13 +1369,12 @@ $mainHight: 100vh;
             word-break: break-all;
             text-overflow: ellipsis;
         }
-
         .tips-text {
             color: #8c8c8c;
             margin-top: 8px;
-            .el-icon{
+            .el-icon {
                 margin-right: 8px;
-                display:inline-block;
+                display: inline-block;
             }
         }
     }
@@ -1471,14 +1382,12 @@ $mainHight: 100vh;
 
 .shadow {
     margin: 4px 0;
-
     .grid-content {
         background-color: $white-bg;
         border-radius: 4px;
         text-align: center;
         padding: 10px 0;
         cursor: pointer;
-
         .el-icon {
             width: $el-icon-small;
             height: $el-icon-small;
@@ -1506,4 +1415,4 @@ $mainHight: 100vh;
 
 ::-webkit-scrollbar-thumb:hover {
     background-color: #bbb;
-}
+}

+ 3 - 1
web/src/style/newLogin.scss

@@ -1,7 +1,7 @@
 #userLayout{
   margin: 0;
   padding: 0;
-  background-image: url("~@/assets/login_background.svg");
+  // background-image: url("~@/assets/login_background.svg");
   background-size: cover;
   width: 100%;
   height: 100%;
@@ -53,6 +53,8 @@
         float: right !important;
         background: #ccc;
         img {
+          width: 100%;
+          height: 100%;
           cursor: pointer;
           vertical-align: middle;
         }

+ 5 - 17
web/src/utils/bus.js

@@ -1,18 +1,6 @@
-const install = (Vue) => {
-  const Bus = new Vue({
-    methods: {
-      emit(event, ...args) {
-        this.$emit(event, ...args)
-      },
-      on(event, cb) {
-        this.$on(event, cb)
-      },
-      off(event, cb) {
-        this.$off(event, cb)
-      }
-    }
-  })
-  Vue.prototype.$bus = Bus
-}
 
-export default install
+// using ES6 modules
+import mitt from 'mitt'
+
+export const emitter = mitt()
+

+ 12 - 11
web/src/utils/request.js

@@ -1,8 +1,7 @@
 import axios from 'axios' // 引入axios
-import { Message } from 'element-ui'
+import { ElMessage, ElMessageBox } from 'element-plus'
 import { store } from '@/store'
-import context from '@/main'
-import { MessageBox } from 'element-ui'
+import { emitter } from '@/utils/bus.js'
 
 const service = axios.create({
   baseURL: process.env.VUE_APP_BASE_API,
@@ -17,7 +16,7 @@ const showLoading = () => {
   }
   timer = setTimeout(() => {
     if (acitveAxios > 0) {
-      context.$bus.emit('showLoading')
+      emitter.emit('showLoading')
     }
   }, 400)
 }
@@ -26,7 +25,7 @@ const closeLoading = () => {
   acitveAxios--
   if (acitveAxios <= 0) {
     clearTimeout(timer)
-    context.$bus.emit('closeLoading')
+    emitter.emit('closeLoading')
   }
 }
 // http request 拦截器
@@ -47,7 +46,7 @@ service.interceptors.request.use(
   },
   error => {
     closeLoading()
-    Message({
+    ElMessage({
       showClose: true,
       message: error,
       type: 'error'
@@ -60,17 +59,19 @@ service.interceptors.request.use(
 service.interceptors.response.use(
   response => {
     closeLoading()
-
     if (response.headers['new-token']) {
       store.commit('user/setToken', response.headers['new-token'])
     }
     if (response.data.code === 0 || response.headers.success === 'true') {
+      if (response.headers.msg) {
+        response.data.msg = decodeURI(response.headers.msg)
+      }
       return response.data
     } else {
-      Message({
+      ElMessage({
         showClose: true,
-        message: response.data.msg || decodeURI(response.headers.msg),
-        type: response.headers.msgtype || 'error'
+        message: response.data.msg,
+        type: 'error'
       })
       if (response.data.data && response.data.data.reload) {
         store.commit('user/LoginOut')
@@ -80,7 +81,7 @@ service.interceptors.response.use(
   },
   error => {
     closeLoading()
-    MessageBox.confirm(`
+    ElMessageBox.confirm(`
     <p>检测到接口错误${error}</p>
     <p>错误码500:此类错误内容常见于后台panic,如果影响您正常使用可强制登出清理缓存</p>
     <p>错误码404:此类错误多为接口未注册(或未重启)或者请求路径(方法)与api路径(方法)不符</p>

+ 17 - 13
web/src/view/about/index.vue

@@ -3,7 +3,9 @@
     <el-row :gutter="10">
       <el-col :span="12">
         <el-card>
-          <div slot="header">gin-vue-admin</div>
+          <template #header>
+            <el-divider>gin-vue-admin</el-divider>
+          </template>
           <div>
             <el-row>
               <el-col :span="8" :offset="8">
@@ -48,7 +50,9 @@
           </div>
         </el-card>
         <el-card style="margin-top: 20px">
-          <div slot="header">flipped-aurora团队</div>
+          <template #header>
+            <div>flipped-aurora团队</div>
+          </template>
           <div>
             <el-row>
               <el-col :span="8" :offset="8">
@@ -62,23 +66,23 @@
               </el-col>
             </el-row>
             <el-row style="margin-left: 40px" :gutter="20">
-              <template v-for="(item, index) in members">
-                <el-col :key="index" :span="8">
-                  <a :href="item.html_url">
-                    <img class="avatar-img" :src="item.avatar_url">
-                    <a class="author-name" style="">{{ item.login }}</a>
-                  </a>
-                </el-col>
-              </template>
+              <el-col v-for="(item, index) in members" :key="index" :span="8">
+                <a :href="item.html_url">
+                  <img class="avatar-img" :src="item.avatar_url">
+                  <a class="author-name" style="">{{ item.login }}</a>
+                </a>
+              </el-col>
             </el-row>
           </div>
         </el-card>
       </el-col>
       <el-col :span="12">
         <el-card>
-          <div slot="header">
-            提交记录
-          </div>
+          <template #header>
+            <div>
+              提交记录
+            </div>
+          </template>
           <div>
             <Timeline
               :timeline-items="dataTimeline"

+ 0 - 44
web/src/view/dashboard/component/musicPlayer.vue

@@ -1,44 +0,0 @@
-<template>
-  <div>
-    <div style="width: 100%">
-      <APlayer :audio="audio" />
-    </div>
-  </div>
-</template>
-
-<script>
-import { APlayer } from '@moefe/vue-aplayer'
-export default {
-  name: 'MusicPlayer',
-  components: {
-    APlayer
-  },
-  data() {
-    return {
-      audio: [
-        {
-          name: '东西(Cover:林俊呈)',
-          artist: '纳豆',
-          url: 'http://music.163.com/song/media/outer/url?id=1321594530.mp3',
-          cover: 'https://p1.music.126.net/5zs7IvmLv7KahY3BFzUmrg==/109951163635241613.jpg?param=300y300', // prettier-ignore
-          lrc: 'https://cdn.moefe.org/music/lrc/thing.lrc'
-        },
-        {
-          name: '响喜乱舞(Cover:MARiA)',
-          artist: '泠鸢yousa',
-          url: 'http://music.163.com/song/media/outer/url?id=1318962459.mp3',
-          cover: 'https://p1.music.126.net/AUGVPQ_rVrngDH9ocQrn3Q==/109951163613037822.jpg?param=300y300', // prettier-ignore
-          lrc: 'https://cdn.moefe.org/music/lrc/kyoukiranbu.lrc'
-        },
-        {
-          name: '啵唧',
-          artist: 'Hanser',
-          url: 'http://music.163.com/song/media/outer/url?id=1321424246.mp3',
-          cover: 'https://p1.music.126.net/K0-IPcIQ9QFvA0jXTBqoWQ==/109951163636756693.jpg?param=300y300', // prettier-ignore
-          lrc: 'https://cdn.moefe.org/music/lrc/kiss.lrc'
-        }
-      ]
-    }
-  }
-}
-</script>

+ 0 - 81
web/src/view/dashboard/component/todoList/Todo.vue

@@ -1,81 +0,0 @@
-<template>
-  <li :class="{ completed: todo.done, editing: editing }" class="todo">
-    <div class="view">
-      <input
-        :checked="todo.done"
-        class="toggle"
-        type="checkbox"
-        @change="toggleTodo( todo)"
-      >
-      <label @dblclick="editing = true" v-text="todo.text" />
-      <button class="destroy" @click="deleteTodo( todo )" />
-    </div>
-    <input
-      v-show="editing"
-      v-focus="editing"
-      :value="todo.text"
-      class="edit"
-      @keyup.enter="doneEdit"
-      @keyup.esc="cancelEdit"
-      @blur="doneEdit"
-    >
-  </li>
-</template>
-
-<script>
-export default {
-  name: 'Todo',
-  directives: {
-    focus(el, { value }, { context }) {
-      if (value) {
-        context.$nextTick(() => {
-          el.focus()
-        })
-      }
-    }
-  },
-  props: {
-    todo: {
-      type: Object,
-      default: function() {
-        return {}
-      }
-    }
-  },
-  data() {
-    return {
-      editing: false
-    }
-  },
-  methods: {
-    deleteTodo(todo) {
-      this.$emit('deleteTodo', todo)
-    },
-    editTodo({ todo, value }) {
-      this.$emit('editTodo', { todo, value })
-    },
-    toggleTodo(todo) {
-      this.$emit('toggleTodo', todo)
-    },
-    doneEdit(e) {
-      const value = e.target.value.trim()
-      const { todo } = this
-      if (!value) {
-        this.deleteTodo({
-          todo
-        })
-      } else if (this.editing) {
-        this.editTodo({
-          todo,
-          value
-        })
-        this.editing = false
-      }
-    },
-    cancelEdit(e) {
-      e.target.value = this.todo.text
-      this.editing = false
-    }
-  }
-}
-</script>

+ 0 - 320
web/src/view/dashboard/component/todoList/index.scss

@@ -1,320 +0,0 @@
-.todoapp {
-  font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
-  line-height: 1.4em;
-  color: #4d4d4d;
-  min-width: 230px;
-  max-width: 666px;
-  margin: 0 auto ;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  font-weight: 300;
-  background: #fff;
-  z-index: 1;
-  position: relative;
-  button {
-    margin: 0;
-    padding: 0;
-    border: 0;
-    background: none;
-    font-size: 100%;
-    vertical-align: baseline;
-    font-family: inherit;
-    font-weight: inherit;
-    color: inherit;
-    -webkit-appearance: none;
-    appearance: none;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-  }
-  :focus {
-    outline: 0;
-  }
-  .hidden {
-    display: none;
-  }
-  .todoapp {
-    background: #fff;
-    margin: 130px 0 40px 0;
-    position: relative;
-    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
-  }
-  .todoapp input::-webkit-input-placeholder {
-    font-style: italic;
-    font-weight: 300;
-    color: #e6e6e6;
-  }
-  .todoapp input::-moz-placeholder {
-    font-style: italic;
-    font-weight: 300;
-    color: #e6e6e6;
-  }
-  .todoapp input::input-placeholder {
-    font-style: italic;
-    font-weight: 300;
-    color: #e6e6e6;
-  }
-  .todoapp h1 {
-    position: absolute;
-    top: -155px;
-    width: 100%;
-    font-size: 100px;
-    font-weight: 100;
-    text-align: center;
-    color: rgba(175, 47, 47, 0.15);
-    -webkit-text-rendering: optimizeLegibility;
-    -moz-text-rendering: optimizeLegibility;
-    text-rendering: optimizeLegibility;
-  }
-  .new-todo,
-  .edit {
-    position: relative;
-    margin: 0;
-    width: 100%;
-    font-size: 18px;
-    font-family: inherit;
-    font-weight: inherit;
-    line-height: 1.4em;
-    border: 0;
-    color: inherit;
-    padding: 6px;
-    border: 1px solid #999;
-    box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-    box-sizing: border-box;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-  }
-  .new-todo {
-    padding: 10px 16px 16px 60px;
-    border: none;
-    background: rgba(0, 0, 0, 0.003);
-    box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
-  }
-  .main {
-    position: relative;
-    z-index: 2;
-    border-top: 1px solid #e6e6e6;
-  }
-  .toggle-all {
-    text-align: center;
-    border: none;
-    /* Mobile Safari */
-    opacity: 0;
-    position: absolute;
-  }
-  .toggle-all+label {
-    width: 60px;
-    height: 34px;
-    font-size: 0;
-    position: absolute;
-    top: -52px;
-    left: -13px;
-    -webkit-transform: rotate(90deg);
-    transform: rotate(90deg);
-  }
-  .toggle-all+label:before {
-    content: '❯';
-    font-size: 22px;
-    color: #e6e6e6;
-    padding: 10px 27px 10px 27px;
-  }
-  .toggle-all:checked+label:before {
-    color: #737373;
-  }
-  .todo-list {
-    margin: 0;
-    padding: 0;
-    list-style: none;
-  }
-  .todo-list li {
-    position: relative;
-    font-size: 24px;
-    border-bottom: 1px solid #ededed;
-  }
-  .todo-list li:last-child {
-    border-bottom: none;
-  }
-  .todo-list li.editing {
-    border-bottom: none;
-    padding: 0;
-  }
-  .todo-list li.editing .edit {
-    display: block;
-    width: 506px;
-    padding: 12px 16px;
-    margin: 0 0 0 43px;
-  }
-  .todo-list li.editing .view {
-    display: none;
-  }
-  .todo-list li .toggle {
-    text-align: center;
-    width: 40px;
-    /* auto, since non-WebKit browsers doesn't support input styling */
-    height: auto;
-    position: absolute;
-    top: 0;
-    bottom: 0;
-    margin: auto 0;
-    border: none;
-    /* Mobile Safari */
-    -webkit-appearance: none;
-    appearance: none;
-  }
-  .todo-list li .toggle {
-    opacity: 0;
-  }
-  .todo-list li .toggle+label {
-    /*
-    Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
-    IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
-  */
-    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
-    background-repeat: no-repeat;
-    background-position: center left;
-    background-size: 36px;
-  }
-  .todo-list li .toggle:checked+label {
-    background-size: 36px;
-    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
-  }
-  .todo-list li label {
-    word-break: break-all;
-    padding: 15px 15px 15px 50px;
-    display: block;
-    line-height: 1.0;
-        font-size: 14px;
-    transition: color 0.4s;
-  }
-  .todo-list li.completed label {
-    color: #d9d9d9;
-    text-decoration: line-through;
-  }
-  .todo-list li .destroy {
-    display: none;
-    position: absolute;
-    top: 0;
-    right: 10px;
-    bottom: 0;
-    width: 40px;
-    height: 40px;
-    margin: auto 0;
-    font-size: 30px;
-    color: #cc9a9a;
-    transition: color 0.2s ease-out;
-    cursor: pointer;
-  }
-  .todo-list li .destroy:hover {
-    color: #af5b5e;
-  }
-  .todo-list li .destroy:after {
-    content: '×';
-  }
-  .todo-list li:hover .destroy {
-    display: block;
-  }
-  .todo-list li .edit {
-    display: none;
-  }
-  .todo-list li.editing:last-child {
-    margin-bottom: -1px;
-  }
-  .footer {
-    color: #777;
-    position: relative;
-    padding: 10px 15px;
-    height: 40px;
-    text-align: center;
-    border-top: 1px solid #e6e6e6;
-  }
-  .footer:before {
-    content: '';
-    position: absolute;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    height: 40px;
-    overflow: hidden;
-    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2);
-  }
-  .todo-count {
-    float: left;
-    text-align: left;
-  }
-  .todo-count strong {
-    font-weight: 300;
-  }
-  .filters {
-    margin: 0;
-    padding: 0;
-    position: relative;
-    z-index: 1;
-    list-style: none;
-  }
-  .filters li {
-    display: inline;
-  }
-  .filters li a {
-    color: inherit;
-    font-size: 12px;
-    padding: 3px 7px;
-    text-decoration: none;
-    border: 1px solid transparent;
-    border-radius: 3px;
-  }
-  .filters li a:hover {
-    border-color: rgba(175, 47, 47, 0.1);
-  }
-  .filters li a.selected {
-    border-color: rgba(175, 47, 47, 0.2);
-  }
-  .clear-completed,
-  html .clear-completed:active {
-    float: right;
-    position: relative;
-    line-height: 20px;
-    text-decoration: none;
-    cursor: pointer;
-  }
-  .clear-completed:hover {
-    text-decoration: underline;
-  }
-  .info {
-    margin: 65px auto 0;
-    color: #bfbfbf;
-    font-size: 10px;
-    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
-    text-align: center;
-  }
-  .info p {
-    line-height: 1;
-  }
-  .info a {
-    color: inherit;
-    text-decoration: none;
-    font-weight: 400;
-  }
-  .info a:hover {
-    text-decoration: underline;
-  }
-  /*
-  Hack to remove background from Mobile Safari.
-  Can't use it globally since it destroys checkboxes in Firefox
-*/
-  @media screen and (-webkit-min-device-pixel-ratio:0) {
-    .toggle-all,
-    .todo-list li .toggle {
-      background: none;
-    }
-    .todo-list li .toggle {
-      height: 40px;
-    }
-  }
-  @media (max-width: 430px) {
-    .footer {
-      height: 50px;
-    }
-    .filters {
-      bottom: 10px;
-    }
-  }
-}

+ 0 - 122
web/src/view/dashboard/component/todoList/index.vue

@@ -1,122 +0,0 @@
-<template>
-  <section class="todoapp">
-    <!-- header -->
-    <header class="header">
-      <input class="new-todo" autocomplete="off" placeholder="Todo List" @keyup.enter="addTodo">
-    </header>
-    <!-- main section -->
-    <section v-show="todos.length" class="main">
-      <input id="toggle-all" :checked="allChecked" class="toggle-all" type="checkbox" @change="toggleAll({ done: !allChecked })">
-      <label for="toggle-all" />
-      <ul class="todo-list">
-        <todo
-          v-for="(todo, index) in filteredTodos"
-          :key="index"
-          :todo="todo"
-          @toggleTodo="toggleTodo"
-          @editTodo="editTodo"
-          @deleteTodo="deleteTodo"
-        />
-      </ul>
-    </section>
-    <!-- footer -->
-    <footer v-show="todos.length" class="footer">
-      <span class="todo-count">
-        <strong>{{ remaining }}</strong>
-        {{ remaining | pluralize('item') }} left
-      </span>
-      <ul class="filters">
-        <li v-for="(val, key) in filters" :key="key">
-          <a :class="{ selected: visibility === key }" @click.prevent="visibility = key">{{ key | capitalize }}</a>
-        </li>
-      </ul>
-      <!-- <button class="clear-completed" v-show="todos.length > remaining" @click="clearCompleted">
-        Clear completed
-      </button> -->
-    </footer>
-  </section>
-</template>
-
-<script>
-import Todo from './Todo.vue'
-
-const STORAGE_KEY = 'todos'
-const filters = {
-  all: todos => todos,
-  active: todos => todos.filter(todo => !todo.done),
-  completed: todos => todos.filter(todo => todo.done)
-}
-const defaultList = [
-  { text: '工作流功能绘制工具', done: false },
-  { text: '工作流流转方法', done: false },
-  { text: '自动化代码优化', done: false }
-]
-export default {
-  components: { Todo },
-  filters: {
-    pluralize: (n, w) => n === 1 ? w : w + 's',
-    capitalize: s => s.charAt(0).toUpperCase() + s.slice(1)
-  },
-  data() {
-    return {
-      visibility: 'all',
-      filters,
-      // todos: JSON.parse(window.localStorage.getItem(STORAGE_KEY)) || defaultList
-      todos: defaultList
-    }
-  },
-  computed: {
-    allChecked() {
-      return this.todos.every(todo => todo.done)
-    },
-    filteredTodos() {
-      return filters[this.visibility](this.todos)
-    },
-    remaining() {
-      return this.todos.filter(todo => !todo.done).length
-    }
-  },
-  methods: {
-    setLocalStorage() {
-      window.localStorage.setItem(STORAGE_KEY, JSON.stringify(this.todos))
-    },
-    addTodo(e) {
-      const text = e.target.value
-      if (text.trim()) {
-        this.todos.push({
-          text,
-          done: false
-        })
-        this.setLocalStorage()
-      }
-      e.target.value = ''
-    },
-    toggleTodo(val) {
-      val.done = !val.done
-      this.setLocalStorage()
-    },
-    deleteTodo(todo) {
-      this.todos.splice(this.todos.indexOf(todo), 1)
-      this.setLocalStorage()
-    },
-    editTodo({ todo, value }) {
-      todo.text = value
-      this.setLocalStorage()
-    },
-    clearCompleted() {
-      this.todos = this.todos.filter(todo => !todo.done)
-      this.setLocalStorage()
-    },
-    toggleAll({ done }) {
-      this.todos.forEach(todo => {
-        todo.done = done
-        this.setLocalStorage()
-      })
-    }
-  }
-}
-</script>
-
-<style lang="scss">
-    @import './index.scss';
-</style>

+ 69 - 80
web/src/view/dashboard/index.vue

@@ -1,73 +1,71 @@
 <template>
   <div class="big">
-    <el-row>
-      <div class="card">
-        <el-col :xs="24" :lg="16" :md="16">
-          <div class="car-left">
-            <el-row>
-              <div>
-                <el-col :xs="4" :md="3" :lg="3">
-                  <span class="card-img">
-                    <img :src="userInfo.headerImg" alt="">
-                  </span>
-                </el-col>
-                <el-col :xs="20" :lg="12" :md="12">
-                  <div class="text">
-                    <h4>早安,管理员, 请开始您一天的工作吧!</h4>
-                    <p class="tips-text">
-                      <i class="el-icon-sunny el-icon" />
-                      <span>今日晴,0℃ - 10℃,天气寒冷,注意添加衣物。</span>
-                    </p>
-                  </div>
-                </el-col>
+    <el-row class="card">
+      <el-col :xs="24" :lg="16" :md="16" style="height:90px">
+        <div class="car-left">
+          <el-row>
+            <el-col :xs="4" :md="3" :lg="3">
+              <span class="card-img">
+                <img :src="userInfo.headerImg" alt="">
+              </span>
+            </el-col>
+            <el-col :xs="20" :lg="12" :md="12">
+              <div class="text">
+                <h4>早安,管理员, 请开始您一天的工作吧!</h4>
+                <p class="tips-text">
+                  <i class="el-icon-sunny el-icon" />
+                  <span>今日晴,0℃ - 10℃,天气寒冷,注意添加衣物。</span>
+                </p>
               </div>
-            </el-row>
-          </div>
-        </el-col>
-        <el-col :xs="24" :lg="8" :md="8">
-          <div class="car-right">
-            <el-row>
-              <el-col :span="8">
-                <div class="car-item">
-                  <span class="flow"><i class="el-icon-s-grid" /></span>
-                  <span>今日流量 </span>
-                  <b>13260</b>
-                </div>
-              </el-col>
-              <el-col :span="8">
-                <div class="car-item">
-                  <span class="user-number">
-                    <i class="el-icon-s-custom" />
-                  </span>
-                  <span>总用户 </span>
-                  <b>48286</b>
-                </div>
-              </el-col>
-              <el-col :span="8">
-                <div class="car-item">
-                  <span class="feedback">
-                    <i class="el-icon-star-on" />
-                  </span>
-                  <span>好评率 </span>
-                  <b>98%</b>
-                </div>
-              </el-col>
-            </el-row>
-          </div>
-        </el-col>
-      </div>
+            </el-col>
+          </el-row>
+        </div>
+      </el-col>
+      <el-col :xs="24" :lg="8" :md="8">
+        <div class="car-right">
+          <el-row>
+            <el-col :span="8">
+              <div class="card-item">
+                <span class="flow"><i class="el-icon-s-grid" /></span>
+                <span>今日流量 </span>
+                <b>13260</b>
+              </div>
+            </el-col>
+            <el-col :span="8">
+              <div class="card-item">
+                <span class="user-number">
+                  <i class="el-icon-s-custom" />
+                </span>
+                <span>总用户 </span>
+                <b>48286</b>
+              </div>
+            </el-col>
+            <el-col :span="8">
+              <div class="card-item">
+                <span class="feedback">
+                  <i class="el-icon-star-on" />
+                </span>
+                <span>好评率 </span>
+                <b>98%</b>
+              </div>
+            </el-col>
+          </el-row>
+        </div>
+      </el-col>
     </el-row>
     <el-row>
-      <el-card shadow="hover">
-        <h2>
-          使用教学:<a style="color:#409EFF" target="view_window" href="https://www.bilibili.com/video/BV1fV411y7dT/">https://www.bilibili.com/video/BV1fV411y7dT/</a>
-        </h2>
-        <br>
-        <h2>
-          工作流教学:<a style="color:#409EFF" target="view_window" href="https://www.bilibili.com/video/BV1Ka411F7Ji/">https://www.bilibili.com/video/BV1Ka411F7Ji/</a>
-        </h2>
-        <div />
-      </el-card>
+      <el-col>
+        <el-card shadow="hover">
+          <h2>
+            使用教学:<a style="color:#409EFF" target="view_window" href="https://www.bilibili.com/video/BV1fV411y7dT/">https://www.bilibili.com/video/BV1fV411y7dT/</a>
+          </h2>
+          <br>
+          <h2>
+            工作流教学:<a style="color:#409EFF" target="view_window" href="https://www.bilibili.com/video/BV1Ka411F7Ji/">https://www.bilibili.com/video/BV1Ka411F7Ji/</a>
+          </h2>
+          <div />
+        </el-card>
+      </el-col>
     </el-row>
     <div class="shadow">
       <el-row :gutter="20">
@@ -76,7 +74,7 @@
           :key="key"
           :span="4"
           :xs="8"
-          @click.native="toTarget(card.name)"
+          @click="toTarget(card.name)"
         >
           <el-card shadow="hover" class="grid-content">
             <i :class="card.icon" :style="{ color: card.color }" />
@@ -88,14 +86,10 @@
     <div class="bottom">
       <el-row :gutter="32">
         <el-col :xs="24" :sm="24" :lg="12">
-          <div class="chart-player">
-            <music-player />
-          </div>
+          <div class="chart-player" />
         </el-col>
         <el-col :xs="24" :sm="24" :lg="12">
-          <div class="chart-player">
-            <todo-list />
-          </div>
+          <div class="chart-player" />
         </el-col>
       </el-row>
     </div>
@@ -103,17 +97,12 @@
 </template>
 
 <script>
-import musicPlayer from './component/musicPlayer'
-import TodoList from './component/todoList'
+
 import { mapGetters } from 'vuex'
 export default {
   name: 'Dashboard',
   components: {
-    musicPlayer, // 音乐播放器
-    TodoList // TodoList
-    // RaddarChart, //雷达图
-    // stackMap, //堆叠图
-    // Sunburst, //旭日图
+
   },
   data() {
     return {
@@ -171,7 +160,7 @@ export default {
 <style lang="scss" scoped>
 .big {
   margin: 100px 0 0 0;
-  padding-top: 0;
+  padding-top: 10px;
   background-color: rgb(243, 243, 243);
   .top {
     width: 100%;

+ 15 - 20
web/src/view/example/customer/customer.vue

@@ -17,21 +17,25 @@
     >
       <el-table-column type="selection" width="55" />
       <el-table-column label="接入日期" width="180">
-        <template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
+        <template #default="scope">
+          <span>{{ formatDate(scope.row.CreatedAt) }}</span>
+        </template>
       </el-table-column>
       <el-table-column label="姓名" prop="customerName" width="120" />
       <el-table-column label="电话" prop="customerPhoneData" width="120" />
       <el-table-column label="接入人ID" prop="sysUserId" width="120" />
       <el-table-column label="按钮组" min-width="160">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-button size="small" type="text" @click="updateCustomer(scope.row)">变更</el-button>
-          <el-popover v-model="scope.row.visible" placement="top" width="160">
+          <el-popover v-model:visible="scope.row.visible" placement="top" width="160">
             <p>确定要删除吗?</p>
             <div style="text-align: right; margin: 0">
               <el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
               <el-button type="primary" size="mini" @click="deleteCustomer(scope.row)">确定</el-button>
             </div>
-            <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
+            <template #reference>
+              <el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button>
+            </template>
           </el-popover>
         </template>
       </el-table-column>
@@ -48,7 +52,7 @@
       @size-change="handleSizeChange"
     />
 
-    <el-dialog :before-close="closeDialog" :visible.sync="dialogFormVisible" title="客户">
+    <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="客户">
       <el-form :inline="true" :model="form" label-width="80px">
         <el-form-item label="客户名">
           <el-input v-model="form.customerName" autocomplete="off" />
@@ -57,10 +61,12 @@
           <el-input v-model="form.customerPhoneData" autocomplete="off" />
         </el-form-item>
       </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="closeDialog">取 消</el-button>
-        <el-button type="primary" @click="enterDialog">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDialog">取 消</el-button>
+          <el-button type="primary" @click="enterDialog">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
     <div class="tips">在资源权限中将此角色的资源权限清空 或者不包含创建者的角色 即可屏蔽此客户资源的显示</div>
   </div>
@@ -74,21 +80,10 @@ import {
   getExaCustomer,
   getExaCustomerList
 } from '@/api/customer'
-import { formatTimeToStr } from '@/utils/date'
 import infoList from '@/mixins/infoList'
 
 export default {
   name: 'Customer',
-  filters: {
-    formatDate: function(time) {
-      if (time !== null && time !== '') {
-        var date = new Date(time)
-        return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
-      } else {
-        return ''
-      }
-    }
-  },
   mixins: [infoList],
   data() {
     return {

+ 2 - 1
web/src/view/example/excel/excel.vue

@@ -18,7 +18,8 @@
       <el-table-column label="路由Name" min-width="160" prop="name" />
       <el-table-column label="路由Path" min-width="160" prop="path" />
       <el-table-column label="是否隐藏" min-width="100" prop="hidden">
-        <template slot-scope="scope">
+
+        <template #default="scope">
           <span>{{ scope.row.hidden?"隐藏":"显示" }}</span>
         </template>
       </el-table-column>

+ 12 - 4
web/src/view/example/index.vue

@@ -1,9 +1,17 @@
 <template>
   <div>
-    <keep-alive>
-      <router-view v-if="$route.meta.keepAlive" />
-    </keep-alive>
-    <router-view v-if="!$route.meta.keepAlive" />
+    <router-view v-if="$route.meta.keepAlive" v-slot="{ Component }">
+      <transition mode="out-in" name="el-fade-in-linear">
+        <keep-alive>
+          <component :is="Component" />
+        </keep-alive>
+      </transition>
+    </router-view>
+    <router-view v-if="!$route.meta.keepAlive" v-slot="{ Component }">
+      <transition mode="out-in" name="el-fade-in-linear">
+        <component :is="Component" />
+      </transition>
+    </router-view>
   </div>
 </template>
 

+ 0 - 161
web/src/view/example/simpleUploader/simpleUploader.vue

@@ -1,161 +0,0 @@
-<template>
-  <uploader
-    :options="options"
-    :file-status-text="statusText"
-    :auto-start="false"
-    class="uploader-example"
-    @file-added="fileAdded"
-    @file-progress="onFileProgress"
-    @file-success="onFileSuccess"
-    @file-error="onFileError"
-  >
-    <uploader-unsupport />
-    <uploader-drop>
-      <p>拖拽文件至此或点击</p>
-      <uploader-btn>选择文件</uploader-btn>
-    </uploader-drop>
-    <uploader-list />
-  </uploader>
-</template>
-
-<script>
-var notUploadedChunks = [] // 已经上传过的文件chunkNumber数组
-var isUploaded = false // 文件已经上传成功了
-import { mapGetters } from 'vuex'
-import { checkFileMd5, mergeFileMd5 } from '@/api/simpleUploader'
-import SparkMD5 from 'spark-md5'
-const path = process.env.VUE_APP_BASE_API
-export default {
-  name: 'SimpleUploader',
-  data() {
-    return {
-      md5: ''
-    }
-  },
-  computed: {
-    ...mapGetters('user', ['userInfo', 'token']),
-    statusText() {
-      return {
-        success: '成功了',
-        error: '出错了',
-        uploading: '上传中',
-        paused: '暂停中',
-        waiting: '等待中'
-      }
-    },
-    options() {
-      return {
-        target: path + '/simpleUploader/upload',
-        testChunks: false,
-        simultaneousUploads: 5,
-        chunkSize: 2 * 1024 * 1024,
-        headers: {
-          'x-token': this.token,
-          'x-user-id': this.userInfo.ID
-        },
-        checkChunkUploadedByResponse(chunk) {
-          if (isUploaded) {
-            return true // return true 会忽略当前文件,不会再发送给后台
-          } else {
-            // 根据已经上传过的切片来进行忽略
-            return (
-              notUploadedChunks &&
-                notUploadedChunks.some(
-                  item => item.chunkNumber === chunk.offset + 1
-                )
-            )
-          }
-        }
-      }
-    }
-  },
-  methods: {
-
-    // 上传单个文件
-    fileAdded(file) {
-      this.computeMD5(file) // 生成MD5
-    },
-    // 计算MD5值
-    computeMD5(file) {
-      var that = this
-      isUploaded = false // 这个文件是否已经上传成功过
-      notUploadedChunks = [] // 未成功的chunkNumber
-      var fileReader = new FileReader()
-      var md5 = ''
-
-      file.pause()
-
-      fileReader.readAsArrayBuffer(file.file)
-      fileReader.onload = async function(e) {
-        if (file.size !== e.target.result.byteLength) {
-          this.error(
-            'Browser reported success but could not read the file until the end.'
-          )
-          return false
-        }
-        md5 = SparkMD5.ArrayBuffer.hash(e.target.result, false)
-
-        file.uniqueIdentifier = md5
-        if (md5 !== '') {
-          const res = await checkFileMd5({ md5: md5 })
-          if (res.code === 0) {
-            if (res.data.isDone) {
-              // 上传成功过
-              isUploaded = true
-              that.$message({
-                message: '该文件已经上传成功过了,秒传成功。',
-                type: 'success'
-              })
-
-              file.cancel()
-            } else {
-              isUploaded = false
-              notUploadedChunks = res.data.chunks
-              if (notUploadedChunks.length) {
-                file.resume()
-              }
-            }
-          }
-        }
-      }
-      fileReader.onerror = function() {
-        this.error(
-          'generater md5 时FileReader异步读取文件出错了,FileReader onerror was triggered, maybe the browser aborted due to high memory usage.'
-        )
-        return false
-      }
-    },
-    // 上传进度
-    onFileProgress() {},
-    // 上传成功
-    async onFileSuccess(rootFile, file) {
-      await mergeFileMd5({ md5: file.uniqueIdentifier, fileName: file.name })
-    },
-    onFileError(rootFile, file, response) {
-      this.$message({
-        message: response,
-        type: 'error'
-      })
-    }
-  }
-}
-</script>
-
-<style>
-.uploader-example {
-  width: 880px;
-  padding: 15px;
-  margin: 15px 15px 20px;
-  font-size: 12px;
-  box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
-}
-.uploader-example .uploader-btn {
-  margin-right: 4px;
-}
-.uploader-example .uploader-list {
-  max-height: 440px;
-  overflow: auto;
-  overflow-x: hidden;
-  overflow-y: auto;
-}
-</style>

+ 8 - 17
web/src/view/example/upload/upload.vue

@@ -12,7 +12,9 @@
             :show-file-list="false"
           >
             <el-button size="small" type="primary">点击上传</el-button>
-            <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
+            <template #tip>
+              <div class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
+            </template>
           </el-upload>
         </el-col>
         <el-col :span="12">
@@ -24,19 +26,19 @@
 
       <el-table :data="tableData" border stripe>
         <el-table-column label="预览" width="100">
-          <template slot-scope="scope">
+          <template #default="scope">
             <CustomPic pic-type="file" :pic-src="scope.row.url" />
           </template>
         </el-table-column>
         <el-table-column label="日期" prop="UpdatedAt" width="180">
-          <template slot-scope="scope">
-            <div>{{ scope.row.UpdatedAt | formatDate }}</div>
+          <template #default="scope">
+            <div>{{ formatDate(scope.row.UpdatedAt) }}</div>
           </template>
         </el-table-column>
         <el-table-column label="文件名" prop="name" width="180" />
         <el-table-column label="链接" prop="url" min-width="300" />
         <el-table-column label="标签" prop="tag" width="100">
-          <template slot-scope="scope">
+          <template #default="scope">
             <el-tag
               :type="scope.row.tag === 'jpg' ? 'primary' : 'success'"
               disable-transitions
@@ -44,7 +46,7 @@
           </template>
         </el-table-column>
         <el-table-column label="操作" width="160">
-          <template slot-scope="scope">
+          <template #default="scope">
             <el-button size="small" type="text" @click="downloadFile(scope.row)">下载</el-button>
             <el-button size="small" type="text" @click="deleteFile(scope.row)">删除</el-button>
           </template>
@@ -70,7 +72,6 @@ import { mapGetters } from 'vuex'
 import infoList from '@/mixins/infoList'
 import { getFileList, deleteFile } from '@/api/fileUploadAndDownload'
 import { downloadImage } from '@/utils/downloadImg'
-import { formatTimeToStr } from '@/utils/date'
 import CustomPic from '@/components/customPic'
 import UploadImage from '@/components/upload/image.vue'
 export default {
@@ -79,16 +80,6 @@ export default {
     CustomPic,
     UploadImage
   },
-  filters: {
-    formatDate: function(time) {
-      if (time !== null && time !== '') {
-        var date = new Date(time)
-        return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
-      } else {
-        return ''
-      }
-    }
-  },
   mixins: [infoList],
   data() {
     return {

+ 0 - 1
web/src/view/init/index.vue

@@ -82,7 +82,6 @@ export default {
   methods: {
     showNext() {
       this.hello = this.hello + 1
-      console.log(this.hello)
     },
     goDoc() {
       window.open('https://www.gin-vue-admin.com/docs/first_master#3-init')

+ 4 - 4
web/src/view/layout/aside/asideComponent/asyncSubmenu.vue

@@ -1,11 +1,11 @@
 <template>
-  <el-submenu ref="subMenu" :popper-append-to-body="false" :index="routerInfo.name">
-    <template slot="title">
+  <el-sub-menu ref="subMenu" :index="routerInfo.name">
+    <template #title>
       <i :class="'el-icon-'+routerInfo.meta.icon" />
-      <span slot="title">{{ routerInfo.meta.title }}</span>
+      <span>{{ routerInfo.meta.title }}</span>
     </template>
     <slot />
-  </el-submenu>
+  </el-sub-menu>
 </template>
 
 <script>

+ 3 - 1
web/src/view/layout/aside/asideComponent/menuItem.vue

@@ -1,7 +1,9 @@
 <template>
   <el-menu-item :index="routerInfo.name" :route="{parameters:routerInfo.parameters}">
     <i :class="'el-icon-'+routerInfo.meta.icon" />
-    <span slot="title">{{ routerInfo.meta.title }}</span>
+    <template #title>
+      <span>{{ routerInfo.meta.title }}</span>
+    </template>
   </el-menu-item>
 </template>
 

+ 13 - 10
web/src/view/layout/aside/historyComponent/history.vue

@@ -2,9 +2,9 @@
   <div class="router-history">
     <el-tabs
       v-model="activeValue"
-      :closable="!(historys.length===1&&this.$route.name===defaultRouter)"
+      :closable="!(historys.length===1&&$route.name===defaultRouter)"
       type="card"
-      @contextmenu.prevent.native="openContextMenu($event)"
+      @contextmenu.prevent="openContextMenu($event)"
       @tab-click="changeTab"
       @tab-remove="removeTab"
     >
@@ -16,7 +16,9 @@
         :tab="item"
         class="gva-tab"
       >
-        <span slot="label" :style="{color: activeValue===name(item)?activeColor:'#333'}"><i class="dot" :style="{backgroundColor:activeValue===name(item)?activeColor:'#ddd'}" /> {{ item.meta.title }}</span>
+        <template #label>
+          <span :style="{color: activeValue===name(item)?activeColor:'#333'}"><i class="dot" :style="{backgroundColor:activeValue===name(item)?activeColor:'#ddd'}" /> {{ item.meta.title }}</span>
+        </template>
       </el-tab-pane>
     </el-tabs>
 
@@ -32,6 +34,8 @@
 
 <script>
 import { mapGetters } from 'vuex'
+import { emitter } from '@/utils/bus.js'
+
 const getFmtString = (item) => {
   return item.name +
       JSON.stringify(item.query) +
@@ -75,15 +79,15 @@ export default {
       sessionStorage.setItem('historys', JSON.stringify(this.historys))
       this.activeValue = window.sessionStorage.getItem('activeValue')
       if (now && to && now.name === to.name) {
-        this.$bus.$emit('reload')
+        emitter.emit('reload')
       }
     }
   },
   created() {
-    this.$bus.on('mobile', isMobile => {
+    emitter.on('mobile', isMobile => {
       this.isMobile = isMobile
     })
-    this.$bus.on('collapse', isCollapse => {
+    emitter.on('collapse', isCollapse => {
       this.isCollapse = isCollapse
     })
     const initHistorys = [
@@ -107,8 +111,8 @@ export default {
   },
 
   beforeDestroy() {
-    this.$bus.off('collapse')
-    this.$bus.off('mobile')
+    emitter.off('collapse')
+    emitter.off('mobile')
   },
   methods: {
     name(item) {
@@ -120,7 +124,6 @@ export default {
       }
       let id = ''
       if (e.srcElement.nodeName === 'SPAN') {
-        console.log(e)
         id = e.srcElement.offsetParent.id
       } else {
         id = e.srcElement.id
@@ -236,7 +239,7 @@ export default {
       )
     },
     changeTab(component) {
-      const tab = component.$attrs.tab
+      const tab = component.instance.attrs.tab
       this.$router.push({
         name: tab.name,
         query: tab.query,

+ 7 - 5
web/src/view/layout/aside/index.vue

@@ -4,7 +4,7 @@
       <transition :duration="{ enter: 800, leave: 100 }" mode="out-in" name="el-fade-in-linear">
         <el-menu
           :collapse="isCollapse"
-          :collapse-transition="true"
+          :collapse-transition="false"
           :default-active="active"
           :background-color="sideMode"
           :active-text-color="activeColor"
@@ -25,6 +25,8 @@
 <script>
 import { mapGetters, mapMutations } from 'vuex'
 import AsideComponent from '@/view/layout/aside/asideComponent'
+import { emitter } from '@/utils/bus.js'
+
 export default {
   name: 'Aside',
   components: {
@@ -52,12 +54,12 @@ export default {
       this.isCollapse = !this.isCollapse
     }
 
-    this.$bus.on('collapse', item => {
+    emitter.on('collapse', item => {
       this.isCollapse = item
     })
   },
   beforeDestroy() {
-    this.$bus.off('collapse')
+    emitter.off('collapse')
   },
   methods: {
     ...mapMutations('history', ['addHistory']),
@@ -84,13 +86,13 @@ export default {
 </script>
 
 <style lang="scss">
-.el-submenu__title,.el-menu-item{
+.el-sub-menu__title,.el-menu-item{
   i{
     color: inherit !important;
   }
 }
 
-.el-submenu__title:hover,.el-menu-item:hover{
+.el-sub-menu__title:hover,.el-menu-item:hover{
   i{
     color: inherit !important;
   }

+ 75 - 62
web/src/view/layout/index.vue

@@ -1,7 +1,7 @@
 <template>
   <el-container class="layout-cont">
     <el-container :class="[isSider?'openside':'hideside',isMobile ? 'mobile': '']">
-      <el-row :class="[isShadowBg?'shadowBg':'']" @click.native="changeShadow()" />
+      <el-row :class="[isShadowBg?'shadowBg':'']" @click="changeShadow()" />
       <el-aside class="main-cont main-left">
         <div class="tilte" :style="{background: backgroundColor}">
           <img alt class="logoimg" :src="$GIN_VUE_ADMIN.appLogo">
@@ -18,52 +18,59 @@
           >
             <el-row>
               <!-- :xs="8" :sm="6" :md="4" :lg="3" :xl="1" -->
-              <el-header class="header-cont">
-                <el-col :xs="2" :lg="1" :md="1" :sm="1" :xl="1">
-                  <div class="menu-total" @click="totalCollapse">
-                    <i v-if="isCollapse" class="el-icon-s-unfold" />
-                    <i v-else class="el-icon-s-fold" />
-                  </div>
-                </el-col>
-                <el-col :xs="10" :lg="14" :md="14" :sm="9" :xl="14">
-                  <el-breadcrumb class="breadcrumb" separator-class="el-icon-arrow-right">
-                    <el-breadcrumb-item
-                      v-for="item in matched.slice(1,matched.length)"
-                      :key="item.path"
-                    >{{ item.meta.title }}</el-breadcrumb-item>
-                  </el-breadcrumb>
-                </el-col>
-                <el-col :xs="12" :lg="9" :md="9" :sm="14" :xl="9">
-                  <div class="fl-right right-box">
-                    <Search />
-                    <Screenfull class="screenfull" :style="{cursor:'pointer'}" />
-                    <el-dropdown>
-                      <span class="header-avatar" style="cursor: pointer">
-                        <CustomPic />
-                        <span style="margin-left: 5px">{{ userInfo.nickName }}</span>
-                        <i class="el-icon-arrow-down" />
-                      </span>
-                      <el-dropdown-menu slot="dropdown" class="dropdown-group">
-                        <el-dropdown-item>
-                          <span style="font-weight: 600;">
-                            当前角色:{{ userInfo.authority.authorityName }}
-                          </span>
-                        </el-dropdown-item>
-                        <template v-if="userInfo.authorities">
-                          <el-dropdown-item v-for="item in userInfo.authorities.filter(i=>i.authorityId!==userInfo.authorityId)" :key="item.authorityId" @click.native="changeUserAuth(item.authorityId)">
-                            <span>
-                              切换为:{{ item.authorityName }}
+              <el-col>
+                <el-header class="header-cont">
+                  <el-row class="pd-0">
+                    <el-col :xs="2" :lg="1" :md="1" :sm="1" :xl="1">
+                      <div class="menu-total" @click="totalCollapse">
+                        <i v-if="isCollapse" class="el-icon-s-unfold" />
+                        <i v-else class="el-icon-s-fold" />
+                      </div>
+                    </el-col>
+                    <el-col :xs="10" :lg="14" :md="14" :sm="9" :xl="14">
+                      <el-breadcrumb class="breadcrumb" separator-class="el-icon-arrow-right">
+                        <el-breadcrumb-item
+                          v-for="item in matched.slice(1,matched.length)"
+                          :key="item.path"
+                        >{{ item.meta.title }}</el-breadcrumb-item>
+                      </el-breadcrumb>
+                    </el-col>
+                    <el-col :xs="12" :lg="9" :md="9" :sm="14" :xl="9">
+                      <div class="fl-right right-box">
+                        <Search />
+                        <Screenfull class="screenfull" :style="{cursor:'pointer'}" />
+                        <el-dropdown>
+                          <div class="dp-flex justify-content-center align-items height-full width-full">
+                            <span class="header-avatar" style="cursor: pointer">
+                              <CustomPic />
+                              <span style="margin-left: 5px">{{ userInfo.nickName }}</span>
+                              <i class="el-icon-arrow-down" />
                             </span>
-                          </el-dropdown-item>
-                        </template>
-                        <el-dropdown-item icon="el-icon-s-custom" @click.native="toPerson">个人信息</el-dropdown-item>
-                        <el-dropdown-item icon="el-icon-table-lamp" @click.native="LoginOut">登 出</el-dropdown-item>
-                      </el-dropdown-menu>
-                    </el-dropdown>
-                  </div>
-                </el-col>
-
-              </el-header>
+                          </div>
+                          <template #dropdown>
+                            <el-dropdown-menu class="dropdown-group">
+                              <el-dropdown-item>
+                                <span style="font-weight: 600;">
+                                  当前角色:{{ userInfo.authority.authorityName }}
+                                </span>
+                              </el-dropdown-item>
+                              <template v-if="userInfo.authorities">
+                                <el-dropdown-item v-for="item in userInfo.authorities.filter(i=>i.authorityId!==userInfo.authorityId)" :key="item.authorityId" @click="changeUserAuth(item.authorityId)">
+                                  <span>
+                                    切换为:{{ item.authorityName }}
+                                  </span>
+                                </el-dropdown-item>
+                              </template>
+                              <el-dropdown-item icon="el-icon-s-custom" @click="toPerson">个人信息</el-dropdown-item>
+                              <el-dropdown-item icon="el-icon-table-lamp" @click="LoginOut">登 出</el-dropdown-item>
+                            </el-dropdown-menu>
+                          </template>
+                        </el-dropdown>
+                      </div>
+                    </el-col>
+                  </el-row>
+                </el-header>
+              </el-col>
             </el-row>
             <!-- 当前面包屑用路由自动生成可根据需求修改 -->
             <!--
@@ -71,14 +78,19 @@
             <HistoryComponent />
           </div>
         </transition>
-        <transition mode="out-in" name="el-fade-in-linear">
-          <keep-alive>
-            <router-view v-if="$route.meta.keepAlive && reloadFlag" v-loading="loadingFlag" element-loading-text="正在加载中" class="admin-box" />
-          </keep-alive>
-        </transition>
-        <transition mode="out-in" name="el-fade-in-linear">
-          <router-view v-if="!$route.meta.keepAlive && reloadFlag" v-loading="loadingFlag" element-loading-text="正在加载中" class="admin-box" />
-        </transition>
+        <router-view v-if="$route.meta.keepAlive && reloadFlag" v-slot="{ Component }" v-loading="loadingFlag" element-loading-text="正在加载中" class="admin-box">
+          <transition mode="out-in" name="el-fade-in-linear">
+            <keep-alive>
+              <component :is="Component" />
+            </keep-alive>
+          </transition>
+        </router-view>
+        <router-view v-if="!$route.meta.keepAlive && reloadFlag" v-slot="{ Component }" v-loading="loadingFlag" element-loading-text="正在加载中" class="admin-box">
+          <transition mode="out-in" name="el-fade-in-linear">
+            <component :is="Component" />
+          </transition>
+        </router-view>
+
         <BottomInfo />
         <setting />
       </el-main>
@@ -97,6 +109,7 @@ import { mapGetters, mapActions } from 'vuex'
 import CustomPic from '@/components/customPic'
 import Setting from './setting'
 import { setUserAuthority } from '@/api/user'
+import { emitter } from '@/utils/bus.js'
 export default {
   name: 'Layout',
   components: {
@@ -162,13 +175,13 @@ export default {
       this.isSider = true
       this.isCollapse = false
     }
-    this.$bus.emit('collapse', this.isCollapse)
-    this.$bus.emit('mobile', this.isMobile)
-    this.$bus.on('reload', this.reload)
-    this.$bus.on('showLoading', () => {
+    emitter.emit('collapse', this.isCollapse)
+    emitter.emit('mobile', this.isMobile)
+    emitter.on('reload', this.reload)
+    emitter.on('showLoading', () => {
       this.loadingFlag = true
     })
-    this.$bus.on('closeLoading', () => {
+    emitter.on('closeLoading', () => {
       this.loadingFlag = false
     })
     window.onresize = () => {
@@ -187,8 +200,8 @@ export default {
           this.isSider = true
           this.isCollapse = false
         }
-        this.$bus.emit('collapse', this.isCollapse)
-        this.$bus.emit('mobile', this.isMobile)
+        emitter.emit('collapse', this.isCollapse)
+        emitter.emit('mobile', this.isMobile)
       })()
     }
   },
@@ -212,7 +225,7 @@ export default {
       this.isCollapse = !this.isCollapse
       this.isSider = !this.isCollapse
       this.isShadowBg = !this.isCollapse
-      this.$bus.emit('collapse', this.isCollapse)
+      emitter.emit('collapse', this.isCollapse)
     },
     toPerson() {
       this.$router.push({ name: 'person' })

+ 4 - 2
web/src/view/layout/search/search.vue

@@ -33,6 +33,7 @@
 
 <script>
 import { mapGetters } from 'vuex'
+import { emitter } from '@/utils/bus.js'
 
 export default {
   name: 'SearchComponent',
@@ -44,8 +45,9 @@ export default {
     }
   },
   computed: {
-    ...mapGetters('router', ['routerList'])
+    ...mapGetters('router', ['routerList']),
   },
+
   methods: {
     changeRouter() {
       this.$router.push({ name: this.value })
@@ -62,7 +64,7 @@ export default {
     },
     handleReload() {
       this.reload = true
-      this.$bus.$emit('reload')
+      emitter.emit('reload')
       setTimeout(() => {
         this.reload = false
       }, 500)

+ 1 - 1
web/src/view/layout/setting/index.vue

@@ -2,8 +2,8 @@
   <div>
     <el-button type="primary" class="drawer-container" icon="el-icon-setting" @click="showSettingDrawer" />
     <el-drawer
+      v-model="drawer"
       title="系统配置"
-      :visible.sync="drawer"
       :direction="direction"
       :before-close="handleClose"
     >

+ 10 - 10
web/src/view/login/index.vue

@@ -9,11 +9,13 @@
           ref="loginForm"
           :model="loginForm"
           :rules="rules"
-          @keyup.enter.native="submitForm"
+          @keyup.enter="submitForm"
         >
           <el-form-item prop="username">
             <el-input v-model="loginForm.username" placeholder="请输入用户名">
-              <i slot="suffix" class="el-input__icon el-icon-user" />
+              <template #suffix>
+                <i class="el-input__icon el-icon-user" />
+              </template>
             </el-input>
           </el-form-item>
           <el-form-item prop="password">
@@ -22,11 +24,12 @@
               :type="lock === 'lock' ? 'password' : 'text'"
               placeholder="请输入密码"
             >
-              <i
-                slot="suffix"
-                :class="'el-input__icon el-icon-' + lock"
-                @click="changeLock"
-              />
+              <template #suffix>
+                <i
+                  :class="'el-input__icon el-icon-' + lock"
+                  @click="changeLock"
+                />
+              </template>
             </el-input>
           </el-form-item>
           <el-form-item style="position: relative">
@@ -40,14 +43,11 @@
               <img
                 v-if="picPath"
                 :src="picPath"
-                width="100%"
-                height="100%"
                 alt="请输入验证码"
                 @click="loginVerify()"
               >
             </div>
           </el-form-item>
-          <div />
           <el-form-item>
             <el-button
               type="primary"

+ 7 - 5
web/src/view/person/person.vue

@@ -79,7 +79,7 @@
 
     <ChooseImg ref="chooseImg" @enter-img="enterImg" />
 
-    <el-dialog :visible.sync="showPassword" title="修改密码" width="360px" @close="clearPassword">
+    <el-dialog v-model="showPassword" title="修改密码" width="360px" @close="clearPassword">
       <el-form ref="modifyPwdForm" :model="pwdModify" :rules="rules" label-width="80px">
         <el-form-item :minlength="6" label="原密码" prop="password">
           <el-input v-model="pwdModify.password" show-password />
@@ -91,10 +91,12 @@
           <el-input v-model="pwdModify.confirmPassword" show-password />
         </el-form-item>
       </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="showPassword=false">取 消</el-button>
-        <el-button type="primary" @click="savePassword">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="showPassword=false">取 消</el-button>
+          <el-button type="primary" @click="savePassword">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
   </div>
 </template>

+ 12 - 4
web/src/view/routerHolder.vue

@@ -1,10 +1,18 @@
 <template>
   <!-- 此路由可作为父类路由通用路由页面使用 如需自定义父类路由页面 请参考 @/view/superAdmin/index.vue -->
   <div>
-    <keep-alive>
-      <router-view v-if="$route.meta.keepAlive" />
-    </keep-alive>
-    <router-view v-if="!$route.meta.keepAlive" />
+    <router-view v-if="$route.meta.keepAlive" v-slot="{ Component }">
+      <transition mode="out-in" name="el-fade-in-linear">
+        <keep-alive>
+          <component :is="Component" />
+        </keep-alive>
+      </transition>
+    </router-view>
+    <router-view v-if="!$route.meta.keepAlive" v-slot="{ Component }">
+      <transition mode="out-in" name="el-fade-in-linear">
+        <component :is="Component" />
+      </transition>
+    </router-view>
   </div>
 </template>
 

+ 23 - 23
web/src/view/superAdmin/api/api.vue

@@ -24,13 +24,15 @@
         <el-form-item>
           <el-button size="mini" type="primary" icon="el-icon-search" @click="onSubmit">查询</el-button>
           <el-button size="mini" type="primary" icon="el-icon-plus" @click="openDialog('addApi')">新增</el-button>
-          <el-popover v-model="deleteVisible" placement="top" width="160">
+          <el-popover v-model:visible="deleteVisible" placement="top" width="160">
             <p>确定要删除吗?</p>
             <div style="text-align: right; margin: 0">
               <el-button size="mini" type="text" @click="deleteVisible = false">取消</el-button>
               <el-button size="mini" type="primary" @click="onDelete">确定</el-button>
             </div>
-            <el-button slot="reference" icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
+            <template #reference>
+              <el-button icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
+            </template>
           </el-popover>
         </el-form-item>
       </el-form>
@@ -45,22 +47,21 @@
       <el-table-column label="api分组" min-width="150" prop="apiGroup" sortable="custom" />
       <el-table-column label="api简介" min-width="150" prop="description" sortable="custom" />
       <el-table-column label="请求" min-width="150" prop="method" sortable="custom">
-        <template slot-scope="scope">
+        <template #default="scope">
           <div>
             {{ scope.row.method }}
             <el-tag
               :key="scope.row.methodFiletr"
-              :type="scope.row.method|tagTypeFiletr"
+              :type="tagTypeFiletr(scope.row.method)"
               effect="dark"
               size="mini"
-            >{{ scope.row.method|methodFiletr }}</el-tag>
-            <!-- {{scope.row.method|methodFiletr}} -->
+            >{{ methodFiletr(scope.row.method) }}</el-tag>
           </div>
         </template>
       </el-table-column>
 
       <el-table-column fixed="right" label="操作" width="200">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-button size="small" type="primary" icon="el-icon-edit" @click="editApi(scope.row)">编辑</el-button>
           <el-button
             size="small"
@@ -82,7 +83,7 @@
       @size-change="handleSizeChange"
     />
 
-    <el-dialog :before-close="closeDialog" :title="dialogTitle" :visible.sync="dialogFormVisible">
+    <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" :title="dialogTitle">
       <el-form ref="apiForm" :inline="true" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="路径" prop="path">
           <el-input v-model="form.path" autocomplete="off" />
@@ -105,10 +106,12 @@
         </el-form-item>
       </el-form>
       <div class="warning">新增Api需要在角色管理内配置权限才可使用</div>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="closeDialog">取 消</el-button>
-        <el-button type="primary" @click="enterDialog">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDialog">取 消</el-button>
+          <el-button type="primary" @click="enterDialog">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
   </div>
 </template>
@@ -151,17 +154,6 @@ const methodOptions = [
 
 export default {
   name: 'Api',
-  filters: {
-    methodFiletr(value) {
-      const target = methodOptions.filter(item => item.value === value)[0]
-      // return target && `${target.label}(${target.value})`
-      return target && `${target.label}`
-    },
-    tagTypeFiletr(value) {
-      const target = methodOptions.filter(item => item.value === value)[0]
-      return target && `${target.type}`
-    }
-  },
   mixins: [infoList],
   data() {
     return {
@@ -196,6 +188,14 @@ export default {
     this.getTableData()
   },
   methods: {
+    methodFiletr(value) {
+      const target = methodOptions.filter(item => item.value === value)[0]
+      return target && `${target.label}`
+    },
+    tagTypeFiletr(value) {
+      const target = methodOptions.filter(item => item.value === value)[0]
+      return target && `${target.type}`
+    },
     //  选中api
     handleSelectionChange(val) {
       this.apis = val

+ 15 - 10
web/src/view/superAdmin/authority/authority.vue

@@ -14,7 +14,7 @@
       <el-table-column label="角色id" min-width="180" prop="authorityId" />
       <el-table-column label="角色名称" min-width="180" prop="authorityName" />
       <el-table-column fixed="right" label="操作" width="460">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-button size="mini" type="primary" @click="opdendrawer(scope.row)">设置权限</el-button>
           <el-button
             icon="el-icon-plus"
@@ -45,7 +45,7 @@
     </el-table>
     <span style="color: red;font-size: 12px">注:右上角头像下拉可切换角色</span>
     <!-- 新增角色弹窗 -->
-    <el-dialog :title="dialogTitle" :visible.sync="dialogFormVisible">
+    <el-dialog v-model="dialogFormVisible" :title="dialogTitle">
       <el-form ref="authorityForm" :model="form" :rules="rules">
         <el-form-item label="父级角色" prop="parentId">
           <el-cascader
@@ -64,22 +64,24 @@
           <el-input v-model="form.authorityName" autocomplete="off" />
         </el-form-item>
       </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="closeDialog">取 消</el-button>
-        <el-button type="primary" @click="enterDialog">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDialog">取 消</el-button>
+          <el-button type="primary" @click="enterDialog">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
 
-    <el-drawer v-if="drawer" :visible.sync="drawer" :with-header="false" size="40%" title="角色配置">
+    <el-drawer v-if="drawer" v-model="drawer" :with-header="false" size="40%" title="角色配置">
       <el-tabs :before-leave="autoEnter" class="role-box" type="border-card">
         <el-tab-pane label="角色菜单">
-          <Menus ref="menus" :row="activeRow" />
+          <Menus ref="menus" :row="activeRow" @changeRow="changeRow" />
         </el-tab-pane>
         <el-tab-pane label="角色api">
-          <apis ref="apis" :row="activeRow" />
+          <Apis ref="apis" :row="activeRow" @changeRow="changeRow" />
         </el-tab-pane>
         <el-tab-pane label="资源权限">
-          <Datas ref="datas" :authority="tableData" :row="activeRow" />
+          <Datas ref="datas" :authority="tableData" :row="activeRow" @changeRow="changeRow" />
         </el-tab-pane>
       </el-tabs>
     </el-drawer>
@@ -158,6 +160,9 @@ export default {
     await this.getTableData()
   },
   methods: {
+    changeRow(key, value) {
+      this.activeRow[key] = value
+    },
     autoEnter(activeName, oldActiveName) {
       const paneArr = ['menus', 'apis', 'datas']
       if (oldActiveName) {

+ 5 - 5
web/src/view/superAdmin/authority/components/datas.vue

@@ -25,7 +25,7 @@ export default {
     },
     authority: {
       default: function() {
-        return {}
+        return []
       },
       type: Array
     }
@@ -53,19 +53,19 @@ export default {
     },
     all() {
       this.dataAuthorityId = [...this.authoritys]
-      this.row.dataAuthorityId = this.dataAuthorityId
+      this.$emit('changeRow', 'dataAuthorityId', this.dataAuthorityId)
       this.needConfirm = true
     },
     self() {
       this.dataAuthorityId = this.authoritys.filter(item => item.authorityId === this.row.authorityId)
-      this.row.dataAuthorityId = this.dataAuthorityId
+      this.$emit('changeRow', 'dataAuthorityId', this.dataAuthorityId)
       this.needConfirm = true
     },
     selfAndChildren() {
       const arrBox = []
       this.getChildrenId(this.row, arrBox)
       this.dataAuthorityId = this.authoritys.filter(item => arrBox.indexOf(item.authorityId) > -1)
-      this.row.dataAuthorityId = this.dataAuthorityId
+      this.$emit('changeRow', 'dataAuthorityId', this.dataAuthorityId)
       this.needConfirm = true
     },
     getChildrenId(row, arrBox) {
@@ -95,7 +95,7 @@ export default {
     },
     //   选择
     selectAuthority() {
-      this.row.dataAuthorityId = this.dataAuthorityId
+      this.$emit('changeRow', 'dataAuthorityId', this.dataAuthorityId)
       this.needConfirm = true
     }
   }

+ 16 - 14
web/src/view/superAdmin/authority/components/menus.vue

@@ -14,20 +14,22 @@
       show-checkbox
       @check="nodeChange"
     >
-      <span slot-scope="{ node , data }" class="custom-tree-node">
-        <span>{{ node.label }}</span>
-        <span>
-          <el-button
-            type="text"
-            size="mini"
-            :style="{color:row.defaultRouter === data.name?'#E6A23C':'#85ce61'}"
-            :disabled="!node.checked"
-            @click="() => setDefault(data)"
-          >
-            {{ row.defaultRouter === data.name?"首页":"设为首页" }}
-          </el-button>
+      <template #default="{ node , data }">
+        <span class="custom-tree-node">
+          <span>{{ node.label }}</span>
+          <span>
+            <el-button
+              type="text"
+              size="mini"
+              :style="{color:row.defaultRouter === data.name?'#E6A23C':'#85ce61'}"
+              :disabled="!node.checked"
+              @click="() => setDefault(data)"
+            >
+              {{ row.defaultRouter === data.name?"首页":"设为首页" }}
+            </el-button>
+          </span>
         </span>
-      </span>
+      </template>
     </el-tree>
   </div>
 </template>
@@ -81,7 +83,7 @@ export default {
       const res = await updateAuthority({ authorityId: this.row.authorityId, AuthorityName: this.row.authorityName, parentId: this.row.parentId, defaultRouter: data.name })
       if (res.code === 0) {
         this.$message({ type: 'success', message: '设置成功' })
-        this.row.defaultRouter = res.data.authority.defaultRouter
+        this.$emit('changeRow', 'defaultRouter', res.data.authority.defaultRouter)
       }
     },
     nodeChange() {

+ 14 - 28
web/src/view/superAdmin/dictionary/sysDictionary.vue

@@ -33,7 +33,7 @@
     >
       <el-table-column type="selection" width="55" />
       <el-table-column label="日期" width="180">
-        <template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
+        <template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
       </el-table-column>
 
       <el-table-column label="字典名(中)" prop="name" width="120" />
@@ -41,22 +41,24 @@
       <el-table-column label="字典名(英)" prop="type" width="120" />
 
       <el-table-column label="状态" prop="status" width="120">
-        <template slot-scope="scope">{{ scope.row.status|formatBoolean }}</template>
+        <template #default="scope">{{ formatBoolean(scope.row.status) }}</template>
       </el-table-column>
 
       <el-table-column label="描述" prop="desc" width="280" />
 
       <el-table-column label="按钮组">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-button size="mini" type="success" @click="toDetile(scope.row)">详情</el-button>
           <el-button size="mini" type="primary" @click="updateSysDictionary(scope.row)">变更</el-button>
-          <el-popover v-model="scope.row.visible" placement="top" width="160">
+          <el-popover v-model:visible="scope.row.visible" placement="top" width="160">
             <p>确定要删除吗?</p>
             <div style="text-align: right; margin: 0">
               <el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
               <el-button type="primary" size="mini" @click="deleteSysDictionary(scope.row)">确定</el-button>
             </div>
-            <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" style="margin-left:10px">删除</el-button>
+            <template #reference>
+              <el-button type="danger" icon="el-icon-delete" size="mini" style="margin-left:10px">删除</el-button>
+            </template>
           </el-popover>
         </template>
       </el-table-column>
@@ -73,7 +75,7 @@
       @size-change="handleSizeChange"
     />
 
-    <el-dialog :before-close="closeDialog" :visible.sync="dialogFormVisible" title="弹窗操作">
+    <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作">
       <el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="110px">
         <el-form-item label="字典名(中)" prop="name">
           <el-input
@@ -98,11 +100,13 @@
           <el-input v-model="formData.desc" placeholder="请输入描述" clearable :style="{width: '100%'}" />
         </el-form-item>
       </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDialog">取 消</el-button>
+          <el-button type="primary" @click="enterDialog">确 定</el-button>
+        </div>
+      </template>
 
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="closeDialog">取 消</el-button>
-        <el-button type="primary" @click="enterDialog">确 定</el-button>
-      </div>
     </el-dialog>
 
     <div style="margin-top:40px;color:red">获取字典且缓存方法已在前端utils/dictionary 已经封装完成 不必自己书写 使用方法查看文件内注释</div>
@@ -117,27 +121,9 @@ import {
   findSysDictionary,
   getSysDictionaryList
 } from '@/api/sysDictionary' //  此处请自行替换地址
-import { formatTimeToStr } from '@/utils/date'
 import infoList from '@/mixins/infoList'
 export default {
   name: 'SysDictionary',
-  filters: {
-    formatDate: function(time) {
-      if (time !== null && time !== '') {
-        var date = new Date(time)
-        return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
-      } else {
-        return ''
-      }
-    },
-    formatBoolean: function(bool) {
-      if (bool !== null) {
-        return bool ? '是' : '否'
-      } else {
-        return ''
-      }
-    }
-  },
   mixins: [infoList],
   data() {
     return {

+ 14 - 28
web/src/view/superAdmin/dictionary/sysDictionaryDetail.vue

@@ -32,7 +32,7 @@
     >
       <el-table-column type="selection" width="55" />
       <el-table-column label="日期" width="180">
-        <template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
+        <template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
       </el-table-column>
 
       <el-table-column label="展示值" prop="label" width="120" />
@@ -40,21 +40,23 @@
       <el-table-column label="字典值" prop="value" width="120" />
 
       <el-table-column label="启用状态" prop="status" width="120">
-        <template slot-scope="scope">{{ scope.row.status|formatBoolean }}</template>
+        <template #default="scope">{{ formatBoolean(scope.row.status) }}</template>
       </el-table-column>
 
       <el-table-column label="排序标记" prop="sort" width="120" />
 
       <el-table-column label="按钮组">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-button size="small" type="primary" @click="updateSysDictionaryDetail(scope.row)">变更</el-button>
-          <el-popover v-model="scope.row.visible" placement="top" width="160">
+          <el-popover v-model:visible="scope.row.visible" placement="top" width="160">
             <p>确定要删除吗?</p>
             <div style="text-align: right; margin: 0">
               <el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
               <el-button type="primary" size="mini" @click="deleteSysDictionaryDetail(scope.row)">确定</el-button>
             </div>
-            <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
+            <template #reference>
+              <el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button>
+            </template>
           </el-popover>
         </template>
       </el-table-column>
@@ -71,7 +73,7 @@
       @size-change="handleSizeChange"
     />
 
-    <el-dialog :before-close="closeDialog" :visible.sync="dialogFormVisible" title="弹窗操作">
+    <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作">
       <el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="110px">
         <el-form-item label="展示值" prop="label">
           <el-input
@@ -98,10 +100,12 @@
           <el-input-number v-model.number="formData.sort" placeholder="排序标记" />
         </el-form-item>
       </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="closeDialog">取 消</el-button>
-        <el-button type="primary" @click="enterDialog">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDialog">取 消</el-button>
+          <el-button type="primary" @click="enterDialog">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
   </div>
 </template>
@@ -114,28 +118,10 @@ import {
   findSysDictionaryDetail,
   getSysDictionaryDetailList
 } from '@/api/sysDictionaryDetail' //  此处请自行替换地址
-import { formatTimeToStr } from '@/utils/date'
 import infoList from '@/mixins/infoList'
 
 export default {
   name: 'SysDictionaryDetail',
-  filters: {
-    formatDate: function(time) {
-      if (time !== null && time !== '') {
-        var date = new Date(time)
-        return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
-      } else {
-        return ''
-      }
-    },
-    formatBoolean: function(bool) {
-      if (bool !== null) {
-        return bool ? '是' : '否'
-      } else {
-        return ''
-      }
-    }
-  },
   mixins: [infoList],
   data() {
     return {

+ 12 - 4
web/src/view/superAdmin/index.vue

@@ -1,9 +1,17 @@
 <template>
   <div>
-    <keep-alive>
-      <router-view v-if="$route.meta.keepAlive" />
-    </keep-alive>
-    <router-view v-if="!$route.meta.keepAlive" />
+    <router-view v-if="$route.meta.keepAlive" v-slot="{ Component }">
+      <transition mode="out-in" name="el-fade-in-linear">
+        <keep-alive>
+          <component :is="Component" />
+        </keep-alive>
+      </transition>
+    </router-view>
+    <router-view v-if="!$route.meta.keepAlive" v-slot="{ Component }">
+      <transition mode="out-in" name="el-fade-in-linear">
+        <component :is="Component" />
+      </transition>
+    </router-view>
   </div>
 </template>
 

+ 6 - 2
web/src/view/superAdmin/menu/icon.vue

@@ -9,10 +9,10 @@
       <el-form-item prop="icon" style="width:100%">
         <i
           class="icon"
-          :class="'el-icon-'+ meta.icon"
+          :class="'el-icon-'+ metaData.icon"
           style="position: absolute; z-index: 9999; padding: 5px 10px; "
         />
-        <el-select v-model="meta.icon" clearable filterable class="gva-select" placeholder="请选择">
+        <el-select v-model="metaData.icon" clearable filterable class="gva-select" placeholder="请选择">
           <el-option v-for="item in options" :key="item.key" :label="item.key" :value="item.key">
             <span class="icon" :class="item.label" />
             <span style="float: left">{{ item.key }}</span>
@@ -37,6 +37,7 @@ export default {
   data() {
     return {
       input: '',
+      metaData: {},
       options: [
         { key: 'platform-eleme', label: 'el-icon-platform-eleme' },
         { key: 'eleme', label: 'el-icon-eleme' },
@@ -324,6 +325,9 @@ export default {
       value: ''
     }
   },
+  created() {
+    this.metaData = this.meta
+  },
   methods: {}
 }
 </script>

+ 25 - 21
web/src/view/superAdmin/menu/menu.vue

@@ -10,7 +10,7 @@
       <el-table-column label="路由Name" min-width="160" prop="name" />
       <el-table-column label="路由Path" min-width="160" prop="path" />
       <el-table-column label="是否隐藏" min-width="100" prop="hidden">
-        <template slot-scope="scope">
+        <template #default="scope">
           <span>{{ scope.row.hidden?"隐藏":"显示" }}</span>
         </template>
       </el-table-column>
@@ -18,18 +18,18 @@
       <el-table-column label="排序" min-width="70" prop="sort" />
       <el-table-column label="文件路径" min-width="360" prop="component" />
       <el-table-column label="展示名称" min-width="120" prop="authorityName">
-        <template slot-scope="scope">
+        <template #default="scope">
           <span>{{ scope.row.meta.title }}</span>
         </template>
       </el-table-column>
       <el-table-column label="图标" min-width="140" prop="authorityName">
-        <template slot-scope="scope">
+        <template #default="scope">
           <i :class="`el-icon-${scope.row.meta.icon}`" />
           <span>{{ scope.row.meta.icon }}</span>
         </template>
       </el-table-column>
       <el-table-column fixed="right" label="操作" width="300">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-button
             size="mini"
             type="primary"
@@ -52,8 +52,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog :before-close="handleClose" :title="dialogTitle" :visible.sync="dialogFormVisible">
+    <el-dialog v-model="dialogFormVisible" :before-close="handleClose" :title="dialogTitle">
       <el-form
+        v-if="dialogFormVisible"
         ref="menuForm"
         :inline="true"
         :model="form"
@@ -70,10 +71,13 @@
           />
         </el-form-item>
         <el-form-item prop="path" style="width:30%">
-          <div slot="label" style="display:inline-block">
-            路由path
-            <el-checkbox v-model="checkFlag" style="float:right;margin-left:20px;">添加参数</el-checkbox>
-          </div>
+          <template #label>
+            <div style="display:inline-flex">
+              路由path
+              <el-checkbox v-model="checkFlag" style="float:right;margin-left:20px;">添加参数</el-checkbox>
+            </div>
+          </template>
+
           <el-input
             v-model="form.path"
             :disabled="!checkFlag"
@@ -105,9 +109,7 @@
           <el-input v-model="form.meta.title" autocomplete="off" />
         </el-form-item>
         <el-form-item label="图标" prop="meta.icon" style="width:30%">
-          <icon :meta="form.meta">
-            <template slot="prepend">el-icon-</template>
-          </icon>
+          <icon :meta="form.meta" />
         </el-form-item>
         <el-form-item label="排序标记" prop="sort" style="width:30%">
           <el-input v-model.number="form.sort" autocomplete="off" />
@@ -135,7 +137,7 @@
         >新增菜单参数</el-button>
         <el-table :data="form.parameters" stripe style="width: 100%">
           <el-table-column prop="type" label="参数类型" width="180">
-            <template slot-scope="scope">
+            <template #default="scope">
               <el-select v-model="scope.row.type" placeholder="请选择">
                 <el-option key="query" value="query" label="query" />
                 <el-option key="params" value="params" label="params" />
@@ -143,21 +145,21 @@
             </template>
           </el-table-column>
           <el-table-column prop="key" label="参数key" width="180">
-            <template slot-scope="scope">
+            <template #default="scope">
               <div>
                 <el-input v-model="scope.row.key" />
               </div>
             </template>
           </el-table-column>
           <el-table-column prop="value" label="参数值">
-            <template slot-scope="scope">
+            <template #default="scope">
               <div>
                 <el-input v-model="scope.row.value" />
               </div>
             </template>
           </el-table-column>
           <el-table-column>
-            <template slot-scope="scope">
+            <template #default="scope">
               <div>
                 <el-button
                   type="danger"
@@ -170,10 +172,12 @@
           </el-table-column>
         </el-table>
       </div>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="closeDialog">取 消</el-button>
-        <el-button type="primary" @click="enterDialog">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDialog">取 消</el-button>
+          <el-button type="primary" @click="enterDialog">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
   </div>
 </template>
@@ -244,7 +248,7 @@ export default {
   methods: {
     addParameter(form) {
       if (!form.parameters) {
-        this.$set(form, 'parameters', [])
+        this.form.parameters = []
       }
       form.parameters.push({
         type: 'query',

+ 20 - 30
web/src/view/superAdmin/operation/sysOperationRecord.vue

@@ -13,13 +13,15 @@
         </el-form-item>
         <el-form-item>
           <el-button size="mini" type="primary" icon="el-icon-search" @click="onSubmit">查询</el-button>
-          <el-popover v-model="deleteVisible" placement="top" width="160">
+          <el-popover v-model:visible="deleteVisible" placement="top" width="160">
             <p>确定要删除吗?</p>
             <div style="text-align: right; margin: 0">
               <el-button size="mini" type="text" @click="deleteVisible = false">取消</el-button>
               <el-button size="mini" type="primary" @click="onDelete">确定</el-button>
             </div>
-            <el-button slot="reference" icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
+            <template #reference>
+              <el-button icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
+            </template>
           </el-popover>
         </el-form-item>
       </el-form>
@@ -35,15 +37,15 @@
     >
       <el-table-column type="selection" width="55" />
       <el-table-column label="操作人" width="140">
-        <template slot-scope="scope">
+        <template #default="scope">
           <div>{{ scope.row.user.userName }}({{ scope.row.user.nickName }})</div>
         </template>
       </el-table-column>
       <el-table-column label="日期" width="180">
-        <template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
+        <template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
       </el-table-column>
       <el-table-column label="状态码" prop="status" width="120">
-        <template slot-scope="scope">
+        <template #default="scope">
           <div>
             <el-tag type="success">{{ scope.row.status }}</el-tag>
           </div>
@@ -53,13 +55,15 @@
       <el-table-column label="请求方法" prop="method" width="120" />
       <el-table-column label="请求路径" prop="path" width="240" />
       <el-table-column label="请求" prop="path" width="80">
-        <template slot-scope="scope">
+        <template #default="scope">
           <div>
             <el-popover v-if="scope.row.body" placement="top-start" trigger="hover">
               <div class="popover-box">
                 <pre>{{ fmtBody(scope.row.body) }}</pre>
               </div>
-              <i slot="reference" class="el-icon-view" />
+              <template #reference>
+                <i class="el-icon-view" />
+              </template>
             </el-popover>
 
             <span v-else>无</span>
@@ -67,27 +71,31 @@
         </template>
       </el-table-column>
       <el-table-column label="响应" prop="path" width="80">
-        <template slot-scope="scope">
+        <template #default="scope">
           <div>
             <el-popover v-if="scope.row.resp" placement="top-start" trigger="hover">
               <div class="popover-box">
                 <pre>{{ fmtBody(scope.row.resp) }}</pre>
               </div>
-              <i slot="reference" class="el-icon-view" />
+              <template #reference>
+                <i class="el-icon-view" />
+              </template>
             </el-popover>
             <span v-else>无</span>
           </div>
         </template>
       </el-table-column>
       <el-table-column label="按钮组">
-        <template slot-scope="scope">
-          <el-popover v-model="scope.row.visible" placement="top" width="160">
+        <template #default="scope">
+          <el-popover v-model:visible="scope.row.visible" placement="top" width="160">
             <p>确定要删除吗?</p>
             <div style="text-align: right; margin: 0">
               <el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
               <el-button size="mini" type="primary" @click="deleteSysOperationRecord(scope.row)">确定</el-button>
             </div>
-            <el-button slot="reference" icon="el-icon-delete" size="mini" type="danger">删除</el-button>
+            <template #reference>
+              <el-button icon="el-icon-delete" size="mini" type="danger">删除</el-button>
+            </template>
           </el-popover>
         </template>
       </el-table-column>
@@ -111,28 +119,10 @@ import {
   getSysOperationRecordList,
   deleteSysOperationRecordByIds
 } from '@/api/sysOperationRecord' // 此处请自行替换地址
-import { formatTimeToStr } from '@/utils/date'
 import infoList from '@/mixins/infoList'
 
 export default {
   name: 'SysOperationRecord',
-  filters: {
-    formatDate: function(time) {
-      if (time !== null && time !== '') {
-        var date = new Date(time)
-        return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
-      } else {
-        return ''
-      }
-    },
-    formatBoolean: function(bool) {
-      if (bool !== null) {
-        return bool ? '是' : '否'
-      } else {
-        return ''
-      }
-    }
-  },
   mixins: [infoList],
   data() {
     return {

+ 17 - 12
web/src/view/superAdmin/user/user.vue

@@ -5,7 +5,7 @@
     </div>
     <el-table :data="tableData" border stripe>
       <el-table-column label="头像" min-width="50">
-        <template slot-scope="scope">
+        <template #default="scope">
           <div :style="{'textAlign':'center'}">
             <CustomPic :pic-src="scope.row.headerImg" />
           </div>
@@ -15,7 +15,7 @@
       <el-table-column label="用户名" min-width="150" prop="userName" />
       <el-table-column label="昵称" min-width="150" prop="nickName" />
       <el-table-column label="用户角色" min-width="150">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-cascader
             v-model="scope.row.authorityIds"
             :options="authOptions"
@@ -29,14 +29,16 @@
         </template>
       </el-table-column>
       <el-table-column label="操作" min-width="150">
-        <template slot-scope="scope">
-          <el-popover v-model="scope.row.visible" placement="top" width="160">
+        <template #default="scope">
+          <el-popover v-model:visible="scope.row.visible" placement="top" width="160">
             <p>确定要删除此用户吗</p>
             <div style="text-align: right; margin: 0">
               <el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
               <el-button type="primary" size="mini" @click="deleteUser(scope.row)">确定</el-button>
             </div>
-            <el-button slot="reference" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
+            <template #reference>
+              <el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button>
+            </template>
           </el-popover>
         </template>
       </el-table-column>
@@ -53,7 +55,7 @@
       @size-change="handleSizeChange"
     />
 
-    <el-dialog :visible.sync="addUserDialog" custom-class="user-dialog" title="新增用户">
+    <el-dialog v-model="addUserDialog" custom-class="user-dialog" title="新增用户">
       <el-form ref="userForm" :rules="rules" :model="userInfo">
         <el-form-item label="用户名" label-width="80px" prop="username">
           <el-input v-model="userInfo.username" />
@@ -72,7 +74,8 @@
         </el-form-item>
         <el-form-item label="用户角色" label-width="80px" prop="authorityId">
           <el-cascader
-            v-model="userInfo.authorityIds"
+            v-if="userInfo"
+            v-model:visible="userInfo.authorityIds"
             :options="authOptions"
             :show-all-levels="false"
             :props="{ multiple:true,checkStrictly: true,label:'authorityName',value:'authorityId',disabled:'disabled',emitPath:false}"
@@ -81,10 +84,12 @@
           />
         </el-form-item>
       </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button @click="closeAddUserDialog">取 消</el-button>
-        <el-button type="primary" @click="enterAddUserDialog">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeAddUserDialog">取 消</el-button>
+          <el-button type="primary" @click="enterAddUserDialog">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
     <ChooseImg ref="chooseImg" :target="userInfo" :target-key="`headerImg`" />
   </div>
@@ -155,7 +160,7 @@ export default {
         const authorityIds = user.authorities && user.authorities.map(i => {
           return i.authorityId
         })
-        this.$set(user, 'authorityIds', authorityIds)
+        user.authorityIds = authorityIds
       })
     },
     openHeaderChange() {

+ 22 - 16
web/src/view/system/state.vue

@@ -3,7 +3,9 @@
     <el-row :gutter="15" class="system_state">
       <el-col :span="12">
         <el-card v-if="state.os" class="card_item">
-          <div slot="header">Runtime</div>
+          <template #header>
+            <div>Runtime</div>
+          </template>
           <div>
             <el-row :gutter="10">
               <el-col :span="12">os:</el-col>
@@ -30,7 +32,9 @@
       </el-col>
       <el-col :span="12">
         <el-card v-if="state.disk" class="card_item">
-          <div slot="header">Disk</div>
+          <template #header>
+            <div>Disk</div>
+          </template>
           <div>
             <el-row :gutter="10">
               <el-col :span="12">
@@ -70,30 +74,32 @@
           class="card_item"
           :body-style="{ height: '180px', 'overflow-y': 'scroll' }"
         >
-          <div slot="header">CPU</div>
+          <template #header>
+            <div>CPU</div>
+          </template>
           <div>
             <el-row :gutter="10">
               <el-col :span="12">physical number of cores:</el-col>
               <el-col :span="12" v-text="state.cpu.cores" />
             </el-row>
-            <template v-for="(item, index) in state.cpu.cpus">
-              <el-row :key="index" :gutter="10">
-                <el-col :span="12">core {{ index }}:</el-col>
-                <el-col
-                  :span="12"
-                ><el-progress
-                  type="line"
-                  :percentage="+item.toFixed(0)"
-                  :color="colors"
-                /></el-col>
-              </el-row>
-            </template>
+            <el-row v-for="(item, index) in state.cpu.cpus" :key="index" :gutter="10">
+              <el-col :span="12">core {{ index }}:</el-col>
+              <el-col
+                :span="12"
+              ><el-progress
+                type="line"
+                :percentage="+item.toFixed(0)"
+                :color="colors"
+              /></el-col>
+            </el-row>
           </div>
         </el-card>
       </el-col>
       <el-col :span="12">
         <el-card v-if="state.ram" class="card_item">
-          <div slot="header">Ram</div>
+          <template #header>
+            <div>Ram</div>
+          </template>
           <div>
             <el-row :gutter="10">
               <el-col :span="12">

+ 22 - 20
web/src/view/systemTools/autoCode/component/fieldDialog.vue

@@ -3,14 +3,14 @@
     <span style="color:red">搜索时如果条件为LIKE只支持字符串</span>
     <el-form
       ref="fieldDialogFrom"
-      :model="dialogMiddle"
+      :model="middleDate"
       label-width="120px"
       label-position="left"
       :rules="rules"
     >
       <el-form-item label="Field名称" prop="fieldName">
         <el-col :span="6">
-          <el-input v-model="dialogMiddle.fieldName" autocomplete="off" />
+          <el-input v-model="middleDate.fieldName" autocomplete="off" />
         </el-col>
         <el-col :offset="1" :span="2">
           <el-button size="mini" @click="autoFill">自动填充</el-button>
@@ -18,28 +18,28 @@
       </el-form-item>
       <el-form-item label="Field中文名" prop="fieldDesc">
         <el-col :span="6">
-          <el-input v-model="dialogMiddle.fieldDesc" autocomplete="off" />
+          <el-input v-model="middleDate.fieldDesc" autocomplete="off" />
         </el-col>
       </el-form-item>
       <el-form-item label="FieldJSON" prop="fieldJson">
         <el-col :span="6">
-          <el-input v-model="dialogMiddle.fieldJson" autocomplete="off" />
+          <el-input v-model="middleDate.fieldJson" autocomplete="off" />
         </el-col>
       </el-form-item>
       <el-form-item label="数据库字段名" prop="columnName">
         <el-col :span="6">
-          <el-input v-model="dialogMiddle.columnName" autocomplete="off" />
+          <el-input v-model="middleDate.columnName" autocomplete="off" />
         </el-col>
       </el-form-item>
       <el-form-item label="数据库字段描述" prop="comment">
         <el-col :span="6">
-          <el-input v-model="dialogMiddle.comment" autocomplete="off" />
+          <el-input v-model="middleDate.comment" autocomplete="off" />
         </el-col>
       </el-form-item>
       <el-form-item label="Field数据类型" prop="fieldType">
         <el-col :span="8">
           <el-select
-            v-model="dialogMiddle.fieldType"
+            v-model="middleDate.fieldType"
             placeholder="请选择field数据类型"
             clearable
             @change="getDbfdOptions"
@@ -57,8 +57,8 @@
       <el-form-item label="数据库字段类型" prop="dataType">
         <el-col :span="8">
           <el-select
-            v-model="dialogMiddle.dataType"
-            :disabled="!dialogMiddle.fieldType"
+            v-model="middleDate.dataType"
+            :disabled="!middleDate.fieldType"
             placeholder="请选择数据库字段类型"
             clearable
           >
@@ -73,12 +73,12 @@
       </el-form-item>
       <el-form-item label="数据库字段长度" prop="dataTypeLong">
         <el-col :span="8">
-          <el-input v-model="dialogMiddle.dataTypeLong" placeholder="自定义类型必须指定长度" :disabled="!dialogMiddle.dataType" />
+          <el-input v-model="middleDate.dataTypeLong" placeholder="自定义类型必须指定长度" :disabled="!middleDate.dataType" />
         </el-col>
       </el-form-item>
       <el-form-item label="Field查询条件" prop="fieldSearchType">
         <el-col :span="8">
-          <el-select v-model="dialogMiddle.fieldSearchType" placeholder="请选择Field查询条件" clearable>
+          <el-select v-model="middleDate.fieldSearchType" placeholder="请选择Field查询条件" clearable>
             <el-option
               v-for="item in typeSearchOptions"
               :key="item.value"
@@ -91,7 +91,7 @@
 
       <el-form-item label="关联字典" prop="dictType">
         <el-col :span="8">
-          <el-select v-model="dialogMiddle.dictType" :disabled="dialogMiddle.fieldType!=='int'" placeholder="请选择字典" clearable>
+          <el-select v-model="middleDate.dictType" :disabled="middleDate.fieldType!=='int'" placeholder="请选择字典" clearable>
             <el-option
               v-for="item in dictOptions"
               :key="item.type"
@@ -122,6 +122,7 @@ export default {
   },
   data() {
     return {
+      middleDate: {},
       dbfdOptions: [],
       dictOptions: [],
       typeSearchOptions: [
@@ -188,6 +189,7 @@ export default {
     }
   },
   async created() {
+    this.middleDate = this.dialogMiddle
     const dictRes = await getSysDictionaryList({
       page: 1,
       pageSize: 999999
@@ -197,16 +199,16 @@ export default {
   },
   methods: {
     autoFill() {
-      this.dialogMiddle.fieldJson = toLowerCase(this.dialogMiddle.fieldName)
-      this.dialogMiddle.columnName = toSQLLine(this.dialogMiddle.fieldJson)
+      this.middleDate.fieldJson = toLowerCase(this.middleDate.fieldName)
+      this.middleDate.columnName = toSQLLine(this.middleDate.fieldJson)
     },
     async getDbfdOptions() {
-      this.dialogMiddle.dataType = ''
-      this.dialogMiddle.dataTypeLong = ''
-      this.dialogMiddle.fieldSearchType = ''
-      this.dialogMiddle.dictType = ''
-      if (this.dialogMiddle.fieldType) {
-        this.dbfdOptions = await getDict(this.dialogMiddle.fieldType)
+      this.middleDate.dataType = ''
+      this.middleDate.dataTypeLong = ''
+      this.middleDate.fieldSearchType = ''
+      this.middleDate.dictType = ''
+      if (this.middleDate.fieldType) {
+        this.dbfdOptions = await getDict(this.middleDate.fieldType)
       }
     }
   }

+ 28 - 15
web/src/view/systemTools/autoCode/index.vue

@@ -3,7 +3,7 @@
     <!-- 从数据库直接获取字段 -->
     <el-collapse v-model="activeNames">
       <el-collapse-item name="1">
-        <template slot="title">
+        <template #title>
           <div :style="{fontSize:'16px',paddingLeft:'20px'}">
             点这里从现有数据库创建代码
             <i class="header-icon el-icon-thumb" />
@@ -61,7 +61,7 @@
         <el-input v-model="form.packageName" placeholder="生成文件的默认名称(建议为驼峰格式,首字母小写,如sysXxxXxxx)" />
       </el-form-item>
       <el-form-item>
-        <template slot="label">
+        <template #label>
           <el-tooltip content="注:把自动生成的API注册进数据库" placement="bottom" effect="light">
             <div> 自动创建API </div>
           </el-tooltip>
@@ -69,7 +69,7 @@
         <el-checkbox v-model="form.autoCreateApiToSql" />
       </el-form-item>
       <el-form-item>
-        <template slot="label">
+        <template #label>
           <el-tooltip content="注:自动迁移生成的文件到ymal配置的对应位置" placement="bottom" effect="light">
             <div> 自动移动文件 </div>
           </el-tooltip>
@@ -94,7 +94,7 @@
       <el-table-column prop="fieldSearchType" label="搜索条件" width="130" />
       <el-table-column prop="dictType" label="字典" width="130" />
       <el-table-column label="操作" width="300">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-button
             size="mini"
             type="primary"
@@ -113,13 +113,15 @@
             :disabled="(scope.$index + 1) === form.fields.length"
             @click="moveDownField(scope.$index)"
           >下移</el-button>
-          <el-popover v-model="scope.row.visible" placement="top">
+          <el-popover v-model:visible="scope.row.visible" placement="top">
             <p>确定删除吗?</p>
             <div style="text-align: right; margin: 0">
               <el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
               <el-button type="primary" size="mini" @click="deleteField(scope.$index)">确定</el-button>
             </div>
-            <el-button slot="reference" size="mini" type="danger" icon="el-icon-delete">删除</el-button>
+            <template #reference>
+              <el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button>
+            </template>
           </el-popover>
         </template>
       </el-table-column>
@@ -131,19 +133,23 @@
       <el-button size="mini" type="primary" @click="enterForm(false)">生成代码</el-button>
     </div>
     <!-- 组件弹窗 -->
-    <el-dialog title="组件内容" :visible.sync="dialogFlag">
+    <el-dialog v-model="dialogFlag" title="组件内容">
       <FieldDialog v-if="dialogFlag" ref="fieldDialog" :dialog-middle="dialogMiddle" />
-      <div slot="footer" class="dialog-footer">
-        <el-button size="mini" @click="closeDialog">取 消</el-button>
-        <el-button size="mini" type="primary" @click="enterDialog">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button size="mini" @click="closeDialog">取 消</el-button>
+          <el-button size="mini" type="primary" @click="enterDialog">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
 
-    <el-dialog :visible.sync="previewFlag">
+    <el-dialog v-model="previewFlag">
       <PreviewCodeDialg v-if="previewFlag" :preview-code="preViewCode" />
-      <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="previewFlag = false">确 定</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="previewFlag = false">确 定</el-button>
+        </div>
+      </template>
     </el-dialog>
   </div>
 </template>
@@ -318,6 +324,13 @@ export default {
             if (data.headers?.success === 'false') {
               return
             } else {
+              if (this.form.autoMoveFile) {
+                this.$message({
+                  type: 'success',
+                  message: '自动化代码创建成功,自动移动成功'
+                })
+                return
+              }
               this.$message({
                 type: 'success',
                 message: '自动化代码创建成功,正在下载'

+ 3 - 21
web/src/view/systemTools/autoCodeAdmin/index.vue

@@ -20,13 +20,13 @@
       />
       <el-table-column label="id" width="60" prop="ID" />
       <el-table-column label="日期" width="180">
-        <template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
+        <template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
       </el-table-column>
       <el-table-column label="结构体名" min-width="150" prop="structName" />
       <el-table-column label="结构体描述" min-width="150" prop="structCNName" />
       <el-table-column label="表名称" min-width="150" prop="tableName" />
       <el-table-column label="回滚标记" min-width="150" prop="flag">
-        <template slot-scope="scope">
+        <template #default="scope">
           <el-tag
             v-if="scope.row.flag"
             type="danger"
@@ -46,7 +46,7 @@
         </template>
       </el-table-column>
       <el-table-column label="操作" min-width="180">
-        <template slot-scope="scope">
+        <template #default="scope">
           <div>
             <el-button size="mini" type="primary" :disabled="scope.row.flag === 1" @click="rollback(scope.row)">回滚</el-button>
             <el-button size="mini" type="success" @click="goAutoCode(scope.row)">复用</el-button>
@@ -72,28 +72,10 @@
 <script>
 // 获取列表内容封装在mixins内部  getTableData方法 初始化已封装完成 条件搜索时候 请把条件安好后台定制的结构体字段 放到 this.searchInfo 中即可实现条件搜索
 import { getSysHistory, rollback, delSysHistory } from '@/api/autoCode.js'
-import { formatTimeToStr } from '@/utils/date'
 import infoList from '@/mixins/infoList'
 
 export default {
   name: 'Api',
-  filters: {
-    formatDate: function(time) {
-      if (time !== null && time !== '') {
-        var date = new Date(time)
-        return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
-      } else {
-        return ''
-      }
-    },
-    formatBoolean: function(bool) {
-      if (bool !== null) {
-        return bool ? '是' : '否'
-      } else {
-        return ''
-      }
-    }
-  },
   mixins: [infoList],
   data() {
     return {

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