feat: 页面配置+风险规则增加站点类型默认模板+读取默认按钮
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user