gorm wire
This commit is contained in:
61
internal/pkg/render/funcs/method.go
Normal file
61
internal/pkg/render/funcs/method.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package funcs
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Methods() map[string]any {
|
||||
res := make(map[string]any, 1)
|
||||
|
||||
res["dateFormat"] = func(dt time.Time) template.HTML {
|
||||
return template.HTML(dt.Format(time.DateTime))
|
||||
}
|
||||
|
||||
res["today"] = func() template.HTML {
|
||||
return template.HTML(time.Now().Format("2006-01-02"))
|
||||
}
|
||||
|
||||
res["threeMonth"] = func() template.HTML {
|
||||
return template.HTML(time.Now().AddDate(0, 3, 0).Format("2006-01-02"))
|
||||
}
|
||||
|
||||
res["yearBegin"] = func() template.HTML {
|
||||
dt := time.Now()
|
||||
t := dt.AddDate(0, -int(dt.Month())+1, -dt.Day()+1)
|
||||
return template.HTML(t.Format("2006-01-02") + " 00:00:00")
|
||||
}
|
||||
|
||||
res["monthBegin"] = func() template.HTML {
|
||||
dt := time.Now()
|
||||
t := dt.AddDate(0, 0, -dt.Day()+1)
|
||||
return template.HTML(t.Format("2006-01-02") + " 00:00:00")
|
||||
}
|
||||
|
||||
res["monthEnd"] = func() template.HTML {
|
||||
dt := time.Now()
|
||||
t := dt.AddDate(0, 0, -dt.Day()+1).AddDate(0, 1, -1)
|
||||
return template.HTML(t.Format("2006-01-02") + " 23:59:59")
|
||||
}
|
||||
|
||||
res["trimSpace"] = func(s string) template.HTML {
|
||||
return template.HTML(strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
res["expandTags"] = func(s []string) template.HTML {
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(s) == 1 && s[0] == "all" {
|
||||
return ""
|
||||
}
|
||||
return template.HTML(strings.Join(s, ","))
|
||||
}
|
||||
|
||||
res["toString"] = func(b []byte) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
99
internal/pkg/render/gen/btn.go
Normal file
99
internal/pkg/render/gen/btn.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
)
|
||||
|
||||
func Button() map[string]any {
|
||||
res := make(map[string]any, 3)
|
||||
|
||||
res["genBtn"] = func(buttons []*dto.OwnerMenuDto, actionNames ...string) template.HTML {
|
||||
if len(buttons) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var res string
|
||||
for _, action := range actionNames {
|
||||
for _, btn := range buttons {
|
||||
btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui")
|
||||
base := filepath.Base(btn.Url)
|
||||
if base == action {
|
||||
res += `<button type="button" class="layui-btn ` + btn.Style + `" lay-event="` + firstLower(action) + `" lay-on="` + firstLower(action) + `">`
|
||||
if len(btn.Avatar) > 0 {
|
||||
res += `<i class="` + btn.Avatar + `"></i> `
|
||||
}
|
||||
res += btn.DisplayName + `</button>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return template.HTML(res)
|
||||
}
|
||||
|
||||
res["genLink"] = func(buttons []*dto.OwnerMenuDto, actionNames ...string) template.HTML {
|
||||
if len(buttons) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var res string
|
||||
for _, action := range actionNames {
|
||||
for _, btn := range buttons {
|
||||
btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui")
|
||||
base := filepath.Base(btn.Url)
|
||||
if base == action {
|
||||
res += `<button type="button" style="font-size:12px !important;" class="layui-btn ` + btn.Style + `" lay-event="` + firstLower(action) + `" lay-on="` + firstLower(action) + `">`
|
||||
if len(btn.Avatar) > 0 {
|
||||
res += `<i class="` + btn.Avatar + `"></i> `
|
||||
}
|
||||
res += btn.DisplayName + `</button>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return template.HTML(res)
|
||||
}
|
||||
|
||||
res["previewPicture"] = func(name string) template.HTML {
|
||||
var res string
|
||||
res += `<a><img src="https://school-1251542740.cos.ap-shanghai.myqcloud.com{{` + name + `}}" data-type="1" height="30" width="30" class="preview-all screenshot" onclick="previewPicture('https://school-1251542740.cos.ap-shanghai.myqcloud.com/{{` + name + `}}')"/></a>`
|
||||
|
||||
return template.HTML(res)
|
||||
}
|
||||
|
||||
res["submitBtn"] = func(buttons []*dto.OwnerMenuDto, actionNames ...string) template.HTML {
|
||||
if len(buttons) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var res string
|
||||
for _, action := range actionNames {
|
||||
for _, btn := range buttons {
|
||||
btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui")
|
||||
base := filepath.Base(btn.Url)
|
||||
if base == action {
|
||||
res += `<button type="submit" class="layui-btn ` + btn.Style + `" lay-submit lay-filter="` + firstLower(action) + `">`
|
||||
if len(btn.Avatar) > 0 {
|
||||
res += `<i class="` + btn.Avatar + `"></i> `
|
||||
}
|
||||
res += btn.DisplayName + `</button>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return template.HTML(res)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func firstLower(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
return strings.ToLower(s[:1]) + s[1:]
|
||||
}
|
||||
106
internal/pkg/render/html.go
Normal file
106
internal/pkg/render/html.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package render
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/pkg/know"
|
||||
|
||||
"github.com/justinas/nosurf"
|
||||
)
|
||||
|
||||
type TemplateConfig struct {
|
||||
Root string
|
||||
Extension string
|
||||
Layout string
|
||||
Partial string
|
||||
}
|
||||
|
||||
type HtmlData struct {
|
||||
IsAuthenticated bool
|
||||
AuthorizeUser dto.AuthorizeUser
|
||||
AuthorizeMenus []*dto.OwnerMenuDto
|
||||
Data any
|
||||
}
|
||||
|
||||
func (r *render) HTML(w http.ResponseWriter, req *http.Request, tpl string, data map[string]any) {
|
||||
name := strings.ReplaceAll(tpl, "/", "_")
|
||||
t, ok := r.templates[name]
|
||||
if !ok {
|
||||
http.Error(w, "template is empty", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
hd := r.setDefaultData(req, data)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := t.ExecuteTemplate(buf, filepath.Base(tpl), hd)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = buf.WriteTo(w)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *render) setDefaultData(req *http.Request, data map[string]any) map[string]any {
|
||||
if data == nil {
|
||||
data = make(map[string]any)
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
isAuth := r.session.Exists(ctx, know.StoreName)
|
||||
data["IsAuthenticated"] = isAuth
|
||||
if isAuth {
|
||||
var authUser dto.AuthorizeUser
|
||||
u := r.session.GetBytes(ctx, know.StoreName)
|
||||
_ = json.Unmarshal(u, &authUser)
|
||||
|
||||
data["AuthorizeMenus"] = r.getCurrentPathButtons(ctx, authUser.RoleID, req.URL.Path)
|
||||
}
|
||||
token := nosurf.Token(req)
|
||||
data["CsrfToken"] = token
|
||||
data["CsrfTokenField"] = template.HTML(fmt.Sprintf(`<input type="hidden" name="csrf_token" value="%s" />`, token))
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func (r *render) getCurrentPathButtons(ctx context.Context, roleID int32, path string) []*dto.OwnerMenuDto {
|
||||
var res []*dto.OwnerMenuDto
|
||||
|
||||
// 获取当前登陆角色的权限
|
||||
menus, err := r.menuService.ListByRoleIDToMap(ctx, roleID)
|
||||
if err != nil {
|
||||
return res
|
||||
}
|
||||
|
||||
menu, ok := menus[path]
|
||||
if !ok {
|
||||
return res
|
||||
}
|
||||
|
||||
for _, item := range menus {
|
||||
if menu.IsList {
|
||||
if item.ParentID == menu.ID || item.ID == menu.ID {
|
||||
res = append(res, item)
|
||||
}
|
||||
} else {
|
||||
if item.ParentID == menu.ParentID {
|
||||
res = append(res, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
17
internal/pkg/render/html/select.go
Normal file
17
internal/pkg/render/html/select.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package html
|
||||
|
||||
type SelectDict struct {
|
||||
Name string
|
||||
Value string
|
||||
Selected bool
|
||||
}
|
||||
|
||||
func NewSelectControls(data []*SelectDict, value string) []*SelectDict {
|
||||
for _, item := range data {
|
||||
item.Selected = false
|
||||
if item.Value == value {
|
||||
item.Selected = true
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
19
internal/pkg/render/html/vars.go
Normal file
19
internal/pkg/render/html/vars.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package html
|
||||
|
||||
var SearchStatuses = []*SelectDict{
|
||||
{Name: "状态", Value: "9999", Selected: false},
|
||||
{Name: "正常", Value: "0", Selected: false},
|
||||
{Name: "删除", Value: "-1", Selected: false},
|
||||
}
|
||||
|
||||
var Statuses = []*SelectDict{
|
||||
{Name: "正常", Value: "0", Selected: false},
|
||||
{Name: "删除", Value: "-1", Selected: false},
|
||||
}
|
||||
|
||||
var ProjectStatuses = []*SelectDict{
|
||||
{Name: "未开始", Value: "0", Selected: false},
|
||||
{Name: "进行中", Value: "10", Selected: false},
|
||||
{Name: "已结项", Value: "20", Selected: false},
|
||||
{Name: "已删除", Value: "-1", Selected: false},
|
||||
}
|
||||
57
internal/pkg/render/json.go
Normal file
57
internal/pkg/render/json.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package render
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"msg"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
type ResponseTree struct {
|
||||
Status ResponseTreeStatus `json:"status"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
type ResponseTreeStatus struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type ResponseList struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"msg"`
|
||||
Count int64 `json:"count"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
func (r *render) JSONOk(w http.ResponseWriter, message string) {
|
||||
r.JSON(w, Response{Success: true, Message: message})
|
||||
}
|
||||
|
||||
func (r *render) JSONErr(w http.ResponseWriter, message string) {
|
||||
r.JSON(w, Response{Success: false, Message: message})
|
||||
}
|
||||
|
||||
func (r *render) JSONObj(w http.ResponseWriter, message string, data any) {
|
||||
r.JSON(w, Response{Success: true, Message: message, Data: data})
|
||||
}
|
||||
|
||||
func (r *render) JSON(w http.ResponseWriter, data any) {
|
||||
v, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err = w.Write(v)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
60
internal/pkg/render/render.go
Normal file
60
internal/pkg/render/render.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package render
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
v1 "management/internal/erpserver/service/v1"
|
||||
"management/internal/pkg/render/util"
|
||||
"management/internal/pkg/session"
|
||||
)
|
||||
|
||||
type Render interface {
|
||||
htmlRender
|
||||
jsonRender
|
||||
}
|
||||
|
||||
type htmlRender interface {
|
||||
HTML(w http.ResponseWriter, req *http.Request, name string, data map[string]any)
|
||||
}
|
||||
|
||||
type jsonRender interface {
|
||||
JSON(w http.ResponseWriter, data any)
|
||||
JSONObj(w http.ResponseWriter, message string, data any)
|
||||
JSONOk(w http.ResponseWriter, message string)
|
||||
JSONErr(w http.ResponseWriter, message string)
|
||||
}
|
||||
|
||||
type render struct {
|
||||
templateConfig *TemplateConfig
|
||||
templates map[string]*template.Template
|
||||
session session.Session
|
||||
|
||||
menuService v1.MenuService
|
||||
}
|
||||
|
||||
func New(session session.Session, menuService v1.MenuService) (Render, error) {
|
||||
r := &render{
|
||||
templateConfig: &TemplateConfig{
|
||||
Root: ".",
|
||||
Extension: ".tmpl",
|
||||
Layout: "base",
|
||||
Partial: "partial",
|
||||
},
|
||||
session: session,
|
||||
menuService: menuService,
|
||||
}
|
||||
|
||||
var err error
|
||||
r.templates, err = util.CreateTemplateCache(
|
||||
r.templateConfig.Root,
|
||||
r.templateConfig.Layout,
|
||||
r.templateConfig.Partial,
|
||||
r.templateConfig.Extension,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
99
internal/pkg/render/util/util.go
Normal file
99
internal/pkg/render/util/util.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"management/internal/pkg/render/funcs"
|
||||
"management/internal/pkg/render/gen"
|
||||
templates "management/web/templates/manage"
|
||||
)
|
||||
|
||||
func CreateTemplateCache(root, layout, partial, extension string) (map[string]*template.Template, error) {
|
||||
cache := make(map[string]*template.Template)
|
||||
pages, err := getFiles(root, extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layoutAndPartial, err := getLayoutAndPartials(layout, partial, extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, page := range pages {
|
||||
if strings.HasPrefix(page, layout) || strings.HasSuffix(page, partial) {
|
||||
continue
|
||||
}
|
||||
|
||||
name := filepath.Base(page)
|
||||
pathArr := strings.Split(page, "/")
|
||||
dir := pathArr[len(pathArr)-2 : len(pathArr)-1]
|
||||
templateName := fmt.Sprintf("%s_%s", dir[0], name)
|
||||
ts := template.Must(template.New(templateName).Funcs(gen.Button()).Funcs(funcs.Methods()).ParseFS(templates.TemplateFS, page))
|
||||
ts, err = ts.ParseFS(templates.TemplateFS, layoutAndPartial...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cache[templateName] = ts
|
||||
}
|
||||
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func getLayoutAndPartials(layout, partial, extension string) ([]string, error) {
|
||||
layouts, err := getFiles(layout, extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
partials, err := getFiles(partial, extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return slices.Concat(layouts, partials), nil
|
||||
}
|
||||
|
||||
func getFiles(path string, suffix string) ([]string, error) {
|
||||
files := make([]string, 0)
|
||||
b, err := pathExists(templates.TemplateFS, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !b {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
err = fs.WalkDir(templates.TemplateFS, path, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(path, suffix) {
|
||||
files = append(files, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return files, err
|
||||
}
|
||||
|
||||
func pathExists(fs fs.FS, path string) (bool, error) {
|
||||
_, err := fs.Open(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
Reference in New Issue
Block a user