按原型统一风险报告页面与下载内容
完善报告页栏目和原型样式,统一风险卡片、网页报告、PDF与Word的字段口径;接入真实PDF和DOCX下载,修复合作频次、七维结论及标准判定展示差异。
This commit is contained in:
parent
5397d128be
commit
1cc6b471d3
16
src/api/reports.ts
Normal file
16
src/api/reports.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { http } from "@/api/http";
|
||||||
|
import type { RiskReportResult } from "@/types/risk";
|
||||||
|
|
||||||
|
export function downloadReportPdf(payload: RiskReportResult) {
|
||||||
|
return http.post<Blob>("/v1/reports/pdf", payload, {
|
||||||
|
responseType: "blob",
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function downloadReportWord(payload: RiskReportResult) {
|
||||||
|
return http.post<Blob>("/v1/reports/word", payload, {
|
||||||
|
responseType: "blob",
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -98,9 +98,9 @@ function formatCount(value: unknown) {
|
|||||||
<b>{{ formatCount(data.anomalyCount) }}</b
|
<b>{{ formatCount(data.anomalyCount) }}</b
|
||||||
><span>近3年异常</span>
|
><span>近3年异常</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div v-if="data.cooperationCount != null">
|
||||||
<b>{{ formatCount(data.cooperationCount) }}</b
|
<b>{{ formatCount(data.cooperationCount) }}</b
|
||||||
><span>合作频次(代理)</span>
|
><span>合作频次(采购订单)</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<b>×{{ data.frequencyCoefficient?.toFixed(2) ?? "1.00" }}</b
|
<b>×{{ data.frequencyCoefficient?.toFixed(2) ?? "1.00" }}</b
|
||||||
|
|||||||
@ -19,6 +19,7 @@ const props = defineProps<{
|
|||||||
result: RiskAssessmentResult;
|
result: RiskAssessmentResult;
|
||||||
canMonitor?: boolean;
|
canMonitor?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
const emit = defineEmits<{ generateReport: [] }>();
|
||||||
const expanded = ref(true);
|
const expanded = ref(true);
|
||||||
const feedback = ref("");
|
const feedback = ref("");
|
||||||
|
|
||||||
@ -161,16 +162,9 @@ const stars = computed(() => {
|
|||||||
<span>供应商层级</span
|
<span>供应商层级</span
|
||||||
><strong>{{ result.supplierFreq.tier }}</strong>
|
><strong>{{ result.supplierFreq.tier }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div v-if="result.supplierFreq.cooperationCount != null">
|
||||||
<span>合作频次(代理)</span>
|
<span>合作频次(采购订单)</span>
|
||||||
<strong
|
<strong>{{ result.supplierFreq.cooperationCount }} 次</strong>
|
||||||
>{{
|
|
||||||
result.supplierFreq.cooperationCount ??
|
|
||||||
result.supplierFreq.anomalyCount ??
|
|
||||||
0
|
|
||||||
}}
|
|
||||||
次</strong
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="result.supplierFreq.suggestion">
|
<div v-if="result.supplierFreq.suggestion">
|
||||||
<span>管理建议</span>{{ result.supplierFreq.suggestion }}
|
<span>管理建议</span>{{ result.supplierFreq.suggestion }}
|
||||||
@ -190,7 +184,7 @@ const stars = computed(() => {
|
|||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
result.supplierFreq.note ||
|
result.supplierFreq.note ||
|
||||||
"当前以近3年异常记录数作为合作频次代理,不代表真实采购订单合作次数。"
|
"合作频次按近3年采购订单去重单据编号统计;异常数仍按异常案例库统计。"
|
||||||
}}
|
}}
|
||||||
</small>
|
</small>
|
||||||
</section>
|
</section>
|
||||||
@ -298,7 +292,7 @@ const stars = computed(() => {
|
|||||||
<button
|
<button
|
||||||
class="primary-action"
|
class="primary-action"
|
||||||
type="button"
|
type="button"
|
||||||
@click="notify('生成报告')"
|
@click="emit('generateReport')"
|
||||||
>
|
>
|
||||||
<FileDown :size="14" /> 生成报告
|
<FileDown :size="14" /> 生成报告
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
488
src/pages/ReportPage.vue
Normal file
488
src/pages/ReportPage.vue
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ArrowLeft, Download, FileText } from "@lucide/vue";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
import { downloadReportPdf, downloadReportWord } from "@/api/reports";
|
||||||
|
import type { RiskReportResult } from "@/types/risk";
|
||||||
|
import { formatRiskLevel } from "@/utils/risk";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
let parsed: RiskReportResult | null = null;
|
||||||
|
try {
|
||||||
|
const raw = sessionStorage.getItem("dieya-risk-report");
|
||||||
|
parsed = raw ? (JSON.parse(raw) as RiskReportResult) : null;
|
||||||
|
} catch {
|
||||||
|
parsed = null;
|
||||||
|
}
|
||||||
|
const report = computed(() => parsed);
|
||||||
|
const dimensionRows = computed(() => {
|
||||||
|
if (!report.value) return [];
|
||||||
|
if (report.value.dimensionScores?.length) {
|
||||||
|
return report.value.dimensionScores.filter(
|
||||||
|
(item) => item.dimension !== "节点阶段" && item.dimension !== "缺陷等级",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return report.value.factors
|
||||||
|
.filter(
|
||||||
|
(factor) => factor.name !== "节点阶段" && factor.name !== "缺陷等级",
|
||||||
|
)
|
||||||
|
.map((factor) => ({
|
||||||
|
dimension: factor.name,
|
||||||
|
score: 100 - factor.value,
|
||||||
|
weight: factor.weight,
|
||||||
|
status: "database",
|
||||||
|
reason: null,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
const generatedAt = new Intl.DateTimeFormat("zh-CN", {
|
||||||
|
dateStyle: "medium",
|
||||||
|
timeStyle: "short",
|
||||||
|
}).format(new Date());
|
||||||
|
|
||||||
|
async function downloadWord() {
|
||||||
|
if (!report.value) return;
|
||||||
|
const response = await downloadReportWord(report.value);
|
||||||
|
const blob = response.data;
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const anchor = document.createElement("a");
|
||||||
|
anchor.href = url;
|
||||||
|
anchor.download = `蝶雅新品风险预估报告-${report.value.subjectId}.docx`;
|
||||||
|
anchor.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadPdf() {
|
||||||
|
if (!report.value) return;
|
||||||
|
const response = await downloadReportPdf(report.value);
|
||||||
|
const url = URL.createObjectURL(response.data);
|
||||||
|
const anchor = document.createElement("a");
|
||||||
|
anchor.href = url;
|
||||||
|
anchor.download = `蝶雅新品风险预估报告-${report.value.subjectId}.pdf`;
|
||||||
|
anchor.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(message: string) {
|
||||||
|
window.alert(`${message}功能待接入服务端`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dimensionConclusion(item: {
|
||||||
|
dimension: string;
|
||||||
|
score: number;
|
||||||
|
reason?: string | null;
|
||||||
|
}) {
|
||||||
|
const supplier = report.value?.supplierFreq;
|
||||||
|
if (item.dimension === "供应商表现" && supplier) {
|
||||||
|
const orderText =
|
||||||
|
supplier.cooperationCount != null
|
||||||
|
? `合作 ${supplier.cooperationCount} 次、`
|
||||||
|
: "";
|
||||||
|
return `${Math.round(item.score)} 分 · ${orderText}异常 ${supplier.anomalyCount ?? 0} 次`;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
item.dimension === "标准符合性" &&
|
||||||
|
report.value?.standardJudgment?.length
|
||||||
|
) {
|
||||||
|
return `${Math.round(item.score)} 分 · 命中 ${report.value.standardJudgment.length} 条判定标准,待检测项需人工确认`;
|
||||||
|
}
|
||||||
|
return `${Math.round(item.score)} 分${item.reason ? ` · ${item.reason}` : ""}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="report-page">
|
||||||
|
<header class="report-actions">
|
||||||
|
<button type="button" class="secondary-button" @click="downloadPdf">
|
||||||
|
<Download :size="15" />导出 PDF
|
||||||
|
</button>
|
||||||
|
<button type="button" class="secondary-button" @click="downloadWord">
|
||||||
|
<Download :size="15" />导出 Word
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="secondary-button"
|
||||||
|
@click="notify('钉钉发送')"
|
||||||
|
>
|
||||||
|
<FileText :size="15" />钉钉发送
|
||||||
|
</button>
|
||||||
|
<button type="button" class="primary-button" @click="router.back()">
|
||||||
|
<ArrowLeft :size="15" />返回对话
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<p v-if="!report" class="report-empty">
|
||||||
|
暂无报告数据,请从风险评估结果卡片点击“生成报告”。
|
||||||
|
</p>
|
||||||
|
<article v-else class="report-sheet">
|
||||||
|
<h1>新品风险预估报告</h1>
|
||||||
|
<p class="report-subtitle">
|
||||||
|
蝶雅产品部 · 风险预估系统 · 自动生成 · {{ generatedAt }}
|
||||||
|
</p>
|
||||||
|
<div class="report-meta">
|
||||||
|
<span><b>产品/评估对象:</b>{{ report.subjectId }}</span>
|
||||||
|
><span
|
||||||
|
><b>供应商:</b
|
||||||
|
>{{ report.supplierFreq?.supplierName || "未指定" }}</span
|
||||||
|
><span
|
||||||
|
><b>报告编号:</b>RP-{{ report.subjectId.slice(0, 8) }}-{{
|
||||||
|
report.score.toFixed(0)
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>一、风险结论</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>评估项</th>
|
||||||
|
<th>结论</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>综合评分</td>
|
||||||
|
<td class="risk">
|
||||||
|
{{ Math.round(report.score) }} / 100 ·
|
||||||
|
{{ formatRiskLevel(report.level) }} ·
|
||||||
|
{{ report.riskBadge || "需关注" }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>风险警示</td>
|
||||||
|
<td>
|
||||||
|
{{
|
||||||
|
report.alertText ||
|
||||||
|
"当前暂无足够历史数据,建议按标准库完成基础验证。"
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="item in dimensionRows" :key="item.dimension">
|
||||||
|
<td>{{ item.dimension }}({{ item.weight }}%)</td>
|
||||||
|
<td>{{ dimensionConclusion(item) }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<template v-if="report.categoryWarning">
|
||||||
|
<h2>二、品类风险预警</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>品类</th>
|
||||||
|
<th>历史异常</th>
|
||||||
|
<th>风险等级</th>
|
||||||
|
<th>建议</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{{ report.categoryWarning.category }}</td>
|
||||||
|
<td>{{ report.categoryWarning.anomalyCount }} 条</td>
|
||||||
|
<td>{{ report.categoryWarning.riskLevel }}</td>
|
||||||
|
<td>
|
||||||
|
{{
|
||||||
|
report.categoryWarning.suggestion ||
|
||||||
|
"结合相似品类和标准库预判"
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="report.supplierFreq?.supplierName">
|
||||||
|
<h2>三、供应商合作频次分析</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>维度</th>
|
||||||
|
<th>数值</th>
|
||||||
|
<th>说明</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-if="report.supplierFreq.cooperationCount != null">
|
||||||
|
<td>合作次数</td>
|
||||||
|
<td>{{ report.supplierFreq.cooperationCount }} 次</td>
|
||||||
|
<td>近三年采购订单去重单据数</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>异常次数</td>
|
||||||
|
<td>{{ report.supplierFreq.anomalyCount ?? 0 }} 次</td>
|
||||||
|
<td>异常案例库统计</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="report.supplierFreq.alpha != null">
|
||||||
|
<td>频次风险系数</td>
|
||||||
|
<td>×{{ report.supplierFreq.alpha.toFixed(2) }}</td>
|
||||||
|
<td>α = 1.0 +(采购订单数 / 100)× 0.3,最高1.5</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="report.supplierFreq.tier">
|
||||||
|
<td>供应商层级</td>
|
||||||
|
<td>{{ report.supplierFreq.tier }}</td>
|
||||||
|
<td>{{ report.supplierFreq.suggestion }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<h2>四、相似案例匹配({{ report.similarCases?.length || 0 }} 条)</h2>
|
||||||
|
<template v-if="report.similarCases?.length">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>日期</th>
|
||||||
|
<th>产品</th>
|
||||||
|
<th>问题</th>
|
||||||
|
<th>处理方案</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="item in report.similarCases"
|
||||||
|
:key="`${item.date}-${item.product}-${item.problem}`"
|
||||||
|
>
|
||||||
|
<td>{{ item.date || "未知" }}</td>
|
||||||
|
<td>{{ item.product || "未命名" }}</td>
|
||||||
|
<td>{{ item.problem || "未填写" }}</td>
|
||||||
|
<td>{{ item.solution || "未填写" }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
<p v-else class="report-paragraph">
|
||||||
|
暂无相关案例,RAG 相似案例检索未返回匹配结果。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>五、预防建议</h2>
|
||||||
|
<ol>
|
||||||
|
<li
|
||||||
|
v-for="suggestion in report.aiSuggestions?.length
|
||||||
|
? report.aiSuggestions
|
||||||
|
: ['优先针对低分维度安排专项验证,并明确责任人与完成节点。']"
|
||||||
|
:key="suggestion"
|
||||||
|
>
|
||||||
|
{{ suggestion }}
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>六、AI 推理方案(无历史案例场景)</h2>
|
||||||
|
<p class="report-paragraph">
|
||||||
|
<template v-if="report.similarCases?.length">
|
||||||
|
本次评估已匹配
|
||||||
|
{{ report.similarCases.length }}
|
||||||
|
条相似历史案例,当前采用历史案例与标准库双引擎结论,未单独触发无历史案例类比推理。
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
当前未匹配到足够历史案例,系统将基于相似品类、相似物料生成类比方案,并结合判定标准库进行规则兜底;实际执行前需由产品和品控确认。
|
||||||
|
</template>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>七、标准判定结果</h2>
|
||||||
|
<template v-if="report.standardJudgment?.length">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>判定维度</th>
|
||||||
|
<th>判定标准</th>
|
||||||
|
<th>标准来源</th>
|
||||||
|
<th>当前状态</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="standard in report.standardJudgment"
|
||||||
|
:key="`${standard.category}-${standard.riskPoint}`"
|
||||||
|
>
|
||||||
|
<td>{{ standard.category }} · {{ standard.riskPoint }}</td>
|
||||||
|
<td>{{ standard.rule }}</td>
|
||||||
|
<td>{{ standard.source }}</td>
|
||||||
|
<td>{{ standard.status }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
<p v-else class="report-paragraph">暂无相关标准。</p>
|
||||||
|
<p class="report-paragraph report-engine-note">
|
||||||
|
双引擎融合:历史异常经验引擎提供风险优先级,标准库提供可执行的检测阈值与验证任务。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<footer class="report-footer">
|
||||||
|
<span>编制:蝶雅风险预估系统</span
|
||||||
|
><span>生成时间:{{ generatedAt }}</span>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.report-page {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #f5f7fb;
|
||||||
|
padding: 24px 28px 40px;
|
||||||
|
}
|
||||||
|
.report-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 6px;
|
||||||
|
margin: 0 auto 16px;
|
||||||
|
max-width: 780px;
|
||||||
|
}
|
||||||
|
.action-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.primary-button,
|
||||||
|
.secondary-button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 650;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.primary-button {
|
||||||
|
border: 1px solid #111827;
|
||||||
|
background: #111827;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.secondary-button {
|
||||||
|
border: 1px solid #d1d9e6;
|
||||||
|
background: white;
|
||||||
|
color: #334155;
|
||||||
|
}
|
||||||
|
.report-empty {
|
||||||
|
max-width: 780px;
|
||||||
|
margin: 80px auto;
|
||||||
|
text-align: center;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
.report-sheet {
|
||||||
|
max-width: 780px;
|
||||||
|
margin: 0 auto;
|
||||||
|
border: 1px solid #dce3ed;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: white;
|
||||||
|
padding: 48px;
|
||||||
|
box-shadow: 0 10px 26px rgb(15 23 42 / 10%);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 0 0 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 28px;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
.report-subtitle {
|
||||||
|
margin: 0 0 28px;
|
||||||
|
border-bottom: 2px solid #111827;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.report-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 32px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
color: #334155;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin: 24px 0 12px;
|
||||||
|
border-left: 2px solid #111827;
|
||||||
|
padding-left: 8px;
|
||||||
|
color: #111827;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid #dbe3ef;
|
||||||
|
padding: 9px 12px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #111827;
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.risk {
|
||||||
|
color: #d97706;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
ol {
|
||||||
|
padding-left: 24px;
|
||||||
|
color: #334155;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.9;
|
||||||
|
}
|
||||||
|
.report-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 36px;
|
||||||
|
color: #94a3b8;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
.report-paragraph {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
color: #334155;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.report-page {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
.report-sheet {
|
||||||
|
padding: 24px 16px;
|
||||||
|
}
|
||||||
|
.report-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.action-group {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.report-meta {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
min-width: 680px;
|
||||||
|
}
|
||||||
|
.report-sheet {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media print {
|
||||||
|
.report-page {
|
||||||
|
padding: 0;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
.report-actions {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.report-sheet {
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
max-width: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
break-after: avoid;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
break-inside: avoid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -11,6 +11,7 @@ import OcrRecognitionDialog from "@/components/ocr/OcrRecognitionDialog.vue";
|
|||||||
import { useConversationStore } from "@/stores/conversation.store";
|
import { useConversationStore } from "@/stores/conversation.store";
|
||||||
import { useAuthStore } from "@/stores/auth.store";
|
import { useAuthStore } from "@/stores/auth.store";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
|
import type { RiskAssessmentResult } from "@/types/risk";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -217,6 +218,11 @@ async function openFromRoute() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openReport(result: RiskAssessmentResult) {
|
||||||
|
sessionStorage.setItem("dieya-risk-report", JSON.stringify(result));
|
||||||
|
void router.push({ name: "report" });
|
||||||
|
}
|
||||||
|
|
||||||
async function submit(
|
async function submit(
|
||||||
messageType: "text" | "image" | "file" | "audio" = "text",
|
messageType: "text" | "image" | "file" | "audio" = "text",
|
||||||
) {
|
) {
|
||||||
@ -315,6 +321,7 @@ async function scrollToBottom(behavior: "smooth" | "auto" = "smooth") {
|
|||||||
v-if="message.riskResult"
|
v-if="message.riskResult"
|
||||||
:result="message.riskResult"
|
:result="message.riskResult"
|
||||||
:can-monitor="canMonitor"
|
:can-monitor="canMonitor"
|
||||||
|
@generate-report="openReport(message.riskResult)"
|
||||||
/>
|
/>
|
||||||
<AssistantResponseCard
|
<AssistantResponseCard
|
||||||
v-if="message.responseCard"
|
v-if="message.responseCard"
|
||||||
|
|||||||
@ -41,6 +41,16 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
permission: "risk.assess",
|
permission: "risk.assess",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "report",
|
||||||
|
name: "report",
|
||||||
|
component: () => import("@/pages/ReportPage.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "风险报告",
|
||||||
|
requiresAuth: true,
|
||||||
|
permission: "risk.assess",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "overview",
|
path: "overview",
|
||||||
name: "overview",
|
name: "overview",
|
||||||
|
|||||||
@ -77,6 +77,8 @@ export interface RiskAssessmentResult {
|
|||||||
dimensionScores?: RiskDimensionScore[];
|
dimensionScores?: RiskDimensionScore[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RiskReportResult = RiskAssessmentResult;
|
||||||
|
|
||||||
export interface ModelInfo {
|
export interface ModelInfo {
|
||||||
modelVersion: string;
|
modelVersion: string;
|
||||||
scoreRange: string;
|
scoreRange: string;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user