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
This commit is contained in:
mediabot-pt
2026-07-06 22:35:38 +08:00
parent 15dcccb405
commit 7ec4e16eb6

View File

@@ -178,6 +178,14 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 规则摘要(固定排序) -->
<div v-if="parsed.length" style="margin-top:16px;padding:10px 12px;background:#fafafa;border-radius:4px;font-size:13px;line-height:1.8;color:#303133">
<div style="font-weight:600;margin-bottom:6px;color:#606266;font-size:12px">规则摘要</div>
<div v-for="(r, i) in sortedRules" :key="i">
<span style="font-weight:500">{{ i + 1 }}.</span> {{ ruleText(r) }}
</div>
</div>
</div> </div>
</div> </div>
@@ -367,6 +375,35 @@ const editSiteName = computed(() => {
const site = (sites.value||[]).find(s => s.siteType === editType.value) const site = (sites.value||[]).find(s => s.siteType === editType.value)
return site ? (site.names||[]).join('/') || site.siteId || editType.value : 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 => ({ const merged = computed(() => (Array.isArray(sites.value)?sites.value:[]).map(s => ({
siteId: s.siteId, names: s.names, siteType: s.siteType, siteId: s.siteId, names: s.names, siteType: s.siteType,
ruleCount: rulesMap.value[s.siteType]?.ruleCount || 0, version: rulesMap.value[s.siteType]?.version || null, ruleCount: rulesMap.value[s.siteType]?.ruleCount || 0, version: rulesMap.value[s.siteType]?.version || null,