feat: 页面配置入口地址添加验证按钮,解析失败自动提交错误日志
- 后端新增 POST /api/v1/admin/site-pages/verify 端点 - 接收 siteId/url/pageKey,用对应 Groovy 脚本访问URL并解析 - 解析失败(matched=false)时自动提交 error-log 到 PAR - 支持 HTTP_ERROR/EMPTY_RESPONSE/PARSE_FAILED/SCRIPT_ERROR 多种错误类型 - 前端 SitePages.vue 每条页面入口 URL 后添加验证按钮 - 成功显示绿色 ✓,失败显示红色 ✗,并展示解析结果消息 - 显示响应耗时 - api/index.js 新增 adapter.verifyPage 方法
This commit is contained in:
@@ -92,6 +92,7 @@ export default {
|
||||
adapter: {
|
||||
parse: (configId, accessResultId) => api.post('/admin/adapter/parse', { configId, accessResultId }),
|
||||
parseBatch: (configId, siteId) => api.post('/admin/adapter/parse-batch', { configId, siteId }),
|
||||
verifyPage: (siteId, url, pageKey) => api.post('/admin/site-pages/verify', { siteId, url, pageKey }),
|
||||
},
|
||||
health: () => api.get('/health', { baseURL: '' }),
|
||||
// 原始 axios 实例,供需要直接调用(如 blob 下载)的场景使用
|
||||
|
||||
@@ -92,7 +92,16 @@
|
||||
<div style="flex:1"></div>
|
||||
<el-button size="small" type="danger" text @click="remove(idx)">删除</el-button>
|
||||
</div>
|
||||
<el-input v-model="entry.url" size="small" placeholder="URL模板,支持{base}{keyword}{page}{id}{passkey}" @input="syncToJson" style="margin-bottom:6px;max-width:500px" />
|
||||
<div style="display:flex;gap:8px;align-items:flex-start;margin-bottom:6px;max-width:500px">
|
||||
<el-input v-model="entry.url" size="small" placeholder="URL模板,支持{base}{keyword}{page}{id}{passkey}" @input="syncToJson" style="flex:1" />
|
||||
<el-button size="small" @click="verifyEntry(entry, idx)" :loading="verifying[idx]" :type="verifyResults[idx]?.success===true ? 'success' : verifyResults[idx]?.success===false ? 'danger' : ''">
|
||||
{{ verifyResults[idx]?.success===true ? '✓' : verifyResults[idx]?.success===false ? '✗' : '验证' }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="verifyResults[idx]?.message" style="margin-bottom:6px;font-size:12px;padding:4px 8px;border-radius:4px" :style="{background:verifyResults[idx]?.success? '#f0f9eb':'#fef0f0',color:verifyResults[idx]?.success? '#67c23a':'#f56c6c'}">
|
||||
{{ verifyResults[idx].message }}
|
||||
<span v-if="verifyResults[idx].responseTimeMs != null" style="margin-left:8px;opacity:0.7">({{ verifyResults[idx].responseTimeMs }}ms)</span>
|
||||
</div>
|
||||
<el-input v-model="entry.headers" size="small" type="textarea" :rows="2" placeholder="自定义Header,每行一个 key: value" @input="syncToJson" style="margin-bottom:6px;max-width:500px" />
|
||||
<el-input v-if="entry.method==='POST'" v-model="entry.body" size="small" type="textarea" :rows="2" placeholder="POST Body(可选)" @input="syncToJson" />
|
||||
</div>
|
||||
@@ -110,6 +119,8 @@ const authType = ref('NONE')
|
||||
const defaultTypes = ['nexusphp','mtorrent','gazelle','unit3d']
|
||||
const isDefaultEditor = computed(() => editSiteId.value.startsWith('default:'))
|
||||
const pageKeys = ref([])
|
||||
const verifying = ref({})
|
||||
const verifyResults = ref({})
|
||||
|
||||
const loadPageKeys = async () => {
|
||||
try { const r = await api.raw.get('/admin/page-keys'); pageKeys.value = r?.data || r || [] }
|
||||
@@ -134,6 +145,34 @@ const syncToJson = () => {
|
||||
editorJson.value = JSON.stringify(obj, null, 2)
|
||||
}
|
||||
const resolveUrl = (url) => url?.replace('{base}', sitesList.value.find(s=>s.siteId===editSiteId.value)?.urls?.[0] || '{base}') || ''
|
||||
|
||||
const verifyEntry = async (entry, idx) => {
|
||||
const resolved = resolveUrl(entry.url)
|
||||
if (!resolved || resolved === entry.url) {
|
||||
ElMessage.warning('请先配置站点地址,URL中需包含 {base} 占位符')
|
||||
return
|
||||
}
|
||||
verifying.value = { ...verifying.value, [idx]: true }
|
||||
verifyResults.value = { ...verifyResults.value, [idx]: { message: '验证中...' } }
|
||||
try {
|
||||
const siteId = editSiteId.value.startsWith('default:') ? '' : editSiteId.value
|
||||
if (!siteId) {
|
||||
verifyResults.value = { ...verifyResults.value, [idx]: { success: false, message: '默认模板无关联站点,请在实际站点中验证' } }
|
||||
return
|
||||
}
|
||||
const r = await api.adapter.verifyPage(siteId, resolved, entry.key)
|
||||
const data = r?.data || r
|
||||
verifyResults.value = { ...verifyResults.value, [idx]: {
|
||||
success: data?.success, message: data?.message || (data?.success ? '解析成功' : '解析失败'),
|
||||
matched: data?.matched, responseTimeMs: data?.responseTimeMs,
|
||||
pageName: data?.pageName, rowCount: data?.rowCount, fieldCount: data?.fieldCount
|
||||
}}
|
||||
} catch (e) {
|
||||
verifyResults.value = { ...verifyResults.value, [idx]: { success: false, message: '请求异常: ' + (e.response?.data?.message || e.message) } }
|
||||
} finally {
|
||||
verifying.value = { ...verifying.value, [idx]: false }
|
||||
}
|
||||
}
|
||||
const merged = computed(() => (Array.isArray(sitesList.value)?sitesList.value:[]).map(s => ({
|
||||
siteId: s.siteId, names: s.names,
|
||||
version: pagesMap.value[s.siteId]?.version || null, entryCount: pagesMap.value[s.siteId]?.entryCount || 0,
|
||||
|
||||
Reference in New Issue
Block a user