142 lines
3.9 KiB
JavaScript
142 lines
3.9 KiB
JavaScript
/**
|
||||
|
|
* 申诉提交页(重构:用 appealService.submitAppeal + ui-nav-bar + ui-textarea)
|
|||
|
|
*
|
|||
|
|
* 用户针对封禁/暂停处置提交申诉。类型由入口决定(ban_appeal / suspend_appeal)。
|
|||
|
|
*
|
|||
|
|
* 不变量(AGENTS.md):
|
|||
|
|
* - 第 8 条:敏感数据默认拒绝——本页只接收申诉原因 + 证据截图 + 联系方式,
|
|||
|
|
* 不接受身份证号等敏感字段;联系方式为可选。
|
|||
|
|
* - 第 4 条:Block 优先级最高——申诉不会自动解封或解除拉黑,由运营人工处置。
|
|||
|
|
* 幂等:appealService.submitAppeal 内部以 appealType+relatedActionId+秒级时间生成 Idempotency-Key。
|
|||
|
|
*/
|
|||
|
|
const { appStore } = require('../../stores');
|
|||
|
|
const { appealService } = require('../../services');
|
|||
|
|
|
|||
|
|
const TYPE_LABELS = {
|
|||
|
|
ban_appeal: '封禁申诉',
|
|||
|
|
suspend_appeal: '暂停申诉',
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const MIN_REASON_LEN = 10;
|
|||
|
|
const MAX_EVIDENCE = 5;
|
|||
|
|
|
|||
|
|
Page({
|
|||
|
|
data: {
|
|||
|
|
appealType: '',
|
|||
|
|
actionId: '',
|
|||
|
|
typeLabel: '申诉',
|
|||
|
|
reason: '',
|
|||
|
|
reasonError: '',
|
|||
|
|
evidenceFiles: [],
|
|||
|
|
contactMethod: '',
|
|||
|
|
submitting: false,
|
|||
|
|
submitted: false,
|
|||
|
|
statusBarHeight: 20,
|
|||
|
|
navBarHeight: 44,
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onLoad(options) {
|
|||
|
|
try {
|
|||
|
|
const info = (wx.getWindowInfo && wx.getWindowInfo()) || wx.getSystemInfoSync();
|
|||
|
|
this.setData({
|
|||
|
|
statusBarHeight: info.statusBarHeight || 20,
|
|||
|
|
navBarHeight: info.platform === 'android' ? 48 : 44,
|
|||
|
|
});
|
|||
|
|
} catch (e) { /* 使用默认值 */ }
|
|||
|
|
|
|||
|
|
const appealType = options.type ? decodeURIComponent(options.type) : '';
|
|||
|
|
const actionId = options.actionId ? decodeURIComponent(options.actionId) : '';
|
|||
|
|
this.setData({
|
|||
|
|
appealType,
|
|||
|
|
actionId,
|
|||
|
|
typeLabel: TYPE_LABELS[appealType] || '申诉',
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onReasonInput(e) {
|
|||
|
|
this.setData({ reason: e.detail.value, reasonError: '' });
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onContactInput(e) {
|
|||
|
|
this.setData({ contactMethod: e.detail.value });
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// ============ 证据上传 ============
|
|||
|
|
|
|||
|
|
chooseEvidence() {
|
|||
|
|
const remain = MAX_EVIDENCE - this.data.evidenceFiles.length;
|
|||
|
|
if (remain <= 0) {
|
|||
|
|
appStore.toast('最多上传 5 张证据');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
wx.chooseMedia({
|
|||
|
|
count: remain,
|
|||
|
|
mediaType: ['image'],
|
|||
|
|
sourceType: ['album', 'camera'],
|
|||
|
|
success: (res) => {
|
|||
|
|
const files = res.tempFiles.map((f) => f.tempFilePath);
|
|||
|
|
this.setData({ evidenceFiles: [...this.data.evidenceFiles, ...files] });
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
previewImage(e) {
|
|||
|
|
wx.previewImage({
|
|||
|
|
current: e.currentTarget.dataset.url,
|
|||
|
|
urls: this.data.evidenceFiles,
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
removeImage(e) {
|
|||
|
|
const idx = e.currentTarget.dataset.index;
|
|||
|
|
const files = this.data.evidenceFiles;
|
|||
|
|
files.splice(idx, 1);
|
|||
|
|
this.setData({ evidenceFiles: files });
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// ============ 提交 ============
|
|||
|
|
|
|||
|
|
async submitAppeal() {
|
|||
|
|
const { appealType, actionId, reason, contactMethod } = this.data;
|
|||
|
|
const trimmedReason = (reason || '').trim();
|
|||
|
|
|
|||
|
|
if (!appealType) {
|
|||
|
|
appStore.toast('缺少申诉类型');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (!trimmedReason) {
|
|||
|
|
this.setData({ reasonError: '请填写申诉原因' });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (trimmedReason.length < MIN_REASON_LEN) {
|
|||
|
|
this.setData({ reasonError: `申诉原因请至少 ${MIN_REASON_LEN} 个字` });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.setData({ submitting: true });
|
|||
|
|
try {
|
|||
|
|
await appealService.submitAppeal({
|
|||
|
|
appealType,
|
|||
|
|
relatedActionId: actionId || undefined,
|
|||
|
|
reason: trimmedReason,
|
|||
|
|
// 证据上传至 OSS 后回填 fileId;当前阶段先以本地路径占位,由后端证据服务接入后补全
|
|||
|
|
evidenceFileIds: [],
|
|||
|
|
contactMethod: (contactMethod || '').trim() || undefined,
|
|||
|
|
});
|
|||
|
|
this.setData({ submitted: true, submitting: false });
|
|||
|
|
appStore.toastSuccess('申诉已提交');
|
|||
|
|
} catch (err) {
|
|||
|
|
appStore.toastError(err);
|
|||
|
|
this.setData({ submitting: false });
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
goBack() {
|
|||
|
|
wx.navigateBack();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onBack() {
|
|||
|
|
wx.navigateBack();
|
|||
|
|
},
|
|||
|
|
});
|