fix: 评论配图整条链路补全 + 宠物切换条吸顶
## 评论图片传了等于没传 上次只做了一半:model 加了 Images 字段、前端做了上传 UI,但 handler 的 commentReq 没有 images、service 的 CreateComment 连 参数都没有,图片上传完就丢了;评论列表也从没渲染过图片。 补齐:请求体 → service 落库(JSON 数组) → 列表回显 → 点击大图预览。 实测带两张图的评论和纯文字评论各存各的,互不影响。 ## 宠物切换条吸顶 首页/计划/记录/报告四页的 pet-switch 原本在 scroll-view 里,滚下去 就找不到了。而切宠物是随时要用的动作——多宠用户看着 A 的数据想切到 B,还得先滚回顶部。 移出滚动区,放在导航栏下方做成固定的一条,带毛玻璃和分隔线。首页的 问候语留在滚动区里,滚走就滚走,反正它只在刚进页面时有意义。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -113,7 +113,8 @@ func (h *Handler) ListComments(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type commentReq struct {
|
type commentReq struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
|
Images []string `json:"images"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateComment POST /api/posts/:id/comments
|
// CreateComment POST /api/posts/:id/comments
|
||||||
@@ -127,7 +128,7 @@ func (h *Handler) CreateComment(c *gin.Context) {
|
|||||||
response.FailParams(c, "评论内容不能为空")
|
response.FailParams(c, "评论内容不能为空")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
comment, err := h.svc.CreateComment(middleware.UserID(c), idParam(c, "id"), req.Content)
|
comment, err := h.svc.CreateComment(middleware.UserID(c), idParam(c, "id"), req.Content, req.Images)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondErr(c, err)
|
respondErr(c, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ func (s *Service) ListComments(userID, postID string, offset, limit int) ([]mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateComment 评论
|
// CreateComment 评论
|
||||||
func (s *Service) CreateComment(userID, postID string, content string) (*model.Comment, error) {
|
func (s *Service) CreateComment(userID, postID string, content string, images []string) (*model.Comment, error) {
|
||||||
if _, err := s.GetPost(postID); err != nil {
|
if _, err := s.GetPost(postID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -219,6 +219,10 @@ func (s *Service) CreateComment(userID, postID string, content string) (*model.C
|
|||||||
name = user.Nickname
|
name = user.Nickname
|
||||||
}
|
}
|
||||||
comment := model.Comment{PostID: postID, UserID: userID, AuthorName: name, Content: content, Status: "published"}
|
comment := model.Comment{PostID: postID, UserID: userID, AuthorName: name, Content: content, Status: "published"}
|
||||||
|
// 配图存 JSON 数组。空数组也要显式写,否则前端拿到 null 还要额外判空
|
||||||
|
if b, err := json.Marshal(images); err == nil && len(images) > 0 {
|
||||||
|
comment.Images = b
|
||||||
|
}
|
||||||
if err := s.db.Transaction(func(tx *gorm.DB) error {
|
if err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||||
if err := tx.Create(&comment).Error; err != nil {
|
if err := tx.Create(&comment).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -357,3 +357,17 @@ page{scrollbar-width:none;-ms-overflow-style:none}
|
|||||||
}
|
}
|
||||||
.no-pet-b{font-size:var(--fs-lg);font-weight:var(--fw-b)}
|
.no-pet-b{font-size:var(--fs-lg);font-weight:var(--fw-b)}
|
||||||
.no-pet-p{color:var(--muted);font-size:var(--fs-sm);margin:var(--sp-2) 0 var(--sp-4);line-height:1.6}
|
.no-pet-p{color:var(--muted);font-size:var(--fs-sm);margin:var(--sp-2) 0 var(--sp-4);line-height:1.6}
|
||||||
|
|
||||||
|
/* 宠物切换条固定在导航栏下方。它原本在 scroll-view 里,滚下去就找不到了,
|
||||||
|
而切宠物是随时要用的动作——尤其多宠用户,看着 A 的数据想切到 B
|
||||||
|
还得先滚回顶部。移出滚动区,四个主页面通用。 */
|
||||||
|
.pet-bar{
|
||||||
|
flex:none;
|
||||||
|
padding:var(--sp-2) var(--pad-x) 0;
|
||||||
|
background:rgba(250,247,242,.92);
|
||||||
|
backdrop-filter:blur(20rpx);
|
||||||
|
-webkit-backdrop-filter:blur(20rpx);
|
||||||
|
border-bottom:1rpx solid rgba(238,230,220,.7);
|
||||||
|
}
|
||||||
|
/* 条内的 pet-switch 自己带 padding-bottom,这里不再叠加 */
|
||||||
|
.pet-bar .pet-switch{padding-bottom:var(--sp-2)}
|
||||||
|
|||||||
@@ -416,6 +416,7 @@ Component({
|
|||||||
timeText: fmtAgo(c.created_at),
|
timeText: fmtAgo(c.created_at),
|
||||||
// 没有头像字段,用昵称首字做个色块,比一律显示同一个 emoji 强
|
// 没有头像字段,用昵称首字做个色块,比一律显示同一个 emoji 强
|
||||||
initial: (c.author_name || '?').slice(0, 1),
|
initial: (c.author_name || '?').slice(0, 1),
|
||||||
|
imgs: Array.isArray(c.images) ? c.images : [],
|
||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -954,6 +955,10 @@ Component({
|
|||||||
wx.showToast({ title: (e && e.message) || '上传失败', icon: 'none' });
|
wx.showToast({ title: (e && e.message) || '上传失败', icon: 'none' });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
onPreviewCommentImage(e) {
|
||||||
|
const { urls, cur } = e.currentTarget.dataset;
|
||||||
|
if (urls && urls.length) wx.previewImage({ urls, current: cur });
|
||||||
|
},
|
||||||
onRemoveCommentImage(e) {
|
onRemoveCommentImage(e) {
|
||||||
const list = this.data.commentImages.slice();
|
const list = this.data.commentImages.slice();
|
||||||
list.splice(e.currentTarget.dataset.index, 1);
|
list.splice(e.currentTarget.dataset.index, 1);
|
||||||
|
|||||||
@@ -330,6 +330,11 @@ module.exports.sel = function (map, key, index, def) {
|
|||||||
<text class="cm-time">{{item.timeText}}</text>
|
<text class="cm-time">{{item.timeText}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="cm-text">{{item.content}}</view>
|
<view class="cm-text">{{item.content}}</view>
|
||||||
|
<view wx:if="{{item.imgs.length}}" class="cm-imgs">
|
||||||
|
<image wx:for="{{item.imgs}}" wx:for-item="u" wx:key="*this" class="cm-img"
|
||||||
|
src="{{u}}" mode="aspectFill" catchtap="onPreviewCommentImage"
|
||||||
|
data-urls="{{item.imgs}}" data-cur="{{u}}"></image>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|||||||
@@ -125,3 +125,5 @@
|
|||||||
.cm-bar{display:flex;align-items:center;gap:var(--sp-3);margin-top:var(--sp-3)}
|
.cm-bar{display:flex;align-items:center;gap:var(--sp-3);margin-top:var(--sp-3)}
|
||||||
.cm-pic{display:flex;align-items:center;gap:var(--sp-1);color:var(--muted);font-size:var(--fs-sm)}
|
.cm-pic{display:flex;align-items:center;gap:var(--sp-1);color:var(--muted);font-size:var(--fs-sm)}
|
||||||
.cm-count{margin-left:auto;color:var(--muted2);font-size:var(--fs-cap)}
|
.cm-count{margin-left:auto;color:var(--muted2);font-size:var(--fs-cap)}
|
||||||
|
.cm-imgs{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin-top:var(--sp-2)}
|
||||||
|
.cm-img{width:150rpx;height:150rpx;border-radius:var(--r-sm)}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<nav-bar title="肉垫计划"></nav-bar>
|
<nav-bar title="肉垫计划"></nav-bar>
|
||||||
|
|
||||||
|
<view class="pet-bar"><pet-switch bind:add="onAddPet"></pet-switch></view>
|
||||||
|
|
||||||
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
|
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
|
||||||
<view class="page-body">
|
<view class="page-body">
|
||||||
<view class="top-row">
|
<view class="top-row">
|
||||||
@@ -13,8 +15,6 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<pet-switch bind:add="onAddPet"></pet-switch>
|
|
||||||
|
|
||||||
<!-- 门面:完成度环 + 当下状态。点进去编辑档案 -->
|
<!-- 门面:完成度环 + 当下状态。点进去编辑档案 -->
|
||||||
<view class="card hero-card" bindtap="onHeroTap">
|
<view class="card hero-card" bindtap="onHeroTap">
|
||||||
<view class="hero-row">
|
<view class="hero-row">
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<nav-bar title="养宠计划"></nav-bar>
|
<nav-bar title="养宠计划"></nav-bar>
|
||||||
|
|
||||||
|
<view class="pet-bar"><pet-switch bind:add="onAddPet"></pet-switch></view>
|
||||||
|
|
||||||
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
|
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
|
||||||
<view class="page-body">
|
<view class="page-body">
|
||||||
<pet-switch bind:add="onAddPet"></pet-switch>
|
|
||||||
|
|
||||||
<view wx:if="{{!pet.id}}" class="no-pet">
|
<view wx:if="{{!pet.id}}" class="no-pet">
|
||||||
<view class="no-pet-ic"><pt-icon name="community" size="{{52}}"></pt-icon></view>
|
<view class="no-pet-ic"><pt-icon name="community" size="{{52}}"></pt-icon></view>
|
||||||
<view class="no-pet-b">还没有毛孩子的档案</view>
|
<view class="no-pet-b">还没有毛孩子的档案</view>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<nav-bar title="快速记录"></nav-bar>
|
<nav-bar title="快速记录"></nav-bar>
|
||||||
|
|
||||||
|
<view class="pet-bar"><pet-switch bind:add="onAddPet"></pet-switch></view>
|
||||||
|
|
||||||
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
|
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
|
||||||
<view class="page-body">
|
<view class="page-body">
|
||||||
<pet-switch bind:add="onAddPet"></pet-switch>
|
|
||||||
|
|
||||||
<view wx:if="{{!pet.id}}" class="no-pet">
|
<view wx:if="{{!pet.id}}" class="no-pet">
|
||||||
<view class="no-pet-ic"><pt-icon name="community" size="{{52}}"></pt-icon></view>
|
<view class="no-pet-ic"><pt-icon name="community" size="{{52}}"></pt-icon></view>
|
||||||
<view class="no-pet-b">还没有毛孩子的档案</view>
|
<view class="no-pet-b">还没有毛孩子的档案</view>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<nav-bar title="成长报告"></nav-bar>
|
<nav-bar title="成长报告"></nav-bar>
|
||||||
|
|
||||||
|
<view class="pet-bar"><pet-switch bind:add="onAddPet"></pet-switch></view>
|
||||||
|
|
||||||
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
|
<scroll-view class="page-scroll" scroll-y="{{true}}" enhanced="{{true}}" show-scrollbar="{{false}}">
|
||||||
<view class="page-body">
|
<view class="page-body">
|
||||||
<pet-switch bind:add="onAddPet"></pet-switch>
|
|
||||||
|
|
||||||
<view wx:if="{{!pet.id}}" class="no-pet">
|
<view wx:if="{{!pet.id}}" class="no-pet">
|
||||||
<view class="no-pet-ic"><pt-icon name="community" size="{{52}}"></pt-icon></view>
|
<view class="no-pet-ic"><pt-icon name="community" size="{{52}}"></pt-icon></view>
|
||||||
<view class="no-pet-b">还没有毛孩子的档案</view>
|
<view class="no-pet-b">还没有毛孩子的档案</view>
|
||||||
|
|||||||
Reference in New Issue
Block a user