1
This commit is contained in:
71
miniprogram/pages/admin-dashboard/index.js
Normal file
71
miniprogram/pages/admin-dashboard/index.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const { request } = require('../../utils/request')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
loading: false,
|
||||
stats: null,
|
||||
kpis: [],
|
||||
bars: [],
|
||||
sourceDist: [],
|
||||
topKeywords: []
|
||||
},
|
||||
|
||||
formatPercent(value, digits = 2) {
|
||||
const num = Number(value || 0)
|
||||
return `${(num * 100).toFixed(digits)}%`
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.fetchStats()
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.fetchStats(true)
|
||||
},
|
||||
|
||||
normalizeKpis(stats) {
|
||||
return [
|
||||
{ label: '系统用户', value: stats.user_count || 0 },
|
||||
{ label: '发布总量', value: stats.post_count || 0 },
|
||||
{ label: '拦截总量', value: stats.blocked_count || 0 },
|
||||
{ label: '待处理申诉', value: stats.pending_appeal_count || 0 },
|
||||
{ label: '训练样本', value: stats.sample_count || 0 },
|
||||
{ label: '近7天拦截率', value: this.formatPercent(stats.blocked_ratio_7d, 2) }
|
||||
]
|
||||
},
|
||||
|
||||
normalizeBars(trend) {
|
||||
const rows = Array.isArray(trend) ? trend : []
|
||||
const maxVal = Math.max(1, ...rows.map((r) => Number(r.post_count || 0)))
|
||||
return rows.map((row) => {
|
||||
const value = Number(row.post_count || 0)
|
||||
const ratio = value / maxVal
|
||||
return {
|
||||
...row,
|
||||
value,
|
||||
percent_text: `${Math.max(6, Math.round(ratio * 100))}%`
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async fetchStats(fromPullDown = false) {
|
||||
this.setData({ loading: true })
|
||||
try {
|
||||
const stats = await request({ url: '/admin/stats' })
|
||||
const normalizedStats = {
|
||||
...stats,
|
||||
threshold_text: stats && stats.threshold ? this.formatPercent(stats.threshold.spam_threshold, 1) : '--'
|
||||
}
|
||||
this.setData({
|
||||
stats: normalizedStats,
|
||||
kpis: this.normalizeKpis(normalizedStats),
|
||||
bars: this.normalizeBars(normalizedStats.trend_7d || []),
|
||||
sourceDist: normalizedStats.source_distribution || [],
|
||||
topKeywords: normalizedStats.top_keywords || []
|
||||
})
|
||||
} finally {
|
||||
this.setData({ loading: false })
|
||||
if (fromPullDown) wx.stopPullDownRefresh()
|
||||
}
|
||||
}
|
||||
})
|
||||
4
miniprogram/pages/admin-dashboard/index.json
Normal file
4
miniprogram/pages/admin-dashboard/index.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "运营看板",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
57
miniprogram/pages/admin-dashboard/index.wxml
Normal file
57
miniprogram/pages/admin-dashboard/index.wxml
Normal file
@@ -0,0 +1,57 @@
|
||||
<view class="container">
|
||||
<view class="hero fade-up">
|
||||
<view class="hero-badge">OPS DASHBOARD</view>
|
||||
<view class="hero-title">垃圾信息运营看板</view>
|
||||
<view class="hero-sub">覆盖发布、拦截、申诉、样本与模型状态,支持日常运营与风险监控。</view>
|
||||
</view>
|
||||
|
||||
<view class="card fade-up fade-up-delay-1" wx:if="{{kpis.length}}">
|
||||
<view class="card-title">核心指标</view>
|
||||
<view class="grid-2">
|
||||
<view class="kpi" wx:for="{{kpis}}" wx:key="label">
|
||||
<view class="kpi-value">{{item.value}}</view>
|
||||
<view class="kpi-label">{{item.label}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card fade-up fade-up-delay-2" wx:if="{{stats && stats.threshold}}">
|
||||
<view class="card-title">检测阈值配置</view>
|
||||
<view class="row"><text class="label">当前阈值</text><text class="value">{{stats.threshold_text}}</text></view>
|
||||
<view class="row"><text class="label">更新时间</text><text class="value">{{stats.threshold.updated_at || '--'}}</text></view>
|
||||
</view>
|
||||
|
||||
<view class="card fade-up fade-up-delay-2" wx:if="{{stats && stats.model_info}}">
|
||||
<view class="card-title">模型信息</view>
|
||||
<view class="row"><text class="label">模型版本</text><text class="value">{{stats.model_info.version || '未训练'}}</text></view>
|
||||
<view class="row"><text class="label">训练时间</text><text class="value">{{stats.model_info.trained_at || '--'}}</text></view>
|
||||
<view class="row"><text class="label">样本数量</text><text class="value">{{stats.model_info.sample_count || 0}}</text></view>
|
||||
</view>
|
||||
|
||||
<view class="card fade-up fade-up-delay-3" wx:if="{{bars.length}}">
|
||||
<view class="card-title">近 7 天发布趋势</view>
|
||||
<view class="list-item" wx:for="{{bars}}" wx:key="date">
|
||||
<view class="row">
|
||||
<text class="label">{{item.label}}</text>
|
||||
<text class="value">{{item.value}} 条</text>
|
||||
</view>
|
||||
<view class="progress-track">
|
||||
<view class="progress-fill-safe" style="width: {{item.percent_text}};"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card fade-up fade-up-delay-3" wx:if="{{sourceDist.length}}">
|
||||
<view class="card-title">训练样本来源</view>
|
||||
<view class="list-item" wx:for="{{sourceDist}}" wx:key="name">
|
||||
<view class="row"><text class="item-title">{{item.name}}</text><text class="value">{{item.value}}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card fade-up fade-up-delay-3" wx:if="{{topKeywords.length}}">
|
||||
<view class="card-title">高频风险词</view>
|
||||
<view class="chip-group">
|
||||
<text class="tag" wx:for="{{topKeywords}}" wx:key="token">{{item.token}} × {{item.count}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
1
miniprogram/pages/admin-dashboard/index.wxss
Normal file
1
miniprogram/pages/admin-dashboard/index.wxss
Normal file
@@ -0,0 +1 @@
|
||||
/* admin-dashboard styles use global theme */
|
||||
Reference in New Issue
Block a user