ReportUnknownCluster.vue 5.6 KB

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