feat: 配置审核页增加查看详情(语法高亮JSON)

This commit is contained in:
mediabot-pt
2026-06-29 22:55:38 +08:00
parent 377aa90c96
commit 98e040c06b
2 changed files with 89 additions and 1 deletions

View File

@@ -39,6 +39,7 @@ export default {
configs: {
list: () => api.get('/configs'),
pending: () => api.get('/configs/pending'),
download: (siteId, version) => api.get(`/configs/${siteId}/${version}`, { responseType: 'text' }),
approve: (id, comment) => api.post(`/admin/reviews/${id}/approve`, null, { params: { comment } }),
reject: (id, comment) => api.post(`/admin/reviews/${id}/reject`, null, { params: { comment } })
},

View File

@@ -11,14 +11,29 @@
<el-table-column prop="schemaVersion" label="Schema" width="100" />
<el-table-column prop="submitterId" label="提交者" width="100" />
<el-table-column prop="createdAt" label="提交时间" width="180" />
<el-table-column label="操作" width="200">
<el-table-column label="操作" width="280">
<template #default="scope">
<el-button size="small" type="primary" @click="handleView(scope.row)">查看</el-button>
<el-button size="small" type="success" @click="handleApprove(scope.row)">通过</el-button>
<el-button size="small" type="danger" @click="handleReject(scope.row)">拒绝</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="viewVisible" title="配置详情" width="700px" :close-on-click-modal="false">
<div class="config-info">
<span class="label">站点:</span> {{ viewing.siteId }} &nbsp;
<span class="label">版本:</span> {{ viewing.version }} &nbsp;
<span class="label">提交者:</span> {{ viewing.submitterId || '-' }}
</div>
<div style="margin-top: 12px; max-height: 500px; overflow: auto">
<pre class="config-json" v-html="highlightedJson"></pre>
</div>
<template #footer>
<el-button @click="viewVisible = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -27,6 +42,9 @@ import api from '../api'
const pendingConfigs = ref([])
const loading = ref(false)
const viewVisible = ref(false)
const viewing = ref({})
const configContent = ref('')
const fetchPending = async () => {
loading.value = true
@@ -40,6 +58,48 @@ const fetchPending = async () => {
}
}
const handleView = async (row) => {
viewing.value = row
configContent.value = ''
viewVisible.value = true
try {
const res = await api.configs.download(row.siteId, row.version)
configContent.value = typeof res === 'string' ? res : JSON.stringify(res, null, 2)
} catch {
configContent.value = '(加载失败)'
}
}
const highlightedJson = computed(() => {
try {
const obj = JSON.parse(configContent.value)
return syntaxHighlight(JSON.stringify(obj, null, 2))
} catch {
return escapeHtml(configContent.value)
}
})
const syntaxHighlight = (json) => {
return escapeHtml(json).replace(
/("(\\u[a-fA-F0-9]{4}|\\[^u]|[^"\\])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
(match) => {
let cls = 'number'
if (/^"/.test(match)) {
cls = /:$/.test(match) ? 'key' : 'string'
} else if (/true|false/.test(match)) {
cls = 'boolean'
} else if (/null/.test(match)) {
cls = 'null'
}
return `<span class="${cls}">${match}</span>`
}
)
}
const escapeHtml = (str) => {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
const handleApprove = async (row) => {
try {
await ElMessageBox.confirm(`确认通过站点 ${row.siteId} 的配置版本 ${row.version}?`, '提示')
@@ -68,3 +128,30 @@ const handleReject = async (row) => {
onMounted(fetchPending)
</script>
<style scoped>
.config-info {
font-size: 13px;
color: #606266;
}
.config-info .label {
font-weight: bold;
color: #303133;
}
.config-json {
background: #1e1e1e;
color: #d4d4d4;
padding: 12px;
border-radius: 4px;
font-size: 12px;
line-height: 1.5;
margin: 0;
white-space: pre-wrap;
word-break: break-all;
}
.config-json :deep(.key) { color: #9cdcfe; }
.config-json :deep(.string) { color: #ce9178; }
.config-json :deep(.number) { color: #b5cea8; }
.config-json :deep(.boolean) { color: #569cd6; }
.config-json :deep(.null) { color: #569cd6; }
</style>