feat: 合同审查轮询方案(先审查后扣积分、HTML报告下载、数据库记录)
This commit is contained in:
+255
-118
@@ -502,22 +502,27 @@
|
||||
<!-- 合同审查 -->
|
||||
<view v-if="menuIndex === '4'" class="review-container">
|
||||
<view class="review-title">📋 AI 合同审查</view>
|
||||
<!-- 积分提示 -->
|
||||
<view class="points-notice">
|
||||
<span class="pn-icon">💎</span>
|
||||
<view class="pn-text">
|
||||
<view class="pn-title">合同审查消耗积分</view>
|
||||
<view class="pn-desc">每页消耗 60 积分,上传后自动计算。</view>
|
||||
</view>
|
||||
<view class="pn-balance">余额:{{ pointsBalance }} 分</view>
|
||||
</view>
|
||||
<!-- 上传区 -->
|
||||
<view v-if="!reviewFile" class="upload-area" @click="selectReviewFile">
|
||||
<view class="upload-icon">📤</view>
|
||||
<view class="upload-title">上传合同文件</view>
|
||||
<view class="upload-desc">支持 PDF / Word / 图片,最大 20MB</view>
|
||||
<view class="upload-btn">选择文件上传</view>
|
||||
<!-- 积分提示 -->
|
||||
<view class="points-notice">
|
||||
<span class="pn-icon">💎</span>
|
||||
<view class="pn-text">
|
||||
<view class="pn-title">合同审查消耗积分</view>
|
||||
<view class="pn-desc">根据文件大小自动计算,每 100KB 约 150 积分。审查后余额需保留至少 200 积分。</view>
|
||||
</view>
|
||||
<view class="pn-balance">余额:{{ pointsBalance }} 分</view>
|
||||
</view>
|
||||
<!-- 免责声明 -->
|
||||
<view class="disclaimer-notice">
|
||||
<span class="dn-icon">⚠️</span>
|
||||
<view class="dn-text">请确保合同内容清晰可见。如因合同不清晰导致分析失败,平台不承担后果,积分正常消耗。</view>
|
||||
</view>
|
||||
<!-- 上传区 -->
|
||||
<view v-if="!reviewFile" class="upload-area" @click="selectReviewFile">
|
||||
<view class="upload-icon">📤</view>
|
||||
<view class="upload-title">上传合同文件</view>
|
||||
<view class="upload-desc">仅支持 PDF / Word(.pdf .doc .docx),最大 20MB</view>
|
||||
<view class="upload-btn">选择文件上传</view>
|
||||
</view>
|
||||
<!-- 上传中提示 -->
|
||||
<view v-if="reviewUploading" style="text-align:center;padding:30px 0;">
|
||||
<i class="el-icon-loading" style="font-size:32px;color:#a92924;"></i>
|
||||
@@ -547,11 +552,17 @@
|
||||
<view class="opt-points">{{ reviewPoints * 2 }} 积分</view>
|
||||
</view> -->
|
||||
</view>
|
||||
<view style="text-align:center;margin-top:16px;">
|
||||
<el-button type="primary" :loading="reviewLoading" @click="startReview">
|
||||
{{ reviewLoading ? '审查中...' : '开始审查(' + (reviewMode === 0 ? reviewPoints : reviewPoints * 2) + ' 积分)' }}
|
||||
</el-button>
|
||||
</view>
|
||||
<view style="text-align:center;margin-top:16px;">
|
||||
<el-button type="primary" :loading="reviewLoading"
|
||||
:disabled="pointsBalance < reviewPoints + 200"
|
||||
@click="startReview">
|
||||
{{ reviewLoading ? '审查中...' : '开始审查(' + reviewPoints + ' 积分)' }}
|
||||
</el-button>
|
||||
<p v-if="pointsBalance < reviewPoints + 200" style="font-size:12px;color:#f56c6c;margin-top:6px;">
|
||||
⚠️ 积分不足,当前余额 {{ pointsBalance }},需要 {{ reviewPoints }} 积分且审查后余额需保留至少 200 积分。
|
||||
<span style="color:#a92924;cursor:pointer;text-decoration:underline;" @click="$refs.aiChat ? null : null">去充值</span>
|
||||
</p>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 审查中 -->
|
||||
<view v-if="reviewLoading" style="text-align:center;padding:40px 0;">
|
||||
@@ -560,8 +571,8 @@
|
||||
<el-progress :percentage="reviewProgress" :stroke-width="8"
|
||||
style="max-width:400px;margin:0 auto;"></el-progress>
|
||||
</view>
|
||||
<!-- 审查结果 -->
|
||||
<view v-if="reviewResult">
|
||||
<!-- 审查结果 -->
|
||||
<view v-if="reviewResult">
|
||||
<view class="result-header">
|
||||
<h3>📋 审查结果 — {{ reviewFile.name }}</h3>
|
||||
<span class="result-status">✅ 审查完成</span>
|
||||
@@ -569,25 +580,35 @@
|
||||
<view class="result-body">
|
||||
<div v-html="reviewResult"></div>
|
||||
</view>
|
||||
<view class="result-footer">
|
||||
<el-button type="primary" size="small" @click="resetReview">重新审查</el-button>
|
||||
</view>
|
||||
<view class="result-footer">
|
||||
<el-button v-if="reviewHtmlUrl" type="success" size="small" @click="openUrl(reviewHtmlUrl)">📄 查看完整 HTML 报告</el-button>
|
||||
<el-button size="small" @click="resetReview">重新审查</el-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的报告 -->
|
||||
<view v-if="menuIndex === '5'" class="review-container">
|
||||
<view class="review-title">📈 我的报告</view>
|
||||
<view class="review-title">
|
||||
📈 我的报告
|
||||
<el-button size="mini" type="primary" style="margin-left:12px;" @click="queryReports()">🔍 查询报告</el-button>
|
||||
</view>
|
||||
<view v-if="reportList.length === 0" style="text-align:center;padding:60px 0;color:#999;">
|
||||
<div style="font-size:48px;margin-bottom:12px;">📋</div>
|
||||
暂无审查报告
|
||||
暂无审查报告,点击上方「查询报告」从服务器加载
|
||||
</view>
|
||||
<view v-for="(r, i) in reportList" :key="i" class="report-item">
|
||||
<view class="report-info">
|
||||
<view class="report-name">{{ r.fileName }}</view>
|
||||
<view class="report-time">{{ r.time }}</view>
|
||||
<view class="report-name">📄 {{ r.fileName }}</view>
|
||||
<view class="report-time">{{ r.time }} · 消耗 {{ Math.abs(r.amount) }} 积分</view>
|
||||
</view>
|
||||
<el-button size="mini" type="text" @click="viewReport(r)">查看</el-button>
|
||||
<el-tag v-if="r.status === 'uploaded'" size="mini" type="info">待审查</el-tag>
|
||||
<el-tag v-else-if="r.status === 'reviewing'" size="mini" type="warning">审查中</el-tag>
|
||||
<el-tag v-else-if="r.status === 'completed'" size="mini" type="success">已完成</el-tag>
|
||||
<el-tag v-else-if="r.status === 'failed'" size="mini" type="danger">失败</el-tag>
|
||||
<el-button v-if="r.htmlUrl" size="mini" type="text" @click="openUrl(r.htmlUrl)">查看报告</el-button>
|
||||
<el-button v-if="r.fileUrl" size="mini" type="text" @click="previewFile(r)">查看文件</el-button>
|
||||
<!-- <el-button v-if="r.status === 'completed'" size="mini" type="text" @click="viewReport(r)">查看摘要</el-button> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -625,12 +646,15 @@
|
||||
<!-- 使用说明 -->
|
||||
<view class="review-title" style="font-size:14px;margin-top:16px;">📖 积分使用说明</view>
|
||||
<view style="font-size:13px;color:#666;line-height:2;margin-bottom:20px;">
|
||||
<p>• <strong>标准审查</strong>:每页 60 积分 — 风险条款识别、合规性检查</p>
|
||||
<p>• <strong>深度分析</strong>:标准审查 × 2 — 标准审查 + 估价参考 + 修改建议</p>
|
||||
<p>• <strong>合同审查</strong>:每 100KB 约 150 积分(根据文件大小自动计算)</p>
|
||||
<p>• <strong>AI 客服</strong>:免费 — 签约宝专属问答</p>
|
||||
<p>• 新用户注册自动赠送 500 体验积分</p>
|
||||
<p>• 积分长期有效,不过期</p>
|
||||
</view>
|
||||
<view class="review-title" style="font-size:14px;">
|
||||
📜 积分明细
|
||||
<el-button size="mini" type="text" style="margin-left:12px;" @click="getPointsLog()">🔍 查看</el-button>
|
||||
</view>
|
||||
<view class="review-title" style="font-size:14px;">📜 积分明细</view>
|
||||
<el-table :data="pointsLogList" style="width:100%">
|
||||
<el-table-column prop="_add_time_str" label="时间" width="180"></el-table-column>
|
||||
<el-table-column label="类型" width="80">
|
||||
@@ -844,6 +868,10 @@
|
||||
// 合同审查数据
|
||||
reviewFile: null,
|
||||
reviewFileId: null,
|
||||
reviewRecordId: null,
|
||||
reviewHtmlUrl: '',
|
||||
reviewPdfUrl: '',
|
||||
reviewContractInfo: {},
|
||||
reviewUploading: false,
|
||||
reviewMode: 0,
|
||||
reviewPoints: 100,
|
||||
@@ -1474,6 +1502,8 @@
|
||||
this.userInfo = vk.getVuex('$user.userInfo')
|
||||
this.getUserInfo()
|
||||
this.getPointsBalance()
|
||||
if (this.menuIndex === '6') this.getPointsLog();
|
||||
if (this.menuIndex === '5') this.getReportList();
|
||||
},
|
||||
methods: {
|
||||
dayjs,
|
||||
@@ -1487,8 +1517,6 @@
|
||||
this.isSignTips = false
|
||||
// AI 智能服务菜单(3/4/5/6)不调 query,加载对应数据
|
||||
if (e >= 3) {
|
||||
if (e === 6) this.getPointsLog();
|
||||
if (e === 5) this.getReportList();
|
||||
if (e === 4 || e === 6) this.getPointsBalance();
|
||||
return;
|
||||
}
|
||||
@@ -1640,6 +1668,35 @@
|
||||
html = html.replace(/\n/g, '<br>');
|
||||
return html;
|
||||
},
|
||||
openUrl(url) {
|
||||
if (url) window.open(url, '_blank');
|
||||
},
|
||||
formatReviewResult(text) {
|
||||
if (!text) return '';
|
||||
let html = text;
|
||||
// 处理 markdown 标题 ## / ###
|
||||
html = html.replace(/^###\s+(.+)$/gm, '<h4 style="margin:16px 0 8px;color:#333;">$1</h4>');
|
||||
html = html.replace(/^##\s+(.+)$/gm, '<h3 style="margin:20px 0 10px;color:#222;">$1</h3>');
|
||||
// 处理加粗
|
||||
html = html.replace(/\*\*(.*?)\*\*/g, '<strong style="color:#a92924;">$1</strong>');
|
||||
// 处理链接(PDF下载链接等)— 自动识别 http/https 链接
|
||||
html = html.replace(/(https?:\/\/[^\s\)\]\"\<]+)/g, '<a href="$1" target="_blank" style="color:#a92924;text-decoration:underline;word-break:break-all;">$1</a>');
|
||||
// 处理列表
|
||||
html = html.replace(/^[\-\•]\s+(.+)$/gm, '<div style="padding-left:16px;margin:4px 0;">• $1</div>');
|
||||
html = html.replace(/^(\d+)\.\s+(.+)$/gm, '<div style="padding-left:16px;margin:4px 0;">$1. $2</div>');
|
||||
// 处理表格行(简单的 | 分隔表格)
|
||||
html = html.replace(/\|(.+)\|/g, function(match) {
|
||||
const cells = match.split('|').filter(c => c.trim());
|
||||
if (cells.length > 1) {
|
||||
return '<div style="display:flex;gap:8px;padding:6px 0;border-bottom:1px solid #eee;">' +
|
||||
cells.map(c => '<span style="flex:1;font-size:13px;">' + c.trim() + '</span>').join('') + '</div>';
|
||||
}
|
||||
return match;
|
||||
});
|
||||
// 换行
|
||||
html = html.replace(/\n/g, '<br>');
|
||||
return html;
|
||||
},
|
||||
// ===== 积分相关 =====
|
||||
async getPointsBalance() {
|
||||
try {
|
||||
@@ -1663,36 +1720,64 @@
|
||||
}
|
||||
});
|
||||
if (res.code === 0) {
|
||||
this.pointsLogList = (res.data.rows || []).map(r => ({
|
||||
...r,
|
||||
_add_time_str: dayjs(r._add_time).format('YYYY-MM-DD HH:mm:ss')
|
||||
this.pointsLogList = (res.data.rows || []).map(r => ({
|
||||
...r,
|
||||
_add_time_str: dayjs(r._add_time && r._add_time['$date'] ? r._add_time['$date'] : r._add_time).format('YYYY-MM-DD HH:mm:ss')
|
||||
}));
|
||||
}
|
||||
} catch (e) {
|
||||
/* 静默 */ }
|
||||
},
|
||||
async getReportList() {
|
||||
try {
|
||||
const reports = JSON.parse(localStorage.getItem('jjx_review_reports') || '[]');
|
||||
this.reportList = reports.map(r => ({
|
||||
fileName: r.fileName,
|
||||
fileSize: r.fileSize,
|
||||
time: r.time,
|
||||
amount: r.points,
|
||||
result: r.result,
|
||||
status: 'completed'
|
||||
}));
|
||||
} catch (e) { /* 静默 */ }
|
||||
},
|
||||
async queryReports() {
|
||||
try {
|
||||
const res = await vk.callFunction({
|
||||
url: 'client/points/kh/getLog',
|
||||
data: {
|
||||
pageIndex: 1,
|
||||
pageSize: 50,
|
||||
biz_type: 'contract_review'
|
||||
}
|
||||
url: 'client/ai/pub/getMyReports',
|
||||
data: { pageIndex: 1, pageSize: 50 }
|
||||
});
|
||||
if (res.code === 0) {
|
||||
this.reportList = (res.data.rows || []).map(r => ({
|
||||
fileName: r.reason ? r.reason.replace('合同审查 — ', '') : '未知文件',
|
||||
time: dayjs(r._add_time).format('YYYY-MM-DD HH:mm:ss'),
|
||||
bizId: r.biz_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,
|
||||
fileUrl: r.file_url || '',
|
||||
htmlUrl: r.html_url || '',
|
||||
time: dayjs(r._add_time && r._add_time['$date'] ? r._add_time['$date'] : r._add_time).format('YYYY-MM-DD HH:mm:ss'),
|
||||
amount: r.points_used || 0,
|
||||
result: r.result,
|
||||
status: r.status,
|
||||
recordId: r._id
|
||||
}));
|
||||
this.$message.success('加载了 ' + this.reportList.length + ' 条报告');
|
||||
}
|
||||
} catch (e) {
|
||||
/* 静默 */ }
|
||||
this.$message.error('查询失败');
|
||||
}
|
||||
},
|
||||
viewReport(r) {
|
||||
this.$message.info('查看报告:' + r.fileName);
|
||||
// 跳转到合同审查页并显示该报告的结果
|
||||
this.menuIndex = '4';
|
||||
this.reviewResult = r.result || '暂无审查结果';
|
||||
this.reviewFile = { name: r.fileName, size: r.fileSize || 0 };
|
||||
},
|
||||
previewFile(r) {
|
||||
if (!r.fileUrl) {
|
||||
this.$message.warning('文件链接不存在');
|
||||
return;
|
||||
}
|
||||
// 新窗口打开文件(PDF直接预览,Word下载)
|
||||
window.open(r.fileUrl, '_blank');
|
||||
},
|
||||
formatFileSize(bytes) {
|
||||
if (bytes < 1024) return bytes + ' B';
|
||||
@@ -1703,7 +1788,7 @@
|
||||
selectReviewFile() {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.pdf,.doc,.docx,.jpg,.png';
|
||||
input.accept = '.pdf,.doc,.docx';
|
||||
input.onchange = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
@@ -1725,17 +1810,19 @@
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
const res = await vk.callFunction({
|
||||
url: 'client/ai/pub/review',
|
||||
data: {
|
||||
file_base64: fileBase64,
|
||||
file_name: file.name,
|
||||
upload_only: true
|
||||
}
|
||||
});
|
||||
const res = await vk.callFunction({
|
||||
url: 'client/ai/pub/review',
|
||||
data: {
|
||||
file_base64: fileBase64,
|
||||
file_name: file.name,
|
||||
file_size: file.size,
|
||||
upload_only: true
|
||||
}
|
||||
});
|
||||
|
||||
if (res.code === 0 && res.data && res.data.file_id) {
|
||||
this.reviewFileId = res.data.file_id;
|
||||
this.reviewRecordId = res.data.record_id; // 保存待审查记录ID
|
||||
this.$message.success('文件上传成功');
|
||||
} else {
|
||||
this.$message.error(res.msg || '文件上传失败');
|
||||
@@ -1755,83 +1842,125 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const pointsNeeded = this.reviewMode === 0 ? this.reviewPoints : this.reviewPoints * 2;
|
||||
if (this.pointsBalance < pointsNeeded) {
|
||||
this.$message.warning('积分不足,当前余额 ' + this.pointsBalance + ',需要 ' + pointsNeeded);
|
||||
const pointsNeeded = this.reviewPoints;
|
||||
if (this.pointsBalance < pointsNeeded + 200) {
|
||||
this.$message.warning('积分不足,当前余额 ' + this.pointsBalance + ',需要 ' + pointsNeeded + ' 且审查后余额需保留至少 200 积分');
|
||||
return;
|
||||
}
|
||||
|
||||
this.reviewLoading = true;
|
||||
this.reviewProgress = 0;
|
||||
|
||||
// 模拟进度
|
||||
const interval = setInterval(() => {
|
||||
this.reviewProgress = Math.min(this.reviewProgress + Math.random() * 10, 95);
|
||||
}, 800);
|
||||
// 模拟进度条
|
||||
const progressTimer = setInterval(() => {
|
||||
this.reviewProgress = Math.min(this.reviewProgress + 1, 90);
|
||||
}, 1500);
|
||||
|
||||
try {
|
||||
// 1. 扣积分
|
||||
const deductRes = await vk.callFunction({
|
||||
url: 'client/points/kh/deduct',
|
||||
// 1. 发起审查(只更新状态,立即返回)
|
||||
const res = await vk.callFunction({
|
||||
url: 'client/ai/pub/review',
|
||||
data: {
|
||||
amount: pointsNeeded,
|
||||
reason: '合同审查 — ' + this.reviewFile.name,
|
||||
biz_type: 'contract_review'
|
||||
file_id: this.reviewFileId,
|
||||
record_id: this.reviewRecordId,
|
||||
file_name: this.reviewFile.name,
|
||||
file_size: this.reviewFile.size
|
||||
}
|
||||
});
|
||||
if (deductRes.code !== 0) {
|
||||
this.$message.error(deductRes.msg || '扣积分失败');
|
||||
|
||||
if (res.code !== 0) {
|
||||
this.$message.error(res.msg || '审查发起失败');
|
||||
this.reviewLoading = false;
|
||||
clearInterval(interval);
|
||||
clearInterval(progressTimer);
|
||||
return;
|
||||
}
|
||||
this.pointsBalance = deductRes.data.balance;
|
||||
|
||||
// 2. 调用合同审查(异步,立即返回)
|
||||
const res = await vk.callFunction({
|
||||
url: 'client/ai/pub/review',
|
||||
data: { file_id: this.reviewFileId, file_name: this.reviewFile.name }
|
||||
});
|
||||
if (res.code !== 0) {
|
||||
this.$message.error(res.msg || '审查发起失败');
|
||||
this.reviewLoading = false;
|
||||
clearInterval(interval);
|
||||
return;
|
||||
const recordId = res.data.record_id;
|
||||
|
||||
// 2. 轮询结果(每 5 秒查一次,最多 10 分钟)
|
||||
let result = '';
|
||||
let pointsUsed = 0;
|
||||
let htmlUrl = '';
|
||||
let contractInfo = {};
|
||||
|
||||
for (let i = 0; i < 60; i++) {
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
|
||||
const pollRes = await vk.callFunction({
|
||||
url: 'client/ai/pub/getReviewResult',
|
||||
data: { record_id: recordId }
|
||||
});
|
||||
|
||||
if (pollRes.code === 0 && pollRes.data) {
|
||||
if (pollRes.data.status === 'completed') {
|
||||
result = pollRes.data.result;
|
||||
pointsUsed = pollRes.data.points_used || 0;
|
||||
htmlUrl = pollRes.data.html_url || '';
|
||||
contractInfo = pollRes.data.contract_info || {};
|
||||
break;
|
||||
}
|
||||
if (pollRes.data.status === 'failed') {
|
||||
this.$message.error(pollRes.data.result || '审查失败');
|
||||
this.reviewLoading = false;
|
||||
clearInterval(progressTimer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const chatId = res.data.chat_id;
|
||||
const conversationId = res.data.conversation_id;
|
||||
|
||||
// 3. 轮询结果(每 3 秒查一次,最多 5 分钟)
|
||||
let answer = '';
|
||||
for (let i = 0; i < 100; i++) {
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
this.reviewProgress = Math.min(this.reviewProgress + 1, 95);
|
||||
|
||||
const pollRes = await vk.callFunction({
|
||||
url: 'client/ai/pub/getReviewResult',
|
||||
data: { chat_id: chatId, conversation_id: conversationId }
|
||||
});
|
||||
|
||||
if (pollRes.code === 0 && pollRes.data.status === 'completed') {
|
||||
answer = pollRes.data.answer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
clearInterval(interval);
|
||||
|
||||
clearInterval(progressTimer);
|
||||
this.reviewProgress = 100;
|
||||
|
||||
if (answer) {
|
||||
this.reviewResult = answer;
|
||||
this.getReportList();
|
||||
this.$message.success('合同审查完成');
|
||||
} else {
|
||||
this.$message.error('审查超时,请稍后在「我的报告」中查看');
|
||||
}
|
||||
|
||||
if (result) {
|
||||
// 解析 JSON 结果
|
||||
try {
|
||||
const jsonMatch = result.match(/```json\s*([\s\S]*?)```/);
|
||||
if (jsonMatch) {
|
||||
const info = JSON.parse(jsonMatch[1].trim());
|
||||
this.reviewContractInfo = info;
|
||||
// 生成摘要文本用于页面显示
|
||||
let summary = '<strong>合同名称:</strong>' + (info.contract_name || '-') + '<br>';
|
||||
summary += '<strong>合同类型:</strong>' + (info.contract_type || '-') + '<br>';
|
||||
summary += '<strong>综合风险:</strong><span style="color:#f56c6c;font-weight:700;">' + (info.risk_level || '-') + '</span><br>';
|
||||
summary += '<strong>风险统计:</strong>高 ' + (info.risk_count && info.risk_count.high || 0) + ' / 中 ' + (info.risk_count && info.risk_count.medium || 0) + ' / 低 ' + (info.risk_count && info.risk_count.low || 0) + '<br>';
|
||||
summary += '<strong>总体评估:</strong>' + (info.risk_summary || '-') + '<br>';
|
||||
// 高风险摘要
|
||||
const highRisks = (info.risks || []).filter(r => r.level === 'high');
|
||||
if (highRisks.length > 0) {
|
||||
summary += '<br><strong style="color:#f56c6c;">⚠️ 高风险摘要:</strong><br>';
|
||||
highRisks.forEach(r => {
|
||||
summary += '<div style="padding:8px 12px;margin:6px 0;background:#fff1f0;border-left:3px solid #f56c6c;border-radius:0 4px 4px 0;font-size:13px;">';
|
||||
summary += '<strong>风险 #' + r.id + '</strong> ' + (r.location || '') + ':' + (r.problem || '');
|
||||
summary += '</div>';
|
||||
});
|
||||
}
|
||||
this.reviewResult = summary;
|
||||
} else {
|
||||
this.reviewResult = result;
|
||||
}
|
||||
} catch (e) {
|
||||
this.reviewResult = result;
|
||||
}
|
||||
this.reviewHtmlUrl = htmlUrl;
|
||||
this.pointsBalance -= pointsUsed;
|
||||
// 保存报告到本地
|
||||
const reports = JSON.parse(localStorage.getItem('jjx_review_reports') || '[]');
|
||||
reports.unshift({
|
||||
fileName: this.reviewFile.name,
|
||||
fileSize: this.reviewFile.size,
|
||||
time: new Date().toLocaleString(),
|
||||
points: pointsUsed,
|
||||
result: result,
|
||||
htmlUrl: htmlUrl
|
||||
});
|
||||
localStorage.setItem('jjx_review_reports', JSON.stringify(reports.slice(0, 50)));
|
||||
this.getReportList();
|
||||
this.$message.success('合同审查完成,消耗 ' + pointsUsed + ' 积分');
|
||||
} else {
|
||||
this.$message.warning('审查时间较长,请稍后在「我的报告」中查看结果');
|
||||
}
|
||||
} catch (e) {
|
||||
clearInterval(interval);
|
||||
clearInterval(progressTimer);
|
||||
this.$message.error('审查异常,请稍后重试');
|
||||
}
|
||||
|
||||
@@ -1840,6 +1969,7 @@
|
||||
resetReview() {
|
||||
this.reviewFile = null;
|
||||
this.reviewFileId = null;
|
||||
this.reviewRecordId = null;
|
||||
this.reviewUploading = false;
|
||||
this.reviewResult = null;
|
||||
this.reviewProgress = 0;
|
||||
@@ -2739,6 +2869,13 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.disclaimer-notice {
|
||||
display: flex; align-items: flex-start; gap: 8px;
|
||||
background: #fff8e1; border: 1px solid #ffe082; border-radius: 8px;
|
||||
padding: 12px 16px; margin-bottom: 20px; font-size: 12px; color: #e6a23c;
|
||||
}
|
||||
.dn-icon { font-size: 16px; flex-shrink: 0; margin-top: 1px; }
|
||||
.dn-text { line-height: 1.6; }
|
||||
.points-notice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user