chore(prod): 移除全部社区/社交相关前端代码(过审用)

- 删除页面:community(社区)、comments(评论)、user(他人主页)、relations(关注/粉丝)
- app.json 去掉四个页面注册与社区 tab;tabBar 只剩 首页/报告/我的
- bottom-sheet 移除发帖(createPost)与评论(comments)整块 UI 及对应 JS/数据/常量
- profile-head 移除帖子/关注/粉丝社交数与关注按钮
- mine 去掉「我的帖子」「别人眼里的我」及关注/粉丝跳转
- api.js 移除 posts/comments/follow/relations 等社区接口(保留 userCard 供我的页头部)

说明:后端社区接口未动(微信审核只看小程序包,前端已无任何引用);
完整社区代码保留在 feat/dev 分支与 git 历史。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-08-04 11:16:03 +08:00
parent 04a99df434
commit f417962453
25 changed files with 6 additions and 1352 deletions
@@ -17,22 +17,6 @@ function daysLater(n) {
// "2026-07-30" → "2026-07-30T12:00:00+08:00"
//
// 相对时间。社区里关心的是「多久以前发的」,精确到秒没意义
function fmtAgo(iso) {
if (!iso) return '';
const d = new Date(iso);
if (isNaN(d.getTime())) return '';
const min = Math.floor((Date.now() - d.getTime()) / 60000);
if (min < 1) return '刚刚';
if (min < 60) return min + ' 分钟前';
if (min < 60 * 24) return Math.floor(min / 60) + ' 小时前';
if (min < 60 * 24 * 7) return Math.floor(min / 1440) + ' 天前';
const p = (n) => (n < 10 ? '0' + n : '' + n);
const sameYear = d.getFullYear() === new Date().getFullYear();
return (sameYear ? '' : d.getFullYear() + '-') + p(d.getMonth() + 1) + '-' + p(d.getDate());
}
// 提醒类型(与后端 model.Reminder* 常量对应)
const REMINDER_TYPES = [
{ key: 'vaccine', label: '疫苗' },
@@ -43,9 +27,6 @@ const REMINDER_TYPES = [
const REMINDER_LABELS = REMINDER_TYPES.map((t) => t.label);
// 标签和社区顶部筛选保持一致(晒宠 / 新手求助 / 经验)
const PTAG = ['晒宠', '新手求助', '经验'];
Component({
options: { addGlobalClass: true },
properties: {
@@ -58,11 +39,6 @@ Component({
innerType: '',
pet: {},
segSel: {},
optSel: {},
// 通用简易记录(洗护/清洁那 15 种共用)
postContent: '',
pfFocus: false, // 发帖内容框聚焦标记(点整块 field 聚焦用)
postImages: [],
remForm: { id: '', typeIdx: 0, title: '', date: '', freq: '' },
remTypeLabels: REMINDER_LABELS,
myPets: [],
@@ -70,21 +46,12 @@ Component({
fbContent: '',
fbContact: '',
fbDone: false,
commentText: '',
comments: [],
reminders: [],
poster: null,
reportDetail: null,
proInfo: null,
saving: false,
posterSaving: false,
commentImages: [],
replyTo: {},
cmFocus: false,
composing: false, // 评论输入面板是否展开(收起时只是一条细条)
sendingComment: false, // 发送+审核在途,防连点
kbHeight: 0,
total: 0,
},
observers: {
show: function (show) {
@@ -107,22 +74,12 @@ Component({
const patch = { innerType: type, pet };
if (fresh) {
patch.segSel = {};
patch.optSel = {};
patch.postContent = '';
patch.postImages = [];
patch.medDose = '';
patch.remForm = { id: '', typeIdx: 0, title: '', date: '', freq: '' };
patch.exportText = '';
patch.fbContent = '';
patch.fbContact = '';
patch.fbDone = false;
patch.commentText = '';
patch.commentImages = [];
patch.replyTo = {};
patch.cmFocus = false;
patch.composing = false;
patch.sendingComment = false;
patch.kbHeight = 0;
patch.saving = false;
}
this.setData(patch);
@@ -138,8 +95,6 @@ Component({
this.loadMyPets();
} else if (type === 'exportData') {
this.buildExport();
} else if (type === 'comments' && this.data.postId) {
this.loadComments();
} else if (type === 'reminders' && id) {
this.loadReminders();
} else if (type === 'poster' && id) {
@@ -357,54 +312,6 @@ Component({
const segSel = Object.assign({}, this.data.segSel, { [group]: Number(index) });
this.setData({ segSel });
},
onOpt(e) {
const { group, index } = e.currentTarget.dataset;
const optSel = Object.assign({}, this.data.optSel, { [group]: Number(index) });
this.setData({ optSel });
},
onPostInput(e) { this.setData({ postContent: e.detail.value }); },
// 点内容 field 任意处都聚焦(原生 textarea 在 scroll-view 里命中区域会偏)
onPostFieldTap() { this.setData({ pfFocus: true }); },
onPostBlur() { this.setData({ pfFocus: false }); },
loadComments() {
if (!this.data.postId) return;
api.listComments(this.data.postId)
.then((page) =>
this.setData({
total: page.total || 0,
comments: (page.list || []).map((c) => ({
...c,
timeText: fmtAgo(c.created_at),
// 没有头像字段,用昵称首字做个色块,比一律显示同一个 emoji 强
initial: (c.author_name || '?').slice(0, 1),
imgs: Array.isArray(c.images) ? c.images : [],
replies: (c.replies || []).map((r) => ({
...r,
timeText: fmtAgo(r.created_at),
initial: (r.author_name || '?').slice(0, 1),
})),
})),
}),
)
.catch(() => {});
},
// 长按删除自己的评论
onDeleteComment(e) {
const { id, self } = e.currentTarget.dataset;
if (!id || !self) return;
const c = { id };
wx.showModal({
title: '删除评论',
content: '确定删除这条评论?',
confirmColor: '#D9534F',
success: (res) => {
if (!res.confirm) return;
api.deleteComment(c.id)
.then(() => { this.loadComments(); this.triggerEvent('commented'); })
.catch((err) => wx.showToast({ title: err.message || '删除失败', icon: 'none' }));
},
});
},
// ---- 多宠物管理 ----
loadMyPets() {
const cur = store.currentPetId();
@@ -559,33 +466,11 @@ Component({
if (!id) return;
api.getTasks(id).then((tasks) => this.setData({ manageTasks: tasks || [] })).catch(() => {});
},
onPickPostImages() {
const left = 2 - this.data.postImages.length;
if (left <= 0) return wx.showToast({ title: '最多 2 张', icon: 'none' });
upload
.chooseAndUploadImages(left)
.then((list) => this.setData({ postImages: this.data.postImages.concat(list) }))
.catch((e) => {
if (e && e.canceled) return;
wx.showToast({ title: (e && e.message) || '上传失败', icon: 'none' });
});
},
onRemovePostImage(e) {
const i = e.currentTarget.dataset.index;
const arr = this.data.postImages.slice();
arr.splice(i, 1);
this.setData({ postImages: arr });
},
onCommentInput(e) { this.setData({ commentText: e.detail.value }); },
segIdx(group) {
const v = this.data.segSel[group];
return v === undefined ? 0 : v;
},
optIdx(group) {
const v = this.data.optSel[group];
return v === undefined ? 0 : v;
},
// 记录表单整体搬去 pages/addrecord 之后,这里只剩「生成就医前摘要」
// 会往 health_records 写一条 note —— 它是个动作留痕,不是用户填的表单
buildRecord() {
@@ -622,139 +507,6 @@ Component({
onSave() {
this.createAndClose(this.buildRecord());
},
async onCreatePost() {
const content = (this.data.postContent || '').trim();
if (!content) return wx.showToast({ title: '写点什么吧', icon: 'none' });
const tag = PTAG[this.data.optSel.ptag === undefined ? 0 : this.data.optSel.ptag];
if (this.data.saving) return;
this.setData({ saving: true });
try {
// 去掉发布身份,统一用发布者昵称(后端按 user 取昵称)
const post = await api.createPost({
identity: 'user',
content,
tags: [tag],
image_file_ids: this.data.postImages.map((i) => i.id),
});
this.triggerEvent('posted');
this.close();
// 内容安全审核:没直接过审的,明确告诉用户在审核,别以为发失败/是 bug
if (post && post.status && post.status !== 'published') {
wx.showModal({
title: '发布成功,审核中',
content: '内容需要通过安全审核后才会出现在社区。可在「我的 → 我的帖子」查看审核状态。',
showCancel: false,
confirmText: '知道了',
});
} else {
wx.showToast({ title: '已发布', icon: 'success' });
}
} catch (e) {
wx.showToast({ title: e.message || '发布失败', icon: 'none' });
this.setData({ saving: false });
}
},
// 评论
async onSendComment() {
if (this.data.sendingComment) return; // 防连点:审核请求在途时忽略后续点击
const text = (this.data.commentText || '').trim();
// 原来空评论会直接把弹层关掉,看起来像发成功了。改成明确提示
if (!text) return wx.showToast({ title: '写点什么再发', icon: 'none' });
if (!this.data.postId) return this.close();
this.setData({ sendingComment: true });
wx.showLoading({ title: '审核中…', mask: true });
try {
const res = await api.createComment(
this.data.postId,
text,
this.data.commentImages.map((i) => i.url),
this.data.replyTo.id || '',
);
wx.hideLoading();
this.setData({
commentText: '', commentImages: [], replyTo: {},
composing: false, cmFocus: false, kbHeight: 0, sendingComment: false,
});
// 过审即时展示;否则挂在人工/异步图片审核,通过后才出现
if (res && res.status === 'published') {
this.loadComments();
this.triggerEvent('commented');
wx.showToast({ title: '评论成功', icon: 'success' });
} else {
wx.showToast({ title: '已提交,审核通过后展示', icon: 'none' });
}
} catch (e) {
wx.hideLoading();
this.setData({ sendingComment: false });
// 违规内容后端直接拒收,这里把原因透出来
wx.showToast({ title: (e && e.message) || '评论失败', icon: 'none' });
}
},
// 评论配图:只允许 1 张
onPickCommentImages() {
if (this.data.commentImages.length >= 1) {
return wx.showToast({ title: '评论只能配 1 张图', icon: 'none' });
}
upload
.chooseAndUploadImages(1)
.then((files) => this.setData({ commentImages: (files || []).slice(0, 1) }))
.catch((e) => {
if (e && e.canceled) return;
wx.showToast({ title: (e && e.message) || '上传失败', icon: 'none' });
});
},
// 回复某条评论:输入区顶部显示「回复 xxx」,发送时带上 parent_id
// 小红书那套:点任意一条评论/回复,直接把输入框激活并填上「回复 @xxx」,
// 不用再去底部找输入框、也不用单独点一个「回复」按钮
onReplyTo(e) {
const { id, name } = e.currentTarget.dataset;
if (!id) return;
// 输入框常驻、属性不变,直接置 focus 拉键盘即可;只填「回复对象」这一个横幅
this.setData({ replyTo: { id, name }, cmFocus: true });
},
onCommentFocus() {
// 只复位一次性 focus 开关。键盘高度交给 onKbChange,用 transform 平移弹层来避让
this.setData({ cmFocus: false });
},
// 键盘高度变化(真机可靠来源)。把高度写进 data,
// wxml 里只用它算 transform 平移量 —— 纯合成层操作,不 reflow、不抖焦点
onKbChange(e) {
this.setData({ kbHeight: (e.detail && e.detail.height) || 0 });
},
onCommentBlur() {
// 失焦不主动清 kbHeight:键盘真正收起时 onKbChange 会回调 0,避免和收键盘动画抢
this.setData({ cmFocus: false });
},
onCancelReply() {
this.setData({ replyTo: {} });
},
// 一级评论默认只带 3 条回复,点开拉全量
onExpandReplies(e) {
const { id, index } = e.currentTarget.dataset;
api
.listReplies(id)
.then((list) =>
this.setData({
[`comments[${index}].replies`]: (list || []).map((r) => ({
...r,
timeText: fmtAgo(r.created_at),
initial: (r.author_name || '?').slice(0, 1),
})),
}),
)
.catch((err) => wx.showToast({ title: (err && err.message) || '加载失败', icon: 'none' }));
},
onPreviewCommentImage(e) {
const { urls, cur } = e.currentTarget.dataset;
if (urls && urls.length) wx.previewImage({ urls, current: cur });
},
onRemoveCommentImage(e) {
const list = this.data.commentImages.slice();
list.splice(e.currentTarget.dataset.index, 1);
this.setData({ commentImages: list });
},
},
});