feat: 小程序前端显示分类标签

各页面增加垃圾信息分类标签显示:
- 检测结果页显示分类标签
- 批量识别页和CSV导出增加分类标签列
- 历史记录页显示分类标签
- 管理后台审核页显示分类标签

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
刘正航
2026-04-22 21:52:17 +08:00
parent cedfd066c4
commit 2dcd7ce9f6
7 changed files with 38 additions and 4 deletions

View File

@@ -35,6 +35,13 @@ const APPEAL_STATUS_TEXT = {
rejected: '已驳回' rejected: '已驳回'
} }
const CATEGORY_LABELS = {
fraud: '疑似诈骗',
harassment: '疑似骚扰',
advertisement: '疑似广告',
spam: '疑似垃圾'
}
function buildPager(total, page, pageSize) { function buildPager(total, page, pageSize) {
const totalValue = Number(total || 0) const totalValue = Number(total || 0)
const totalPages = Math.max(Math.ceil(totalValue / pageSize), 1) const totalPages = Math.max(Math.ceil(totalValue / pageSize), 1)
@@ -105,7 +112,8 @@ Page({
created_text: (item.created_at || '').replace('T', ' ').slice(0, 19), created_text: (item.created_at || '').replace('T', ' ').slice(0, 19),
review_status_text: REVIEW_STATUS_TEXT[item.manual_review_status] || item.manual_review_status, review_status_text: REVIEW_STATUS_TEXT[item.manual_review_status] || item.manual_review_status,
spam_probability_text: this.formatPercent(item.spam_probability, 2), spam_probability_text: this.formatPercent(item.spam_probability, 2),
appeal_status_text: APPEAL_STATUS_TEXT[item.appeal_status] || item.appeal_status appeal_status_text: APPEAL_STATUS_TEXT[item.appeal_status] || item.appeal_status,
category_label: CATEGORY_LABELS[item.category] || ''
})) }))
}, },
@@ -116,6 +124,7 @@ Page({
...item, ...item,
created_text: (item.created_at || '').replace('T', ' ').slice(0, 19), 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,
category_label: CATEGORY_LABELS[item.category] || '',
appeal_evidence_urls: (item.appeal_evidence_urls || []).map((url) => appeal_evidence_urls: (item.appeal_evidence_urls || []).map((url) =>
url.startsWith('http') ? url : `${serverBase}${url}` url.startsWith('http') ? url : `${serverBase}${url}`
) )

View File

@@ -51,6 +51,7 @@
<view class="list-item" wx:for="{{intercepts}}" wx:key="id"> <view class="list-item" wx:for="{{intercepts}}" wx:key="id">
<view class="item-title">{{item.text}}</view> <view class="item-title">{{item.text}}</view>
<view class="item-sub">用户:{{item.nickname || item.username}} · 垃圾概率:{{item.spam_probability_text}}</view> <view class="item-sub">用户:{{item.nickname || item.username}} · 垃圾概率:{{item.spam_probability_text}}</view>
<view class="item-sub" wx:if="{{item.category_label}}">分类标签:<text class="status-spam">{{item.category_label}}</text></view>
<view class="item-sub">复核状态:{{item.review_status_text}} · 申诉状态:{{item.appeal_status_text}}</view> <view class="item-sub">复核状态:{{item.review_status_text}} · 申诉状态:{{item.appeal_status_text}}</view>
<view class="item-sub">发布时间:{{item.created_text}}</view> <view class="item-sub">发布时间:{{item.created_text}}</view>
@@ -100,6 +101,7 @@
<view class="list-item" wx:for="{{appeals}}" wx:key="id"> <view class="list-item" wx:for="{{appeals}}" wx:key="id">
<view class="item-title">{{item.text}}</view> <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.nickname || item.username}} · 当前状态:{{item.appeal_status_text}}</view>
<view class="item-sub" wx:if="{{item.category_label}}">分类标签:<text class="status-spam">{{item.category_label}}</text></view>
<view class="item-sub">申诉理由类型:{{item.appeal_reason_type || '未选择'}}</view> <view class="item-sub">申诉理由类型:{{item.appeal_reason_type || '未选择'}}</view>
<view class="item-sub">申诉理由:{{item.appeal_reason || '未填写'}}</view> <view class="item-sub">申诉理由:{{item.appeal_reason || '未填写'}}</view>
<view class="item-sub">时间:{{item.created_text}}</view> <view class="item-sub">时间:{{item.created_text}}</view>

View File

@@ -85,9 +85,10 @@ Page({
const items = this.data.items const items = this.data.items
if (!items.length) return '' if (!items.length) return ''
const headers = ['文本', '判定结果', '置信度', '垃圾概率', '正常概率', '风险关键词'] const headers = ['文本', '判定结果', '分类标签', '置信度', '垃圾概率', '正常概率', '风险关键词']
const rows = items.map((item) => { const rows = items.map((item) => {
const prediction = item.prediction === 'spam' ? '垃圾信息' : '正常信息' const prediction = item.prediction === 'spam' ? '垃圾信息' : '正常信息'
const categoryLabel = item.category_label || ''
const confidence = item.confidence_text || '0%' const confidence = item.confidence_text || '0%'
const spamProb = this.formatPercent(item.spam_probability, 4) const spamProb = this.formatPercent(item.spam_probability, 4)
const hamProb = this.formatPercent(item.ham_probability, 4) const hamProb = this.formatPercent(item.ham_probability, 4)
@@ -95,7 +96,7 @@ Page({
// CSV 转义:文本中的逗号和换行需要处理 // CSV 转义:文本中的逗号和换行需要处理
const text = (item.text || '').replace(/"/g, '""') const text = (item.text || '').replace(/"/g, '""')
const tokensEscaped = tokens.replace(/"/g, '""') const tokensEscaped = tokens.replace(/"/g, '""')
return `"${text}","${prediction}","${confidence}","${spamProb}","${hamProb}","${tokensEscaped}"` return `"${text}","${prediction}","${categoryLabel}","${confidence}","${spamProb}","${hamProb}","${tokensEscaped}"`
}) })
return [headers.join(','), ...rows].join('\n') return [headers.join(','), ...rows].join('\n')

View File

@@ -66,6 +66,10 @@
<text class="label">判定结果</text> <text class="label">判定结果</text>
<text class="{{item.prediction === 'spam' ? 'status-spam' : 'status-ham'}}">{{item.prediction_text}}</text> <text class="{{item.prediction === 'spam' ? 'status-spam' : 'status-ham'}}">{{item.prediction_text}}</text>
</view> </view>
<view class="row" wx:if="{{item.category_label}}">
<text class="label">分类标签</text>
<text class="status-spam">{{item.category_label}}</text>
</view>
<view class="row"> <view class="row">
<text class="label">置信度</text> <text class="label">置信度</text>
<text class="value">{{item.confidence_text}}</text> <text class="value">{{item.confidence_text}}</text>

View File

@@ -46,6 +46,11 @@
<text class="{{result.publish_allowed ? 'status-ham' : 'status-spam'}}">{{result.publish_allowed ? '发布成功' : '已拦截,需申诉'}}</text> <text class="{{result.publish_allowed ? 'status-ham' : 'status-spam'}}">{{result.publish_allowed ? '发布成功' : '已拦截,需申诉'}}</text>
</view> </view>
<view class="row" wx:if="{{result.detect.category_label}}">
<text class="label">分类标签</text>
<text class="status-spam">{{result.detect.category_label}}</text>
</view>
<view class="row"> <view class="row">
<text class="label">模型判断</text> <text class="label">模型判断</text>
<text class="value">{{result.detect.prediction_text}}</text> <text class="value">{{result.detect.prediction_text}}</text>

View File

@@ -26,6 +26,13 @@ const APPEAL_STATUS_TEXT = {
rejected: '已驳回' rejected: '已驳回'
} }
const CATEGORY_LABELS = {
fraud: '疑似诈骗',
harassment: '疑似骚扰',
advertisement: '疑似广告',
spam: '疑似垃圾'
}
const REASON_TYPE_OPTIONS = [ const REASON_TYPE_OPTIONS = [
{ value: '', label: '请选择申诉理由类型' }, { value: '', label: '请选择申诉理由类型' },
{ value: '正常活动文案', label: '正常活动文案' }, { value: '正常活动文案', label: '正常活动文案' },
@@ -86,7 +93,8 @@ Page({
created_text: (item.created_at || '').replace('T', ' ').slice(0, 19), created_text: (item.created_at || '').replace('T', ' ').slice(0, 19),
spam_probability_text: this.formatPercent(item.spam_probability, 2), spam_probability_text: this.formatPercent(item.spam_probability, 2),
visibility_text: VIS_LABEL[item.visibility] || item.visibility, visibility_text: VIS_LABEL[item.visibility] || item.visibility,
appeal_status_text: APPEAL_STATUS_TEXT[item.appeal_status] || item.appeal_status appeal_status_text: APPEAL_STATUS_TEXT[item.appeal_status] || item.appeal_status,
category_label: CATEGORY_LABELS[item.category] || ''
})) }))
this.setData({ list }) this.setData({ list })
} finally { } finally {

View File

@@ -35,6 +35,11 @@
<text class="{{item.status === 'blocked' ? 'status-spam' : 'status-ham'}}">{{item.status === 'blocked' ? '已拦截' : '已发布'}}</text> <text class="{{item.status === 'blocked' ? 'status-spam' : 'status-ham'}}">{{item.status === 'blocked' ? '已拦截' : '已发布'}}</text>
</view> </view>
<view class="row" wx:if="{{item.category_label}}">
<text class="label">分类标签</text>
<text class="status-spam">{{item.category_label}}</text>
</view>
<view class="row"> <view class="row">
<text class="label">垃圾概率</text> <text class="label">垃圾概率</text>
<text class="value">{{item.spam_probability_text}}</text> <text class="value">{{item.spam_probability_text}}</text>