feat: 页面配置+风险规则增加站点类型默认模板+读取默认按钮

This commit is contained in:
mediabot-pt
2026-07-05 01:03:03 +08:00
parent f8c55349f4
commit a2b906ffaa
2 changed files with 103 additions and 4 deletions

View File

@@ -1,7 +1,22 @@
<template>
<div>
<h2>风险规则</h2>
<el-card style="margin-top:20px">
<el-card style="margin-top:12px">
<template #header><span style="font-weight:500">站点类型默认模板</span></template>
<div style="display:flex;gap:12px;flex-wrap:wrap">
<div v-for="dt in defaults" :key="dt.type" style="border:1px solid #ebeef5;border-radius:4px;padding:10px 14px;min-width:200px">
<div style="font-weight:500;margin-bottom:4px"><el-tag size="small">{{ dt.type }}</el-tag></div>
<div style="font-size:12px;color:#909399;margin-bottom:8px">{{ dt.ruleCount }} 条规则</div>
<el-popover trigger="click" width="500" placement="bottom">
<pre style="font-size:11px;max-height:300px;overflow:auto">{{ JSON.stringify(dt.rules, null, 2) }}</pre>
<template #reference><el-button size="small">查看</el-button></template>
</el-popover>
</div>
</div>
</el-card>
<el-card style="margin-top:12px">
<div style="margin-bottom:12px;display:flex;gap:8px">
<el-button @click="fetchAll" :loading="loading">刷新</el-button>
</div>
@@ -23,11 +38,12 @@
<el-table-column label="更新时间" width="150">
<template #default="s">{{ fmt(s.row.updatedAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<el-table-column label="操作" width="180" fixed="right">
<template #default="s">
<el-button size="small" @click="startEdit(s.row)" :type="editSiteId===s.row.siteId?'warning':''">
{{ editSiteId===s.row.siteId ? '收起' : '编辑' }}
</el-button>
<el-button v-if="!s.row.ruleCount" size="small" @click="loadDefaultRules(s.row)">读取默认</el-button>
</template>
</el-table-column>
</el-table>
@@ -76,6 +92,26 @@ const sites = ref([]), rulesMap = ref({})
const editSiteId = ref(''), editSiteType = ref(''), editorMode = ref('table'), editorJson = ref('')
const editingRules = ref([])
const defaultRules = {
nexusphp: [
{type:'KILL', name:'登录间隔超过30天', metric:'daysSinceLastLogin', threshold:30, unit:'DAY', condition:'GT', level:'CRITICAL'},
{type:'RATIO', name:'分享率过低', metric:'shareRatio', threshold:0.3, condition:'LT', level:'CRITICAL', applicable:{daysSinceRegistration:30}},
{type:'TRAFFIC',name:'超过5天无流量', metric:'daysSinceLastTraffic', threshold:5, unit:'DAY', condition:'GT', level:'WARN', applicable:{daysSinceRegistration:30}},
],
mtorrent: [
{type:'KILL', name:'登录过期', metric:'daysSinceLastLogin', threshold:30, unit:'DAY', condition:'GT', level:'CRITICAL'},
],
}
const defaults = computed(() => {
const types = [...new Set((Array.isArray(sites.value)?sites.value:[]).map(s=>s.siteType).filter(Boolean))]
const base = ['nexusphp','mtorrent','gazelle','unit3d']
return [...new Set([...types,...base])].map(t => {
const rules = defaultRules[t] || defaultRules['nexusphp'] || []
return {type:t, ruleCount:rules.length, rules}
})
})
const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-'
watch(editorMode, (m) => {
@@ -113,6 +149,15 @@ const fetchAll = async () => {
} finally { loading.value = false }
}
const loadDefaultRules = (row) => {
editSiteId.value = row.siteId
editSiteType.value = row.siteType
editingRules.value = JSON.parse(JSON.stringify(defaultRules[row.siteType] || defaultRules['nexusphp'] || []))
if (editingRules.value.length === 0) editingRules.value.push({type:'KILL',name:'',metric:'',threshold:0,unit:'DAY',condition:'GT',level:'CRITICAL'})
editorMode.value = 'table'
window.scrollTo({top: document.body.scrollHeight, behavior:'smooth'})
}
const startEdit = (row) => {
if (editSiteId.value === row.siteId) { editSiteId.value = ''; return }
editSiteId.value = row.siteId

View File

@@ -1,7 +1,20 @@
<template>
<div>
<h2>页面配置</h2>
<el-card style="margin-top:20px">
<!-- 站点类型默认模板 -->
<el-card style="margin-top:12px" v-if="defaults.length">
<template #header><span style="font-weight:500">站点类型默认模板</span></template>
<div style="display:flex;gap:12px;flex-wrap:wrap">
<div v-for="dt in defaults" :key="dt.type" style="border:1px solid #ebeef5;border-radius:4px;padding:10px 14px;min-width:200px">
<div style="font-weight:500;margin-bottom:6px">{{ dt.label }} <el-tag size="small">{{ dt.type }}</el-tag></div>
<div style="font-size:12px;color:#909399;margin-bottom:8px">{{ dt.entries.length }} 个入口</div>
<el-button size="small" @click="previewDefault(dt)">查看模板</el-button>
</div>
</div>
</el-card>
<el-card style="margin-top:12px">
<div style="margin-bottom:12px;display:flex;gap:8px">
<el-button @click="fetchAll" :loading="loading">刷新</el-button>
</div>
@@ -20,11 +33,12 @@
<el-table-column label="更新时间" width="150">
<template #default="s">{{ fmt(s.row.updatedAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<el-table-column label="操作" width="180" fixed="right">
<template #default="s">
<el-button size="small" @click="toggleEdit(s.row)" :type="editSiteId===s.row.siteId?'warning':''">
{{ editSiteId===s.row.siteId ? '收起' : '编辑' }}
</el-button>
<el-button v-if="!s.row.entryCount" size="small" @click="loadDefault(s.row)">读取默认</el-button>
</template>
</el-table-column>
@@ -68,6 +82,34 @@ const sites = ref([]), pagesMap = ref({})
const editSiteId = ref(''), editorMode = ref('table'), editorJson = ref('')
const editingEntries = ref([])
const defaultPages = {
nexusphp: [
{key:'home', label:'首页', url:'{base}', method:'GET'},
{key:'search', label:'搜索页', url:'{base}/torrents.php', method:'GET'},
{key:'detail', label:'种子详情', url:'{base}/details.php?id=', method:'GET'},
{key:'signIn', label:'签到页', url:'{base}/attendance.php', method:'POST'},
{key:'user', label:'用户详情', url:'{base}/userdetails.php?id=', method:'GET'},
],
mtorrent: [
{key:'home', label:'首页', url:'{base}/api/member/profile', method:'GET'},
{key:'search',label:'搜索页', url:'{base}/api/torrent/search', method:'POST'},
],
gazelle: [
{key:'home', label:'首页', url:'{base}/index.php', method:'GET'},
{key:'search',label:'搜索', url:'{base}/torrents.php', method:'GET'},
],
unit3d: [
{key:'home', label:'首页', url:'{base}/api/torrents/filter', method:'GET'},
{key:'search',label:'搜索', url:'{base}/api/torrents/filter?name=', method:'GET'},
],
}
const defaults = computed(() => {
const types = [...new Set((Array.isArray(sites.value)?sites.value:[]).map(s=>s.siteType).filter(Boolean))]
const base = ['nexusphp','mtorrent','gazelle','unit3d']
return [...new Set([...types,...base])].map(t => ({type:t, label:t, entries: (defaultPages[t]||[]) }))
})
const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-'
watch(editorMode, (m) => {
@@ -104,6 +146,18 @@ const fetchAll = async () => {
} finally { loading.value = false }
}
const loadDefault = (row) => {
editSiteId.value = row.siteId
editingEntries.value = JSON.parse(JSON.stringify(defaultPages[row.siteType] || defaultPages['nexusphp']))
if (editingEntries.value.length === 0) editingEntries.value.push({key:'home',label:'首页',url:'{base}',method:'GET'})
editorMode.value = 'table'
window.scrollTo({top: document.body.scrollHeight, behavior:'smooth'})
}
const previewDefault = (dt) => {
ElMessageBox.alert(JSON.stringify(dt.entries, null, 2), dt.type + ' 默认模板', { dangerouslyUseHTMLString: false })
}
const toggleEdit = (row) => {
if (editSiteId.value === row.siteId) { editSiteId.value = ''; return }
editSiteId.value = row.siteId