模板可切换,模板引擎模块化有问题

This commit is contained in:
张超
2025-06-19 22:27:06 +08:00
parent 4c9f6c140f
commit 8241b8c61f
16 changed files with 401 additions and 122 deletions

View File

@@ -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"})
}

View File

@@ -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
}

View File

@@ -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:

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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
View 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"` // 用户ID0表示全局配置
}
// 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
}

View File

@@ -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
}

View File

@@ -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": "主题切换成功"})
}

View 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>

View 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">&#xe612;</i> {{.AuthorID}} </span>
<span><i class="layui-icon">&#xe60e;</i> {{.Created}}</span>
<span><i class="layui-icon">&#xe63a;</i> {{.CommentsNum}}</span>
</div>
</div>
<div class="text" itemprop="articleBody">
{{.Text}}
</div>
<div class="tags-text">
<i class="layui-icon">&#xe66e;</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>

View 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>

View File

@@ -0,0 +1,4 @@
name: "Default Theme"
author: "Blog System"
version: "1.0.0"
description: "Default clean theme for the blog"

View 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 }}

View File

@@ -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">