| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- <template>
- <div class="report-import admin-page">
- <el-card>
- <div slot="header">
- <span>解析器导入管理</span>
- <div style="float: right; display: flex; gap: 10px;">
- <el-button type="primary" size="small" icon="el-icon-upload2" @click="uploadDialogVisible = true">上传导入包</el-button>
- </div>
- </div>
- <el-table :data="imports" v-loading="loading" border stripe>
- <el-table-column prop="id" label="ID" width="60" />
- <el-table-column prop="packageName" label="导入包名称" min-width="200" />
- <el-table-column prop="packageFileUrl" label="文件" width="150" />
- <el-table-column label="状态" width="100">
- <template slot-scope="{ row }">
- <el-tag :type="statusType(row.status)" size="small">{{ statusText(row.status) }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="createdAt" label="导入时间" width="180" />
- <el-table-column label="操作" width="160" fixed="right">
- <template slot-scope="{ row }">
- <template v-if="row.status === 'pending'">
- <el-button size="mini" type="success" @click="handleApprove(row)">批准</el-button>
- <el-button size="mini" type="danger" @click="handleReject(row)">驳回</el-button>
- </template>
- <el-button size="mini" type="primary" @click="handlePreview(row)">预览</el-button>
- </template>
- </el-table-column>
- </el-table>
- </el-card>
- <!-- 上传导入包对话框 -->
- <el-dialog :visible.sync="uploadDialogVisible" title="上传导入包" width="600px" :close-on-click-modal="false">
- <el-upload
- drag
- :auto-upload="false"
- :limit="1"
- accept=".json"
- :on-change="handleFileChange">
- <i class="el-icon-upload"></i>
- <div class="el-upload__text">将 json 文件拖到此处,或<em>点击上传</em></div>
- <div class="el-upload__tip" slot="tip">导入包 JSON 格式:{ packageName, typeId, displayName, family, description, parserScript, fingerprints: [{pattern, weight, isExclusion}] }</div>
- </el-upload>
- <div slot="footer">
- <el-button @click="uploadDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleImport" :loading="submitting">确认导入</el-button>
- </div>
- </el-dialog>
- <!-- 预览导入包对话框 -->
- <el-dialog :visible.sync="previewDialogVisible" title="导入包预览" width="700px">
- <div v-if="previewData">
- <el-descriptions :column="2" border size="small">
- <el-descriptions-item label="名称">{{ previewData.packageName }}</el-descriptions-item>
- <el-descriptions-item label="类型ID">{{ previewData.typeId }}</el-descriptions-item>
- <el-descriptions-item label="显示名称">{{ previewData.displayName }}</el-descriptions-item>
- <el-descriptions-item label="家族">{{ previewData.family }}</el-descriptions-item>
- <el-descriptions-item label="描述" :span="2">{{ previewData.description }}</el-descriptions-item>
- </el-descriptions>
- <h4>指纹规则</h4>
- <el-table :data="previewData.fingerprints || []" border size="small">
- <el-table-column prop="pattern" label="文本特征" min-width="200" />
- <el-table-column prop="weight" label="权重" width="70" />
- <el-table-column label="类型" width="80">
- <template slot-scope="{ row }">
- <el-tag :type="row.isExclusion ? 'danger' : 'success'" size="mini">{{ row.isExclusion ? '排除' : '正向' }}</el-tag>
- </template>
- </el-table-column>
- </el-table>
- <h4>解析脚本</h4>
- <pre class="script-preview">{{ previewData.parserScript }}</pre>
- </div>
- </el-dialog>
- </div>
- </template>
- <script>
- import { getParserImports, approveParserImport, rejectParserImport, importParserPackage } from '@/api/reportParser'
- export default {
- name: 'ReportParserImport',
- data() {
- return {
- loading: false, submitting: false,
- imports: [],
- uploadDialogVisible: false,
- previewDialogVisible: false,
- selectedFile: null,
- previewData: null,
- }
- },
- mounted() { this.fetchImports() },
- methods: {
- async fetchImports() {
- this.loading = true
- try {
- const res = await getParserImports({ page: 1, size: 50 })
- this.imports = res.data?.records || res.data || []
- } catch (e) { this.$message.error('加载失败') }
- this.loading = false
- },
- statusType(status) {
- return { pending: 'warning', approved: 'success', rejected: 'danger' }[status] || 'info'
- },
- statusText(status) {
- return { pending: '待审核', approved: '已批准', rejected: '已驳回' }[status] || status
- },
- handleFileChange(file) {
- this.selectedFile = file.raw
- },
- async handleImport() {
- if (!this.selectedFile) {
- this.$message.warning('请先选择文件')
- return
- }
- this.submitting = true
- const reader = new FileReader()
- reader.onload = async (e) => {
- try {
- const jsonContent = e.target.result
- const res = await importParserPackage({ jsonContent, fileName: this.selectedFile.name })
- this.$message.success('导入成功,待审核')
- this.uploadDialogVisible = false
- this.selectedFile = null
- this.fetchImports()
- } catch (err) { this.$message.error('导入失败') }
- this.submitting = false
- }
- reader.readAsText(this.selectedFile)
- },
- handlePreview(row) {
- try {
- const jsonContent = row.fingerprintJson
- this.previewData = JSON.parse(jsonContent)
- this.previewDialogVisible = true
- } catch (e) { this.$message.error('预览失败:JSON 解析错误') }
- },
- handleApprove(row) {
- this.$confirm('确认批准此导入?将注册报告类型和指纹规则。', '提示', { type: 'warning' })
- .then(async () => {
- try {
- await approveParserImport({ id: row.id })
- this.$message.success('已批准')
- this.fetchImports()
- } catch (e) { this.$message.error('操作失败') }
- }).catch(() => {})
- },
- handleReject(row) {
- this.$prompt('请输入驳回原因', '驳回导入', { inputPlaceholder: '驳回原因', inputValidator: (v) => !!v || '请输入原因' })
- .then(async ({ value }) => {
- try {
- await rejectParserImport({ id: row.id, note: value })
- this.$message.success('已驳回')
- this.fetchImports()
- } catch (e) { this.$message.error('操作失败') }
- }).catch(() => {})
- },
- }
- }
- </script>
- <style scoped>
- .script-preview {
- background: #f5f7fa;
- border: 1px solid #e4e7ed;
- border-radius: 4px;
- padding: 10px;
- max-height: 300px;
- overflow: auto;
- font-size: 12px;
- white-space: pre-wrap;
- word-break: break-all;
- }
- </style>
|