Преглед изворни кода

Merge branch 'develop' into develop

奇淼(piexlmax пре 4 година
родитељ
комит
9fc24724a6
39 измењених фајлова са 719 додато и 384 уклоњено
  1. 1 1
      .github/FUNDING.yml
  2. 99 0
      server/api/v1/exa_excel.go
  3. 1 85
      server/api/v1/exa_file_upload_download.go
  4. 28 3
      server/api/v1/sys_auto_code.go
  5. 1 1
      server/cmd/gva/mysql.go
  6. 5 0
      server/cmd/information/system/api.go
  7. 5 0
      server/cmd/information/system/casbin.go
  8. 10 0
      server/config.yaml
  9. 2 1
      server/config/config.go
  10. 10 0
      server/config/oss.go
  11. 3 2
      server/go.mod
  12. 1 0
      server/initialize/router.go
  13. 6 0
      server/model/exa_excel.go
  14. 0 8
      server/model/request/exa_file_upload_and_download.go
  15. BIN
      server/resource/excel/ExcelExport.xlsx
  16. 0 0
      server/router/exa_customer.go
  17. 16 0
      server/router/exa_excel.go
  18. 0 4
      server/router/exa_file_upload_and_download.go
  19. 1 0
      server/router/sys_auto_code.go
  20. 118 41
      server/service/sys_auto_code.go
  21. 3 4
      server/service/sys_menu.go
  22. 60 0
      server/utils/upload/tencent_cos.go
  23. 3 1
      server/utils/upload/upload.go
  24. 75 73
      web/package.json
  25. 9 13
      web/src/api/autoCode.js
  26. 85 0
      web/src/api/excel.js
  27. 0 84
      web/src/api/fileUploadAndDownload.js
  28. 8 1
      web/src/router/index.js
  29. 2 2
      web/src/view/example/excel/excel.vue
  30. 9 6
      web/src/view/example/index.vue
  31. 2 2
      web/src/view/layout/index.vue
  32. 1 1
      web/src/view/person/person.vue
  33. 16 0
      web/src/view/routerHolder.vue
  34. 9 6
      web/src/view/superAdmin/index.vue
  35. 2 1
      web/src/view/superAdmin/menu/menu.vue
  36. 58 0
      web/src/view/systemTools/autoCode/component/previewCodeDialg.vue
  37. 52 32
      web/src/view/systemTools/autoCode/index.vue
  38. 9 6
      web/src/view/systemTools/index.vue
  39. 9 6
      web/src/view/workflow/index.vue

+ 1 - 1
.github/FUNDING.yml

@@ -2,7 +2,7 @@
 
 github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
 patreon: # Replace with a single Patreon username
-open_collective: # Replace with a single Open Collective username
+open_collective: gin-vue-admin
 ko_fi: # Replace with a single Ko-fi username
 tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
 community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry

+ 99 - 0
server/api/v1/exa_excel.go

@@ -0,0 +1,99 @@
+package v1
+
+import (
+	"gin-vue-admin/global"
+	"gin-vue-admin/model"
+	"gin-vue-admin/model/response"
+	"gin-vue-admin/service"
+	"gin-vue-admin/utils"
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+// /excel/importExcel 接口,与upload接口作用类似,只是把文件存到resource/excel目录下,用于导入Excel时存放Excel文件(ExcelImport.xlsx)
+// /excel/loadExcel接口,用于读取resource/excel目录下的文件((ExcelImport.xlsx)并加载为[]model.SysBaseMenu类型的示例数据
+// /excel/exportExcel 接口,用于读取前端传来的tableData,生成Excel文件并返回
+// /excel/downloadTemplate 接口,用于下载resource/excel目录下的 ExcelTemplate.xlsx 文件,作为导入的模板
+
+// @Tags excel
+// @Summary 导出Excel
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce  application/octet-stream
+// @Param data body model.ExcelInfo true "导出Excel文件信息"
+// @Success 200
+// @Router /excel/exportExcel [post]
+func ExportExcel(c *gin.Context) {
+	var excelInfo model.ExcelInfo
+	_ = c.ShouldBindJSON(&excelInfo)
+	filePath := global.GVA_CONFIG.Excel.Dir + excelInfo.FileName
+	err := service.ParseInfoList2Excel(excelInfo.InfoList, filePath)
+	if err != nil {
+		global.GVA_LOG.Error("转换Excel失败!", zap.Any("err", err))
+		response.FailWithMessage("转换Excel失败", c)
+		return
+	}
+	c.Writer.Header().Add("success", "true")
+	c.File(filePath)
+}
+
+// @Tags excel
+// @Summary 导入Excel文件
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param file formData file true "导入Excel文件"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"导入成功"}"
+// @Router /excel/importExcel [post]
+func ImportExcel(c *gin.Context) {
+	_, header, err := c.Request.FormFile("file")
+	if err != nil {
+		global.GVA_LOG.Error("接收文件失败!", zap.Any("err", err))
+		response.FailWithMessage("接收文件失败", c)
+		return
+	}
+	_ = c.SaveUploadedFile(header, global.GVA_CONFIG.Excel.Dir+"ExcelImport.xlsx")
+	response.OkWithMessage("导入成功", c)
+}
+
+// @Tags excel
+// @Summary 加载Excel数据
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"加载数据成功"}"
+// @Router /excel/loadExcel [get]
+func LoadExcel(c *gin.Context) {
+	menus, err := service.ParseExcel2InfoList()
+	if err != nil {
+		global.GVA_LOG.Error("加载数据失败", zap.Any("err", err))
+		response.FailWithMessage("加载数据失败", c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     menus,
+		Total:    int64(len(menus)),
+		Page:     1,
+		PageSize: 999,
+	}, "加载数据成功", c)
+}
+
+// @Tags excel
+// @Summary 下载模板
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param fileName query fileName true "模板名称"
+// @Success 200
+// @Router /excel/downloadTemplate [get]
+func DownloadTemplate(c *gin.Context) {
+	fileName := c.Query("fileName")
+	filePath := global.GVA_CONFIG.Excel.Dir + fileName
+	ok, err := utils.PathExists(filePath)
+	if !ok || err != nil {
+		global.GVA_LOG.Error("文件不存在", zap.Any("err", err))
+		response.FailWithMessage("文件不存在", c)
+		return
+	}
+	c.Writer.Header().Add("success", "true")
+	c.File(filePath)
+}

+ 1 - 85
server/api/v1/exa_file_upload_download.go

@@ -6,7 +6,6 @@ import (
 	"gin-vue-admin/model/request"
 	"gin-vue-admin/model/response"
 	"gin-vue-admin/service"
-	"gin-vue-admin/utils"
 	"github.com/gin-gonic/gin"
 	"go.uber.org/zap"
 )
@@ -76,89 +75,6 @@ func GetFileList(c *gin.Context) {
 			Total:    total,
 			Page:     pageInfo.Page,
 			PageSize: pageInfo.PageSize,
-		},"获取成功", c)
+		}, "获取成功", c)
 	}
 }
-
-// @Tags ExaFileUploadAndDownload
-// @Summary 导出Excel
-// @Security ApiKeyAuth
-// @accept application/json
-// @Produce  application/octet-stream
-// @Param data body request.ExcelInfo true "导出Excel文件信息"
-// @Success 200
-// @Router /fileUploadAndDownload/exportExcel [post]
-func ExportExcel(c *gin.Context) {
-	var excelInfo request.ExcelInfo
-    c.ShouldBindJSON(&excelInfo)
-	filePath := global.GVA_CONFIG.Excel.Dir+excelInfo.FileName
-	err := service.ParseInfoList2Excel(excelInfo.InfoList, filePath)
-	if err != nil {
-		global.GVA_LOG.Error("转换Excel失败!", zap.Any("err", err))
-		response.FailWithMessage("转换Excel失败", c)
-		return
-	}
-	c.Writer.Header().Add("success", "true")
-	c.File(filePath)
-}
-
-// @Tags ExaFileUploadAndDownload
-// @Summary 导入Excel文件
-// @Security ApiKeyAuth
-// @accept multipart/form-data
-// @Produce  application/json
-// @Param file formData file true "导入Excel文件"
-// @Success 200 {string} string "{"success":true,"data":{},"msg":"导入成功"}"
-// @Router /fileUploadAndDownload/importExcel [post]
-func ImportExcel(c *gin.Context) {
-	_, header, err := c.Request.FormFile("file")
-	if err != nil {
-		global.GVA_LOG.Error("接收文件失败!", zap.Any("err", err))
-		response.FailWithMessage("接收文件失败", c)
-		return
-	}
-	c.SaveUploadedFile(header, 	global.GVA_CONFIG.Excel.Dir+"ExcelImport.xlsx")
-	response.OkWithMessage("导入成功", c)
-}
-
-// @Tags ExaFileUploadAndDownload
-// @Summary 加载Excel数据
-// @Security ApiKeyAuth
-// @Produce  application/json
-// @Success 200 {string} string "{"success":true,"data":{},"msg":"加载数据成功"}"
-// @Router /fileUploadAndDownload/loadExcel [get]
-func LoadExcel(c *gin.Context) {
-	menus, err := service.ParseExcel2InfoList()
-	if err != nil {
-		global.GVA_LOG.Error("加载数据失败", zap.Any("err", err))
-		response.FailWithMessage("加载数据失败", c)
-		return
-	}
-	response.OkWithDetailed(response.PageResult{
-		List: menus,
-		Total: int64(len(menus)),
-		Page: 1,
-		PageSize: 999,
-	},"加载数据成功", c)
-}
-
-// @Tags ExaFileUploadAndDownload
-// @Summary 下载模板
-// @Security ApiKeyAuth
-// @accept multipart/form-data
-// @Produce  application/json
-// @Param fileName query fileName true "模板名称"
-// @Success 200
-// @Router /fileUploadAndDownload/downloadTemplate [get]
-func DownloadTemplate(c *gin.Context) {
-	fileName := c.Query("fileName")
-	filePath := global.GVA_CONFIG.Excel.Dir+fileName
-	ok, err := utils.PathExists(filePath)
-	if !ok || err != nil {
-		global.GVA_LOG.Error("文件不存在", zap.Any("err", err))
-		response.FailWithMessage("文件不存在", c)
-		return
-	}
-	c.Writer.Header().Add("success", "true")
-	c.File(filePath)
-}

+ 28 - 3
server/api/v1/sys_auto_code.go

@@ -1,19 +1,44 @@
 package v1
 
 import (
+	"errors"
 	"fmt"
 	"gin-vue-admin/global"
 	"gin-vue-admin/model"
 	"gin-vue-admin/model/response"
 	"gin-vue-admin/service"
 	"gin-vue-admin/utils"
-	"github.com/gin-gonic/gin"
-	"github.com/pkg/errors"
-	"go.uber.org/zap"
 	"net/url"
 	"os"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
 )
 
+// @Tags AutoCode
+// @Summary 预览创建后的代码
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body model.AutoCodeStruct true "预览创建代码"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
+// @Router /autoCode/preview [post]
+func PreviewTemp(c *gin.Context) {
+	var a model.AutoCodeStruct
+	_ = c.ShouldBindJSON(&a)
+	if err := utils.Verify(a, utils.AutoCodeVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	autoCode, err := service.PreviewTemp(a)
+	if err != nil {
+		global.GVA_LOG.Error("预览失败!", zap.Any("err", err))
+		response.FailWithMessage("预览失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"autoCode": autoCode}, "预览成功", c)
+	}
+}
+
 // @Tags AutoCode
 // @Summary 自动代码模板
 // @Security ApiKeyAuth

+ 1 - 1
server/cmd/gva/mysql.go

@@ -44,7 +44,7 @@ func (m *_mysql) Init() {
 	m._config.DisableForeignKeyConstraintWhenMigrating = true
 	m.db, m.err = gorm.Open(mysql.New(mysql.Config{
 		DSN:                       global.GVA_CONFIG.Mysql.Dsn(), // DSN data source name
-		DefaultStringSize:         256,                           // string 类型字段的默认长度
+		DefaultStringSize:         191,                           // string 类型字段的默认长度
 		DisableDatetimePrecision:  true,                          // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
 		DontSupportRenameIndex:    true,                          // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
 		DontSupportRenameColumn:   true,                          // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列

+ 5 - 0
server/cmd/information/system/api.go

@@ -92,6 +92,11 @@ var apis = []model.SysApi{
 	{global.GVA_MODEL{ID: 77, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/workflowProcess/getMyNeed", "获取我的待办", "workflowProcess", "GET"},
 	{global.GVA_MODEL{ID: 78, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/workflowProcess/getWorkflowMoveByID", "根据id获取当前节点详情和历史", "workflowProcess", "GET"},
 	{global.GVA_MODEL{ID: 79, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/workflowProcess/completeWorkflowMove", "提交工作流", "workflowProcess", "POST"},
+	{global.GVA_MODEL{ID: 80, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/autoCode/preview", "预览自动化代码", "autoCode", "POST"},
+	{global.GVA_MODEL{ID: 81, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/excel/importExcel", "预览自动化代码", "autoCode", "POST"},
+	{global.GVA_MODEL{ID: 82, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/excel/loadExcel", "预览自动化代码", "autoCode", "POST"},
+	{global.GVA_MODEL{ID: 83, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/excel/exportExcel", "预览自动化代码", "autoCode", "POST"},
+	{global.GVA_MODEL{ID: 84, CreatedAt: time.Now(), UpdatedAt: time.Now()}, "/excel/downloadTemplate", "预览自动化代码", "autoCode", "POST"},
 }
 
 //@author: [SliverHorn](https://github.com/SliverHorn)

+ 5 - 0
server/cmd/information/system/casbin.go

@@ -55,6 +55,7 @@ var carbines = []gormadapter.CasbinRule{
 	{PType: "p", V0: "888", V1: "/customer/customer", V2: "GET"},
 	{PType: "p", V0: "888", V1: "/customer/customerList", V2: "GET"},
 	{PType: "p", V0: "888", V1: "/autoCode/createTemp", V2: "POST"},
+	{PType: "p", V0: "888", V1: "/autoCode/preview", V2: "POST"},
 	{PType: "p", V0: "888", V1: "/autoCode/getTables", V2: "GET"},
 	{PType: "p", V0: "888", V1: "/autoCode/getDB", V2: "GET"},
 	{PType: "p", V0: "888", V1: "/autoCode/getColumn", V2: "GET"},
@@ -91,6 +92,10 @@ var carbines = []gormadapter.CasbinRule{
 	{PType: "p", V0: "888", V1: "/workflowProcess/getMyStated", V2: "GET"},
 	{PType: "p", V0: "888", V1: "/workflowProcess/getMyNeed", V2: "GET"},
 	{PType: "p", V0: "888", V1: "/workflowProcess/getWorkflowMoveByID", V2: "GET"},
+	{PType: "p", V0: "888", V1: "/excel/importExcel", V2: "POST"},
+	{PType: "p", V0: "888", V1: "/excel/loadExcel", V2: "GET"},
+	{PType: "p", V0: "888", V1: "/excel/exportExcel", V2: "POST"},
+	{PType: "p", V0: "888", V1: "/excel/downloadTemplate", V2: "GET"},
 	{PType: "p", V0: "8881", V1: "/base/login", V2: "POST"},
 	{PType: "p", V0: "8881", V1: "/user/register", V2: "POST"},
 	{PType: "p", V0: "8881", V1: "/api/createApi", V2: "POST"},

+ 10 - 0
server/config.yaml

@@ -78,6 +78,7 @@ qiniu:
   secret-key: 'pgdbqEsf7ooZh7W3xokP833h3dZ_VecFXPDeG5JY'
   use-cdn-domains: false
 
+
 # aliyun oss configuration
 aliyun-oss:
   endpoint: 'yourEndpoint'
@@ -86,6 +87,15 @@ aliyun-oss:
   bucket-name: 'yourBucketName'
   bucket-url: 'yourBucketUrl'
 
+# tencent cos configuration
+tencent-cos:
+  bucket: 'xxxxx-10005608'
+  region: 'ap-shanghai'
+  secret-id: 'xxxxxxxx'
+  secret-key: 'xxxxxxxx'
+  base-url: 'https://gin.vue.admin'
+  path-prefix: 'gin-vue-admin'
+
 # excel configuration
 excel:
   dir: './resource/excel/'

+ 2 - 1
server/config/config.go

@@ -14,5 +14,6 @@ type Server struct {
 	Local     Local     `mapstructure:"local" json:"local" yaml:"local"`
 	Qiniu     Qiniu     `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"`
 	AliyunOSS AliyunOSS `mapstructure:"aliyun-oss" json:"aliyunOSS" yaml:"aliyun-oss"`
-	Excel     Excel     `mapstructure:"excel" json:"excel" yaml:"excel"`
+	TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencentCOS" yaml:"tencent-cos"`
+	Excel Excel `mapstructure:"excel" json:"excel" yaml:"excel"`
 }

+ 10 - 0
server/config/oss.go

@@ -14,10 +14,20 @@ type Qiniu struct {
 	UseCdnDomains bool   `mapstructure:"use-cdn-domains" json:"useCdnDomains" yaml:"use-cdn-domains"`
 }
 
+
 type AliyunOSS struct {
 	Endpoint        string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
 	AccessKeyId     string `mapstructure:"access-key-id" json:"accessKeyId" yaml:"access-key-id"`
 	AccessKeySecret string `mapstructure:"access-key-secret" json:"accessKeySecret" yaml:"access-key-secret"`
 	BucketName      string `mapstructure:"bucket-name" json:"bucketName" yaml:"bucket-name"`
 	BucketUrl       string `mapstructure:"bucket-url" json:"bucketUrl" yaml:"bucket-url"`
+
+type TencentCOS struct {
+	Bucket     string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
+	Region     string `mapstructure:"region" json:"region" yaml:"region"`
+	SecretID   string `mapstructure:"secret-id" json:"secretID" yaml:"secret-id"`
+	SecretKey  string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"`
+	BaseURL    string `mapstructure:"base-url" json:"baseURL" yaml:"base-url"`
+	PathPrefix string `mapstructure:"path-prefix" json:"pathPrefix" yaml:"path-prefix"`
+
 }

+ 3 - 2
server/go.mod

@@ -36,10 +36,10 @@ require (
 	github.com/onsi/ginkgo v1.7.0 // indirect
 	github.com/onsi/gomega v1.4.3 // indirect
 	github.com/pelletier/go-toml v1.6.0 // indirect
-	github.com/pkg/errors v0.9.1
+	github.com/pkg/errors v0.9.1 // indirect
 	github.com/qiniu/api.v7/v7 v7.4.1
 	github.com/satori/go.uuid v1.2.0
-	github.com/shirou/gopsutil v2.20.8+incompatible
+	github.com/shirou/gopsutil v3.21.1+incompatible
 	github.com/spf13/afero v1.2.2 // indirect
 	github.com/spf13/cast v1.3.1 // indirect
 	github.com/spf13/cobra v1.1.1
@@ -48,6 +48,7 @@ require (
 	github.com/swaggo/gin-swagger v1.2.0
 	github.com/swaggo/swag v1.6.7
 	github.com/tebeka/strftime v0.1.3 // indirect
+	github.com/tencentyun/cos-go-sdk-v5 v0.7.19
 	github.com/unrolled/secure v1.0.7
 	go.uber.org/zap v1.10.0
 	golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect

+ 1 - 0
server/initialize/router.go

@@ -47,6 +47,7 @@ func Routers() *gin.Engine {
 		router.InitSysDictionaryDetailRouter(PrivateGroup)   // 字典详情管理
 		router.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
 		router.InitWorkflowProcessRouter(PrivateGroup)       // 工作流相关接口
+		router.InitExcelRouter(PrivateGroup)                 // 表格导入导出
 	}
 	global.GVA_LOG.Info("router register success")
 	return Router

+ 6 - 0
server/model/exa_excel.go

@@ -0,0 +1,6 @@
+package model
+
+type ExcelInfo struct {
+	FileName string        `json:"fileName"`
+	InfoList []SysBaseMenu `json:"infoList"`
+}

+ 0 - 8
server/model/request/exa_file_upload_and_download.go

@@ -1,8 +0,0 @@
-package request
-
-import "gin-vue-admin/model"
-
-type ExcelInfo struct {
-	FileName string `json:"fileName"`
-	InfoList []model.SysBaseMenu `json:"infoList"`
-}

BIN
server/resource/excel/ExcelExport.xlsx


+ 0 - 0
server/router/exp_customer.go → server/router/exa_customer.go


+ 16 - 0
server/router/exa_excel.go

@@ -0,0 +1,16 @@
+package router
+
+import (
+	"gin-vue-admin/api/v1"
+	"github.com/gin-gonic/gin"
+)
+
+func InitExcelRouter(Router *gin.RouterGroup) {
+	FileUploadAndDownloadGroup := Router.Group("excel")
+	{
+		FileUploadAndDownloadGroup.POST("/importExcel", v1.ImportExcel)          // 导入Excel
+		FileUploadAndDownloadGroup.GET("/loadExcel", v1.LoadExcel)               // 加载Excel数据
+		FileUploadAndDownloadGroup.POST("/exportExcel", v1.ExportExcel)          // 导出Excel
+		FileUploadAndDownloadGroup.GET("/downloadTemplate", v1.DownloadTemplate) // 下载模板文件
+	}
+}

+ 0 - 4
server/router/exp_file_upload_and_download.go → server/router/exa_file_upload_and_download.go

@@ -15,9 +15,5 @@ func InitFileUploadAndDownloadRouter(Router *gin.RouterGroup) {
 		FileUploadAndDownloadGroup.GET("/findFile", v1.FindFile)                                  // 查询当前文件成功的切片
 		FileUploadAndDownloadGroup.POST("/breakpointContinueFinish", v1.BreakpointContinueFinish) // 查询当前文件成功的切片
 		FileUploadAndDownloadGroup.POST("/removeChunk", v1.RemoveChunk)                           // 查询当前文件成功的切片
-		FileUploadAndDownloadGroup.POST("/importExcel", v1.ImportExcel)                          // 导入Excel
-		FileUploadAndDownloadGroup.GET("/loadExcel", v1.LoadExcel)                                // 加载Excel数据
-		FileUploadAndDownloadGroup.POST("/exportExcel", v1.ExportExcel)                           // 导出Excel
-		FileUploadAndDownloadGroup.GET("/downloadTemplate", v1.DownloadTemplate)                  // 下载模板文件
 	}
 }

+ 1 - 0
server/router/sys_auto_code.go

@@ -8,6 +8,7 @@ import (
 func InitAutoCodeRouter(Router *gin.RouterGroup) {
 	AutoCodeRouter := Router.Group("autoCode")
 	{
+		AutoCodeRouter.POST("preview", v1.PreviewTemp)   // 获取自动创建代码预览
 		AutoCodeRouter.POST("createTemp", v1.CreateTemp) // 创建自动化代码
 		AutoCodeRouter.GET("getTables", v1.GetTables)    // 获取对应数据库的表
 		AutoCodeRouter.GET("getDB", v1.GetDB)            // 获取数据库

+ 118 - 41
server/service/sys_auto_code.go

@@ -16,6 +16,11 @@ import (
 	"gorm.io/gorm"
 )
 
+const (
+	autoPath = "autoCode/"
+	basePath = "resource/template"
+)
+
 type tplData struct {
 	template         *template.Template
 	locationPath     string
@@ -23,59 +28,82 @@ type tplData struct {
 	autoMoveFilePath string
 }
 
-//@author: [piexlmax](https://github.com/piexlmax)
-//@function: CreateTemp
-//@description: 创建代码
+//@author: [songzhibin97](https://github.com/songzhibin97)
+//@function: PreviewTemp
+//@description: 预览创建代码
 //@param: model.AutoCodeStruct
-//@return: error
+//@return: map[string]string, error
 
-func CreateTemp(autoCode model.AutoCodeStruct) (err error) {
-	basePath := "resource/template"
-	// 获取 basePath 文件夹下所有tpl文件
-	tplFileList, err := GetAllTplFile(basePath, nil)
+func PreviewTemp(autoCode model.AutoCodeStruct) (map[string]string, error) {
+	dataList, _, needMkdir, err := getNeedList(&autoCode)
 	if err != nil {
-		return err
-	}
-	dataList := make([]tplData, 0, len(tplFileList))
-	fileList := make([]string, 0, len(tplFileList))
-	needMkdir := make([]string, 0, len(tplFileList)) // 当文件夹下存在多个tpl文件时,改为map更合理
-	// 根据文件路径生成 tplData 结构体,待填充数据
-	for _, value := range tplFileList {
-		dataList = append(dataList, tplData{locationPath: value})
+		return nil, err
 	}
-	// 生成 *Template, 填充 template 字段
-	for index, value := range dataList {
-		dataList[index].template, err = template.ParseFiles(value.locationPath)
-		if err != nil {
-			return err
-		}
+
+	// 写入文件前,先创建文件夹
+	if err = utils.CreateDir(needMkdir...); err != nil {
+		return nil, err
 	}
 
-	// 生成文件路径,填充 autoCodePath 字段,readme.txt.tpl不符合规则,需要特殊处理
-	// resource/template/web/api.js.tpl -> autoCode/web/autoCode.PackageName/api/autoCode.PackageName.js
-	// resource/template/readme.txt.tpl -> autoCode/readme.txt
-	autoPath := "autoCode/"
-	for index, value := range dataList {
-		trimBase := strings.TrimPrefix(value.locationPath, basePath+"/")
-		if trimBase == "readme.txt.tpl" {
-			dataList[index].autoCodePath = autoPath + "readme.txt"
+	// 创建map
+	ret := make(map[string]string)
+
+	// 生成map
+	for _, value := range dataList {
+		ext := ""
+		if ext = filepath.Ext(value.autoCodePath); ext == ".txt" {
 			continue
 		}
-
-		if lastSeparator := strings.LastIndex(trimBase, "/"); lastSeparator != -1 {
-			origFileName := strings.TrimSuffix(trimBase[lastSeparator+1:], ".tpl")
-			firstDot := strings.Index(origFileName, ".")
-			if firstDot != -1 {
-				dataList[index].autoCodePath = filepath.Join(autoPath, trimBase[:lastSeparator], autoCode.PackageName,
-					origFileName[:firstDot], autoCode.PackageName+origFileName[firstDot:])
-			}
+		f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0755)
+		if err != nil {
+			return nil, err
 		}
+		if err = value.template.Execute(f, autoCode); err != nil {
+			return nil, err
+		}
+		_ = f.Close()
+		f, err = os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_RDONLY, 0755)
+		if err != nil {
+			return nil, err
+		}
+		builder := strings.Builder{}
+		builder.WriteString("```")
 
-		if lastSeparator := strings.LastIndex(dataList[index].autoCodePath, string(os.PathSeparator)); lastSeparator != -1 {
-			needMkdir = append(needMkdir, dataList[index].autoCodePath[:lastSeparator])
+		if ext != "" && strings.Contains(ext, ".") {
+			builder.WriteString(strings.Replace(ext, ".", "", -1))
 		}
+		builder.WriteString("\n\n")
+		data, err := ioutil.ReadAll(f)
+		if err != nil {
+			return nil, err
+		}
+		builder.Write(data)
+		builder.WriteString("\n\n```")
+
+		pathArr := strings.Split(value.autoCodePath, string(os.PathSeparator))
+		ret[pathArr[1]+"-"+pathArr[3]] = builder.String()
+		_ = f.Close()
+
 	}
+	defer func() { // 移除中间文件
+		if err := os.RemoveAll(autoPath); err != nil {
+			return
+		}
+	}()
+	return ret, nil
+}
 
+//@author: [piexlmax](https://github.com/piexlmax)
+//@function: CreateTemp
+//@description: 创建代码
+//@param: model.AutoCodeStruct
+//@return: error
+
+func CreateTemp(autoCode model.AutoCodeStruct) (err error) {
+	dataList, fileList, needMkdir, err := getNeedList(&autoCode)
+	if err != nil {
+		return err
+	}
 	// 写入文件前,先创建文件夹
 	if err = utils.CreateDir(needMkdir...); err != nil {
 		return err
@@ -83,7 +111,6 @@ func CreateTemp(autoCode model.AutoCodeStruct) (err error) {
 
 	// 生成文件
 	for _, value := range dataList {
-		fileList = append(fileList, value.autoCodePath)
 		f, err := os.OpenFile(value.autoCodePath, os.O_CREATE|os.O_WRONLY, 0755)
 		if err != nil {
 			return err
@@ -276,3 +303,53 @@ func AutoCreateApi(a *model.AutoCodeStruct) (err error) {
 	})
 	return err
 }
+
+func getNeedList(autoCode *model.AutoCodeStruct) (dataList []tplData, fileList []string, needMkdir []string, err error) {
+	// 获取 basePath 文件夹下所有tpl文件
+	tplFileList, err := GetAllTplFile(basePath, nil)
+	if err != nil {
+		return nil, nil, nil, err
+	}
+	dataList = make([]tplData, 0, len(tplFileList))
+	fileList = make([]string, 0, len(tplFileList))
+	needMkdir = make([]string, 0, len(tplFileList)) // 当文件夹下存在多个tpl文件时,改为map更合理
+	// 根据文件路径生成 tplData 结构体,待填充数据
+	for _, value := range tplFileList {
+		dataList = append(dataList, tplData{locationPath: value})
+	}
+	// 生成 *Template, 填充 template 字段
+	for index, value := range dataList {
+		dataList[index].template, err = template.ParseFiles(value.locationPath)
+		if err != nil {
+			return nil, nil, nil, err
+		}
+	}
+	// 生成文件路径,填充 autoCodePath 字段,readme.txt.tpl不符合规则,需要特殊处理
+	// resource/template/web/api.js.tpl -> autoCode/web/autoCode.PackageName/api/autoCode.PackageName.js
+	// resource/template/readme.txt.tpl -> autoCode/readme.txt
+	autoPath := "autoCode/"
+	for index, value := range dataList {
+		trimBase := strings.TrimPrefix(value.locationPath, basePath+"/")
+		if trimBase == "readme.txt.tpl" {
+			dataList[index].autoCodePath = autoPath + "readme.txt"
+			continue
+		}
+
+		if lastSeparator := strings.LastIndex(trimBase, "/"); lastSeparator != -1 {
+			origFileName := strings.TrimSuffix(trimBase[lastSeparator+1:], ".tpl")
+			firstDot := strings.Index(origFileName, ".")
+			if firstDot != -1 {
+				dataList[index].autoCodePath = filepath.Join(autoPath, trimBase[:lastSeparator], autoCode.PackageName,
+					origFileName[:firstDot], autoCode.PackageName+origFileName[firstDot:])
+			}
+		}
+
+		if lastSeparator := strings.LastIndex(dataList[index].autoCodePath, string(os.PathSeparator)); lastSeparator != -1 {
+			needMkdir = append(needMkdir, dataList[index].autoCodePath[:lastSeparator])
+		}
+	}
+	for _, value := range dataList {
+		fileList = append(fileList, value.autoCodePath)
+	}
+	return dataList, fileList, needMkdir, err
+}

+ 3 - 4
server/service/sys_menu.go

@@ -89,12 +89,11 @@ func getBaseChildrenList(menu *model.SysBaseMenu, treeMap map[string][]model.Sys
 //@param: menu model.SysBaseMenu
 //@return: err error
 
-func AddBaseMenu(menu model.SysBaseMenu) (err error) {
+func AddBaseMenu(menu model.SysBaseMenu) error {
 	if !errors.Is(global.GVA_DB.Where("name = ?", menu.Name).First(&model.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) {
-		err = errors.New("存在重复name,请修改name")
+		return errors.New("存在重复name,请修改name")
 	}
-	err = global.GVA_DB.Create(&menu).Error
-	return err
+	return global.GVA_DB.Create(&menu).Error
 }
 
 //@author: [piexlmax](https://github.com/piexlmax)

+ 60 - 0
server/utils/upload/tencent_cos.go

@@ -0,0 +1,60 @@
+package upload
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"gin-vue-admin/global"
+	"mime/multipart"
+	"net/http"
+	"net/url"
+	"time"
+
+	"github.com/tencentyun/cos-go-sdk-v5"
+	"go.uber.org/zap"
+)
+
+type TencentCOS struct{}
+
+// UploadFile upload file to COS
+func (*TencentCOS) UploadFile(file *multipart.FileHeader) (string, string, error) {
+	c := NewClient()
+	f, openError := file.Open()
+	if openError != nil {
+		global.GVA_LOG.Error("function file.Open() Filed", zap.Any("err", openError.Error()))
+
+		return "", "", errors.New("function file.Open() Filed, err:" + openError.Error())
+	}
+	fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename)
+
+	_, err := c.Object.Put(context.Background(), global.GVA_CONFIG.TencentCOS.PathPrefix+"/"+fileKey, f, nil)
+	if err != nil {
+		panic(err)
+	}
+	return global.GVA_CONFIG.TencentCOS.BaseURL + "/" + global.GVA_CONFIG.TencentCOS.PathPrefix + "/" + fileKey, fileKey, nil
+}
+
+// DeleteFile delete file form COS
+func (*TencentCOS) DeleteFile(key string) error {
+	c := NewClient()
+	name := global.GVA_CONFIG.TencentCOS.PathPrefix + "/" + key
+	_, err := c.Object.Delete(context.Background(), name)
+	if err != nil {
+		global.GVA_LOG.Error("function bucketManager.Delete() Filed", zap.Any("err", err.Error()))
+		return errors.New("function bucketManager.Delete() Filed, err:" + err.Error())
+	}
+	return nil
+}
+
+// NewClient init COS client
+func NewClient() *cos.Client {
+	u, _ := url.Parse("https://" + global.GVA_CONFIG.TencentCOS.Bucket + ".cos." + global.GVA_CONFIG.TencentCOS.Region + ".myqcloud.com")
+	b := &cos.BaseURL{BucketURL: u}
+	c := cos.NewClient(b, &http.Client{
+		Transport: &cos.AuthorizationTransport{
+			SecretID:  global.GVA_CONFIG.TencentCOS.SecretID,
+			SecretKey: global.GVA_CONFIG.TencentCOS.SecretKey,
+		},
+	})
+	return c
+}

+ 3 - 1
server/utils/upload/upload.go

@@ -28,7 +28,9 @@ func NewOss() OSS {
 		return &Local{}
 	case "qiniu":
 		return &Qiniu{}
+	case "tencent-cos":
+		return &TencentCOS{}
 	default:
 		return &Local{}
 	}
-}
+}

+ 75 - 73
web/package.json

@@ -1,75 +1,77 @@
 {
-    "name": "gin-vue-admin",
-    "version": "0.1.0",
-    "private": true,
-    "scripts": {
-        "serve": "node openDocument.js && vue-cli-service serve",
-        "build": "vue-cli-service build",
-        "lint": "vue-cli-service lint"
+  "name": "gin-vue-admin",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "node openDocument.js && vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "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",
+    "core-js": "^3.6.5",
+    "echarts": "^4.7.0",
+    "element-ui": "^2.12.0",
+    "highlight.js": "^10.6.0",
+    "marked": "^2.0.0",
+    "node-sass": "^4.14.1",
+    "path": "^0.12.7",
+    "qs": "^6.8.0",
+    "quill": "^1.3.7",
+    "sass-loader": "^8.0.0",
+    "screenfull": "^5.0.2",
+    "script-ext-html-webpack-plugin": "^2.1.4",
+    "spark-md5": "^3.0.1",
+    "timeline-vuejs": "1.1.1",
+    "vue": "^2.6.10",
+    "vue-particle-line": "^0.1.4",
+    "vue-router": "^3.1.3",
+    "vue-simple-uploader": "^0.7.4",
+    "vuescroll": "^4.14.4",
+    "vuex": "^3.1.1",
+    "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",
+    "babel-eslint": "^10.1.0",
+    "eslint": "^6.7.2",
+    "eslint-plugin-vue": "^6.2.2",
+    "vue-template-compiler": "^2.6.10",
+    "core-js": "^3.3.2",
+    "node-sass": "^4.12.0",
+    "numericjs": "^1.2.6",
+    "raw-loader": "^3.1.0",
+    "sass-loader": "^8.0.0"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "node": true
     },
-    "dependencies": {
-        "@moefe/vue-aplayer": "^2.0.0-beta.5",
-        "axios": "^0.19.2",
-        "core-js": "^3.6.5",
-        "echarts": "^4.7.0",
-        "element-ui": "^2.12.0",
-        "node-sass": "^4.14.1",
-        "path": "^0.12.7",
-        "qs": "^6.8.0",
-        "quill": "^1.3.7",
-        "sass-loader": "^8.0.0",
-        "screenfull": "^5.0.2",
-        "script-ext-html-webpack-plugin": "^2.1.4",
-        "spark-md5": "^3.0.1",
-        "timeline-vuejs": "1.1.1",
-        "vue": "^2.6.10",
-        "vue-particle-line": "^0.1.4",
-        "vue-router": "^3.1.3",
-        "vue-simple-uploader": "^0.7.4",
-        "vuescroll": "^4.14.4",
-        "vuex": "^3.1.1",
-        "vuex-persist": "^2.1.0",
-        "@antv/dom-util": "2.0.2",
-        "@antv/matrix-util": "2.0.7",
-        "@antv/g-canvas": "^0.4.12",
-        "@antv/g6": "3.5.2",
-        "@antv/util": "~2.0.9"
-    },
-    "devDependencies": {
-        "@vue/cli-plugin-babel": "^4.5.6",
-        "@vue/cli-plugin-eslint": "^4.5.6",
-        "@vue/cli-service": "^4.5.6",
-        "babel-eslint": "^10.1.0",
-        "eslint": "^6.7.2",
-        "eslint-plugin-vue": "^6.2.2",
-        "vue-template-compiler": "^2.6.10",
-        "core-js": "^3.3.2",
-        "node-sass": "^4.12.0",
-        "numericjs": "^1.2.6",
-        "raw-loader": "^3.1.0",
-        "sass-loader": "^8.0.0"
-    },
-    "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"
-    ]
-}
+    "extends": [
+      "plugin:vue/essential",
+      "eslint:recommended"
+    ],
+    "rules": {},
+    "parserOptions": {
+      "parser": "babel-eslint"
+    }
+  },
+  "postcss": {
+    "plugins": {
+      "autoprefixer": {}
+    }
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions"
+  ]
+}

+ 9 - 13
web/src/api/autoCode.js

@@ -1,16 +1,13 @@
 import service from '@/utils/request'
-// @Tags api
-// @Summary 分页获取角色列表
-// @Security ApiKeyAuth
-// @accept application/json
-// @Produce application/json
-// @Param data body modelInterface.PageInfo true "分页获取用户列表"
-// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
-// @Router /api/getApiList [post]
-// {
-//  page     int
-//	pageSize int
-// }
+
+export const preview = (data) => {
+    return service({
+        url: "/autoCode/preview",
+        method: 'post',
+        data,
+    })
+}
+
 export const createTemp = (data) => {
     return service({
         url: "/autoCode/createTemp",
@@ -20,7 +17,6 @@ export const createTemp = (data) => {
     })
 }
 
-
 // @Tags SysApi
 // @Summary 获取当前所有数据库
 // @Security ApiKeyAuth

+ 85 - 0
web/src/api/excel.js

@@ -0,0 +1,85 @@
+import service from '@/utils/request';
+import { Message } from 'element-ui';
+
+const handleFileError = (res, fileName) => {
+    if (typeof(res.data) !== "undefined") {
+        if (res.data.type == "application/json") {
+            const reader = new FileReader();
+            reader.onload = function() {
+                let message = JSON.parse(reader.result).msg;
+                Message({
+                    showClose: true,
+                    message: message,
+                    type: 'error'
+                })
+            };
+            reader.readAsText(new Blob([res.data]));
+        }
+    } else {
+        var downloadUrl = window.URL.createObjectURL(new Blob([res]));
+        var a = document.createElement('a');
+        a.style.display = 'none';
+        a.href = downloadUrl;
+        a.download = fileName;
+        var event = new MouseEvent("click");
+        a.dispatchEvent(event);
+    }
+}
+
+// @Tags excel
+// @Summary 导出Excel
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce  application/octet-stream
+// @Param data body model.ExcelInfo true "导出Excel文件信息"
+// @Success 200
+// @Router /excel/exportExcel [post]
+export const exportExcel = (tableData, fileName) => {
+    service({
+        url: "/excel/exportExcel",
+        method: 'post',
+        data: {
+            fileName: fileName,
+            infoList: tableData
+        },
+        responseType: 'blob'
+    }).then((res) => {
+        handleFileError(res, fileName)
+    })
+}
+
+// @Tags excel
+// @Summary 导入Excel文件
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param file formData file true "导入Excel文件"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"导入成功"}"
+// @Router /excel/importExcel [post]
+export const loadExcelData = () => {
+    return service({
+        url: "/excel/loadExcel",
+        method: 'get'
+    })
+}
+
+// @Tags excel
+// @Summary 下载模板
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param fileName query fileName true "模板名称"
+// @Success 200
+// @Router /excel/downloadTemplate [get]
+export const downloadTemplate = (fileName) => {
+    return service({
+        url: "/excel/downloadTemplate",
+        method: 'get',
+        params: {
+            fileName: fileName
+        },
+        responseType: 'blob'
+    }).then((res) => {
+        handleFileError(res, fileName)
+    })
+}

+ 0 - 84
web/src/api/fileUploadAndDownload.js

@@ -1,30 +1,4 @@
 import service from '@/utils/request';
-import { Message } from 'element-ui';
-
-const handleFileError = (res, fileName) => {
-    if (typeof(res.data) !== "undefined") {
-        if (res.data.type == "application/json") {
-            const reader = new FileReader();
-            reader.onload = function() {
-                let message = JSON.parse(reader.result).msg;
-                Message({
-                    showClose: true,
-                    message: message,
-                    type: 'error'
-                })
-            };
-            reader.readAsText(new Blob([res.data]));
-        } 
-    } else {
-        var downloadUrl = window.URL.createObjectURL(new Blob([res]));
-        var a = document.createElement('a');
-        a.style.display = 'none';
-        a.href = downloadUrl;
-        a.download = fileName;
-        var event = new MouseEvent("click");
-        a.dispatchEvent(event);
-    }        
-} 
 
 // @Tags FileUploadAndDownload
 // @Summary 分页文件列表
@@ -55,62 +29,4 @@ export const deleteFile = (data) => {
         method: "post",
         data
     })
-}
-
-// @Tags ExaFileUploadAndDownload
-// @Summary 导出Excel
-// @Security ApiKeyAuth
-// @accept application/json
-// @Produce  application/octet-stream
-// @Param data body request.ExcelInfo true "导出Excel文件信息"
-// @Success 200
-// @Router /fileUploadAndDownload/exportExcel [post]
-export const exportExcel = (tableData, fileName) => {
-    service({
-        url: "/fileUploadAndDownload/exportExcel",
-        method: 'post',
-        data: {
-            fileName: fileName,
-            infoList: tableData
-        },
-        responseType: 'blob'
-    }).then((res)=>{
-        handleFileError(res, fileName)
-    })
-}
-
-// @Tags ExaFileUploadAndDownload
-// @Summary 导入Excel文件
-// @Security ApiKeyAuth
-// @accept multipart/form-data
-// @Produce  application/json
-// @Param file formData file true "导入Excel文件"
-// @Success 200 {string} string "{"success":true,"data":{},"msg":"导入成功"}"
-// @Router /fileUploadAndDownload/importExcel [post]
-export const loadExcelData = () => {
-    return service({
-        url: "/fileUploadAndDownload/loadExcel",
-        method: 'get'
-    })
-}
-
-// @Tags ExaFileUploadAndDownload
-// @Summary 下载模板
-// @Security ApiKeyAuth
-// @accept multipart/form-data
-// @Produce  application/json
-// @Param fileName query fileName true "模板名称"
-// @Success 200
-// @Router /fileUploadAndDownload/downloadTemplate [get]
-export const downloadTemplate = (fileName) => {   
-    return service({
-        url: "/fileUploadAndDownload/downloadTemplate",
-        method: 'get',
-        params:{
-            fileName: fileName
-        },
-        responseType: 'blob'
-    }).then((res)=>{
-        handleFileError(res, fileName)        
-    })
 }

+ 8 - 1
web/src/router/index.js

@@ -3,6 +3,13 @@ import Router 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 baseRouters = [{
         path: '/',
         redirect: '/login'
@@ -23,4 +30,4 @@ const createRouter = () => new Router({
 
 const router = createRouter()
 
-export default router
+export default router

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

@@ -3,7 +3,7 @@
     <el-row>
       <el-col :span="2">
         <el-upload
-          :action="`${path}/fileUploadAndDownload/importExcel`"
+          :action="`${path}/excel/importExcel`"
           :headers="{'x-token':token}"
           :on-success="loadExcel"
           :show-file-list="false"
@@ -37,7 +37,7 @@
 const path = process.env.VUE_APP_BASE_API;
 import { mapGetters } from 'vuex';
 import infoList from "@/mixins/infoList";
-import { exportExcel, loadExcelData, downloadTemplate } from "@/api/fileUploadAndDownload";
+import { exportExcel, loadExcelData, downloadTemplate } from "@/api/excel";
 import { getMenuList } from "@/api/menu";
 export default {
   name: 'Excel',

+ 9 - 6
web/src/view/example/index.vue

@@ -1,12 +1,15 @@
 <template>
-    <router-view></router-view>
+  <div>
+    <keep-alive>
+      <router-view v-if="$route.meta.keepAlive"></router-view>
+    </keep-alive>
+    <router-view v-if="!$route.meta.keepAlive"></router-view>
+  </div>
 </template>
 
 <script>
 export default {
-    name:"Example",
-}
+  name: "Example",
+};
 </script>
-<style lang="scss">
-    
-</style>
+<style lang="scss"></style>

+ 2 - 2
web/src/view/layout/index.vue

@@ -67,11 +67,11 @@
         </transition>
         <transition mode="out-in" name="el-fade-in-linear">
           <keep-alive>
-            <router-view :key="$route.fullPath" v-loading="loadingFlag"  element-loading-text="正在加载中" class="admin-box" v-if="$route.meta.keepAlive && reloadFlag"></router-view>
+            <router-view v-loading="loadingFlag"  element-loading-text="正在加载中" class="admin-box" v-if="$route.meta.keepAlive && reloadFlag"></router-view>
           </keep-alive>
         </transition>
         <transition mode="out-in" name="el-fade-in-linear">
-          <router-view :key="$route.fullPath" v-loading="loadingFlag"  element-loading-text="正在加载中" class="admin-box" v-if="!$route.meta.keepAlive && reloadFlag"></router-view>
+          <router-view v-loading="loadingFlag"  element-loading-text="正在加载中" class="admin-box" v-if="!$route.meta.keepAlive && reloadFlag"></router-view>
         </transition>
        <BottomInfo />
       </el-main>

+ 1 - 1
web/src/view/person/person.vue

@@ -4,7 +4,7 @@
       <el-col :span="6">
         <div class="fl-left avatar-box">
           <div class="user-card">
-              <div class="user-headpic-update" :style="{ 'background-image': 'url(' + userInfo.headerImg + ')','background-repeat':'no-repeat','background-size':'cover' }" >
+              <div class="user-headpic-update" :style="{ 'background-image': `url(${(userInfo.headerImg && userInfo.headerImg.slice(0, 4) !== 'http')?path+userInfo.headerImg:userInfo.headerImg})`,'background-repeat':'no-repeat','background-size':'cover' }" >
               <span class="update" @click="openChooseImg">
                 <i class="el-icon-edit"></i>
                 重新上传</span>

+ 16 - 0
web/src/view/routerHolder.vue

@@ -0,0 +1,16 @@
+<template>
+<!-- 此路由可作为父类路由通用路由页面使用 如需自定义父类路由页面 请参考 @/view/superAdmin/index.vue -->
+  <div>
+    <keep-alive>
+      <router-view v-if="$route.meta.keepAlive"></router-view>
+    </keep-alive>
+    <router-view v-if="!$route.meta.keepAlive"></router-view>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "RouterHolder",
+};
+</script>
+<style lang="scss"></style>

+ 9 - 6
web/src/view/superAdmin/index.vue

@@ -1,12 +1,15 @@
 <template>
-    <router-view></router-view>
+  <div>
+    <keep-alive>
+      <router-view v-if="$route.meta.keepAlive"></router-view>
+    </keep-alive>
+    <router-view v-if="!$route.meta.keepAlive"></router-view>
+  </div>
 </template>
 
 <script>
 export default {
-    name:"SuperAdmin",
-}
+  name: "SuperAdmin",
+};
 </script>
-<style lang="scss">
-    
-</style>
+<style lang="scss"></style>

+ 2 - 1
web/src/view/superAdmin/menu/menu.vue

@@ -97,8 +97,9 @@
             v-model="form.parentId"
           ></el-cascader>
         </el-form-item>
-        <el-form-item label="文件路径" prop="component" style="width:30%">
+        <el-form-item label="文件路径" prop="component" style="width:60%">
           <el-input autocomplete="off" v-model="form.component"></el-input>
+          <span style="font-size:12px;margin-right:12px;">如果菜单包含子菜单,请创建router-view二级路由页面或者</span><el-button size="mini" @click="form.component = 'view/routerHolder.vue'">点我设置</el-button>
         </el-form-item>
         <el-form-item label="展示名称" prop="meta.title" style="width:30%">
           <el-input autocomplete="off" v-model="form.meta.title"></el-input>

+ 58 - 0
web/src/view/systemTools/autoCode/component/previewCodeDialg.vue

@@ -0,0 +1,58 @@
+<template>
+    <div class="previewCode">
+        <el-tabs v-model="activeName">
+            <el-tab-pane :label="key" :name="key" v-for="(item,key) in previewCode" :key="key">
+                <div style="background:#fff;padding:0 20px" :id="key"></div>
+            </el-tab-pane>
+        </el-tabs>
+    </div>
+</template>
+
+<script>
+import marked from "marked"
+import hljs from "highlight.js";
+// import 'highlight.js/styles/atelier-cave-light.css';
+import 'highlight.js/styles/atelier-plateau-light.css';
+export default {
+    props:{
+        previewCode:{
+            type:Object,
+            default(){
+                return {}
+            }
+        }
+    },
+    data(){
+        return{
+             activeName: "",
+        }
+    },
+    mounted(){
+        marked.setOptions({
+          renderer: new marked.Renderer(),
+          highlight: function(code) {
+            return hljs.highlightAuto(code).value;
+          },
+          pedantic: false,
+          gfm: true,
+          tables: true,
+          breaks: false,
+          sanitize: false,
+          smartLists: true,
+          smartypants: false,
+          xhtml: false
+        }
+      );
+        for(const key in this.previewCode){
+            if(this.activeName == ""){
+                this.activeName = key
+            }
+            document.getElementById(key).innerHTML = marked(this.previewCode[key])
+        }
+    }
+}
+</script>
+
+<style lang="scss">
+    
+</style>

+ 52 - 32
web/src/view/systemTools/autoCode/index.vue

@@ -58,7 +58,7 @@
         <el-input v-model="form.description" placeholder="中文描述作为自动api描述"></el-input>
       </el-form-item>
       <el-form-item label="文件名称" prop="packageName">
-        <el-input v-model="form.packageName"></el-input>
+        <el-input v-model="form.packageName" placeholder="生成文件的默认名称"></el-input>
       </el-form-item>
       <el-form-item label="自动创建api">
         <el-checkbox v-model="form.autoCreateApiToSql"></el-checkbox>
@@ -117,7 +117,8 @@
     <el-tag type="danger">id , created_at , updated_at , deleted_at 会自动生成请勿重复创建</el-tag>
     <!-- 组件列表 -->
     <div class="button-box clearflex">
-      <el-button @click="enterForm" type="primary">生成代码</el-button>
+      <el-button @click="enterForm(true)" type="primary">预览代码</el-button>
+      <el-button @click="enterForm(false)" type="primary">生成代码</el-button>
     </div>
     <!-- 组件弹窗 -->
     <el-dialog title="组件内容" :visible.sync="dialogFlag">
@@ -127,6 +128,13 @@
         <el-button type="primary" @click="enterDialog">确 定</el-button>
       </div>
     </el-dialog>
+
+    <el-dialog :visible.sync="previewFlag">
+      <PreviewCodeDialg v-if="previewFlag" :previewCode="preViewCode"></PreviewCodeDialg>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="previewFlag = false">确 定</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 <script>
@@ -144,8 +152,9 @@ const fieldTemplate = {
 };
 
 import FieldDialog from "@/view/systemTools/autoCode/component/fieldDialog.vue";
+import PreviewCodeDialg from "@/view/systemTools/autoCode/component/previewCodeDialg.vue";
 import { toUpperCase, toHump } from "@/utils/stringFun.js";
-import { createTemp, getDB, getTable, getColumn } from "@/api/autoCode.js";
+import { createTemp, getDB, getTable, getColumn, preview } from "@/api/autoCode.js";
 import { getDict } from "@/utils/dictionary";
 
 export default {
@@ -153,6 +162,7 @@ export default {
   data() {
     return {
       activeNames: [""],
+      preViewCode:{},
       dbform: {
         dbName: "",
         tableName: ""
@@ -191,11 +201,13 @@ export default {
       },
       dialogMiddle: {},
       bk: {},
-      dialogFlag: false
+      dialogFlag: false,
+      previewFlag:false
     };
   },
   components: {
-    FieldDialog
+    FieldDialog,
+    PreviewCodeDialg
   },
   methods: {
     editAndAddField(item) {
@@ -250,7 +262,7 @@ export default {
     deleteField(index) {
       this.form.fields.splice(index, 1);
     },
-    async enterForm() {
+    async enterForm(isPreview) {
       if (this.form.fields.length <= 0) {
         this.$message({
           type: "error",
@@ -277,31 +289,38 @@ export default {
             });
             return false;
           }
-          const data = await createTemp(this.form);
-          if (data.headers?.success == "false") {
-            return;
-          } else {
-            this.$message({
-              type: "success",
-              message: "自动化代码创建成功,正在下载"
-            });
-          }
-          const blob = new Blob([data]);
-          const fileName = "ginvueadmin.zip";
-          if ("download" in document.createElement("a")) {
-            // 不是IE浏览器
-            let url = window.URL.createObjectURL(blob);
-            let link = document.createElement("a");
-            link.style.display = "none";
-            link.href = url;
-            link.setAttribute("download", fileName);
-            document.body.appendChild(link);
-            link.click();
-            document.body.removeChild(link); // 下载完成移除元素
-            window.URL.revokeObjectURL(url); // 释放掉blob对象
-          } else {
-            // IE 10+
-            window.navigator.msSaveBlob(blob, fileName);
+          if(isPreview){
+            const data = await preview(this.form);
+            console.log(data.code == 0)
+            this.preViewCode = data.data.autoCode
+            this.previewFlag = true
+          }else{
+            const data = await createTemp(this.form);
+            if (data.headers?.success == "false") {
+              return;
+            } else {
+              this.$message({
+                type: "success",
+                message: "自动化代码创建成功,正在下载"
+              });
+            }
+            const blob = new Blob([data]);
+            const fileName = "ginvueadmin.zip";
+            if ("download" in document.createElement("a")) {
+              // 不是IE浏览器
+              let url = window.URL.createObjectURL(blob);
+              let link = document.createElement("a");
+              link.style.display = "none";
+              link.href = url;
+              link.setAttribute("download", fileName);
+              document.body.appendChild(link);
+              link.click();
+              document.body.removeChild(link); // 下载完成移除元素
+              window.URL.revokeObjectURL(url); // 释放掉blob对象
+            } else {
+              // IE 10+
+              window.navigator.msSaveBlob(blob, fileName);
+            }
           }
         } else {
           return false;
@@ -357,7 +376,7 @@ export default {
       const fdTypes = ["string", "int", "bool", "float64", "time.Time"];
       fdTypes.map(async fdtype => {
         const res = await getDict(fdtype);
-        res.map(item => {
+        res&&res.map(item => {
           this.fdMap[item.label] = fdtype;
         });
       });
@@ -373,6 +392,7 @@ export default {
 .button-box {
   padding: 10px 20px;
   .el-button {
+    margin-right: 20px;
     float: right;
   }
 }

+ 9 - 6
web/src/view/systemTools/index.vue

@@ -1,12 +1,15 @@
 <template>
-    <router-view></router-view>
+  <div>
+    <keep-alive>
+      <router-view v-if="$route.meta.keepAlive"></router-view>
+    </keep-alive>
+    <router-view v-if="!$route.meta.keepAlive"></router-view>
+  </div>
 </template>
 
 <script>
 export default {
-    name:"System",
-}
+  name: "System",
+};
 </script>
-<style lang="scss">
-    
-</style>
+<style lang="scss"></style>

+ 9 - 6
web/src/view/workflow/index.vue

@@ -1,12 +1,15 @@
 <template>
-    <router-view></router-view>
+  <div>
+    <keep-alive>
+      <router-view v-if="$route.meta.keepAlive"></router-view>
+    </keep-alive>
+    <router-view v-if="!$route.meta.keepAlive"></router-view>
+  </div>
 </template>
 
 <script>
 export default {
-    name:"Workflow",
-}
+  name: "Workflow",
+};
 </script>
-<style lang="scss">
-    
-</style>
+<style lang="scss"></style>