refactor: 页面配置+风险规则改用纯CodeEditor(去掉表格模式)
This commit is contained in:
@@ -52,30 +52,11 @@
|
|||||||
<div style="margin-bottom:8px;display:flex;gap:8px;align-items:center">
|
<div style="margin-bottom:8px;display:flex;gap:8px;align-items:center">
|
||||||
<strong>编辑: {{ editSiteId }} ({{ editSiteType }})</strong>
|
<strong>编辑: {{ editSiteId }} ({{ editSiteType }})</strong>
|
||||||
<span style="color:#909399;font-size:12px">修改会影响所有 {{ editSiteType }} 类型的站点</span>
|
<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>
|
|
||||||
</el-radio-group>
|
|
||||||
</div>
|
</div>
|
||||||
<template v-if="editorMode==='table'">
|
<div style="height:400px;border-radius:4px;overflow:hidden;margin-bottom:8px">
|
||||||
<el-table :data="editingRules" stripe size="small" style="margin-bottom:8px">
|
|
||||||
<el-table-column prop="type" label="类型" width="90">
|
|
||||||
<template #default="e"><el-select v-model="e.row.type" size="small"><el-option v-for="x in ['KILL','TRAFFIC','RATIO','LOGIN','HD']" :key="x" :label="x" :value="x"/></el-select></template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="name" label="名称" min-width="160"><template #default="e"><el-input v-model="e.row.name" size="small"/></template></el-table-column>
|
|
||||||
<el-table-column prop="metric" label="指标" width="130"><template #default="e"><el-input v-model="e.row.metric" size="small"/></template></el-table-column>
|
|
||||||
<el-table-column prop="threshold" label="阈值" width="70"><template #default="e"><el-input v-model.number="e.row.threshold" size="small" type="number"/></template></el-table-column>
|
|
||||||
<el-table-column prop="unit" label="单位" width="60"><template #default="e"><el-input v-model="e.row.unit" size="small"/></template></el-table-column>
|
|
||||||
<el-table-column prop="condition" label="条件" width="70"><template #default="e"><el-select v-model="e.row.condition" size="small"><el-option v-for="x in ['GT','LT','GTE','LTE']" :key="x" :label="x" :value="x"/></el-select></template></el-table-column>
|
|
||||||
<el-table-column prop="level" label="级别" width="80"><template #default="e"><el-select v-model="e.row.level" size="small"><el-option v-for="x in ['CRITICAL','WARN','INFO']" :key="x" :label="x" :value="x"/></el-select></template></el-table-column>
|
|
||||||
<el-table-column label="" width="50"><template #default="e"><el-button size="small" type="danger" text @click="editingRules.splice(e.$index,1)">×</el-button></template></el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<el-button size="small" @click="editingRules.push({type:'TRAFFIC',name:'',metric:'',threshold:0,unit:'DAY',condition:'GT',level:'WARN'})">+ 添加</el-button>
|
|
||||||
</template>
|
|
||||||
<div v-else style="height:300px;border-radius:4px;overflow:hidden;margin-bottom:8px">
|
|
||||||
<CodeEditor v-model="editorJson" language="json" />
|
<CodeEditor v-model="editorJson" language="json" />
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top:8px;display:flex;gap:8px">
|
<div style="display:flex;gap:8px">
|
||||||
<el-button size="small" type="primary" @click="saveRules" :loading="saving">保存</el-button>
|
<el-button size="small" type="primary" @click="saveRules" :loading="saving">保存</el-button>
|
||||||
<el-button size="small" @click="editSiteId=''">取消</el-button>
|
<el-button size="small" @click="editSiteId=''">取消</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,8 +76,7 @@ import api from '../api'
|
|||||||
|
|
||||||
const loading = ref(false), saving = ref(false), viewVisible = ref(false), viewType = ref(''), viewJson = ref('')
|
const loading = ref(false), saving = ref(false), viewVisible = ref(false), viewType = ref(''), viewJson = ref('')
|
||||||
const sites = ref([]), rulesMap = ref({})
|
const sites = ref([]), rulesMap = ref({})
|
||||||
const editSiteId = ref(''), editSiteType = ref(''), editorMode = ref('table'), editorJson = ref('')
|
const editSiteId = ref(''), editSiteType = ref(''), editorJson = ref('')
|
||||||
const editingRules = ref([])
|
|
||||||
|
|
||||||
const defaultRules = {
|
const defaultRules = {
|
||||||
nexusphp: [
|
nexusphp: [
|
||||||
@@ -120,11 +100,6 @@ const defaults = computed(() => {
|
|||||||
|
|
||||||
const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-'
|
const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-'
|
||||||
|
|
||||||
watch(editorMode, (m) => {
|
|
||||||
if (m === 'json') editorJson.value = JSON.stringify(editingRules.value, null, 2)
|
|
||||||
else { try { editingRules.value = JSON.parse(editorJson.value) } catch {} }
|
|
||||||
})
|
|
||||||
|
|
||||||
const merged = computed(() => {
|
const merged = computed(() => {
|
||||||
return (Array.isArray(sites.value)?sites.value:[]).map(s => ({
|
return (Array.isArray(sites.value)?sites.value:[]).map(s => ({
|
||||||
siteId: s.siteId,
|
siteId: s.siteId,
|
||||||
@@ -164,17 +139,15 @@ const viewDefault = (dt) => {
|
|||||||
const editDefault = (dt) => {
|
const editDefault = (dt) => {
|
||||||
editSiteId.value = dt.type
|
editSiteId.value = dt.type
|
||||||
editSiteType.value = dt.type
|
editSiteType.value = dt.type
|
||||||
editingRules.value = JSON.parse(JSON.stringify(dt.rules))
|
editorJson.value = JSON.stringify(dt.rules, null, 2)
|
||||||
editorMode.value = 'table'
|
|
||||||
window.scrollTo({top: document.body.scrollHeight, behavior:'smooth'})
|
window.scrollTo({top: document.body.scrollHeight, behavior:'smooth'})
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadDefaultRules = (row) => {
|
const loadDefaultRules = (row) => {
|
||||||
editSiteId.value = row.siteId
|
editSiteId.value = row.siteId
|
||||||
editSiteType.value = row.siteType
|
editSiteType.value = row.siteType
|
||||||
editingRules.value = JSON.parse(JSON.stringify(defaultRules[row.siteType] || defaultRules['nexusphp'] || []))
|
const rules = 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'})
|
editorJson.value = JSON.stringify(rules, null, 2)
|
||||||
editorMode.value = 'table'
|
|
||||||
window.scrollTo({top: document.body.scrollHeight, behavior:'smooth'})
|
window.scrollTo({top: document.body.scrollHeight, behavior:'smooth'})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,17 +155,14 @@ const startEdit = (row) => {
|
|||||||
if (editSiteId.value === row.siteId) { editSiteId.value = ''; return }
|
if (editSiteId.value === row.siteId) { editSiteId.value = ''; return }
|
||||||
editSiteId.value = row.siteId
|
editSiteId.value = row.siteId
|
||||||
editSiteType.value = row.siteType
|
editSiteType.value = row.siteType
|
||||||
editorMode.value = 'table'
|
try { editorJson.value = JSON.stringify(JSON.parse(row.rulesJson||'[]'), null, 2) }
|
||||||
try { editingRules.value = JSON.parse(JSON.stringify(JSON.parse(row.rulesJson||'[]'))) }
|
catch { editorJson.value = '[]' }
|
||||||
catch { editingRules.value = [] }
|
|
||||||
if (editingRules.value.length===0) editingRules.value.push({type:'KILL',name:'',metric:'',threshold:0,unit:'DAY',condition:'GT',level:'CRITICAL'})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveRules = async () => {
|
const saveRules = async () => {
|
||||||
saving.value = true
|
saving.value = true
|
||||||
try {
|
try {
|
||||||
const rules = editorMode.value==='json' ? JSON.parse(editorJson.value) : editingRules.value
|
await api.raw.post('/risk-rules/'+editSiteType.value, { rules: JSON.parse(editorJson.value) })
|
||||||
await api.raw.post('/risk-rules/'+editSiteType.value, { rules })
|
|
||||||
ElMessage.success('已保存'); editSiteId.value = ''; fetchAll()
|
ElMessage.success('已保存'); editSiteId.value = ''; fetchAll()
|
||||||
} catch { ElMessage.error('保存失败') }
|
} catch { ElMessage.error('保存失败') }
|
||||||
finally { saving.value = false }
|
finally { saving.value = false }
|
||||||
|
|||||||
@@ -50,25 +50,11 @@
|
|||||||
<div v-if="editSiteId" 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">
|
<div style="margin-bottom:8px;display:flex;gap:8px;align-items:center">
|
||||||
<strong>编辑: {{ editSiteId }}</strong>
|
<strong>编辑: {{ editSiteId }}</strong>
|
||||||
<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>
|
|
||||||
</el-radio-group>
|
|
||||||
</div>
|
</div>
|
||||||
<template v-if="editorMode==='table'">
|
<div style="height:400px;border-radius:4px;overflow:hidden;margin-bottom:8px">
|
||||||
<el-table :data="editingEntries" stripe size="small" style="margin-bottom:8px">
|
|
||||||
<el-table-column prop="key" label="key" width="100"><template #default="e"><el-input v-model="e.row.key" size="small"/></template></el-table-column>
|
|
||||||
<el-table-column prop="label" label="名称" width="100"><template #default="e"><el-input v-model="e.row.label" size="small"/></template></el-table-column>
|
|
||||||
<el-table-column prop="url" label="URL" min-width="200"><template #default="e"><el-input v-model="e.row.url" size="small"/></template></el-table-column>
|
|
||||||
<el-table-column prop="method" label="方法" width="80"><template #default="e"><el-select v-model="e.row.method" size="small"><el-option label="GET" value="GET"/><el-option label="POST" value="POST"/></el-select></template></el-table-column>
|
|
||||||
<el-table-column label="" width="50"><template #default="e"><el-button size="small" type="danger" text @click="editingEntries.splice(e.$index,1)">×</el-button></template></el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<el-button size="small" @click="editingEntries.push({key:'',label:'',url:'{base}',method:'GET'})">+ 添加</el-button>
|
|
||||||
</template>
|
|
||||||
<div v-else style="height:300px;border-radius:4px;overflow:hidden;margin-bottom:8px">
|
|
||||||
<CodeEditor v-model="editorJson" language="json" />
|
<CodeEditor v-model="editorJson" language="json" />
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top:8px;display:flex;gap:8px">
|
<div style="display:flex;gap:8px">
|
||||||
<el-button size="small" type="primary" @click="savePages" :loading="saving">保存</el-button>
|
<el-button size="small" type="primary" @click="savePages" :loading="saving">保存</el-button>
|
||||||
<el-button size="small" @click="editSiteId=''">取消</el-button>
|
<el-button size="small" @click="editSiteId=''">取消</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,8 +74,7 @@ import api from '../api'
|
|||||||
|
|
||||||
const loading = ref(false), saving = ref(false)
|
const loading = ref(false), saving = ref(false)
|
||||||
const sites = ref([]), pagesMap = ref({})
|
const sites = ref([]), pagesMap = ref({})
|
||||||
const editSiteId = ref(''), editorMode = ref('table'), editorJson = ref('')
|
const editSiteId = ref(''), editorJson = ref('')
|
||||||
const editingEntries = ref([])
|
|
||||||
|
|
||||||
const defaultPages = {
|
const defaultPages = {
|
||||||
nexusphp: [
|
nexusphp: [
|
||||||
@@ -121,11 +106,6 @@ const defaults = computed(() => {
|
|||||||
|
|
||||||
const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-'
|
const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-'
|
||||||
|
|
||||||
watch(editorMode, (m) => {
|
|
||||||
if (m === 'json') editorJson.value = JSON.stringify(editingEntries.value, null, 2)
|
|
||||||
else { try { editingEntries.value = JSON.parse(editorJson.value) } catch {} }
|
|
||||||
})
|
|
||||||
|
|
||||||
const merged = computed(() => {
|
const merged = computed(() => {
|
||||||
return (Array.isArray(sites.value)?sites.value:[]).map(s => ({
|
return (Array.isArray(sites.value)?sites.value:[]).map(s => ({
|
||||||
siteId: s.siteId,
|
siteId: s.siteId,
|
||||||
@@ -157,17 +137,13 @@ const fetchAll = async () => {
|
|||||||
|
|
||||||
const loadDefault = (row) => {
|
const loadDefault = (row) => {
|
||||||
editSiteId.value = row.siteId
|
editSiteId.value = row.siteId
|
||||||
editingEntries.value = JSON.parse(JSON.stringify(defaultPages[row.siteType] || defaultPages['nexusphp']))
|
editorJson.value = JSON.stringify(defaultPages[row.siteType] || defaultPages['nexusphp'], null, 2)
|
||||||
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'})
|
window.scrollTo({top: document.body.scrollHeight, behavior:'smooth'})
|
||||||
}
|
}
|
||||||
|
|
||||||
const editDefault = (dt) => {
|
const editDefault = (dt) => {
|
||||||
editSiteId.value = dt.type
|
editSiteId.value = dt.type
|
||||||
editingEntries.value = JSON.parse(JSON.stringify(dt.entries))
|
editorJson.value = JSON.stringify(dt.entries, null, 2)
|
||||||
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'})
|
window.scrollTo({top: document.body.scrollHeight, behavior:'smooth'})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,17 +158,14 @@ const previewDefault = (dt) => {
|
|||||||
const toggleEdit = (row) => {
|
const toggleEdit = (row) => {
|
||||||
if (editSiteId.value === row.siteId) { editSiteId.value = ''; return }
|
if (editSiteId.value === row.siteId) { editSiteId.value = ''; return }
|
||||||
editSiteId.value = row.siteId
|
editSiteId.value = row.siteId
|
||||||
editorMode.value = 'table'
|
try { editorJson.value = JSON.stringify(JSON.parse(row.entriesJson||'[]'), null, 2) }
|
||||||
try { editingEntries.value = JSON.parse(JSON.stringify(JSON.parse(row.entriesJson||'[]'))) }
|
catch { editorJson.value = JSON.stringify([{key:'home',label:'首页',url:'{base}',method:'GET'}], null, 2) }
|
||||||
catch { editingEntries.value = [{key:'home',label:'首页',url:'{base}',method:'GET'}] }
|
|
||||||
if (editingEntries.value.length===0) editingEntries.value.push({key:'home',label:'首页',url:'{base}',method:'GET'})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const savePages = async () => {
|
const savePages = async () => {
|
||||||
saving.value = true
|
saving.value = true
|
||||||
try {
|
try {
|
||||||
const entries = editorMode.value==='json' ? JSON.parse(editorJson.value) : editingEntries.value
|
await api.raw.put('/sites/'+editSiteId.value+'/pages', { entries: JSON.parse(editorJson.value) })
|
||||||
await api.raw.put('/sites/'+editSiteId.value+'/pages', { entries })
|
|
||||||
ElMessage.success('已保存'); editSiteId.value = ''; fetchAll()
|
ElMessage.success('已保存'); editSiteId.value = ''; fetchAll()
|
||||||
} catch { ElMessage.error('保存失败') }
|
} catch { ElMessage.error('保存失败') }
|
||||||
finally { saving.value = false }
|
finally { saving.value = false }
|
||||||
|
|||||||
Reference in New Issue
Block a user