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

代码生成支持图片、附件上传及富文本编辑器

yxh пре 4 година
родитељ
комит
3685967728

+ 1 - 1
app/common/global/cache_key.go

@@ -2,7 +2,7 @@ package global
 
 const (
 	//缓存前缀
-	cachePrefix = "cache_"
+	cachePrefix = "cacheV2_"
 
 	// SysAuthMenu 缓存菜单KEY
 	SysAuthMenu = cachePrefix + "sysAuthMenu"

+ 89 - 0
app/system/api/upload.go

@@ -11,6 +11,9 @@ import (
 	"gfast/app/common/adapter"
 	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/text/gregex"
+	"github.com/gogf/gf/text/gstr"
+	"net/url"
 )
 
 type upload struct {
@@ -31,3 +34,89 @@ func (c *upload) UpImg(r *ghttp.Request) {
 	}
 	c.SusJsonExit(r, res)
 }
+
+// @Summary CkEditor编辑器上传附件
+// @Description CkEditor编辑器上传附件
+// @Tags 公共
+// @Param upFile body string  true "upFile"
+// @Success 0 {object} c.Response "{"code": 200, "data": [...]}"
+// @Router /system/upload/ckEditorUp [post]
+// @Security
+func (c *upload) CkEditorUp(r *ghttp.Request) {
+	upFile := r.GetUploadFile("upload")
+	fType := gstr.ToLower(r.GetString("type"))
+	var info *adapter.FileInfo
+	var err error
+	if fType == "images" {
+		info, err = adapter.Upload.UpImg(upFile)
+	} else if fType == "files" {
+		info, err = adapter.Upload.UpFile(upFile)
+	}
+	if err != nil {
+		r.Response.WriteJson(g.Map{"error": g.Map{"message": "上传失败," + err.Error(), "number": 105}})
+	} else {
+		parseInfo, _ := url.Parse(r.GetUrl())
+		var fileUrl = info.FileUrl
+		if !gregex.IsMatchString("^http", info.FileUrl) {
+			fileUrl = parseInfo.Scheme + "://" + parseInfo.Host + "/" + info.FileUrl
+		}
+		r.Response.WriteJson(g.Map{"fileName": info.FileName, "uploaded": 1, "url": fileUrl})
+	}
+}
+
+// @Summary 批量上传图片
+// @Description 批量上传图片
+// @Tags 公共
+// @Param file body string  true "file"
+// @Success 0 {object} c.Response "{"code": 200, "data": [...]}"
+// @Router /system/upload/upImgs [post]
+// @Security
+func (c *upload) UpImgs(r *ghttp.Request) {
+	upFiles := r.GetUploadFiles("file")
+	infos, err := adapter.Upload.UpImgs(upFiles)
+	if err != nil {
+		c.FailJson(true, r, "上传失败,"+err.Error())
+	}
+	res := g.Map{
+		"fileInfos": infos,
+	}
+	c.SusJson(true, r, "上传成功", res)
+}
+
+// @Summary 单文件上传
+// @Description 单文件上传
+// @Tags 公共
+// @Param file body string  true "file"
+// @Success 0 {object} c.Response "{"code": 200, "data": [...]}"
+// @Router /system/upload/upFile [post]
+// @Security
+func (c *upload) UpFile(r *ghttp.Request) {
+	upFile := r.GetUploadFile("file")
+	info, err := adapter.Upload.UpFile(upFile)
+	if err != nil {
+		c.FailJson(true, r, "上传失败,"+err.Error())
+	}
+	res := g.Map{
+		"fileInfo": info,
+	}
+	c.SusJson(true, r, "上传成功", res)
+}
+
+// @Summary 批量上传文件
+// @Description 批量上传文件
+// @Tags 公共
+// @Param file body string  true "file"
+// @Success 0 {object} c.Response "{"code": 200, "data": [...]}"
+// @Router /system/upload/upFiles [post]
+// @Security
+func (c *upload) UpFiles(r *ghttp.Request) {
+	upFiles := r.GetUploadFiles("file")
+	infos, err := adapter.Upload.UpFiles(upFiles)
+	if err != nil {
+		c.FailJson(true, r, "上传失败,"+err.Error())
+	}
+	res := g.Map{
+		"fileInfos": infos,
+	}
+	c.SusJson(true, r, "上传成功", res)
+}

+ 4 - 0
app/system/router/router.go

@@ -28,6 +28,10 @@ func init() {
 			group.Group("/upload", func(group *ghttp.RouterGroup) {
 				//单图上传
 				group.POST("/upImg", api.Upload.UpImg)
+				group.POST("/ckEditorUp", api.Upload.CkEditorUp)
+				group.POST("/upImgs", api.Upload.UpImgs)
+				group.POST("/upFile", api.Upload.UpFile)
+				group.POST("/upFiles", api.Upload.UpFiles)
 			})
 			//用户相关
 			group.Group("/user", func(group *ghttp.RouterGroup) {

+ 3 - 4
app/system/service/sys_user.go

@@ -512,16 +512,15 @@ func (s *sysUser) AddUserRole(roleIds interface{}, userId int64) (err error) {
 
 // AddUserPost 添加用户岗位信息
 func (s *sysUser) AddUserPost(postIds []int64, userId int64, tx *gdb.TX) (err error) {
-	if err != nil {
-		g.Log().Error(err)
-		return gerror.New("用户岗位设置失败")
-	}
 	//删除旧岗位信息
 	_, err = dao.SysUserPost.TX(tx).Where(dao.SysUserPost.Columns.UserId, userId).Delete()
 	if err != nil {
 		g.Log().Error(err)
 		return
 	}
+	if len(postIds) == 0 {
+		return
+	}
 	//添加用户岗位信息
 	data := g.List{}
 	for _, v := range postIds {

+ 10 - 3
app/system/service/tools_gen_table.go

@@ -451,7 +451,7 @@ func (s *toolsGenTable) GenCode(ids []int, ctx context.Context) (err error) {
 				err = s.createFile(path, code, false)
 				if !hasSql {
 					//第一次生成则向数据库写入菜单数据
-					err = s.writeDb(path)
+					err = s.writeDb(path, ctx)
 					if err != nil {
 						return
 					}
@@ -626,14 +626,14 @@ func (s *toolsGenTable) GenData(tableId int64, ctx context.Context) (data g.MapS
 //剔除多余的换行
 func (s *toolsGenTable) trimBreak(str string) (rStr string, err error) {
 	var b []byte
-	if b, err = gregex.Replace("(([\\s\t]*)\r?\n){2,}", []byte("$2\n"), []byte(str)); err == nil {
+	if b, err = gregex.Replace("(([\\s\t]*)\r?\n){2,}", []byte("$2\n\n"), []byte(str)); err == nil {
 		rStr = gconv.String(b)
 	}
 	return
 }
 
 // 写入菜单数据
-func (s *toolsGenTable) writeDb(path string) (err error) {
+func (s *toolsGenTable) writeDb(path string, ctx context.Context) (err error) {
 	isAnnotation := false
 	var fi *os.File
 	fi, err = os.Open(path)
@@ -646,11 +646,16 @@ func (s *toolsGenTable) writeDb(path string) (err error) {
 	now := gtime.Now()
 	var res sql.Result
 	var id int64
+	var tx *gdb.TX
+	tx, err = g.DB().Ctx(ctx).Begin()
 	for {
 		bytes, e := br.ReadBytes('\n')
 		if e == io.EOF {
 			break
 		}
+		if gstr.Trim(string(bytes)) == "" {
+			continue
+		}
 		bytes = bytes[:len(bytes)-2]
 		str := string(bytes)
 
@@ -683,6 +688,7 @@ func (s *toolsGenTable) writeDb(path string) (err error) {
 			//插入业务
 			res, err = g.DB().Exec(sql)
 			if err != nil {
+				tx.Rollback()
 				return
 			}
 			sqlStr = nil
@@ -690,5 +696,6 @@ func (s *toolsGenTable) writeDb(path string) (err error) {
 			sqlStr = []string{str}
 		}
 	}
+	tx.Commit()
 	return
 }

+ 2 - 1
library/utils.go

@@ -212,7 +212,8 @@ func GetRealFilesUrl(r *ghttp.Request, path string) (realPath string, err error)
 
 //获取附件相对路径
 func GetFilesPath(fileUrl string) (path string, err error) {
-	if !gstr.ContainsI(fileUrl, "http") {
+	upType := gstr.ToLower(g.Cfg().GetString("upload.type"))
+	if upType != "local" || (upType == "local" && !gstr.ContainsI(fileUrl, "http")) {
 		path = fileUrl
 		return
 	}

+ 39 - 5
template/vm/go/controller.template

@@ -10,9 +10,14 @@
 package api
 
 {{$hasGStr:=false}}
+{{$gjsonTag:=false}}
+{{$libTag:=false}}
 {{range $index,$column:=.table.Columns}}
 {{if eq $column.HtmlType "checkbox"}}
 {{$hasGStr = true}}
+{{else if eq $column.HtmlType "images" "file" "files"}}
+{{$gjsonTag = true}}
+{{$libTag = true}}
 {{end}}
 {{end}}
 
@@ -20,6 +25,9 @@ import (
     {{if ne $.table.ModuleName "system"}}
     sysApi "gfast/app/system/api"
     {{end}}
+    {{if $libTag}}
+    "gfast/library"
+    {{end}}
     "{{.table.PackageName}}/dao"
     "{{.table.PackageName}}/service"
     "github.com/gogf/gf/frame/g"
@@ -28,6 +36,9 @@ import (
     {{if $hasGStr}}
     "github.com/gogf/gf/text/gstr"
     {{end}}
+    {{if $gjsonTag}}
+    "github.com/gogf/gf/encoding/gjson"
+    {{end}}
 )
 
 type {{$structName}} struct {
@@ -47,6 +58,7 @@ func (c *{{$structName}}) List(r *ghttp.Request) {
 	if err := r.Parse(&req); err != nil {
 		c.FailJsonExit(r, err.(gvalid.Error).FirstString())
 	}
+	req.Ctx = r.GetCtx()
 	total, page, list, err := service.{{.table.ClassName}}.GetList(req)
 	if err != nil {
 		c.FailJsonExit(r, err.Error())
@@ -77,9 +89,20 @@ func (c *{{$structName}}) Add(r *ghttp.Request) {
     }else{
        req.{{$column.GoField}} = ""
     }
+    {{else if eq $column.HtmlType "images" "file" "files"}}
+    up{{$column.GoField}}:=gjson.New(req.{{$column.GoField}})
+    for _,obj:=range up{{$column.GoField}}.Array(){
+        mp := obj.(g.MapStrAny)
+        var err error
+        mp["url"],err = library.GetFilesPath(mp["url"].(string))
+        if err!=nil{
+            c.FailJsonExit(r, err.Error())
+        }
+    }
+    req.{{$column.GoField}} = up{{$column.GoField}}.MustToJsonString()
     {{end}}
     {{end}}
-    err := service.{{.table.ClassName}}.Add(req)
+    err := service.{{.table.ClassName}}.Add(req,r.GetCtx())
     if err != nil {
         c.FailJsonExit(r, err.Error())
     }
@@ -90,7 +113,7 @@ func (c *{{$structName}}) Add(r *ghttp.Request) {
 // Get 获取
 func (c *{{$structName}}) Get(r *ghttp.Request) {
 	id := r.GetInt64("id")
-	info, err := service.{{.table.ClassName}}.GetInfoById(id)
+	info, err := service.{{.table.ClassName}}.GetInfoById(id,r.GetCtx())
 	if err != nil {
 		c.FailJsonExit(r, err.Error())
 	}
@@ -115,9 +138,20 @@ func (c *{{$structName}}) Edit(r *ghttp.Request) {
     }else{
        req.{{$column.GoField}} = ""
     }
+    {{else if eq $column.HtmlType "images" "file" "files"}}
+    up{{$column.GoField}}:=gjson.New(req.{{$column.GoField}})
+    for _,obj:=range up{{$column.GoField}}.Array(){
+        mp := obj.(g.MapStrAny)
+        var err error
+        mp["url"],err = library.GetFilesPath(mp["url"].(string))
+        if err!=nil{
+            c.FailJsonExit(r, err.Error())
+        }
+    }
+    req.{{$column.GoField}} = up{{$column.GoField}}.MustToJsonString()
     {{end}}
     {{end}}
-    err := service.{{.table.ClassName}}.Edit(req)
+    err := service.{{.table.ClassName}}.Edit(req,r.GetCtx())
     if err != nil {
         c.FailJsonExit(r, err.Error())
     }
@@ -128,7 +162,7 @@ func (c *{{$structName}}) Edit(r *ghttp.Request) {
 // Delete 删除
 func (c *{{$structName}}) Delete(r *ghttp.Request) {
 	ids := r.GetInts("ids")
-	err := service.{{.table.ClassName}}.DeleteByIds(ids)
+	err := service.{{.table.ClassName}}.DeleteByIds(ids,r.GetCtx())
 	if err != nil {
 		c.FailJsonExit(r, err.Error())
 	}
@@ -144,7 +178,7 @@ func (c *{{$structName}})Change{{$column.GoField}}(r *ghttp.Request){
 	   if err := r.Parse(&req); err != nil {
 	       c.FailJsonExit(r, err.(gvalid.Error).FirstString())
 	   }
-	   if err := service.{{$.table.ClassName}}.Change{{$column.GoField}}(req); err != nil {
+	   if err := service.{{$.table.ClassName}}.Change{{$column.GoField}}(req,r.GetCtx()); err != nil {
 	       c.FailJsonExit(r, err.Error())
 	   } else {
 	       c.SusJsonExit(r, "状态设置成功")

+ 4 - 7
template/vm/go/dao.template

@@ -30,15 +30,12 @@ type {{.table.BusinessName | CaseCamelLower}}Dao struct {
 }
 
 var (
-	// {{.table.ClassName}} is globally public accessible object for table tools_gen_table operations.
-	{{.table.ClassName}} {{.table.BusinessName | CaseCamelLower}}Dao
+    // {{.table.ClassName}} is globally public accessible object for table tools_gen_table operations.
+    {{.table.ClassName}} = {{.table.BusinessName | CaseCamelLower}}Dao{
+        internal.New{{.table.ClassName}}Dao(),
+    }
 )
 
-func init() {
-	{{.table.ClassName}} = {{.table.BusinessName | CaseCamelLower}}Dao{
-            internal.New{{.table.ClassName}}Dao(),
-	}
-}
 
 // Fill with you ideas below.
 

+ 38 - 19
template/vm/go/dao_internal.template

@@ -9,40 +9,59 @@ package internal
 
 
 import (
-	"github.com/gogf/gf/database/gdb"
-	"github.com/gogf/gf/frame/g"
-	"github.com/gogf/gf/frame/gmvc"
+    "context"
+    "github.com/gogf/gf/database/gdb"
+    "github.com/gogf/gf/frame/g"
 )
 
 
 // {{.table.ClassName}}Dao is the manager for logic model data accessing and custom defined data operations functions management.
 type {{.table.ClassName}}Dao struct {
-	gmvc.M                      // M is the core and embedded struct that inherits all chaining operations from gdb.Model.
-	C      {{.table.BusinessName | CaseCamelLower}}Columns // C is the short type for Columns, which contains all the column names of Table for convenient usage.
-	DB     gdb.DB               // DB is the raw underlying database management object.
-	Table  string               // Table is the underlying table name of the DAO.
+    Table   string         // Table is the underlying table name of the DAO.
+    Group   string         // Group is the database configuration group name of current DAO.
+    Columns {{.table.ClassName}}Columns // Columns is the short type for Columns, which contains all the column names of Table for convenient usage.
 }
 
 
-// {{.table.BusinessName | CaseCamelLower}}Columns defines and stores column names for table {{.table.TableName}}.
-type {{.table.BusinessName | CaseCamelLower}}Columns struct {
+// {{.table.ClassName}}Columns defines and stores column names for table {{.table.TableName}}.
+type {{.table.ClassName}}Columns struct {
     {{range $index, $column := .table.Columns}}
-        {{$column.GoField}}  string  // {{$column.ColumnComment}}
+    {{$column.GoField}}  string  // {{$column.ColumnComment}}
     {{end}}
 }
 
+var {{.table.BusinessName | CaseCamelLower}}Columns = {{.table.ClassName}}Columns{
+    {{range $index, $column := .table.Columns}}
+    {{$column.GoField}}:  "{{$column.ColumnName}}",
+    {{end}}
+}
 
 // New{{.table.ClassName}}Dao creates and returns a new DAO object for table data access.
 func New{{.table.ClassName}}Dao() *{{.table.ClassName}}Dao {
-	columns := {{.table.BusinessName | CaseCamelLower}}Columns{
-	    {{range $index, $column := .table.Columns}}
-            {{$column.GoField}}:  "{{$column.ColumnName}}",
-        {{end}}
-	}
 	return &{{.table.ClassName}}Dao{
-		C:     columns,
-		M:     g.DB("default").Model("{{.table.TableName}}").Safe(),
-		DB:    g.DB("default"),
-		Table: "{{.table.TableName}}",
+        Group:    "default",
+        Table: "{{.table.TableName}}",
+        Columns:{{.table.BusinessName | CaseCamelLower}}Columns,
 	}
 }
+
+
+// DB retrieves and returns the underlying raw database management object of current DAO.
+func (dao *{{.table.ClassName}}Dao) DB() gdb.DB {
+	return g.DB(dao.Group)
+}
+
+// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
+func (dao *{{.table.ClassName}}Dao) Ctx(ctx context.Context) *gdb.Model {
+	return dao.DB().Model(dao.Table).Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note that, you should not Commit or Rollback the transaction in function f
+// as it is automatically handled by this function.
+func (dao *{{.table.ClassName}}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) {
+	return dao.Ctx(ctx).Transaction(ctx, f)
+}

+ 25 - 23
template/vm/go/service.template

@@ -10,6 +10,7 @@ package service
 
 
 import (
+    "context"
     {{if ne .table.TplCategory "tree"}}
 	comModel "gfast/app/common/model"
 	{{end}}
@@ -49,32 +50,32 @@ var {{.table.ClassName}} = new({{$structName}})
 
 // GetList 获取任务列表
 func (s *{{$structName}}) GetList(req *dao.{{.table.ClassName}}SearchReq) (total, page int, list []*model.{{.table.ClassName}}, err error) {
-	model := dao.{{.table.ClassName}}.M
+	model := dao.{{.table.ClassName}}.Ctx(req.Ctx)
 	if req != nil {
 	{{range $index, $column := .table.Columns}} {{if eq $column.IsQuery "1"}}
         {{if eq $column.QueryType "LIKE"}}
             if req.{{$column.GoField}} != "" {
-                model = model.Where(dao.{{$.table.ClassName}}.C.{{$column.GoField}}+" like ?", "%"+req.{{$column.GoField}}+"%")
+                model = model.Where(dao.{{$.table.ClassName}}.Columns.{{$column.GoField}}+" like ?", "%"+req.{{$column.GoField}}+"%")
             } {{end}}
         {{if eq $column.QueryType "EQ"}} {{if eq $column.GoType "string"}}
             if req.{{$column.GoField}} != "" {
-                model = model.Where(dao.{{$.table.ClassName}}.C.{{$column.GoField}}+" = ?", req.{{$column.GoField}})
+                model = model.Where(dao.{{$.table.ClassName}}.Columns.{{$column.GoField}}+" = ?", req.{{$column.GoField}})
             }
         {{else if and (eq $column.GoType "Time") (eq $column.ColumnName "created_at")}}
             if req.BeginTime != "" {
-                model = model.Where(dao.{{$.table.ClassName}}.C.{{$column.GoField}}+" >=", req.BeginTime)
+                model = model.Where(dao.{{$.table.ClassName}}.Columns.{{$column.GoField}}+" >=", req.BeginTime)
             }
             if req.EndTime != "" {
-                model = model.Where(dao.{{$.table.ClassName}}.C.{{$column.GoField}}+" <", req.EndTime)
+                model = model.Where(dao.{{$.table.ClassName}}.Columns.{{$column.GoField}}+" <", req.EndTime)
             }
         {{else if or (eq $column.GoType "int") (eq $column.GoType "int64") (eq $column.GoType "uint") (eq $column.GoType "uint64") }}
             if req.{{$column.GoField}} != "" {
-                model = model.Where(dao.{{$.table.ClassName}}.C.{{$column.GoField}}+" = ?", req.{{$column.GoField}})
+                model = model.Where(dao.{{$.table.ClassName}}.Columns.{{$column.GoField}}+" = ?", req.{{$column.GoField}})
             }
         {{end}} {{end}}
         {{if and (eq $column.QueryType "BETWEEN") (eq $column.ColumnType "datetime") }}
             if req.{{$column.GoField}} != nil {
-                model = model.Where(dao.{{$.table.ClassName}}.C.{{$column.GoField}}+" >= ? AND "+dao.{{$.table.ClassName}}.C.{{$column.GoField}}+" < ?", req.{{$column.GoField}}, req.{{$column.GoField}}.Add(gtime.D))
+                model = model.Where(dao.{{$.table.ClassName}}.Columns.{{$column.GoField}}+" >= ? AND "+dao.{{$.table.ClassName}}.Columns.{{$column.GoField}}+" < ?", req.{{$column.GoField}}, req.{{$column.GoField}}.Add(gtime.D))
             }
         {{end}}
     {{end}}{{end}}
@@ -114,12 +115,12 @@ func (s *{{$structName}}) GetList(req *dao.{{.table.ClassName}}SearchReq) (total
 
 
 // GetInfoById 通过id获取
-func (s *{{$structName}}) GetInfoById(id int64) (info *model.{{.table.ClassName}}, err error) {
+func (s *{{$structName}}) GetInfoById(id int64,ctx context.Context) (info *model.{{.table.ClassName}}, err error) {
 	if id == 0 {
 		err = gerror.New("参数错误")
 		return
 	}
-	err = dao.{{.table.ClassName}}.Where(dao.{{.table.ClassName}}.C.{{$pkGoField}}, id).Scan(&info)
+	err = dao.{{.table.ClassName}}.Ctx(ctx).Where(dao.{{.table.ClassName}}.Columns.{{$pkGoField}}, id).Scan(&info)
 	if err != nil {
 		g.Log().Error(err)
 	}
@@ -130,35 +131,36 @@ func (s *{{$structName}}) GetInfoById(id int64) (info *model.{{.table.ClassName}
 }
 
 // Add 添加
-func (s *{{$structName}}) Add(req *dao.{{.table.ClassName}}AddReq) (err error) {
-	_, err = dao.{{.table.ClassName}}.Insert(req)
+func (s *{{$structName}}) Add(req *dao.{{.table.ClassName}}AddReq,ctx context.Context) (err error) {
+	_, err = dao.{{.table.ClassName}}.Ctx(ctx).Insert(req)
 	return
 }
 
 // Edit 修改
-func (s *{{$structName}}) Edit(req *dao.{{.table.ClassName}}EditReq) error {
-    {{ $fieldsEx:= concat "dao." $.table.ClassName ".C." $pkGoField }}
+func (s *{{$structName}}) Edit(req *dao.{{.table.ClassName}}EditReq,ctx context.Context) error {
+    {{ $fieldsEx:= concat "dao." $.table.ClassName ".Columns." $pkGoField }}
     {{if ne $createdAt ""}}
-        {{$fieldsEx = concat "dao." $.table.ClassName ".C." $pkGoField  "," "dao." $.table.ClassName ".C." $createdAtGoField}}
+        {{$fieldsEx = concat "dao." $.table.ClassName ".Columns." $pkGoField  "," "dao." $.table.ClassName ".Columns." $createdAtGoField}}
     {{end}}
-	_, err := dao.{{.table.ClassName}}.FieldsEx({{$fieldsEx}}).Where(dao.{{.table.ClassName}}.C.{{$pkGoField}}, req.{{$pkGoField}}).
+	_, err := dao.{{.table.ClassName}}.Ctx(ctx).FieldsEx({{$fieldsEx}}).Where(dao.{{.table.ClassName}}.Columns.{{$pkGoField}}, req.{{$pkGoField}}).
 		Update(req)
 	return err
 }
 
 
 // DeleteByIds 删除
-func (s *{{$structName}}) DeleteByIds(ids []int) (err error) {
+func (s *{{$structName}}) DeleteByIds(ids []int,ctx context.Context) (err error) {
 	if len(ids) == 0 {
 		err = gerror.New("参数错误")
+		return
 	}
 	{{if eq .table.TplCategory "tree"}}
-    ids, err = s.GetChildrenIds(ids)
+    ids, err = s.GetChildrenIds(ids,ctx)
     if err != nil {
         return
     }
 	{{end}}
-	_, err = dao.{{.table.ClassName}}.Delete(dao.{{.table.ClassName}}.C.{{$pkGoField}}+" in (?)", ids)
+	_, err = dao.{{.table.ClassName}}.Ctx(ctx).Delete(dao.{{.table.ClassName}}.Columns.{{$pkGoField}}+" in (?)", ids)
 	if err != nil {
 		g.Log().Error(err)
 		err = gerror.New("删除失败")
@@ -170,9 +172,9 @@ func (s *{{$structName}}) DeleteByIds(ids []int) (err error) {
 {{range $index,$column:= .table.Columns}}
 {{if and (HasSuffix $column.ColumnName "status") (eq $column.IsList "1") }}
 // Change{{$column.GoField}} 修改状态
-func (s *{{$structName}}) Change{{$column.GoField}}(req *dao.{{$.table.ClassName}}{{$column.GoField}}Req) error {
-	_, err := dao.{{$.table.ClassName}}.WherePri(req.{{$pkGoField}}).Update(g.Map{
-		dao.{{$.table.ClassName}}.C.{{$column.GoField}}: req.{{$column.GoField}},
+func (s *{{$structName}}) Change{{$column.GoField}}(req *dao.{{$.table.ClassName}}{{$column.GoField}}Req,ctx context.Context) error {
+	_, err := dao.{{$.table.ClassName}}.Ctx(ctx).WherePri(req.{{$pkGoField}}).Update(g.Map{
+		dao.{{$.table.ClassName}}.Columns.{{$column.GoField}}: req.{{$column.GoField}},
 	})
 	return err
 }
@@ -181,9 +183,9 @@ func (s *{{$structName}}) Change{{$column.GoField}}(req *dao.{{$.table.ClassName
 
 {{if eq .table.TplCategory "tree"}}
 // GetChildrenIds 通过ID获取子级ID
-func (s *{{$structName}})GetChildrenIds(ids []int) ([]int, error) {
+func (s *{{$structName}})GetChildrenIds(ids []int,ctx context.Context) ([]int, error) {
 	//获取所有
-	_,_,all, err := s.GetList(new(dao.{{.table.ClassName}}SearchReq))
+	_,_,all, err := s.GetList(&dao.{{.table.ClassName}}SearchReq{Ctx:ctx})
 	if err != nil {
 		return nil, err
 	}

+ 173 - 7
template/vm/vue/list-vue.template

@@ -102,9 +102,17 @@
       {{else if and (eq $column.IsList "1") (eq $column.HtmlType "datetime")}}
       <el-table-column label="{{$column.ColumnComment}}" align="center" prop="{{$column.HtmlField}}" width="180">
         <template slot-scope="scope">
-            <span>{{VueTag "{{"}} parseTime(scope.row.{{$column.HtmlField}}, {y}-{m}-{d}) {{VueTag "}}"}}</span>
+            <span>{{VueTag "{{"}} parseTime(scope.row.{{$column.HtmlField}}, '{y}-{m}-{d}') {{VueTag "}}"}}</span>
         </template>
       </el-table-column>
+      {{else if and (eq $column.IsList "1") (eq $column.HtmlType "imagefile")}}
+      <el-table-column align="center" label="缩略图" width="100">
+      <template slot-scope="scope">
+        <img v-if="scope.row.{{$column.HtmlField}}!=''" :src="scope.row.{{$column.HtmlField}}|urlCorrection"
+             style="width: 150px; "
+        >
+      </template>
+      </el-table-column>
       {{else if and (eq $column.IsList "1") (HasSuffix $column.ColumnName "status")}}
       <el-table-column label="{{$column.ColumnComment}}" align="center">
         <template slot-scope="scope">
@@ -156,7 +164,7 @@
     />
 
     <!-- 添加或修改{{.table.FunctionName}}对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body :close-on-click-modal="false">
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body :close-on-click-modal="false">
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
       {{range $index, $column := .table.Columns}}
       {{if and (eq $column.IsInsert "1") (ne $column.IsPk "1")}}
@@ -227,7 +235,7 @@
        </el-form-item>
        {{else if eq $column.HtmlType "textarea"}}
        <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}">
-           <el-input v-model="form.{{$column.HtmlField}}" type="textarea" placeholder="请输入内容" />
+           <el-input v-model="form.{{$column.HtmlField}}" type="textarea" placeholder="请输入{{$column.ColumnComment}}" />
        </el-form-item>
        {{else if eq $column.HtmlType "checkbox" }}
         <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}">
@@ -239,6 +247,38 @@
               >{{ VueTag "{{" }}dict.value {{VueTag "}}"}}</el-checkbox>
            </el-checkbox-group>
         </el-form-item>
+       {{else if eq $column.HtmlType "richtext"}}
+        <el-form-item label="{{$column.ColumnComment}}">
+          <Editor ref="cke" v-model="form.{{$column.HtmlField}}" @setEditContent="set{{$column.GoField}}EditContent"/>
+        </el-form-item>
+       {{else if eq $column.HtmlType "imagefile"}}
+       <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}">
+         <el-upload
+           v-loading="upLoading{{$column.GoField}}"
+           :action="apiUrl+'/system/upload/upImg'"
+           :before-upload="beforeAvatarUpload{{$column.GoField}}"
+           :data="setUpData()"
+           :on-success="handleAvatarSuccess{{$column.GoField}}"
+           :show-file-list="false"
+           class="avatar-uploader"
+           name="file"
+         >
+           <img v-if="imageUrl{{$column.GoField}}" :src="imageUrl{{$column.GoField}}" class="avatar">
+           <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+         </el-upload>
+       </el-form-item>
+       {{else if eq $column.HtmlType "images"}}
+       <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}" >
+         <up-imgs :action="apiUrl+'/system/upload/upImg'" v-model="form.{{$column.HtmlField}}" @setUpImgList="setUpImgList{{$column.GoField}}"></up-imgs>
+       </el-form-item>
+       {{else if eq $column.HtmlType "file"}}
+        <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}" >
+        <up-file :action="apiUrl+'/system/upload/upFile'" v-model="form.{{$column.HtmlField}}" @setUpFielList="setUpFileList{{$column.GoField}}" :limit="1"></up-file>
+        </el-form-item>
+        {{else if eq $column.HtmlType "files"}}
+        <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}" >
+        <up-file :action="apiUrl+'/system/upload/upFile'" v-model="form.{{$column.HtmlField}}" @setUpFielList="setUpFileList{{$column.GoField}}" :limit="10"></up-file>
+        </el-form-item>
        {{end}}
        {{end}}
        {{end}}
@@ -268,7 +308,48 @@ import {
     {{end}}
 } from "@/api/{{.table.ModuleName}}/{{$businessName}}";
 
+{{$tokenImp := false}}
+{{$imgsImp := false}}
+{{$editImp := false }}
+{{$fileImp := false}}
+
+{{range $index,$column:=.table.Columns}}
+{{if eq $column.HtmlType "richtext"}}
+{{$editImp = true}}
+{{else if eq $column.HtmlType "imagefile"}}
+{{$tokenImp = true}}
+{{else if eq $column.HtmlType "images"}}
+{{$imgsImp = true}}
+{{else if eq $column.HtmlType "file" "files"}}
+{{$fileImp = true}}
+{{end}}
+{{end}}
+
+{{if $editImp}}
+import Editor from "@/components/CKeditor";
+{{end}}
+{{if $tokenImp}}
+import { getToken } from '@/utils/auth'
+{{end}}
+{{if $imgsImp}}
+import upImgs from "@/components/upImgs"
+{{end}}
+{{if $fileImp}}
+import upFile from "@/components/upfile"
+{{end}}
+
 export default {
+  components:{
+    {{if $editImp}}
+    Editor,
+    {{end}}
+    {{if $imgsImp}}
+    upImgs,
+    {{end}}
+    {{if $fileImp}}
+    upFile,
+    {{end}}
+  },
   name: "{{.table.ClassName}}",
   data() {
     return {
@@ -295,6 +376,11 @@ export default {
       {{else if ne $column.LinkTableName ""}}
       // {{$column.HtmlField}}Options关联表数据
       {{$column.HtmlField}}Options: [],
+      {{else if eq $column.HtmlType "imagefile"}}
+       //图片上传地址
+       imageUrl{{$column.GoField}}: '',
+       //上传加载
+       upLoading{{$column.GoField}}: false,
       {{end}}
       {{end}}
       // 查询参数
@@ -327,6 +413,7 @@ export default {
     this.getList();
   },
   methods: {
+    {{$setUpData:=true}}
     {{range $index, $column := .table.Columns}}
     {{if ne $column.LinkTableName ""}}
     //关联{{$column.LinkTableName}}表选项
@@ -335,6 +422,40 @@ export default {
         this.{{$column.HtmlField}}Options = this.setItems(res, '{{$column.LinkLabelId}}', '{{$column.LinkLabelName}}')
       })
     },
+    {{else if eq $column.HtmlType "richtext"}}
+    //富文本编辑器{{$column.ColumnComment}}
+    set{{$column.GoField}}EditContent(data) {
+      this.form.{{$column.HtmlField}} = data
+    },
+    {{else if eq $column.HtmlType "imagefile"}}
+    //单图上传{{$column.ColumnComment}}
+    handleAvatarSuccess{{$column.GoField}}(res, file) {
+      if (res.code === 0) {
+        this.imageUrl{{$column.GoField}} = URL.createObjectURL(file.raw)
+        this.form.{{$column.HtmlField}} = res.data.fileInfo.fileUrl
+      } else {
+        this.msgError(res.msg)
+      }
+      this.upLoading{{$column.GoField}} = false
+    },
+    beforeAvatarUpload{{$column.GoField}}(file) {
+      this.upLoading{{$column.GoField}} = true
+      return true
+    },
+    {{if $setUpData}}
+    setUpData() {
+      return { token: getToken() }
+    },
+    {{$setUpData = false}}
+    {{end}}
+    {{else if eq $column.HtmlType "images"}}
+    setUpImgList{{$column.GoField}}(data){
+        this.form.{{$column.HtmlField}} = data
+    },
+    {{else if eq $column.HtmlType "file" "files"}}
+    setUpFileList{{$column.GoField}}(data){
+        this.form.{{$column.HtmlField}} = data
+    },
     {{else if and (HasSuffix $column.ColumnName "status") (eq $column.IsList "1")}}
     // {{$column.ColumnComment}}修改
     {{$column.HtmlField}}Change(row) {
@@ -397,12 +518,19 @@ export default {
       this.form = {
         {{range $index, $column := .table.Columns}}
         {{if eq $column.HtmlType "radio"}}
-        {{$column.HtmlField}}: "0" {{if ne $lens $index}},{{end}}
+        {{$column.HtmlField}}: "0" ,
         {{else if eq $column.HtmlType "checkbox"}}
-        {{$column.HtmlField}}: [] {{if ne $lens $index}},{{end}}
+        {{$column.HtmlField}}: [] ,
         {{else}}
-        {{$column.HtmlField}}: undefined{{if ne $lens $index}},{{end}}{{end}}{{end}}
+        {{$column.HtmlField}}: undefined,
+        {{end}}
+        {{end}}
       };
+      {{range $index, $column := .table.Columns}}
+      {{if eq $column.HtmlType "imagefile"}}
+      this.imageUrl{{$column.GoField}} = ''
+      {{end}}
+      {{end}}
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -438,6 +566,11 @@ export default {
         data.{{$column.HtmlField}} = data.{{$column.HtmlField}}.split(",")
         {{else if eq $column.HtmlType "radio" "select"}}
         data.{{$column.HtmlField}} = ''+data.{{$column.HtmlField}}
+        {{else if eq $column.HtmlType "imagefile"}}
+        //单图地址赋值
+        this.imageUrl{{$column.GoField}} = data.{{$column.HtmlField}} ? this.getUpFileUrl(process.env.VUE_APP_BASE_API,data.{{$column.HtmlField}}) : ''
+        {{else if eq $column.HtmlType "images" "file" "files"}}
+        data.{{$column.HtmlField}} =data.{{$column.HtmlField}}?JSON.parse(data.{{$column.HtmlField}}):[]
         {{end}}
         {{end}}
         this.form = data;
@@ -489,4 +622,37 @@ export default {
     }
   }
 };
-</script>
+</script>
+
+<style>
+{{range $index,$column:=.table.Columns}}
+{{if eq $column.HtmlType "imagefile"}}
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+
+.avatar-uploader .el-upload:hover {
+  border-color: #409EFF;
+}
+
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+{{end}}
+{{end}}
+</style>

+ 171 - 5
template/vm/vue/tree-vue.template

@@ -110,9 +110,17 @@
       {{else if and (eq $column.IsList "1") (eq $column.HtmlType "datetime")}}
       <el-table-column label="{{$column.ColumnComment}}" align="center" prop="{{$column.HtmlField}}" width="180">
         <template slot-scope="scope">
-            <span>{{VueTag "{{"}} parseTime(scope.row.{{$column.HtmlField}}, {y}-{m}-{d}) {{VueTag "}}"}}</span>
+            <span>{{VueTag "{{"}} parseTime(scope.row.{{$column.HtmlField}}, '{y}-{m}-{d}') {{VueTag "}}"}}</span>
         </template>
       </el-table-column>
+      {{else if and (eq $column.IsList "1") (eq $column.HtmlType "imagefile")}}
+      <el-table-column align="center" label="缩略图" width="100">
+      <template slot-scope="scope">
+        <img v-if="scope.row.{{$column.HtmlField}}!=''" :src="scope.row.{{$column.HtmlField}}|urlCorrection"
+             style="width: 150px; "
+        >
+      </template>
+      </el-table-column>
       {{else if and (eq $column.IsList "1") (HasSuffix $column.ColumnName "status")}}
       <el-table-column label="{{$column.ColumnComment}}" align="center">
         <template slot-scope="scope">
@@ -156,7 +164,7 @@
     </el-table>
 
     <!-- 添加或修改{{.table.FunctionName}}对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body :close-on-click-modal="false">
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body :close-on-click-modal="false">
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
       {{range $index, $column := .table.Columns}}
       {{if and (eq $column.IsInsert "1") (ne $column.IsPk "1")}}
@@ -231,7 +239,7 @@
        </el-form-item>
        {{else if eq $column.HtmlType "textarea"}}
        <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}">
-           <el-input v-model="form.{{$column.HtmlField}}" type="textarea" placeholder="请输入内容" />
+           <el-input v-model="form.{{$column.HtmlField}}" type="textarea" placeholder="请输入{{$column.ColumnComment}}" />
        </el-form-item>
        {{else if eq $column.HtmlType "checkbox" }}
         <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}">
@@ -243,6 +251,38 @@
               >{{ VueTag "{{" }}dict.value {{VueTag "}}"}}</el-checkbox>
            </el-checkbox-group>
         </el-form-item>
+        {{else if eq $column.HtmlType "richtext"}}
+        <el-form-item label="{{$column.ColumnComment}}">
+          <Editor ref="cke" v-model="form.{{$column.HtmlField}}" @setEditContent="set{{$column.GoField}}EditContent"/>
+        </el-form-item>
+       {{else if eq $column.HtmlType "imagefile"}}
+       <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}">
+         <el-upload
+           v-loading="upLoading{{$column.GoField}}"
+           :action="apiUrl+'/system/upload/upImg'"
+           :before-upload="beforeAvatarUpload{{$column.GoField}}"
+           :data="setUpData()"
+           :on-success="handleAvatarSuccess{{$column.GoField}}"
+           :show-file-list="false"
+           class="avatar-uploader"
+           name="file"
+         >
+           <img v-if="imageUrl{{$column.GoField}}" :src="imageUrl{{$column.GoField}}" class="avatar">
+           <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+         </el-upload>
+       </el-form-item>
+       {{else if eq $column.HtmlType "images"}}
+       <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}" >
+         <up-imgs :action="apiUrl+'/system/upload/upImg'" v-model="form.{{$column.HtmlField}}" @setUpImgList="setUpImgList{{$column.GoField}}"></up-imgs>
+       </el-form-item>
+       {{else if eq $column.HtmlType "file"}}
+        <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}" >
+        <up-file :action="apiUrl+'/system/upload/upFile'" v-model="form.{{$column.HtmlField}}" @setUpFielList="setUpFileList{{$column.GoField}}" :limit="1"></up-file>
+        </el-form-item>
+        {{else if eq $column.HtmlType "files"}}
+        <el-form-item label="{{$column.ColumnComment}}" prop="{{$column.HtmlField}}" >
+        <up-file :action="apiUrl+'/system/upload/upFile'" v-model="form.{{$column.HtmlField}}" @setUpFielList="setUpFileList{{$column.GoField}}" :limit="10"></up-file>
+        </el-form-item>
        {{end}}
        {{end}}
        {{end}}
@@ -274,9 +314,51 @@ import {
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 
+{{$tokenImp := false}}
+{{$imgsImp := false}}
+{{$editImp := false }}
+{{$fileImp := false}}
+
+{{range $index,$column:=.table.Columns}}
+{{if eq $column.HtmlType "richtext"}}
+{{$editImp = true}}
+{{else if eq $column.HtmlType "imagefile"}}
+{{$tokenImp = true}}
+{{else if eq $column.HtmlType "images"}}
+{{$imgsImp = true}}
+{{else if eq $column.HtmlType "file" "files"}}
+{{$fileImp = true}}
+{{end}}
+{{end}}
+
+{{if $editImp}}
+import Editor from "@/components/CKeditor";
+{{end}}
+{{if $tokenImp}}
+import { getToken } from '@/utils/auth'
+{{end}}
+{{if $imgsImp}}
+import upImgs from "@/components/upImgs"
+{{end}}
+{{if $fileImp}}
+import upFile from "@/components/upfile"
+{{end}}
+
+
 export default {
   name: "{{.table.ClassName}}",
-  components: { Treeselect },
+  components: {
+    Treeselect,
+    {{if $editImp}}
+    Editor,
+    {{end}}
+    {{if $imgsImp}}
+    upImgs,
+    {{end}}
+    {{if $fileImp}}
+    upFile,
+    {{end}}
+  },
   data() {
     return {
       // 遮罩层
@@ -304,6 +386,11 @@ export default {
       {{else if ne $column.LinkTableName ""}}
       // {{$column.HtmlField}}Options关联表数据
       {{$column.HtmlField}}Options: [],
+      {{else if eq $column.HtmlType "imagefile"}}
+       //图片上传地址
+       imageUrl{{$column.GoField}}: '',
+       //上传加载
+       upLoading{{$column.GoField}}: false,
       {{end}}
       {{end}}
       // 查询参数
@@ -336,6 +423,7 @@ export default {
     this.getList();
   },
   methods: {
+    {{$setUpData:=true}}
     {{range $index, $column := .table.Columns}}
     {{if ne $column.LinkTableName ""}}
     //关联{{$column.LinkTableName}}表选项
@@ -344,6 +432,40 @@ export default {
         this.{{$column.HtmlField}}Options = this.setItems(res, '{{$column.LinkLabelId}}', '{{$column.LinkLabelName}}')
       })
     },
+    {{else if eq $column.HtmlType "richtext"}}
+    //富文本编辑器{{$column.ColumnComment}}
+    set{{$column.GoField}}EditContent(data) {
+      this.form.{{$column.HtmlField}} = data
+    },
+    {{else if eq $column.HtmlType "imagefile"}}
+    //单图上传{{$column.ColumnComment}}
+    handleAvatarSuccess{{$column.GoField}}(res, file) {
+      if (res.code === 0) {
+        this.imageUrl{{$column.GoField}} = URL.createObjectURL(file.raw)
+        this.form.{{$column.HtmlField}} = res.data.fileInfo.fileUrl
+      } else {
+        this.msgError(res.msg)
+      }
+      this.upLoading{{$column.GoField}} = false
+    },
+    beforeAvatarUpload{{$column.GoField}}(file) {
+      this.upLoading{{$column.GoField}} = true
+      return true
+    },
+    {{if $setUpData}}
+    setUpData() {
+      return { token: getToken() }
+    },
+    {{$setUpData = false}}
+    {{end}}
+    {{else if eq $column.HtmlType "images"}}
+    setUpImgList{{$column.GoField}}(data){
+        this.form.{{$column.HtmlField}} = data
+    },
+    {{else if eq $column.HtmlType "file" "files"}}
+    setUpFileList{{$column.GoField}}(data){
+        this.form.{{$column.HtmlField}} = data
+    },
     {{else if and (HasSuffix $column.ColumnName "status") (eq $column.IsList "1")}}
     // {{$column.ColumnComment}}修改
     {{$column.HtmlField}}Change(row) {
@@ -431,6 +553,11 @@ export default {
         {{else}}
         {{$column.HtmlField}}: undefined{{if ne $lens $index}},{{end}}{{end}}{{end}}
       };
+      {{range $index, $column := .table.Columns}}
+      {{if eq $column.HtmlType "imagefile"}}
+      this.imageUrl{{$column.GoField}} = ''
+      {{end}}
+      {{end}}
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -468,6 +595,11 @@ export default {
         data.{{$column.HtmlField}} = data.{{$column.HtmlField}}.split(",")
         {{else if eq $column.HtmlType "radio" "select"}}
         data.{{$column.HtmlField}} = ''+data.{{$column.HtmlField}}
+        {{else if eq $column.HtmlType "imagefile"}}
+        //单图地址赋值
+        this.imageUrl{{$column.GoField}} = data.{{$column.HtmlField}} ? this.getUpFileUrl(process.env.VUE_APP_BASE_API,data.{{$column.HtmlField}}) : ''
+        {{else if eq $column.HtmlType "images" "file" "files"}}
+        data.{{$column.HtmlField}} =data.{{$column.HtmlField}}?JSON.parse(data.{{$column.HtmlField}}):[]
         {{end}}
         {{end}}
         this.form = data;
@@ -519,4 +651,38 @@ export default {
     }
   }
 };
-</script>
+</script>
+
+
+<style>
+{{range $index,$column:=.table.Columns}}
+{{if eq $column.HtmlType "imagefile"}}
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+
+.avatar-uploader .el-upload:hover {
+  border-color: #409EFF;
+}
+
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+
+.avatar {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+{{end}}
+{{end}}
+</style>