fix: 风险规则改为按站点展示,每站点一行
This commit is contained in:
@@ -6,30 +6,36 @@
|
||||
<el-button @click="fetchAll" :loading="loading">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="merged" stripe size="small" v-loading="loading" empty-text="暂无站点类型">
|
||||
<el-table-column prop="siteType" label="站点类型" width="120" />
|
||||
<el-table :data="merged" stripe size="small" v-loading="loading" empty-text="暂无站点">
|
||||
<el-table-column prop="siteId" label="站点" width="140" />
|
||||
<el-table-column label="名称" min-width="130">
|
||||
<template #default="s">{{ (s.row.names||[]).join('/') || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="siteType" label="类型" width="100" />
|
||||
<el-table-column label="规则数" width="80" align="center">
|
||||
<template #default="s">{{ s.row.ruleCount || 0 }}</template>
|
||||
<template #default="s">
|
||||
<el-tag size="small">{{ s.row.ruleCount || 0 }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="版本" width="70" align="center">
|
||||
<template #default="s">{{ s.row.version || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" width="160">
|
||||
<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">
|
||||
<template #default="s">
|
||||
<el-button size="small" @click="toggleEdit(s.row)" :type="editType===s.row.siteType?'warning':''">
|
||||
{{ editType===s.row.siteType ? '收起' : '编辑' }}
|
||||
<el-button size="small" @click="startEdit(s.row)" :type="editSiteId===s.row.siteId?'warning':''">
|
||||
{{ editSiteId===s.row.siteId ? '收起' : '编辑' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
<div v-if="editType" style="margin-top:16px;padding:12px;background:#fafafa;border-radius:4px">
|
||||
<div v-if="editSiteId" style="margin-top:16px;padding:12px;background:#fafafa;border-radius:4px">
|
||||
<div style="margin-bottom:8px;display:flex;gap:8px;align-items:center">
|
||||
<strong>编辑: {{ editType }}</strong>
|
||||
<strong>编辑: {{ editSiteId }} ({{ editSiteType }})</strong>
|
||||
<span style="color:#909399;font-size:12px">修改会影响所有 {{ editSiteType }} 类型的站点</span>
|
||||
<el-radio-group v-model="editorMode" size="small">
|
||||
<el-radio-button label="table">表格</el-radio-button>
|
||||
<el-radio-button label="json">JSON</el-radio-button>
|
||||
@@ -55,7 +61,7 @@
|
||||
</div>
|
||||
<div style="margin-top:8px;display:flex;gap:8px">
|
||||
<el-button size="small" type="primary" @click="saveRules" :loading="saving">保存</el-button>
|
||||
<el-button size="small" @click="editType=''">取消</el-button>
|
||||
<el-button size="small" @click="editSiteId=''">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -66,8 +72,8 @@
|
||||
import api from '../api'
|
||||
|
||||
const loading = ref(false), saving = ref(false)
|
||||
const siteTypes = ref([]), rulesData = ref([])
|
||||
const editType = ref(''), editorMode = ref('table'), editorJson = ref('')
|
||||
const sites = ref([]), rulesMap = ref({})
|
||||
const editSiteId = ref(''), editSiteType = ref(''), editorMode = ref('table'), editorJson = ref('')
|
||||
const editingRules = ref([])
|
||||
|
||||
const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-'
|
||||
@@ -78,44 +84,42 @@ watch(editorMode, (m) => {
|
||||
})
|
||||
|
||||
const merged = computed(() => {
|
||||
const map = {}
|
||||
const rules = Array.isArray(rulesData.value) ? rulesData.value : []
|
||||
for (const r of rules) {
|
||||
const cnt = (() => { try { return JSON.parse(r.rulesJson||'[]').length } catch { return 0 } })()
|
||||
map[r.siteType] = { siteType: r.siteType, version: r.version, ruleCount: cnt, updatedAt: r.updatedAt, rulesJson: r.rulesJson }
|
||||
}
|
||||
for (const t of siteTypes.value) {
|
||||
if (!map[t]) map[t] = { siteType: t, version: null, ruleCount: 0, updatedAt: null, rulesJson: null }
|
||||
}
|
||||
return Object.values(map)
|
||||
return (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,
|
||||
updatedAt: rulesMap.value[s.siteType]?.updatedAt || null,
|
||||
rulesJson: rulesMap.value[s.siteType]?.rulesJson || null,
|
||||
}))
|
||||
})
|
||||
|
||||
const fetchAll = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 站点类型 = 默认四种 + 实际站点数据中提取的去重
|
||||
const defaults = ['nexusphp','mtorrent','gazelle','unit3d']
|
||||
try {
|
||||
const sr = await api.raw.get('/admin/sites')
|
||||
const siteList = Array.isArray(sr?.data) ? sr.data : Array.isArray(sr) ? sr : []
|
||||
const dbTypes = siteList.map(s => s.siteType).filter(Boolean)
|
||||
siteTypes.value = [...new Set([...defaults, ...dbTypes])]
|
||||
} catch {
|
||||
siteTypes.value = defaults
|
||||
const [sr, rr] = await Promise.allSettled([
|
||||
api.raw.get('/admin/sites'),
|
||||
api.raw.get('/admin/risk-rules'),
|
||||
])
|
||||
sites.value = sr.status==='fulfilled' ? (Array.isArray(sr.value?.data)?sr.value.data:sr.value||[]) : []
|
||||
const rulesData = rr.status==='fulfilled' ? (Array.isArray(rr.value?.data)?rr.value.data:rr.value||[]) : []
|
||||
const map = {}
|
||||
for (const r of rulesData) {
|
||||
const cnt = (() => { try { return JSON.parse(r.rulesJson||'[]').length } catch { return 0 } })()
|
||||
map[r.siteType] = { ruleCount: cnt, version: r.version, updatedAt: r.updatedAt, rulesJson: r.rulesJson }
|
||||
}
|
||||
|
||||
const rr = await api.raw.get('/admin/risk-rules').catch(() => ({}))
|
||||
rulesData.value = Array.isArray(rr?.data) ? rr.data : Array.isArray(rr) ? rr : []
|
||||
} catch { ElMessage.error('加载失败') }
|
||||
finally { loading.value = false }
|
||||
rulesMap.value = map
|
||||
} finally { loading.value = false }
|
||||
}
|
||||
|
||||
const toggleEdit = (row) => {
|
||||
if (editType.value === row.siteType) { editType.value = ''; return }
|
||||
editType.value = row.siteType
|
||||
const startEdit = (row) => {
|
||||
if (editSiteId.value === row.siteId) { editSiteId.value = ''; return }
|
||||
editSiteId.value = row.siteId
|
||||
editSiteType.value = row.siteType
|
||||
editorMode.value = 'table'
|
||||
try { editingRules.value = JSON.parse(JSON.stringify(JSON.parse(row.rulesJson||'[]'))) }
|
||||
catch { editingRules.value = [{type:'KILL',name:'',metric:'',threshold:0,unit:'DAY',condition:'GT',level:'CRITICAL'}] }
|
||||
catch { editingRules.value = [] }
|
||||
if (editingRules.value.length===0) editingRules.value.push({type:'KILL',name:'',metric:'',threshold:0,unit:'DAY',condition:'GT',level:'CRITICAL'})
|
||||
}
|
||||
|
||||
@@ -123,14 +127,11 @@ const saveRules = async () => {
|
||||
saving.value = true
|
||||
try {
|
||||
const rules = editorMode.value==='json' ? JSON.parse(editorJson.value) : editingRules.value
|
||||
await api.raw.post('/risk-rules/'+editType.value, { rules })
|
||||
ElMessage.success('已保存'); editType.value = ''; fetchAll()
|
||||
await api.raw.post('/risk-rules/'+editSiteType.value, { rules })
|
||||
ElMessage.success('已保存'); editSiteId.value = ''; fetchAll()
|
||||
} catch { ElMessage.error('保存失败') }
|
||||
finally { saving.value = false }
|
||||
}
|
||||
|
||||
onMounted(fetchAll)
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user