feat: add custom publish time and last updated time display

- Add custom publish time field to article create/edit forms
  - Support datetime-local input for manual time setting
  - Auto-set on first publish if left blank
  - Works for both admin and user article forms

- Display last updated time on article detail page
  - Show both published time and last updated time
  - Auto-maintained by GORM on each update
  - Format: YYYY-MM-DD HH:MM

- Add i18n support for new fields
  - article_published_at: Published Time / 发布时间
  - article_published_at_hint: Leave blank to auto-set on publish / 留空则在发布时自动设置
  - article_last_updated: Last Updated / 最后更新

- Update handlers and templates
  - handlers/article.go: Add parsePublishedAt/formatPublishedAt functions
  - handlers/home.go: Add formatUpdateTime function
  - handlers/my_articles.go: Support custom publish time in user articles
  - templates: Add datetime-local inputs and time display

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 20:20:19 +08:00
co-authored by Claude Fable 5
parent b600eac21c
commit b0ca76e8dc
9 changed files with 315 additions and 52 deletions
+24 -16
View File
@@ -66,16 +66,17 @@ func MyArticleEditPage(db *gorm.DB) gin.HandlerFunc {
}
renderMyArticleForm(c, db, articleForm{
Title: article.Title,
Slug: article.Slug,
Summary: article.Summary,
Content: article.Content,
Cover: article.Cover,
StatusStr: strconv.Itoa(article.Status),
IsTop: article.IsTop,
Action: "/my/articles/" + id + "/edit",
TitleText: tr["article_edit_title"],
ArticleID: article.ID,
Title: article.Title,
Slug: article.Slug,
Summary: article.Summary,
Content: article.Content,
Cover: article.Cover,
StatusStr: strconv.Itoa(article.Status),
IsTop: article.IsTop,
PublishedAt: formatPublishedAt(article.PublishedAt),
Action: "/my/articles/" + id + "/edit",
TitleText: tr["article_edit_title"],
ArticleID: article.ID,
}, "")
}
}
@@ -117,12 +118,19 @@ func MyArticleUpdate(db *gorm.DB) gin.HandlerFunc {
newStatus := statusFromForm(f.StatusStr)
// Stamp the publish time the first time an article is published.
wasPublished := article.Status == models.ArticlePublished
var publishedAt *time.Time = article.PublishedAt
if newStatus == models.ArticlePublished && !wasPublished && publishedAt == nil {
now := time.Now()
publishedAt = &now
// Handle published_at: use form value if provided, otherwise auto-stamp on first publish.
var publishedAt *time.Time
if f.PublishedAt != "" {
// User provided a custom published time
publishedAt = parsePublishedAt(f.PublishedAt)
} else {
// Stamp the publish time the first time an article is published.
wasPublished := article.Status == models.ArticlePublished
publishedAt = article.PublishedAt
if newStatus == models.ArticlePublished && !wasPublished && publishedAt == nil {
now := time.Now()
publishedAt = &now
}
}
updates := map[string]interface{}{