未验证 提交 9fc24724 编写于 作者: Mr.奇淼('s avatar Mr.奇淼( 提交者: GitHub

Merge branch 'develop' into develop

......@@ -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
......
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)
}
......@@ -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)
}
\ No newline at end of file
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
......
......@@ -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 不支持重命名列
......
......@@ -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)
......
......@@ -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"},
......
......@@ -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/'
\ No newline at end of file
......@@ -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"`
}
......@@ -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"`
}
......@@ -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
......
......@@ -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
......
package model
type ExcelInfo struct {
FileName string `json:"fileName"`
InfoList []SysBaseMenu `json:"infoList"`
}
package request
import "gin-vue-admin/model"
type ExcelInfo struct {
FileName string `json:"fileName"`
InfoList []model.SysBaseMenu `json:"infoList"`
}
\ No newline at end of file
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) // 下载模板文件
}
}
......@@ -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) // 下载模板文件
}
}
......@@ -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) // 获取数据库
......
......@@ -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
}
......@@ -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)
......
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
}
......@@ -28,7 +28,9 @@ func NewOss() OSS {
return &Local{}
case "qiniu":
return &Qiniu{}
case "tencent-cos":
return &TencentCOS{}
default:
return &Local{}
}
}
\ No newline at end of file
}
{
"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"
]
}
\ No newline at end of file
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
......
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)
})
}
\ No newline at end of file
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)
})
}
\ No newline at end of file
......@@ -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
\ No newline at end of file
export default router
......@@ -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',
......
<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>
\ No newline at end of file
<style lang="scss"></style>
......@@ -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>
......
......@@ -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>
......
<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>
<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>
\ No newline at end of file
<style lang="scss"></style>
......@@ -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>
......
<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>
\ No newline at end of file
......@@ -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;
}
}
......
<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>
\ No newline at end of file
<style lang="scss"></style>
<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>
\ No newline at end of file
<style lang="scss"></style>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册