Files
mce/internal/theme/loader.go

121 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package theme
import (
"fmt"
"html/template"
"os"
"path/filepath"
"strings"
"metazone.cc/mce/internal/common"
)
// TemplateRoot 模板根目录配置
type TemplateRoot struct {
Dir string // 目录路径
Prefix string // 模板名前缀(如 "admin/" 表示管理后台)
}
// LoadTemplates 加载多个根目录的 .html 模板到同一模板集
// 模板名 = prefix + 相对于 root.Dir 的相对路径
func LoadTemplates(roots ...TemplateRoot) (*template.Template, error) {
funcMap := template.FuncMap{
"add": func(a, b int) int { return a + b },
"subtract": func(a, b int) int { return a - b },
"formatTTL": formatTTL,
"formatCount": formatCount,
"str": str,
"assetV": common.AssetV,
"substr": func(s string, start, length int) string {
runes := []rune(s)
if start >= len(runes) {
return ""
}
end := start + length
if end > len(runes) {
end = len(runes)
}
return string(runes[start:end])
},
// derefUint 安全解引用 *uint 指针。
// Go 1.26 的模板引擎在将 *uint 传给 printf/print 时不会自动解引用,
// 导致打印指针地址而非值。通过此函数显式解引用后传给 printf。
"derefUint": func(p *uint) uint {
if p == nil {
return 0
}
return *p
},
}
t := template.New("").Funcs(funcMap)
for _, root := range roots {
dir := filepath.Clean(root.Dir) + string(os.PathSeparator)
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() || filepath.Ext(path) != ".html" {
return nil
}
content, err := os.ReadFile(path)
if err != nil {
return err
}
// 模板名prefix + 相对路径
name := root.Prefix + strings.TrimPrefix(path, dir)
_, err = t.New(name).Parse(string(content))
return err
})
if err != nil {
return nil, err
}
}
return t, nil
}
// LoadContent 读取主题内容文件(准则等),返回不转义的 template.HTML。
// 后期改为查数据库时,只需修改此函数实现,调用方不变。
func LoadContent(path string) (template.HTML, error) {
content, err := os.ReadFile(path)
if err != nil {
return "", err
}
return template.HTML(content), nil
}
// formatTTL 将剩余分钟数格式化为人类可读的时间(如 "23 小时"、"3 天"、"15 分钟"
func formatTTL(minutes int) string {
if minutes <= 0 {
return "即将过期"
}
if minutes < 60 {
return fmt.Sprintf("%d 分钟", minutes)
}
hours := minutes / 60
if hours < 24 {
return fmt.Sprintf("%d 小时", hours)
}
days := hours / 24
remainHours := hours % 24
if remainHours == 0 {
return fmt.Sprintf("%d 天", days)
}
return fmt.Sprintf("%d 天 %d 小时", days, remainHours)
}
// formatCount 将 int64 数字格式化为简洁中文计数(如 "2.5万"、"70.3万"、"0"
func formatCount(n int64) string {
if n < 10000 {
return fmt.Sprintf("%d", n)
}
if n < 100000000 {
v := float64(n) / 10000.0
return fmt.Sprintf("%.1f万", v)
}
v := float64(n) / 100000000.0
return fmt.Sprintf("%.1f亿", v)
}
// str 将 uint 转为字符串,用于模板中与字符串比较
func str(n uint) string { return fmt.Sprintf("%d", n) }