From 7ec4e16eb608201b6c2bed8d74242a5a717d6f6f Mon Sep 17 00:00:00 2001
From: mediabot-pt <295750538+mediabot-pt@users.noreply.github.com>
Date: Mon, 6 Jul 2026 22:35:38 +0800
Subject: [PATCH] feat: add fixed-order rule summary text at bottom of visual
editor
Sort order: EXEMPT \u2192 TASK \u2192 HD \u2192 KILL \u2192 RATIO \u2192 DRATIO \u2192 TRAFFIC
Each rule shown as natural language sentence with applicable condition
TASK shows only enabled requirements
Regardless of card drag order, summary always displays in fixed sequence
---
web/src/views/RiskRules.vue | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/web/src/views/RiskRules.vue b/web/src/views/RiskRules.vue
index 551ba10..cc0110c 100644
--- a/web/src/views/RiskRules.vue
+++ b/web/src/views/RiskRules.vue
@@ -178,6 +178,14 @@
+
+
+
+
规则摘要
+
+ {{ i + 1 }}. {{ ruleText(r) }}
+
+
@@ -367,6 +375,35 @@ const editSiteName = computed(() => {
const site = (sites.value||[]).find(s => s.siteType === editType.value)
return site ? (site.names||[]).join('/') || site.siteId || editType.value : editType.value
})
+
+const typeOrder = ['EXEMPT', 'TASK', 'HD', 'KILL', 'RATIO', 'DRATIO', 'TRAFFIC']
+const sortedRules = computed(() => {
+ return [...parsed.value].sort((a, b) => {
+ const ai = typeOrder.indexOf(a.type), bi = typeOrder.indexOf(b.type)
+ return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi)
+ })
+})
+
+const ruleText = (r) => {
+ const appText = r.applicable ? `(${applicableOptions.find(x=>x.value===r.applicable)?.label||r.applicable})` : ''
+ if (r.type === 'EXEMPT') {
+ return `等级豁免:永久保留 ≥ ${rankDisplay(r.permRank)}、封存豁免 ≥ ${rankDisplay(r.sealExemptRank)} → 保号`
+ }
+ if (r.type === 'TASK') {
+ const reqs = (r.requirements||[]).filter(x => x.enabled).map(x => `${x.label} ≥ ${x.threshold}${x.unit}`).join('、')
+ return `新人考核:${r.threshold||30}天内,${reqs} → 封号/删除账号${appText}`
+ }
+ const metric = metricLabel(r.metric)
+ const cond = condLabel(r.condition)
+ const val = `${metric}${cond}${r.threshold}${r.unit||''}`
+ if (r.type === 'DRATIO') {
+ return `动态分享率:下载量 ≥ ${r.threshold}GB 时分享率 ≥ ${r.minRatio} → 封号(缓刑7天)${appText}`
+ }
+ if (r.type === 'RATIO') {
+ return `分享率:${val} → 封号(缓刑7天)${appText}`
+ }
+ return `${typeLabel(r.type)}:${val} → 封号/删除账号${appText}`
+}
const merged = computed(() => (Array.isArray(sites.value)?sites.value:[]).map(s => ({
siteId: s.siteId, names: s.names, siteType: s.siteType,
ruleCount: rulesMap.value[s.siteType]?.ruleCount || 0, version: rulesMap.value[s.siteType]?.version || null,