feat: AI审查报告支持下载Word + 我的报告支持重新审查

- 云函数jjxReviewTimer完成审查后自动将HTML报告转换为Word文档并上传
- 新增buildDocx.js纯正则解析HTML报告生成A4公文版式docx(三级数据提取兼容结构漂移)
- 兼容智能体插件直接返回docx链接(jjx-docx-link标记协议)
- router/jjxDoReview同步支持Word生成(try降级不影响主流程)
- 前端报告列表新增下载Word/重新审查按钮
- 修复上传/查询取真实uid(vk.getVuex而非页面本地data)
This commit is contained in:
2026-10-23 19:07:19 +08:00
parent d7ee134e40
commit a6cdf8642e
8 changed files with 2355 additions and 2 deletions
+72 -1
View File
@@ -31,8 +31,11 @@
<view class="report-meta">{{ r.time }} · 消耗 {{ r.amount }} 积分</view>
<view class="report-actions">
<view v-if="r.htmlUrl" class="action-btn" @click="openUrl(r.htmlUrl)">📄 报告下载</view>
<view v-if="r.docxUrl" class="action-btn" @click="openUrl(r.docxUrl)">📝 下载 Word</view>
<view v-if="r.fileUrl" class="action-btn" @click="openUrl(r.fileUrl)">📎 查看上传文件</view>
<view v-if="r.status === 'completed' && r.result" class="action-btn" @click="viewSummary(r)">📋 在线查看报告</view>
<view v-if="r.status === 'uploaded' && !r._submitting" class="action-btn action-btn-primary" @click="reReview(r)">🔁 重新审查</view>
<view v-if="r._submitting" class="action-btn action-btn-disabled">提交中...</view>
</view>
</view>
@@ -83,14 +86,16 @@ export default {
try {
const res = await vk.callFunction({
url: 'client/ai/pub/getMyReports',
data: { pageIndex: 1, pageSize: 50 }
data: { pageIndex: 1, pageSize: 50, uid: vk.getVuex('$user.userInfo._id') || '' }
})
if (res.code === 0 && res.data && res.data.rows) {
this.reportList = res.data.rows.map(r => ({
fileName: r.file_name,
fileSize: r.file_size,
fileId: r.file_id || '',
fileUrl: r.file_url || '',
htmlUrl: r.html_url || '',
docxUrl: r.docx_url || '',
time: dayjs(r._add_time && r._add_time['$date'] ? r._add_time['$date'] : r._add_time).format('YYYY-MM-DD HH:mm'),
amount: r.points_used || 0,
result: r.result,
@@ -108,6 +113,58 @@ export default {
this.summaryHtmlUrl = r.htmlUrl
this.showSummary = true
},
// 待审查记录 → 重新发起审查
async reReview(r) {
if (!r.fileId) {
uni.showToast({ title: '该记录缺少文件信息', icon: 'none' })
return
}
const confirmed = await new Promise((resolve) => {
uni.showModal({
title: '重新审查',
content: `确认对「${r.fileName}」发起审查?审查完成后将按文件大小扣除相应积分。`,
confirmText: '确认审查',
confirmColor: '#a92924',
success: (res) => resolve(!!res.confirm),
fail: () => resolve(false)
})
})
if (!confirmed) return
uni.showLoading({ title: '提交中...', mask: true })
try {
const balanceRes = await vk.callFunction({ url: 'client/points/kh/getBalance' })
if (balanceRes.code !== 0 || !balanceRes.data || balanceRes.data.balance < 200) {
uni.hideLoading()
uni.showToast({ title: '积分不足,请先充值', icon: 'none' })
return
}
this.$set(r, '_submitting', true)
const res = await vk.callFunction({
url: 'client/ai/pub/review',
data: {
record_id: r.recordId,
file_id: r.fileId,
file_name: r.fileName,
file_size: r.fileSize || 0,
uid: vk.getVuex('$user.userInfo._id') || ''
}
})
uni.hideLoading()
this.$set(r, '_submitting', false)
if (res.code === 0) {
r.status = (res.data && res.data.status) || 'reviewing'
uni.showToast({ title: '已提交审查', icon: 'success' })
setTimeout(() => this.queryReports(), 1500)
} else {
uni.showToast({ title: res.msg || '提交失败', icon: 'none' })
}
} catch (e) {
uni.hideLoading()
this.$set(r, '_submitting', false)
uni.showToast({ title: '提交异常', icon: 'none' })
}
},
formatContent(text) {
if (!text) return '<p>暂无审查结果</p>'
let html = text
@@ -238,6 +295,20 @@ export default {
color: #fff;
}
.action-btn-primary {
background: #a92924;
color: #fff;
}
.action-btn-primary:active {
background: #8c1f1b;
}
.action-btn-disabled {
color: #bbb;
border-color: #ddd;
}
.summary-popup {
padding: 30rpx;
max-height: 70vh;
+1
View File
@@ -122,6 +122,7 @@ export default {
file_base64: base64,
file_name: fileName,
file_size: fileSize,
uid: vk.getVuex('$user.userInfo._id') || '',
upload_only: true
}
})