feat: 适配器解析引擎,用 CSS 选择器从提交的 HTML 中自动提取数据进行校验

This commit is contained in:
mediabot-pt
2026-07-01 01:25:16 +08:00
parent e27a5b59ed
commit e56f1d4b92
9 changed files with 439 additions and 0 deletions

View File

@@ -113,6 +113,7 @@
<el-tag v-if="r.isValid === true" size="small" type="success">有效</el-tag>
<el-tag v-else-if="r.isValid === false" size="small" type="danger">无效</el-tag>
<el-button size="small" @click="viewContent(r)">查看</el-button>
<el-button size="small" type="primary" :disabled="!selectedConfig" @click="parseResult(r)">解析</el-button>
</div>
</div>
<div v-if="!expandedUrls.has(page.url) && page.totalCount > 5" style="padding:2px 0;color:#909399;font-size:12px;text-align:center">
@@ -145,6 +146,36 @@
<el-button v-if="viewingResult.isValid == null" type="danger" size="small" @click="singleValidate(viewingResult.id, false)">无效</el-button>
</template>
</el-dialog>
<!-- 适配解析结果弹窗 -->
<el-dialog v-model="parseVisible" title="适配解析结果" width="850px" :close-on-click-modal="false">
<div v-if="parseLoading" style="text-align:center;padding:40px">解析中...</div>
<div v-else-if="parseError" style="color:#f56c6c;padding:10px">{{ parseError }}</div>
<template v-else>
<el-descriptions :column="3" border size="small" style="margin-bottom:12px">
<el-descriptions-item label="匹配页面">{{ parseData.pageName || '-' }}</el-descriptions-item>
<el-descriptions-item label="提取行数">{{ parseData.rowCount ?? 0 }}</el-descriptions-item>
<el-descriptions-item label="字段数">{{ parseData.fieldCount ?? 0 }}</el-descriptions-item>
</el-descriptions>
<div v-if="!parseData.matched" style="color:#e6a23c;margin-bottom:8px">
未匹配到页面规则。可用页面: {{ (parseData.availablePages || []).join(', ') || '无' }}
</div>
<el-table v-if="parseData.extracted?.length" :data="parseData.extracted" border stripe size="small" max-height="400">
<el-table-column type="index" label="#" width="50" />
<el-table-column v-for="col in parseColumns" :key="col" :prop="col" :label="col" min-width="120" show-overflow-tooltip>
<template #default="scope">
<span :style="scope.row[col] == null ? 'color:#909399' : ''">{{ scope.row[col] ?? 'null' }}</span>
</template>
</el-table-column>
</el-table>
<div v-else-if="parseData.matched" style="text-align:center;padding:20px;color:#909399">
无提取结果(HTML 内容可能不包含匹配元素)
</div>
</template>
<template #footer>
<el-button @click="parseVisible = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -175,6 +206,11 @@ const viewingResult = ref({})
const viewingContentType = ref('html')
const viewingContent = ref('')
const parseVisible = ref(false)
const parseLoading = ref(false)
const parseError = ref('')
const parseData = ref({})
const expandedUrls = reactive(new Set())
const MAX_VISIBLE = 5
@@ -322,6 +358,25 @@ const singleValidate = async (id, isValid) => {
} catch { ElMessage.error('校验失败') }
}
const parseColumns = computed(() => {
if (!parseData.value.extracted?.length) return []
return Object.keys(parseData.value.extracted[0])
})
const parseResult = async (row) => {
if (!selectedConfig.value) { ElMessage.warning('请先选择适配配置'); return }
parseVisible.value = true
parseLoading.value = true
parseError.value = ''
parseData.value = {}
try {
const r = await api.adapter.parse(selectedConfig.value.id, row.id)
parseData.value = r.data || {}
} catch (e) {
parseError.value = e.response?.data?.message || '解析失败'
} finally { parseLoading.value = false }
}
const viewContent = async (row) => {
viewingResult.value = { ...row }
viewingContentType.value = row.contentType || 'html'