feat: 申诉常见理由快捷选择 + 证据截图上传

- 后端: 新增 appeal_reason_type, appeal_evidence_urls 字段
- 后端: 新建 upload_routes.py 图片上传接口
- 前端: history 页面添加快捷理由选择器 + 截图上传
- 前端: admin-review 页面展示证据图片 + 点击预览
- 新增 SQL 更新脚本 update_appeal_fields.sql

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
刘正航
2026-04-21 23:26:25 +08:00
parent 50440e84fb
commit f7d0601c4e
12 changed files with 344 additions and 13 deletions

View File

@@ -110,10 +110,15 @@ Page({
},
normalizeAppeals(rows = []) {
const baseURL = getApp().globalData.baseURL || 'http://127.0.0.1:5000/api'
const serverBase = baseURL.replace('/api', '')
return rows.map((item) => ({
...item,
created_text: (item.created_at || '').replace('T', ' ').slice(0, 19),
appeal_status_text: APPEAL_STATUS_TEXT[item.appeal_status] || item.appeal_status
appeal_status_text: APPEAL_STATUS_TEXT[item.appeal_status] || item.appeal_status,
appeal_evidence_urls: (item.appeal_evidence_urls || []).map((url) =>
url.startsWith('http') ? url : `${serverBase}${url}`
)
}))
},
@@ -155,8 +160,10 @@ Page({
].join('&')
const data = await request({ url: `/admin/appeals?${query}` })
const normalizedAppeals = this.normalizeAppeals(data.items || [])
console.log('normalized appeals:', normalizedAppeals)
this.setData({
appeals: this.normalizeAppeals(data.items || []),
appeals: normalizedAppeals,
appealPager: buildPager(data.total || 0, appealPager.page, appealPager.pageSize)
})
},
@@ -294,5 +301,15 @@ Page({
wx.showToast({ title: '申诉处理完成', icon: 'success' })
this.setData({ [`appealNoteMap.${id}`]: '' })
await Promise.all([this.fetchIntercepts(), this.fetchAppeals()])
},
previewEvidence(e) {
const url = e.currentTarget.dataset.url
const item = this.data.appeals.find((a) => a.appeal_evidence_urls && a.appeal_evidence_urls.includes(url))
const urls = item ? item.appeal_evidence_urls : [url]
wx.previewImage({
current: url,
urls
})
}
})

View File

@@ -100,9 +100,19 @@
<view class="list-item" wx:for="{{appeals}}" wx:key="id">
<view class="item-title">{{item.text}}</view>
<view class="item-sub">申诉人:{{item.nickname || item.username}} · 当前状态:{{item.appeal_status_text}}</view>
<view class="item-sub">申诉理由类型:{{item.appeal_reason_type || '未选择'}}</view>
<view class="item-sub">申诉理由:{{item.appeal_reason || '未填写'}}</view>
<view class="item-sub">时间:{{item.created_text}}</view>
<view class="field" wx:if="{{item.appeal_evidence_urls && item.appeal_evidence_urls.length}}">
<text class="field-label">证据截图</text>
<view class="evidence-grid">
<view class="evidence-item" wx:for="{{item.appeal_evidence_urls}}" wx:for-item="evidenceUrl" wx:key="*this">
<image class="evidence-thumb evidence-clickable" src="{{evidenceUrl}}" mode="aspectFill" data-url="{{evidenceUrl}}" bindtap="previewEvidence" />
</view>
</view>
</view>
<textarea class="textarea note-textarea" placeholder="可填写申诉处理备注" value="{{appealNoteMap[item.id] || ''}}" data-id="{{item.id}}" bindinput="onAppealNoteInput" />
<view class="btn-row">