AI审核改为 列表页+详情页 结构,列表带审核摘要
- 表 oa_ai_review 增加 summary 列(审核结论摘要,纯文本,列表展示用),
已应用到生产库;分析时由结果 Markdown 提炼前160字纯文本写入
- 列表查询清空大字段 result_md 减小响应体,详情接口仍返回完整结果
- 前端拆分:
· index.vue 重写为列表页:搜索(类型/关键字)+表格(类型/文件名/岗位/结论标签/
审核摘要/时间)+分页,「新增审核」改为弹窗上传(类型/岗位/文件),
审核完成后跳转详情;行可删除
· 新增 detail.vue 详情页:元信息(文件名+下载原件/岗位/模型/时间/审核人)
+ 结论标签 + 完整 Markdown 结果,返回列表按钮
· router 增加 /hint/aiReview/detail/:id 隐藏路由
- 原件已通过 OSS 留存,详情页可下载,下次可直接查看
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
110
ruoyi-ui/src/views/oa/aiReview/detail.vue
Normal file
110
ruoyi-ui/src/views/oa/aiReview/detail.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="app-container ai-review-detail" v-loading="loading">
|
||||
<el-card shadow="never" class="panel">
|
||||
<div slot="header" class="hd">
|
||||
<div class="hd-left">
|
||||
<el-button size="small" icon="el-icon-back" @click="goBack">返回列表</el-button>
|
||||
<span class="title"><i class="el-icon-document-checked" /> 审核详情</span>
|
||||
</div>
|
||||
<div v-if="info" class="hd-right">
|
||||
<el-tag size="small" :type="info.reviewType === 'contract' ? 'warning' : 'success'">
|
||||
{{ info.reviewType === 'contract' ? '合同审核' : '简历审核' }}
|
||||
</el-tag>
|
||||
<el-tag v-if="info.reviewType === 'resume' && info.matchScore != null"
|
||||
size="small" type="success" effect="dark">匹配度 {{ info.matchScore }}</el-tag>
|
||||
<el-tag v-if="info.reviewType === 'contract' && info.riskLevel"
|
||||
size="small" :type="riskTagType(info.riskLevel)" effect="dark">{{ info.riskLevel }}风险</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-descriptions v-if="info" :column="3" size="small" border class="meta">
|
||||
<el-descriptions-item label="文件名">
|
||||
<span>{{ info.fileName }}</span>
|
||||
<el-button v-if="info.fileUrl" type="text" size="mini" icon="el-icon-download"
|
||||
style="margin-left:8px" @click="downloadFile">下载原件</el-button>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-if="info.reviewType === 'resume'" label="目标岗位">
|
||||
{{ info.position || '—' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="模型">{{ info.model || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">{{ info.createTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核人">{{ info.createBy || '—' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div v-if="info" class="md-body" v-html="renderedMd" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAiReview } from '@/api/oa/aiReview'
|
||||
const marked = require('marked')
|
||||
|
||||
export default {
|
||||
name: 'OaAiReviewDetail',
|
||||
data () {
|
||||
return {
|
||||
loading: true,
|
||||
info: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
renderedMd () {
|
||||
if (!this.info || !this.info.resultMd) return ''
|
||||
try { return marked(this.info.resultMd) } catch (e) { return this.info.resultMd }
|
||||
}
|
||||
},
|
||||
created () {
|
||||
marked.setOptions({ breaks: true })
|
||||
const id = this.$route.params.id
|
||||
if (id) this.load(id)
|
||||
else this.loading = false
|
||||
},
|
||||
methods: {
|
||||
load (id) {
|
||||
this.loading = true
|
||||
getAiReview(id).then(res => { this.info = res.data })
|
||||
.finally(() => { this.loading = false })
|
||||
},
|
||||
goBack () {
|
||||
this.$router.push('/hint/aiReview')
|
||||
},
|
||||
downloadFile () {
|
||||
if (this.info && this.info.fileUrl) window.open(this.info.fileUrl, '_blank')
|
||||
},
|
||||
riskTagType (r) {
|
||||
return r === '高' ? 'danger' : (r === '中' ? 'warning' : 'success')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.ai-review-detail { padding: 10px; }
|
||||
.panel { ::v-deep .el-card__header { padding: 10px 14px; } }
|
||||
.hd { display: flex; justify-content: space-between; align-items: center;
|
||||
.hd-left { display: flex; align-items: center; gap: 12px;
|
||||
.title { font-weight: 600; font-size: 14px; color: #303133; i { color: #409eff; margin-right: 4px; } }
|
||||
}
|
||||
.hd-right { display: flex; align-items: center; gap: 8px; }
|
||||
}
|
||||
.meta { margin-bottom: 16px; }
|
||||
|
||||
.md-body {
|
||||
font-size: 14px; color: #2c3e50; line-height: 1.8; padding: 4px 6px;
|
||||
::v-deep {
|
||||
h1, h2, h3 { color: #303133; margin: 18px 0 10px; font-weight: 600; }
|
||||
h1 { font-size: 19px; } h2 { font-size: 16px; border-left: 3px solid #409eff; padding-left: 8px; }
|
||||
h3 { font-size: 14px; }
|
||||
p { margin: 8px 0; }
|
||||
ul, ol { padding-left: 22px; margin: 8px 0; }
|
||||
li { margin: 4px 0; }
|
||||
strong { color: #c0392b; }
|
||||
code { background: #f4f7fa; padding: 1px 5px; border-radius: 3px; font-size: 13px; color: #e6a23c; }
|
||||
table { border-collapse: collapse; width: 100%; margin: 10px 0; }
|
||||
th, td { border: 1px solid #ebeef5; padding: 6px 10px; font-size: 13px; }
|
||||
th { background: #f5f7fa; }
|
||||
blockquote { border-left: 3px solid #dcdfe6; padding-left: 10px; color: #909399; margin: 8px 0; }
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user