模板可切换,模板引擎模块化有问题
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"go_blog/themes"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var themeManager = themes.NewManager("themes")
|
||||
var router = gin.Default()
|
||||
|
||||
// 切换主题(管理后台)
|
||||
func switchTheme(c *gin.Context) {
|
||||
newTheme := c.PostForm("theme")
|
||||
if err := themeManager.LoadTheme(newTheme); err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
themeManager.RegisterStaticRoutes(router) // 重新注册静态路由
|
||||
c.JSON(200, gin.H{"status": "success"})
|
||||
}
|
||||
@@ -20,12 +20,6 @@ const (
|
||||
)
|
||||
|
||||
type DataBaseConfig struct {
|
||||
Host string
|
||||
Port string
|
||||
User string
|
||||
Password string
|
||||
DBName string
|
||||
Charset string
|
||||
Prefix string
|
||||
Driver string
|
||||
DSN string
|
||||
@@ -117,3 +111,8 @@ func LoadConfig(configPath string) (*Config, error) {
|
||||
globalConfig = &cfg // 保存配置到全局变量
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// GetGlobalConfig 获取全局配置
|
||||
func GetGlobalConfig() *Config {
|
||||
return globalConfig
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
database:
|
||||
Driver: mysql
|
||||
Host: 47.93.160.42
|
||||
Port: 3630
|
||||
User: blog
|
||||
Password: qI7=bL4@iJ
|
||||
DBName: blog
|
||||
Charset: utf8mb4
|
||||
Prefix: gin_
|
||||
DSN: "mysql:mysql@tcp(47.93.160.42:3630)/gin_blog?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
DSN: "blog:qI7=bL4@iJ@tcp(47.93.160.42:3630)/blog?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
# Prefix: gin_ # This line is commented out in the original .ini file
|
||||
|
||||
jwt:
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"go_blog/config"
|
||||
"go_blog/models"
|
||||
"go_blog/themes"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -63,23 +64,38 @@ func SwitchTheme(c *gin.Context) {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "主题管理器类型错误"})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取前端提交的主题名称
|
||||
newTheme := c.PostForm("theme")
|
||||
if newTheme == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "主题名称不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
// 加载新主题
|
||||
if err := themeManager.LoadTheme(newTheme); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "加载主题失败: " + err.Error()})
|
||||
// 校验主题是否存在(保持原有逻辑)
|
||||
availableThemes, _ := themeManager.GetAvailableThemes()
|
||||
themeExists := false
|
||||
for _, t := range availableThemes {
|
||||
if t == newTheme {
|
||||
themeExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !themeExists {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的主题名称"})
|
||||
return
|
||||
}
|
||||
|
||||
// 持久化主题配置(假设 config 包支持保存到配置文件)
|
||||
if err := config.SetCurrentTheme(newTheme); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存主题配置失败: " + err.Error()})
|
||||
// 加载新主题(保持原有逻辑)
|
||||
if err := themeManager.LoadTheme(newTheme); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "主题加载失败,请检查主题文件"})
|
||||
return
|
||||
}
|
||||
// 更新全局配置
|
||||
globalConfig := config.GetGlobalConfig()
|
||||
globalConfig.Theme.Current = newTheme
|
||||
|
||||
// 调用options.go的SetOptionValue持久化配置
|
||||
if err := models.SetOptionValue(models.DB, "current_theme", 0, newTheme); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "主题配置保存失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
8
go.mod
8
go.mod
@@ -11,6 +11,8 @@ require (
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
golang.org/x/crypto v0.37.0
|
||||
gorm.io/driver/mysql v1.5.6
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
gorm.io/driver/sqlite v1.5.7
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -22,8 +24,13 @@ require (
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.4.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
@@ -35,6 +42,7 @@ require (
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
|
||||
|
||||
20
go.sum
20
go.sum
@@ -49,6 +49,14 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
@@ -69,6 +77,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -127,6 +137,8 @@ golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
@@ -135,8 +147,8 @@ golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -144,6 +156,10 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
|
||||
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go_blog/config"
|
||||
"go_blog/pkg/util"
|
||||
"log/slog"
|
||||
@@ -15,6 +14,8 @@ import (
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
@@ -22,16 +23,20 @@ import (
|
||||
var DB *gorm.DB
|
||||
|
||||
func InitDatabase(conf *config.Config) {
|
||||
var dialector gorm.Dialector
|
||||
switch conf.DataBase.Driver {
|
||||
case "mysql":
|
||||
dialector = mysql.Open(conf.DataBase.DSN)
|
||||
case "postgres":
|
||||
dialector = postgres.Open(conf.DataBase.DSN)
|
||||
case "sqlite":
|
||||
dialector = sqlite.Open(conf.DataBase.DSN)
|
||||
default:
|
||||
slog.Error("不支持的数据库驱动", "driver", conf.DataBase.Driver)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
conUri := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local",
|
||||
conf.DataBase.User,
|
||||
conf.DataBase.Password,
|
||||
conf.DataBase.Host,
|
||||
conf.DataBase.Port,
|
||||
conf.DataBase.DBName,
|
||||
conf.DataBase.Charset)
|
||||
// 2. 初始化数据库
|
||||
db, err := gorm.Open(mysql.Open(conUri), &gorm.Config{
|
||||
db, err := gorm.Open(dialector, &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: conf.DataBase.Prefix,
|
||||
},
|
||||
|
||||
42
models/options.go
Normal file
42
models/options.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package models
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
// Option 对应 typecho_options 数据表
|
||||
// 用于存储系统配置项
|
||||
type Option struct {
|
||||
Name string `gorm:"column:name;type:varchar(32);primaryKey"` // 配置名称(主键)
|
||||
Value string `gorm:"column:value;type:text"` // 配置值
|
||||
User int `gorm:"column:user;type:int;default:0"` // 用户ID(0表示全局配置)
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Option) TableName() string {
|
||||
return "typecho_options"
|
||||
}
|
||||
|
||||
// GetOptionValue 根据name和user获取配置值
|
||||
// 返回:配置值,错误(无记录时返回gorm.ErrRecordNotFound)
|
||||
func GetOptionValue(db *gorm.DB, name string, user int) (string, error) {
|
||||
|
||||
var option Option
|
||||
result := db.Where("name = ? AND user = ?", name, user).First(&option)
|
||||
if result.Error != nil {
|
||||
return "", result.Error // 可能返回gorm.ErrRecordNotFound或其他数据库错误
|
||||
}
|
||||
return option.Value, nil
|
||||
}
|
||||
|
||||
// SetOptionValue 根据name和user设置配置值(存在则更新,不存在则新增)
|
||||
func SetOptionValue(db *gorm.DB, name string, user int, value string) error {
|
||||
// 构造要更新/插入的配置项
|
||||
option := Option{
|
||||
Name: name,
|
||||
User: user,
|
||||
Value: value,
|
||||
}
|
||||
|
||||
// 使用GORM的Save方法(自动判断是否存在,存在则更新,不存在则插入)
|
||||
result := db.Save(&option)
|
||||
return result.Error
|
||||
}
|
||||
@@ -46,25 +46,25 @@ var (
|
||||
)
|
||||
|
||||
// 核心方法:加载主题
|
||||
func (m *ThemeManager) LoadTheme(themeName string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
func (themeManager *ThemeManager) LoadTheme(themeName string) error {
|
||||
themeManager.mu.Lock()
|
||||
defer themeManager.mu.Unlock()
|
||||
|
||||
// 1. 验证主题目录结构
|
||||
themePath := filepath.Join(m.themesDir, themeName)
|
||||
themePath := filepath.Join(themeManager.themesDir, themeName)
|
||||
if !isValidTheme(themePath) {
|
||||
return fmt.Errorf("%w: %s", ErrInvalidThemeStructure, themeName)
|
||||
}
|
||||
|
||||
// 2. 加载模板文件
|
||||
tpls, err := m.parseTemplates(themePath)
|
||||
tpls, err := themeManager.parseTemplates(themePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("template parsing failed: %w", err)
|
||||
}
|
||||
|
||||
// 3. 更新当前主题
|
||||
m.currentTheme = themeName
|
||||
m.templates = tpls
|
||||
themeManager.currentTheme = themeName
|
||||
themeManager.templates = tpls
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -229,3 +229,15 @@ func (m *ThemeManager) GetAvailableThemes() ([]string, error) {
|
||||
}
|
||||
return themes, nil
|
||||
}
|
||||
|
||||
// 加载主题下所有模板(包括子模板)
|
||||
func (tm *ThemeManager) LoadTemplates(themeName string) error {
|
||||
themePath := filepath.Join("web", "themes", themeName, "templates")
|
||||
// 使用通配符加载所有tmpl文件
|
||||
tmpl, err := template.ParseGlob(filepath.Join(themePath, "*.tmpl"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载主题模板失败: %w", err)
|
||||
}
|
||||
tm.templates[themeName] = tmpl
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package themes // Declare the package at the top
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
config "go_blog/config" // Adjust import path as needed
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RenderTemplate renders a template based on the current theme
|
||||
func RenderTemplate(c *gin.Context, tmpl string, data gin.H) {
|
||||
// Get the current theme
|
||||
theme := config.GetCurrentTheme()
|
||||
|
||||
// Construct the template path: "themes/default/home.html"
|
||||
tplPath := fmt.Sprintf("themes/%s/%s", theme, tmpl)
|
||||
|
||||
// Render the template using html/template
|
||||
c.HTML(http.StatusOK, tplPath, data)
|
||||
}
|
||||
|
||||
// SwitchTheme handles POST requests to switch the current theme
|
||||
// SwitchTheme 处理主题切换请求(修正后)
|
||||
func SwitchTheme(c *gin.Context) {
|
||||
// 从上下文中获取主题管理器(通过 main.go 的 themeMiddleware 注入)
|
||||
tm, exists := c.Get("ThemeManager")
|
||||
if !exists {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "主题管理器未找到"})
|
||||
return
|
||||
}
|
||||
themeManager, ok := tm.(*ThemeManager)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "主题管理器类型错误"})
|
||||
return
|
||||
}
|
||||
|
||||
newTheme := c.PostForm("theme")
|
||||
if err := themeManager.LoadTheme(newTheme); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "加载主题失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
config.SetCurrentTheme(newTheme) // 持久化主题配置(需确保 config 包支持)
|
||||
c.JSON(http.StatusOK, gin.H{"status": "主题切换成功"})
|
||||
}
|
||||
164
web/themes/default copy/templates/index.tmpl
Normal file
164
web/themes/default copy/templates/index.tmpl
Normal file
@@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ .Title}}</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style type="text/css">
|
||||
body{
|
||||
background: #f8f8f8;
|
||||
}
|
||||
.layui-main {
|
||||
width: 1140px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.container {
|
||||
width: 1170px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.header .layui-nav {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
}
|
||||
.post-list {
|
||||
width: 75%;
|
||||
float: left;
|
||||
display: block;
|
||||
}
|
||||
.list-card {
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
padding: 20px 20px 10px 20px;
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 10px;
|
||||
height: 200px;
|
||||
}
|
||||
.sidebar {
|
||||
float: left;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.header {}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
<div class="layui-main">
|
||||
<h1><a class="logo" href="https://www.hanxiaonuan.cn/">韩小暖的博客,这个是copy</a></h1>
|
||||
</div>
|
||||
<ul class="layui-nav">
|
||||
<li class="layui-nav-item layui-hide-xs layui-this">
|
||||
<a href="https://www.hanxiaonuan.cn/">首页</a>
|
||||
</li>
|
||||
|
||||
<li class="layui-nav-item layui-hide-xs ">
|
||||
<a href="https://www.hanxiaonuan.cn/start-page.html" title="关于">关于</a>
|
||||
</li>
|
||||
|
||||
<span class="layui-nav-bar"></span></ul>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="post-list">
|
||||
{{ range .Items }}
|
||||
<div class="list-card">
|
||||
<div><a href="/post/{{ .Cid }}">
|
||||
<h1>Title: {{ .Title }}</h1>
|
||||
</a>
|
||||
<p> Slug: {{ .Slug }}</p>
|
||||
<p>Text: {{ .Text }}</p>
|
||||
<div>Created: {{ .Created }},</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="page-navigator">
|
||||
共 {{ .Total }} 条,每页 {{ .PageSize }} 条,当前第 {{ .Page }} 页</div>
|
||||
<div class="pagination">
|
||||
{{ if .PrevPage }}
|
||||
<a href="/page/{{ .PrevPage }}">上一页</a>
|
||||
{{ end }}
|
||||
{{ if .NextPage }}
|
||||
<a href="/page/{{ .NextPage }}">下一页</a>
|
||||
{{ end }}
|
||||
<a href="/">首页</a>
|
||||
<a href="/page/{{ .Total }}">尾页</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar">
|
||||
<div class="column">
|
||||
<h3 class="title-sidebar"><i class="layui-icon"></i> 博客信息</h3>
|
||||
<div class="personal-information">
|
||||
<div class="user">
|
||||
<img src="https://www.hanxiaonuan.cn/usr/uploads/2021/07/3991382612.jpg" alt="韩小暖的博客的头像"
|
||||
class="rounded-circle avatar">
|
||||
<div class="p-2">
|
||||
<a class="user-name" target="_blank" href="https://www.hanxiaonuan.cn/">
|
||||
韩小暖的博客</a>
|
||||
<p class="introduction mt-1">这里是小暖的日常记录,欢迎!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="component">
|
||||
<form class="layui-form" id="search" method="post" action="https://www.hanxiaonuan.cn/"
|
||||
role="search">
|
||||
<div class="layui-inline input">
|
||||
<input type="text" id="s" name="s" class="layui-input" required="" lay-verify="required"
|
||||
placeholder="输入关键字搜索">
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-primary"><i
|
||||
class="layui-icon"></i></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h3 class="title-sidebar"><i class="layui-icon"></i> 栏目分类</h3>
|
||||
<ul class="layui-row layui-col-space5">
|
||||
<li class="layui-col-md12 layui-col-xs6"><a
|
||||
href="https://www.hanxiaonuan.cn/category/default/"><i class="layui-icon"></i>
|
||||
默认分类<span class="layui-badge layui-bg-gray">12</span></a></li>
|
||||
<li class="layui-col-md12 layui-col-xs6"><a href="https://www.hanxiaonuan.cn/category/zc/"><i
|
||||
class="layui-icon"></i> 日常随想<span class="layui-badge layui-bg-gray">2</span></a>
|
||||
</li>
|
||||
<li class="layui-col-md12 layui-col-xs6"><a
|
||||
href="https://www.hanxiaonuan.cn/category/%E5%85%B3%E4%BA%8E%E6%88%BF%E5%AD%90/"><i
|
||||
class="layui-icon"></i> 关于房子<span class="layui-badge layui-bg-gray">1</span></a>
|
||||
</li>
|
||||
<li class="layui-col-md12 layui-col-xs6"><a
|
||||
href="https://www.hanxiaonuan.cn/category/%E5%B7%A5%E4%BD%9C%E6%97%A5%E5%BF%97/"><i
|
||||
class="layui-icon"></i> 工作日志<span class="layui-badge layui-bg-gray">0</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tags">
|
||||
<h3 class="title-sidebar"><i class="layui-icon"></i>标签云</h3>
|
||||
<div>
|
||||
<a class="layui-btn layui-btn-xs layui-btn-primary" style="color: rgb(231, 229, 26)"
|
||||
href="https://www.hanxiaonuan.cn/tag/%E5%B0%8F%E6%9A%96/" title="小暖">小暖</a>
|
||||
<a class="layui-btn layui-btn-xs layui-btn-primary" style="color: rgb(125, 196, 207)"
|
||||
href="https://www.hanxiaonuan.cn/tag/%E6%84%9F%E6%82%9F/" title="感悟">感悟</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tags">
|
||||
<h3 class="title-sidebar"><i class="layui-icon"></i>系统</h3>
|
||||
<div>
|
||||
<li class="last"><a href="https://www.hanxiaonuan.cn/admin/">进入后台 (admin)</a></li>
|
||||
<li><a href="https://www.hanxiaonuan.cn/action/logout?_=5b09b0e1048bbd45bdddc56fc43667b2">退出</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
64
web/themes/default copy/templates/post.tmpl
Normal file
64
web/themes/default copy/templates/post.tmpl
Normal file
@@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Page 1</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<style type="text/css">
|
||||
.container {
|
||||
width: 1170px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.post-list {
|
||||
width: 75%;
|
||||
float: left;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
float: left;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.header {}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="layui-col-md9 layui-col-lg9">
|
||||
<div class="title-article">
|
||||
<h1>{{.Title}}这个是copy</h1>
|
||||
<div class="title-msg">
|
||||
<span><i class="layui-icon"></i> {{.AuthorID}} </span>
|
||||
<span><i class="layui-icon"></i> {{.Created}}</span>
|
||||
<span><i class="layui-icon"></i> {{.CommentsNum}}条</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text" itemprop="articleBody">
|
||||
{{.Text}}
|
||||
</div>
|
||||
<div class="tags-text">
|
||||
<i class="layui-icon"></i>标签:{{.Type}}
|
||||
</div>
|
||||
<div class="copy-text">
|
||||
<div>
|
||||
<p>非特殊说明,本博所有文章均为博主原创。</p>
|
||||
<p class="hidden-xs">如若转载,请注明出处: </p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-text">
|
||||
<div>
|
||||
<span class="layui-badge layui-bg-gray">上一篇</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="layui-badge layui-bg-gray">下一篇</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comment-text layui-form">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
24
web/themes/default copy/templates/register.tmpl
Normal file
24
web/themes/default copy/templates/register.tmpl
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.Title}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{.Title}}web下</h1>
|
||||
<form action="/register" method="post">
|
||||
<div>
|
||||
<label>用户名:</label>
|
||||
<input type="text" name="username" required>
|
||||
</div>
|
||||
<div>
|
||||
<label>密码:</label>
|
||||
<input type="password" name="password" required>
|
||||
</div>
|
||||
<div>
|
||||
<label>确认密码:</label>
|
||||
<input type="password" name="confirm_password" required>
|
||||
</div>
|
||||
<button type="submit">注册</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
4
web/themes/default copy/theme.yaml
Normal file
4
web/themes/default copy/theme.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
name: "Default Theme"
|
||||
author: "Blog System"
|
||||
version: "1.0.0"
|
||||
description: "Default clean theme for the blog"
|
||||
12
web/themes/default/templates/header.tmpl
Normal file
12
web/themes/default/templates/header.tmpl
Normal file
@@ -0,0 +1,12 @@
|
||||
{{ define "header" }}
|
||||
<header class="site-header">
|
||||
<div class="container">
|
||||
<a href="/" class="logo">My Blog</a>
|
||||
<nav class="main-nav">
|
||||
<a href="/">首页</a>
|
||||
<a href="/about">关于</a>
|
||||
<a href="/archive">归档</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
{{ end }}
|
||||
@@ -48,22 +48,7 @@
|
||||
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
<div class="layui-main">
|
||||
<h1><a class="logo" href="https://www.hanxiaonuan.cn/">韩小暖的博客</a></h1>
|
||||
</div>
|
||||
<ul class="layui-nav">
|
||||
<li class="layui-nav-item layui-hide-xs layui-this">
|
||||
<a href="https://www.hanxiaonuan.cn/">首页</a>
|
||||
</li>
|
||||
|
||||
<li class="layui-nav-item layui-hide-xs ">
|
||||
<a href="https://www.hanxiaonuan.cn/start-page.html" title="关于">关于</a>
|
||||
</li>
|
||||
|
||||
<span class="layui-nav-bar"></span></ul>
|
||||
</div>
|
||||
|
||||
{{ template "header" . }}
|
||||
<div class="container">
|
||||
|
||||
<div class="post-list">
|
||||
|
||||
Reference in New Issue
Block a user