feat: enhance member detail page with dynamic tabs and image handling

- Introduced member business tabs for profile and dynamics, improving navigation and user experience.
- Implemented image parsing and normalization functions to handle article images effectively.
- Updated the UI to display article thumbnails and manage loading states, enhancing visual feedback.
- Refactored data synchronization methods to ensure member business tabs reflect current content accurately.

This update aims to streamline member profile interactions and improve the overall presentation of member-related content.
This commit is contained in:
乘风
2026-05-09 18:15:45 +08:00
parent 99d65dc664
commit 0cc8e2b270
21 changed files with 1013 additions and 249 deletions

View File

@@ -164,6 +164,53 @@ func MiniprogramSuperArticleFeed(c *gin.Context) {
})
}
func deriveSuperArticleTitleFromContent(content string) string {
content = strings.TrimSpace(content)
if content == "" {
return ""
}
parts := strings.SplitN(content, "\n", 2)
line := strings.TrimSpace(parts[0])
line = strings.TrimRight(line, "\r")
if line == "" {
return ""
}
rs := []rune(line)
if len(rs) > 40 {
return string(rs[:40])
}
return line
}
// finalizeSuperArticleTitleAndBody 发动态:标题可省略(由正文首行或默认文案补齐);正文与配图至少一项。
func finalizeSuperArticleTitleAndBody(title, content string, imageCount int) (outTitle, outBody, errMsg string) {
title = strings.TrimSpace(title)
content = strings.TrimSpace(content)
if content == "" && imageCount == 0 {
return "", "", "请填写文字或添加配图"
}
if len([]rune(content)) > 5000 {
return "", "", "正文最多 5000 字"
}
outTitle = title
if outTitle != "" && len([]rune(outTitle)) > 40 {
outTitle = string([]rune(outTitle)[:40])
}
if outTitle == "" {
outTitle = deriveSuperArticleTitleFromContent(content)
}
if outTitle == "" && imageCount > 0 {
outTitle = "分享图片"
}
if outTitle == "" {
outTitle = "动态"
}
if len([]rune(outTitle)) > 40 {
outTitle = string([]rune(outTitle)[:40])
}
return outTitle, content, ""
}
func normalizeSuperArticleImageURLs(in []string) ([]string, string) {
if len(in) == 0 {
return []string{}, ""
@@ -208,16 +255,8 @@ func MiniprogramSuperArticleCreate(c *gin.Context) {
req.UserID = strings.TrimSpace(req.UserID)
req.Title = strings.TrimSpace(req.Title)
req.Content = strings.TrimSpace(req.Content)
if req.UserID == "" || req.Title == "" || req.Content == "" {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "标题和正文不能为空"})
return
}
if len([]rune(req.Title)) > 40 {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "标题最多 40 字"})
return
}
if len([]rune(req.Content)) > 5000 {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "正文最多 5000 字"})
if req.UserID == "" {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
return
}
@@ -232,6 +271,13 @@ func MiniprogramSuperArticleCreate(c *gin.Context) {
return
}
title, body, verr := finalizeSuperArticleTitleAndBody(req.Title, req.Content, len(imgURLs))
if verr != "" {
c.JSON(http.StatusOK, gin.H{"success": false, "error": verr})
return
}
req.Title, req.Content = title, body
db := database.DB()
if _, ok := miniprogramSuperIndividualPerson(db, req.UserID); !ok {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "仅超级个体可发文章", "forbidden": true})
@@ -468,16 +514,8 @@ func MiniprogramSuperArticleUpdate(c *gin.Context) {
req.UserID = strings.TrimSpace(req.UserID)
req.Title = strings.TrimSpace(req.Title)
req.Content = strings.TrimSpace(req.Content)
if req.UserID == "" || req.Title == "" || req.Content == "" {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "标题和正文不能为空"})
return
}
if len([]rune(req.Title)) > 40 {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "标题最多 40 字"})
return
}
if len([]rune(req.Content)) > 5000 {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "正文最多 5000 字"})
if req.UserID == "" {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
return
}
@@ -492,6 +530,13 @@ func MiniprogramSuperArticleUpdate(c *gin.Context) {
return
}
title, body, verr := finalizeSuperArticleTitleAndBody(req.Title, req.Content, len(imgURLs))
if verr != "" {
c.JSON(http.StatusOK, gin.H{"success": false, "error": verr})
return
}
req.Title, req.Content = title, body
db := database.DB()
var row model.SuperArticle
if err := db.First(&row, id).Error; err != nil {
@@ -531,3 +576,42 @@ func MiniprogramSuperArticleUpdate(c *gin.Context) {
}
c.JSON(http.StatusOK, gin.H{"success": true})
}
// MiniprogramSuperArticleDelete 删除动态(作者本人 + 超级个体)。
// DELETE /api/miniprogram/super/articles/:id?userId=
// POST /api/miniprogram/super/articles/:id/delete?userId= (与 DELETE 等价,便于不支持 DELETE 的客户端调试)
func MiniprogramSuperArticleDelete(c *gin.Context) {
idRaw := strings.TrimSpace(c.Param("id"))
id, err := strconv.ParseUint(idRaw, 10, 64)
if err != nil || id == 0 {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
return
}
userID := strings.TrimSpace(c.Query("userId"))
if userID == "" {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 userId"})
return
}
db := database.DB()
if _, ok := miniprogramSuperIndividualPerson(db, userID); !ok {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "仅超级个体可操作", "forbidden": true})
return
}
var row model.SuperArticle
if err := db.First(&row, id).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "动态不存在"})
return
}
if strings.TrimSpace(row.UserID) != userID {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "无权删除"})
return
}
if err := db.Delete(&row).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"success": true})
}