const { request, uploadFile } = require('../../utils/request') const STATUS_OPTIONS = [ { value: '', label: '全部状态' }, { value: 'published', label: '发布成功' }, { value: 'blocked', label: '已拦截' } ] const VISIBILITY_OPTIONS = [ { value: '', label: '全部类型' }, { value: 'public', label: '公开' }, { value: 'private', label: '私有' }, { value: 'direct', label: '私信' } ] const VIS_LABEL = { public: '公开信息', private: '私有信息', direct: '用户私信' } const APPEAL_STATUS_TEXT = { none: '未发起', pending: '处理中', approved: '已通过', rejected: '已驳回' } const REASON_TYPE_OPTIONS = [ { value: '', label: '请选择申诉理由类型' }, { value: '正常活动文案', label: '正常活动文案' }, { value: '正常社区通知', label: '正常社区通知' }, { value: '私信沟通内容', label: '私信沟通内容' }, { value: '其他', label: '其他(需手动填写)' } ] Page({ data: { loading: false, list: [], statusOptions: STATUS_OPTIONS, statusIndex: 0, visibilityOptions: VISIBILITY_OPTIONS, visibilityIndex: 0, appealPostId: null, appealReasonType: '', appealReasonTypeOptions: REASON_TYPE_OPTIONS, appealReasonTypeIndex: 0, appealReason: '', appealEvidenceUrls: [], appealEvidenceFiles: [] }, formatPercent(value, digits = 2) { const num = Number(value || 0) return `${(num * 100).toFixed(digits)}%` }, onShow() { this.fetchList() }, onPullDownRefresh() { this.fetchList(true) }, onStatusChange(e) { this.setData({ statusIndex: Number(e.detail.value || 0) }) this.fetchList() }, onVisibilityChange(e) { this.setData({ visibilityIndex: Number(e.detail.value || 0) }) this.fetchList() }, async fetchList(fromPullDown = false) { this.setData({ loading: true }) try { const status = this.data.statusOptions[this.data.statusIndex].value const visibility = this.data.visibilityOptions[this.data.visibilityIndex].value const query = `status=${encodeURIComponent(status)}&visibility=${encodeURIComponent(visibility)}&page=1&page_size=80` const data = await request({ url: `/content/posts/history?${query}` }) const list = (data.items || []).map((item) => ({ ...item, created_text: (item.created_at || '').replace('T', ' ').slice(0, 19), spam_probability_text: this.formatPercent(item.spam_probability, 2), visibility_text: VIS_LABEL[item.visibility] || item.visibility, appeal_status_text: APPEAL_STATUS_TEXT[item.appeal_status] || item.appeal_status })) this.setData({ list }) } finally { this.setData({ loading: false }) if (fromPullDown) wx.stopPullDownRefresh() } }, startAppeal(e) { const postId = Number(e.currentTarget.dataset.id) this.setData({ appealPostId: postId, appealReasonType: '', appealReasonTypeIndex: 0, appealReason: '', appealEvidenceUrls: [], appealEvidenceFiles: [] }) }, onReasonTypeChange(e) { const idx = Number(e.detail.value || 0) const reasonType = this.data.appealReasonTypeOptions[idx].value this.setData({ appealReasonTypeIndex: idx, appealReasonType: reasonType }) // 如果选择"其他",清空快捷理由,让用户手动输入 if (reasonType === '其他') { this.setData({ appealReasonType: '' }) } }, onAppealInput(e) { this.setData({ appealReason: e.detail.value || '' }) }, chooseEvidence() { wx.chooseMedia({ count: 3, mediaType: ['image'], sourceType: ['album', 'camera'], success: (res) => { const files = res.tempFiles.map((f) => f.tempFilePath) this.setData({ appealEvidenceFiles: [...this.data.appealEvidenceFiles, ...files].slice(0, 3) }) } }) }, removeEvidence(e) { const idx = Number(e.currentTarget.dataset.index || 0) const files = this.data.appealEvidenceFiles.filter((_, i) => i !== idx) const urls = this.data.appealEvidenceUrls.filter((_, i) => i !== idx) this.setData({ appealEvidenceFiles: files, appealEvidenceUrls: urls }) }, async uploadAllEvidence() { const files = this.data.appealEvidenceFiles if (!files.length) return [] const urls = [] for (const filePath of files) { try { const result = await uploadFile(filePath) urls.push(result.url) } catch (err) { console.error('上传失败', filePath, err) } } return urls }, cancelAppeal() { this.setData({ appealPostId: null, appealReasonType: '', appealReasonTypeIndex: 0, appealReason: '', appealEvidenceUrls: [], appealEvidenceFiles: [] }) }, async submitAppeal() { const postId = this.data.appealPostId if (!postId) return const reasonType = (this.data.appealReasonType || '').trim() const reason = (this.data.appealReason || '').trim() // 如果没有选择快捷理由类型,必须手动填写理由 if (!reasonType && reason.length < 2) { wx.showToast({ title: '申诉理由至少 2 个字符', icon: 'none' }) return } // 上传证据图片 const evidenceUrls = await this.uploadAllEvidence() await request({ url: `/content/posts/${postId}/appeal`, method: 'POST', data: { reason_type: reasonType, reason, evidence_urls: evidenceUrls } }) wx.showToast({ title: '申诉提交成功', icon: 'success' }) this.setData({ appealPostId: null, appealReasonType: '', appealReasonTypeIndex: 0, appealReason: '', appealEvidenceUrls: [], appealEvidenceFiles: [] }) this.fetchList() }, removeItem(e) { const id = Number(e.currentTarget.dataset.id) wx.showModal({ title: '删除确认', content: '确定删除这条发布记录吗?', success: async (res) => { if (!res.confirm) return await request({ url: `/content/posts/${id}`, method: 'DELETE' }) wx.showToast({ title: '删除成功', icon: 'success' }) this.fetchList() } }) } })