ReportUnknownCluster.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. <template>
  2. <div class="report-unknown admin-page">
  3. <el-card>
  4. <div slot="header">
  5. <span>未知报告审核</span>
  6. <div style="float: right; display: flex; gap: 10px;">
  7. <el-select v-model="statusFilter" size="small" style="width: 140px;" @change="fetchClusters">
  8. <el-option label="全部" value="" />
  9. <el-option label="收集" value="collecting" />
  10. <el-option label="可生成" value="ready" />
  11. <el-option label="已生成" value="generated" />
  12. </el-select>
  13. </div>
  14. </div>
  15. <el-table :data="clusters" v-loading="loading" border stripe>
  16. <el-table-column prop="id" label="ID" width="60" />
  17. <el-table-column prop="clusterKey" label="聚类键" width="140" />
  18. <el-table-column prop="reportCount" label="报告数" width="80" />
  19. <el-table-column label="状态" width="100">
  20. <template slot-scope="{ row }">
  21. <el-tag :type="statusType(row.status)" size="small">{{ statusText(row.status) }}</el-tag>
  22. </template>
  23. </el-table-column>
  24. <el-table-column prop="generatedTypeId" label="生成类型" width="140" />
  25. <el-table-column prop="updatedAt" label="更新时间" width="180" />
  26. <el-table-column label="操作" width="160" fixed="right">
  27. <template slot-scope="{ row }">
  28. <el-button size="mini" type="primary" @click="handleDetail(row)">详情</el-button>
  29. <el-button v-if="row.status === 'ready'" size="mini" type="success" @click="handleGenerate(row)">生成类型</el-button>
  30. </template>
  31. </el-table-column>
  32. </el-table>
  33. </el-card>
  34. <!-- 详情对话框 -->
  35. <el-dialog :visible.sync="detailVisible" title="聚类详情" width="750px" :close-on-click-modal="false">
  36. <template v-if="currentCluster">
  37. <el-descriptions :column="2" border size="small">
  38. <el-descriptions-item label="聚类键">{{ currentCluster.clusterKey }}</el-descriptions-item>
  39. <el-descriptions-item label="报告数">{{ currentCluster.reportCount }}</el-descriptions-item>
  40. <el-descriptions-item label="状态">{{ statusText(currentCluster.status) }}</el-descriptions-item>
  41. <el-descriptions-item label="生成类型">{{ currentCluster.generatedTypeId || '—' }}</el-descriptions-item>
  42. </el-descriptions>
  43. <h4>公共文本特征</h4>
  44. <pre class="detail-pre">{{ currentCluster.commonSignatures }}</pre>
  45. <h4>LLM 解析模板</h4>
  46. <pre class="detail-pre">{{ formatJson(currentCluster.llmTemplate) }}</pre>
  47. <h4>聚类内报告</h4>
  48. <el-table :data="uploads" v-loading="uploadLoading" border size="small">
  49. <el-table-column prop="id" label="ID" width="60" />
  50. <el-table-column prop="fileUrl" label="文件" min-width="200" />
  51. <el-table-column prop="uploadedAt" label="上传时间" width="180" />
  52. </el-table>
  53. </template>
  54. </el-dialog>
  55. </div>
  56. </template>
  57. <script>
  58. import { getUnknownClusters, getClusterUploads, generateTypeFromCluster } from '@/api/reportParser'
  59. export default {
  60. name: 'ReportUnknownCluster',
  61. data() {
  62. return {
  63. loading: false, uploadLoading: false,
  64. clusters: [], statusFilter: '',
  65. detailVisible: false, currentCluster: null,
  66. uploads: [],
  67. }
  68. },
  69. mounted() { this.fetchClusters() },
  70. methods: {
  71. async fetchClusters() {
  72. this.loading = true
  73. try {
  74. const res = await getUnknownClusters({ page: 1, size: 50, status: this.statusFilter })
  75. this.clusters = res.data?.records || res.data || []
  76. } catch (e) { this.$message.error('加载失败') }
  77. this.loading = false
  78. },
  79. statusType(status) {
  80. return { collecting: 'info', ready: 'warning', generated: 'success', imported: 'success' }[status] || 'info'
  81. },
  82. statusText(status) {
  83. return { collecting: '收集', ready: '可生成', generated: '已生成', imported: '已导入' }[status] || status
  84. },
  85. formatJson(str) {
  86. if (!str) return ''
  87. try { return JSON.stringify(JSON.parse(str), null, 2) } catch (e) { return str }
  88. },
  89. async handleDetail(row) {
  90. this.currentCluster = row
  91. this.detailVisible = true
  92. this.uploads = []
  93. this.uploadLoading = true
  94. try {
  95. const res = await getClusterUploads({ clusterId: row.id })
  96. this.uploads = res.data || []
  97. } catch (e) { this.$message.error('加载报告失败') }
  98. this.uploadLoading = false
  99. },
  100. handleGenerate(row) {
  101. this.$confirm('确认为此聚类生成报告类型?将创建待审核的报告类型和指纹规则。', '提示', { type: 'warning' })
  102. .then(async () => {
  103. try {
  104. const res = await generateTypeFromCluster({ clusterId: row.id })
  105. this.$message.success('已生成类型: ' + (res.data?.typeId || ''))
  106. this.fetchClusters()
  107. } catch (e) { this.$message.error('生成失败') }
  108. }).catch(() => {})
  109. },
  110. }
  111. }
  112. </script>
  113. <style scoped>
  114. .detail-pre {
  115. background: #f5f7fa;
  116. border: 1px solid #e4e7ed;
  117. border-radius: 4px;
  118. padding: 10px;
  119. max-height: 300px;
  120. overflow: auto;
  121. font-size: 12px;
  122. white-space: pre-wrap;
  123. word-break: break-all;
  124. }
  125. </style>