feat: 记录删除与筛选、用户侧内容删除、AI 聊天历史
修复雪花 ID 精度丢失(真 bug) - bottom-sheet 的 postId 声明为 Number,而 ID 是 18 位雪花字符串, 超出 JS 安全整数范围:339346584095428608 会被转成 ...600, 评论弹层实际查的是不存在的帖子。改为 String 记录 - 后端 DELETE /records/:id 早已就绪但前端从未调用,记错了删不掉。 时间轴支持长按删除 - 时间轴加类型筛选(体重/便便/饮食/异常/用药/疫苗/消费/照片), 后端 ?type= 本就支持 用户侧内容删除(UGC 合规:用户应能删除自己发布的内容) - 新增 DELETE /posts/:id 与 DELETE /comments/:id,仅限本人, 越权返回 40300;帖子软删除,评论删除同步递减 comment_count - 评论列表返回 is_self;社区长按自己的帖子、长按自己的评论可删 AI 聊天历史 - ai_messages 一直在写但没有查询接口,每次打开弹层都是空白。 新增 GET /api/ai/messages,弹层打开时接在开场白后加载 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,7 @@ Page({
|
||||
posts: [],
|
||||
sheetShow: false,
|
||||
sheetType: '',
|
||||
sheetPostId: 0,
|
||||
sheetPostId: '',
|
||||
},
|
||||
onLoad() {
|
||||
this._unsub = store.subscribe((pet) => this.setData({ pet }));
|
||||
@@ -77,7 +77,7 @@ Page({
|
||||
this.setData({ sheetPostId: e.currentTarget.dataset.id, sheetType: 'comments', sheetShow: true });
|
||||
},
|
||||
openSheet(e) {
|
||||
this.setData({ sheetType: e.currentTarget.dataset.type, sheetPostId: 0, sheetShow: true });
|
||||
this.setData({ sheetType: e.currentTarget.dataset.type, sheetPostId: '', sheetShow: true });
|
||||
},
|
||||
closeSheet() {
|
||||
this.setData({ sheetShow: false });
|
||||
@@ -93,7 +93,23 @@ Page({
|
||||
wx.navigateTo({ url: '/pages/learn/learn' });
|
||||
},
|
||||
onFab() {
|
||||
this.setData({ sheetType: 'ai', sheetPostId: 0, sheetShow: true });
|
||||
this.setData({ sheetType: 'ai', sheetPostId: '', sheetShow: true });
|
||||
},
|
||||
// 长按自己的帖子可删除
|
||||
onLongPressPost(e) {
|
||||
const p = this.data.posts[e.currentTarget.dataset.index];
|
||||
if (!p || !p.is_self) return;
|
||||
wx.showModal({
|
||||
title: '删除帖子',
|
||||
content: '确定删除这条帖子?删除后其他人将看不到。',
|
||||
confirmColor: '#D9534F',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return;
|
||||
api.deletePost(p.id)
|
||||
.then(() => { wx.showToast({ title: '已删除', icon: 'success' }); this.loadPosts(); })
|
||||
.catch((err) => wx.showToast({ title: err.message || '删除失败', icon: 'none' }));
|
||||
},
|
||||
});
|
||||
},
|
||||
// 关注 / 取关帖子作者
|
||||
toggleFollow(e) {
|
||||
|
||||
@@ -19,7 +19,7 @@ module.exports.isUrl = function (s) { return s && s.indexOf('http') === 0; }
|
||||
<button class="btn btn-primary btn-block" bindtap="openSheet" data-type="createPost">+ 发布图文</button>
|
||||
</view>
|
||||
|
||||
<view wx:for="{{posts}}" wx:key="id" class="post-card">
|
||||
<view wx:for="{{posts}}" wx:key="id" class="post-card" bindlongpress="onLongPressPost" data-index="{{index}}">
|
||||
<view class="post-head">
|
||||
<view class="post-avatar">{{item.author_emoji}}</view>
|
||||
<view class="post-user"><text class="pu-b">{{item.author_name}}<text wx:if="{{item.is_ai}}" class="ai-tag">AI</text></text><text class="pu-s">宠友</text></view>
|
||||
|
||||
@@ -16,6 +16,7 @@ function fmtTime(iso) {
|
||||
}
|
||||
function mapRecord(r) {
|
||||
return {
|
||||
id: r.id,
|
||||
icon: r.icon || '✍️',
|
||||
title: r.title,
|
||||
desc: fmtTime(r.occurred_at) + (r.description ? '|' + r.description : ''),
|
||||
@@ -23,6 +24,19 @@ function mapRecord(r) {
|
||||
};
|
||||
}
|
||||
|
||||
// 时间轴类型筛选
|
||||
const REC_FILTERS = [
|
||||
{ key: '', label: '全部' },
|
||||
{ key: 'weight', label: '体重' },
|
||||
{ key: 'poop', label: '便便' },
|
||||
{ key: 'food', label: '饮食' },
|
||||
{ key: 'symptom', label: '异常' },
|
||||
{ key: 'medicine', label: '用药' },
|
||||
{ key: 'vaccine', label: '疫苗' },
|
||||
{ key: 'cost', label: '消费' },
|
||||
{ key: 'photo', label: '照片' },
|
||||
];
|
||||
|
||||
function fmtDay(iso) {
|
||||
if (!iso) return '';
|
||||
const d = new Date(iso);
|
||||
@@ -63,6 +77,8 @@ Page({
|
||||
recPage: 1,
|
||||
recTotal: 0,
|
||||
recHasMore: false,
|
||||
recFilters: REC_FILTERS,
|
||||
recFilterIdx: 0,
|
||||
trendSvg: '',
|
||||
trendStats: null,
|
||||
trendDots: [],
|
||||
@@ -92,11 +108,39 @@ Page({
|
||||
})
|
||||
.catch((e) => wx.showToast({ title: e.message || '加载失败', icon: 'none' }));
|
||||
},
|
||||
curFilter() {
|
||||
return this.data.recFilters[this.data.recFilterIdx].key;
|
||||
},
|
||||
onFilterTap(e) {
|
||||
const i = Number(e.currentTarget.dataset.index);
|
||||
if (i === this.data.recFilterIdx) return;
|
||||
this.setData({ recFilterIdx: i }, () => this.loadRecords());
|
||||
},
|
||||
// 长按删除一条记录
|
||||
onDeleteRecord(e) {
|
||||
const r = this.data.timeline[e.currentTarget.dataset.index];
|
||||
if (!r || !r.id) return;
|
||||
wx.showModal({
|
||||
title: '删除记录',
|
||||
content: '确定删除「' + r.title + '」?删除后不可恢复。',
|
||||
confirmColor: '#D9534F',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return;
|
||||
api
|
||||
.deleteRecord(r.id)
|
||||
.then(() => {
|
||||
wx.showToast({ title: '已删除', icon: 'success' });
|
||||
this.loadRecords();
|
||||
})
|
||||
.catch((err) => wx.showToast({ title: err.message || '删除失败', icon: 'none' }));
|
||||
},
|
||||
});
|
||||
},
|
||||
loadRecords() {
|
||||
const id = store.currentPetId();
|
||||
if (!id) return;
|
||||
api
|
||||
.getRecords(id, { page: 1, pageSize: REC_PAGE })
|
||||
.getRecords(id, { page: 1, pageSize: REC_PAGE, type: this.curFilter() })
|
||||
.then((res) => {
|
||||
const list = (res.list || []).map(mapRecord);
|
||||
this.setData({ timeline: list, recPage: 1, recTotal: res.total || 0, recHasMore: list.length < (res.total || 0) });
|
||||
@@ -109,7 +153,7 @@ Page({
|
||||
if (!id) return;
|
||||
const next = this.data.recPage + 1;
|
||||
api
|
||||
.getRecords(id, { page: next, pageSize: REC_PAGE })
|
||||
.getRecords(id, { page: next, pageSize: REC_PAGE, type: this.curFilter() })
|
||||
.then((res) => {
|
||||
const merged = this.data.timeline.concat((res.list || []).map(mapRecord));
|
||||
this.setData({ timeline: merged, recPage: next, recTotal: res.total || 0, recHasMore: merged.length < (res.total || 0) });
|
||||
|
||||
@@ -50,16 +50,24 @@
|
||||
|
||||
<view class="card">
|
||||
<view class="section-head"><view class="sh-title">健康时间轴</view><view class="link" data-type="vetSummary" bindtap="openSheet">摘要</view></view>
|
||||
<scroll-view class="rec-filter" scroll-x="true" enhanced="{{true}}" show-scrollbar="{{false}}">
|
||||
<view wx:for="{{recFilters}}" wx:key="key"
|
||||
class="rf-chip {{recFilterIdx === index ? 'on' : ''}}"
|
||||
data-index="{{index}}" bindtap="onFilterTap">{{item.label}}</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="health-timeline">
|
||||
<view wx:for="{{timeline}}" wx:key="index" class="health-event">
|
||||
<view wx:for="{{timeline}}" wx:key="id" class="health-event"
|
||||
bindlongpress="onDeleteRecord" data-index="{{index}}">
|
||||
<view class="event-dot">{{item.icon}}</view>
|
||||
<view class="he-body">
|
||||
<text class="he-b">{{item.title}}</text><view class="he-p">{{item.desc}}</view>
|
||||
<image wx:if="{{item.image}}" class="he-img" src="{{item.image}}" mode="aspectFill" bindtap="previewImage" data-src="{{item.image}}"></image>
|
||||
<image wx:if="{{item.image}}" class="he-img" src="{{item.image}}" mode="aspectFill" catchtap="previewImage" data-src="{{item.image}}"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{timeline.length === 0}}" class="tl-empty">还没有记录,用上面的按钮记一条吧</view>
|
||||
<view wx:if="{{timeline.length === 0}}" class="tl-empty">这个分类下还没有记录</view>
|
||||
</view>
|
||||
<view wx:if="{{timeline.length}}" class="tl-tip">长按任意一条可删除</view>
|
||||
<view wx:if="{{recHasMore}}" class="tl-more" bindtap="loadMoreRecords">查看更多(共 {{recTotal}} 条)</view>
|
||||
<view wx:elif="{{recPage > 1}}" class="tl-more" bindtap="collapseRecords">收起</view>
|
||||
</view>
|
||||
|
||||
@@ -50,3 +50,12 @@
|
||||
/* 页面不自身滚动,改由 page-scroll 承载滚动(隐藏滚动条)*/
|
||||
page{height:100vh;overflow:hidden;display:flex;flex-direction:column}
|
||||
.page-scroll{flex:1;min-height:0}
|
||||
|
||||
/* 时间轴类型筛选 */
|
||||
.rec-filter{white-space:nowrap;margin-bottom:20rpx}
|
||||
.rf-chip{
|
||||
display:inline-block;padding:10rpx 26rpx;margin-right:14rpx;border-radius:999rpx;
|
||||
background:#fff;border:1rpx solid var(--line);color:var(--muted);font-size:24rpx;font-weight:700;
|
||||
}
|
||||
.rf-chip.on{background:#FFF2DC;color:var(--primary-dark);border-color:#FFD39A}
|
||||
.tl-tip{margin-top:16rpx;text-align:center;color:var(--muted);font-size:22rpx}
|
||||
|
||||
Reference in New Issue
Block a user