feat: DRATIO supports multiple threshold-ratio levels
Replace single threshold+minRatio with levels array Card header: \u4e0b\u8f7d\u91cf\u5206\u7ea7\u63a7\u5236\uff082\u7ea7\uff09 Card body: editable rows with add/delete buttons per level Default preset: 100GB\u21921.0, 500GB\u21922.0, 1000GB\u21923.0 Auto-migrate old single-value format on load
This commit is contained in:
@@ -87,7 +87,7 @@
|
||||
<el-icon class="drag-handle"><Rank /></el-icon>
|
||||
<span class="type-tag">{{ typeLabel(rule.type) }}</span>
|
||||
<span v-if="rule.type==='TASK'" class="rule-fixed-text">考核周期 {{ rule.threshold }}天</span>
|
||||
<span v-else-if="rule.type==='DRATIO'" class="rule-fixed-text">下载量 ≥ {{ rule.threshold }}GB 时分享率 ≥ {{ rule.minRatio }}</span>
|
||||
<span v-else-if="rule.type==='DRATIO'" class="rule-fixed-text">下载量分级控制({{ (rule.levels||[]).length }} 级)</span>
|
||||
<span v-else-if="rule.type==='EXEMPT'" class="rule-fixed-text">永久保留 ≥ {{ rankDisplay(rule.permRank) }} / 封存豁免 ≥ {{ rankDisplay(rule.sealExemptRank) }}</span>
|
||||
<span v-else class="rule-fixed-text">{{ metricLabel(rule.metric) }} {{ condLabel(rule.condition) }} {{ rule.threshold }}{{ rule.unit || '' }}</span>
|
||||
<span class="then-arrow" :style="rule.type==='EXEMPT'?'color:#67c23a':rule.type==='RATIO'||rule.type==='DRATIO'?'color:#e6a23c':''">→</span>
|
||||
@@ -125,16 +125,16 @@
|
||||
|
||||
<!-- DRATIO 动态分享率 -->
|
||||
<template v-else-if="rule.type==='DRATIO'">
|
||||
<div class="field-row">
|
||||
<span class="field-label">下载量阈值</span>
|
||||
<el-input v-model.number="rule.threshold" size="small" type="number" placeholder="GB" @input="sync" style="width:100px" />
|
||||
<span class="field-hint">GB(达到此下载量后检查分享率)</span>
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<span class="field-label">要求分享率</span>
|
||||
<el-input v-model.number="rule.minRatio" size="small" type="number" :step="0.1" placeholder="分享率" @input="sync" style="width:100px" />
|
||||
<span class="field-hint">(分享率低于此值则降级至Peasant,7天缓刑后封号/删除账号)</span>
|
||||
<div v-for="(lv, li) in (rule.levels||[])" :key="li" class="field-row">
|
||||
<span class="field-label">下载量 ≥</span>
|
||||
<el-input v-model.number="lv.threshold" size="small" type="number" placeholder="GB" @input="sync" style="width:90px" />
|
||||
<span class="field-hint">GB 时,分享率 ≥</span>
|
||||
<el-input v-model.number="lv.ratio" size="small" type="number" :step="0.1" placeholder="分享率" @input="sync" style="width:80px" />
|
||||
<el-button size="small" type="danger" text :icon="Delete" @click="removeDRatioLevel(rule, li)" />
|
||||
</div>
|
||||
<el-button size="small" text type="primary" @click="addDRatioLevel(rule)" style="align-self:flex-start">
|
||||
<el-icon style="margin-right:2px"><Plus /></el-icon>添加等级
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<!-- EXEMPT 等级豁免 -->
|
||||
@@ -182,7 +182,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ArrowDown, ArrowUp, Rank, Delete, Refresh } from '@element-plus/icons-vue'
|
||||
import { ArrowDown, ArrowUp, Rank, Delete, Refresh, Plus } from '@element-plus/icons-vue'
|
||||
import api from '../api'
|
||||
|
||||
// ============ 选项配置 ============
|
||||
@@ -270,7 +270,7 @@ const presets = [
|
||||
// 新人考核
|
||||
{ name:'新人考核', type:'TASK', threshold:30, unit:'天', description:'新注册用户需在考核周期内完成各项要求' },
|
||||
// 动态分享率
|
||||
{ name:'动态分享率', type:'DRATIO', threshold:100, minRatio:1.0, unit:'GB', description:'下载量达到阈值时分享率不低于要求值' },
|
||||
{ name:'动态分享率', type:'DRATIO', levels:[{threshold:100,ratio:1.0},{threshold:500,ratio:2.0},{threshold:1000,ratio:3.0}], description:'下载量分级控制分享率' },
|
||||
// 等级豁免
|
||||
{ name:'等级豁免', type:'EXEMPT', permRank:'Elite User', sealExemptRank:'Power User', description:'达到等级的账号可豁免清理' },
|
||||
]
|
||||
@@ -284,7 +284,20 @@ const dragIdx = ref(null)
|
||||
const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-'
|
||||
|
||||
const parsed = computed({
|
||||
get() { try { const arr = JSON.parse(editorJson.value); return Array.isArray(arr) ? arr : [] } catch { return [] } },
|
||||
get() {
|
||||
try {
|
||||
const arr = JSON.parse(editorJson.value)
|
||||
if (!Array.isArray(arr)) return []
|
||||
// migrate old DRATIO format
|
||||
for (const r of arr) {
|
||||
if (r.type === 'DRATIO' && !r.levels && r.threshold !== undefined) {
|
||||
r.levels = [{ threshold: r.threshold || 100, ratio: r.minRatio || 1.0 }]
|
||||
delete r.threshold; delete r.minRatio
|
||||
}
|
||||
}
|
||||
return arr
|
||||
} catch { return [] }
|
||||
},
|
||||
set(v) { editorJson.value = JSON.stringify(v, null, 2) }
|
||||
})
|
||||
|
||||
@@ -314,9 +327,12 @@ const ruleText = (r) => {
|
||||
const metric = metricLabel(r.metric)
|
||||
const cond = condLabel(r.condition)
|
||||
const val = `${metric}${cond}${r.threshold}${r.unit||''}`
|
||||
if (r.type === 'DRATIO' || r.type === 'RATIO') {
|
||||
const prefix = r.type === 'DRATIO' ? `动态分享率:下载量 ≥ ${r.threshold}GB 时分享率 ≥ ${r.minRatio}` : `分享率:${val}`
|
||||
return `${prefix} → <span style="color:#e6a23c;font-weight:500">封号(缓刑7天)</span>${appText}`
|
||||
if (r.type === 'DRATIO') {
|
||||
const levels = (r.levels||[]).map(l => `下载量≥${l.threshold}GB时分享率≥${l.ratio}`).join(',')
|
||||
return `动态分享率:${levels} → <span style="color:#e6a23c;font-weight:500">封号(缓刑7天)</span>${appText}`
|
||||
}
|
||||
if (r.type === 'RATIO') {
|
||||
return `分享率:${val} → <span style="color:#e6a23c;font-weight:500">封号(缓刑7天)</span>${appText}`
|
||||
}
|
||||
return `${typeLabel(r.type)}:${val} → <span style="color:#f56c6c;font-weight:500">封号/删除账号</span>${appText}`
|
||||
}
|
||||
@@ -364,6 +380,24 @@ const ensureTaskReqs = (rule) => {
|
||||
}
|
||||
}
|
||||
|
||||
const addDRatioLevel = (rule) => {
|
||||
if (!rule.levels) rule.levels = []
|
||||
rule.levels.push({ threshold: 100, ratio: 1.0 })
|
||||
sync()
|
||||
}
|
||||
const removeDRatioLevel = (rule, li) => {
|
||||
rule.levels.splice(li, 1)
|
||||
sync()
|
||||
}
|
||||
const ensureDRatioLevels = (rule) => {
|
||||
if (rule.type === 'DRATIO') {
|
||||
if (rule.levels && rule.levels.length) return
|
||||
// migrate old single-value format
|
||||
rule.levels = [{ threshold: rule.threshold || 100, ratio: rule.minRatio || 1.0 }]
|
||||
delete rule.threshold; delete rule.minRatio
|
||||
}
|
||||
}
|
||||
|
||||
const ruleKey = (r) => `${r.type}|${r.applicable||''}`
|
||||
|
||||
const existsRule = (rule) => {
|
||||
@@ -373,6 +407,7 @@ const existsRule = (rule) => {
|
||||
const addPreset = (p) => {
|
||||
const r = { ...p, _fold: false }
|
||||
ensureTaskReqs(r)
|
||||
ensureDRatioLevels(r)
|
||||
if (existsRule(r)) {
|
||||
ElMessage.warning(`规则「${p.name}」已存在,同类型+适用条件只能添加一次`)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user