按类型归并报告页面追加内容

This commit is contained in:
kx_guo 2026-09-09 17:23:56 +08:00
parent 343c04530c
commit 8ed60807f9

View File

@ -34,6 +34,48 @@ const reportCards = computed<ReportCard[]>(
})); }));
}, },
); );
const reportData = computed(() => {
const cards = reportCards.value;
const unique = <T>(items: T[], key: (item: T) => string) => {
const seen = new Set<string>();
return items.filter((item) => {
const value = key(item);
if (seen.has(value)) return false;
seen.add(value);
return true;
});
};
const standards = unique(
[
...(report.value?.standardJudgment ?? []),
...cards.flatMap((card) => card.standards ?? []),
],
(item) => `${item.category}|${item.riskPoint}|${item.rule}|${item.status}`,
);
const similarCases = unique(
[
...(report.value?.similarCases ?? []),
...cards.flatMap((card) => card.similarCases ?? []),
],
(item) => `${item.caseId}|${item.date}|${item.product}|${item.problem ?? item.description}|${item.solution}`,
);
const suggestions = unique(
[
...(report.value?.aiSuggestions ?? []),
...cards.flatMap((card) => card.suggestions ?? []),
],
(item) => item,
);
return {
cards,
supplierCards: cards.filter((card) => card.type === "supplier"),
materialCards: cards.filter((card) => card.type === "material"),
solutionCards: cards.filter((card) => card.type === "solution"),
standards,
similarCases,
suggestions,
};
});
const dimensionRows = computed(() => { const dimensionRows = computed(() => {
if (!report.value) return []; if (!report.value) return [];
if (report.value.dimensionScores?.length) { if (report.value.dimensionScores?.length) {
@ -208,7 +250,7 @@ function dimensionConclusion(item: {
</table> </table>
</template> </template>
<template v-if="report.supplierFreq?.supplierName"> <template v-if="report.supplierFreq?.supplierName || reportData.supplierCards.length">
<h2>三、供应商合作频次分析</h2> <h2>三、供应商合作频次分析</h2>
<table> <table>
<thead> <thead>
@ -219,32 +261,55 @@ function dimensionConclusion(item: {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-if="report.supplierFreq.cooperationCount != null"> <tr v-if="report.supplierFreq?.cooperationCount != null">
<td>合作次数</td> <td>合作次数</td>
<td>{{ report.supplierFreq.cooperationCount }} 次</td> <td>{{ report.supplierFreq?.cooperationCount }} 次</td>
<td>近三年采购订单去重单据数</td> <td>近三年采购订单去重单据数</td>
</tr> </tr>
<tr> <tr>
<td>异常次数</td> <td>异常次数</td>
<td>{{ report.supplierFreq.anomalyCount ?? 0 }} 次</td> <td>{{ report.supplierFreq?.anomalyCount ?? 0 }} 次</td>
<td>异常案例库统计</td> <td>异常案例库统计</td>
</tr> </tr>
<tr v-if="report.supplierFreq.alpha != null"> <tr v-if="report.supplierFreq?.alpha != null">
<td>频次风险系数</td> <td>频次风险系数</td>
<td>×{{ report.supplierFreq.alpha.toFixed(2) }}</td> <td>×{{ report.supplierFreq?.alpha?.toFixed(2) }}</td>
<td>α = 1.0 +(采购订单数 / 100)× 0.3,最高1.5</td> <td>α = 1.0 +(采购订单数 / 100)× 0.3,最高1.5</td>
</tr> </tr>
<tr v-if="report.supplierFreq.tier"> <tr v-if="report.supplierFreq?.tier">
<td>供应商层级</td> <td>供应商层级</td>
<td>{{ report.supplierFreq.tier }}</td> <td>{{ report.supplierFreq?.tier }}</td>
<td>{{ report.supplierFreq.suggestion }}</td> <td>{{ report.supplierFreq?.suggestion }}</td>
</tr>
<template v-for="(card, cardIndex) in reportData.supplierCards" :key="`supplier-card-${cardIndex}`">
<tr v-if="card.supplierScore != null"><td>供应商得分</td><td>{{ card.supplierScore.toFixed(2) }} 分</td><td>供应商画像统计</td></tr>
<tr v-if="card.supplierBaseScore != null"><td>供应商基础分</td><td>{{ card.supplierBaseScore.toFixed(2) }} 分</td><td>供应商画像统计</td></tr>
<tr v-if="card.topProblems?.length"><td>高频问题</td><td>{{ card.topProblems.map((item) => `${item.name}(${item.count}次)`).join("、") }}</td><td>异常案例库统计</td></tr>
<tr v-if="card.note"><td>备注</td><td>{{ card.note }}</td><td>供应商画像统计</td></tr>
</template>
</tbody>
</table>
</template>
<template v-if="reportData.materialCards.length">
<h2>四、物料风险分析</h2>
<table>
<thead><tr><th>物料/字段</th><th>分析结果</th></tr></thead>
<tbody>
<tr v-for="(card, index) in reportData.materialCards" :key="`material-${index}`">
<td>{{ card.material || card.title }}</td>
<td>
<span v-if="card.anomalyCount != null">历史异常:{{ card.anomalyCount }} 条;</span>
<span v-if="card.frequentProblems?.length">常见问题:{{ card.frequentProblems.map((item) => `${item.name}(${item.count}次)`).join("、") }};</span>
<span v-if="card.suggestions?.length">建议:{{ card.suggestions.join(";") }}</span>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</template> </template>
<h2>四、相似案例匹配({{ report.similarCases?.length || 0 }} 条)</h2> <h2>{{ reportData.materialCards.length ? "五" : "四" }}、相似案例匹配({{ reportData.similarCases.length }} 条)</h2>
<template v-if="report.similarCases?.length"> <template v-if="reportData.similarCases.length">
<table> <table>
<thead> <thead>
<tr> <tr>
@ -256,7 +321,7 @@ function dimensionConclusion(item: {
</thead> </thead>
<tbody> <tbody>
<tr <tr
v-for="item in report.similarCases" v-for="item in reportData.similarCases"
:key="`${item.date}-${item.product}-${item.problem}`" :key="`${item.date}-${item.product}-${item.problem}`"
> >
<td>{{ item.date || "未知" }}</td> <td>{{ item.date || "未知" }}</td>
@ -271,11 +336,11 @@ function dimensionConclusion(item: {
暂无相关案例,RAG 相似案例检索未返回匹配结果。 暂无相关案例,RAG 相似案例检索未返回匹配结果。
</p> </p>
<h2>五、预防建议</h2> <h2>{{ reportData.materialCards.length ? "六" : "五" }}、预防建议</h2>
<ol> <ol>
<li <li
v-for="suggestion in report.aiSuggestions?.length v-for="suggestion in reportData.suggestions.length
? report.aiSuggestions ? reportData.suggestions
: ['优先针对低分维度安排专项验证,并明确责任人与完成节点。']" : ['优先针对低分维度安排专项验证,并明确责任人与完成节点。']"
:key="suggestion" :key="suggestion"
> >
@ -283,20 +348,30 @@ function dimensionConclusion(item: {
</li> </li>
</ol> </ol>
<h2>六、AI 推理方案(无历史案例场景)</h2> <h2>{{ reportData.materialCards.length ? "七" : "六" }}、AI 推理方案(无历史案例场景)</h2>
<p class="report-paragraph"> <p class="report-paragraph">
<template v-if="report.similarCases?.length"> <template v-if="reportData.similarCases.length">
本次评估已匹配 本次评估已匹配
{{ report.similarCases.length }} {{ reportData.similarCases.length }}
条相似历史案例,当前采用历史案例与标准库双引擎结论,未单独触发无历史案例类比推理。 条相似历史案例,当前采用历史案例与标准库双引擎结论,未单独触发无历史案例类比推理。
</template> </template>
<template v-else> <template v-else>
当前未匹配到足够历史案例,系统将基于相似品类、相似物料生成类比方案,并结合判定标准库进行规则兜底;实际执行前需由产品和品控确认。 当前未匹配到足够历史案例,系统将基于相似品类、相似物料生成类比方案,并结合判定标准库进行规则兜底;实际执行前需由产品和品控确认。
</template> </template>
</p> </p>
<template v-if="reportData.solutionCards.length">
<section v-for="(card, index) in reportData.solutionCards" :key="`solution-${index}`" class="ai-solution-report">
<header><strong>{{ card.title }}</strong><span v-if="card.confidence">置信度:{{ card.confidence }}</span></header>
<p v-if="card.analysisBase"><b>分析基础:</b>{{ card.analysisBase }}</p>
<p v-if="card.standardFallbackEnabled"><b>标准兜底校验:</b>已启用(无直接历史案例)</p>
<ul v-if="card.risks?.length"><li v-for="risk in card.risks" :key="`${risk.title}-${risk.detail}`"><b>{{ risk.level }} · {{ risk.title }}:</b>{{ risk.detail }}</li></ul>
<ol v-if="card.suggestions?.length"><li v-for="suggestion in card.suggestions" :key="suggestion">{{ suggestion }}</li></ol>
<p v-if="card.warning" class="ai-solution-report__warning">{{ card.warning }}</p>
</section>
</template>
<h2>七、标准判定结果</h2> <h2>{{ reportData.materialCards.length ? "八" : "七" }}、标准判定结果</h2>
<template v-if="report.standardJudgment?.length"> <template v-if="reportData.standards.length">
<table> <table>
<thead> <thead>
<tr> <tr>
@ -308,7 +383,7 @@ function dimensionConclusion(item: {
</thead> </thead>
<tbody> <tbody>
<tr <tr
v-for="standard in report.standardJudgment" v-for="standard in reportData.standards"
:key="`${standard.category}-${standard.riskPoint}`" :key="`${standard.category}-${standard.riskPoint}`"
> >
<td>{{ standard.category }} · {{ standard.riskPoint }}</td> <td>{{ standard.category }} · {{ standard.riskPoint }}</td>
@ -324,41 +399,6 @@ function dimensionConclusion(item: {
双引擎融合:历史异常经验引擎提供风险优先级,标准库提供可执行的检测阈值与验证任务。 双引擎融合:历史异常经验引擎提供风险优先级,标准库提供可执行的检测阈值与验证任务。
</p> </p>
<template v-if="reportCards.length">
<h2>八、追加卡片分析</h2>
<section
v-for="(card, index) in reportCards"
:key="`${card.type}-${card.title}-${index}`"
class="report-card-detail"
>
<header><strong>{{ card.title }}</strong><span>{{ card.type }}</span></header>
<p v-if="card.name || card.material || card.category"><b>分析对象:</b>{{ card.name || card.material || card.category }}</p>
<p v-if="card.partyType"><b>对象类型:</b>{{ card.partyType }}</p>
<p v-if="card.question"><b>原始问题:</b>{{ card.question }}</p>
<p v-if="card.anomalyCount != null"><b>历史异常:</b>{{ card.anomalyCount }} 条</p>
<p v-if="card.cooperationCount != null"><b>合作次数:</b>{{ card.cooperationCount }} 次</p>
<p v-if="card.frequencyBasis"><b>数据口径:</b>{{ card.frequencyBasis }}</p>
<p v-if="card.dataAvailable != null"><b>数据状态:</b>{{ card.dataAvailable ? "有匹配数据" : "暂无匹配数据" }}</p>
<p v-if="card.frequencyCoefficient != null"><b>频次系数:</b>×{{ card.frequencyCoefficient.toFixed(2) }}</p>
<p v-if="card.supplierBaseScore != null"><b>供应商基础分:</b>{{ card.supplierBaseScore.toFixed(2) }} 分</p>
<p v-if="card.supplierScore != null"><b>供应商得分:</b>{{ card.supplierScore.toFixed(2) }} 分</p>
<p v-if="card.tier"><b>层级:</b>{{ card.tier }}</p>
<p v-if="card.matchedCount != null"><b>匹配数量:</b>{{ card.matchedCount }} 条</p>
<p v-if="card.analysisBase"><b>分析基础:</b>{{ card.analysisBase }}</p>
<p v-if="card.confidence"><b>置信度:</b>{{ card.confidence }}</p>
<p v-if="card.standardFallbackEnabled"><b>标准兜底校验:</b>已启用(无直接历史案例)</p>
<p v-if="card.detectedValues?.length"><b>检测值:</b>{{ card.detectedValues.map((item) => `${item.name}:${item.value}`).join("、") }}</p>
<p v-if="card.topProblems?.length"><b>高频问题:</b>{{ card.topProblems.map((item) => `${item.name}(${item.count}次)`).join("、") }}</p>
<p v-if="card.frequentProblems?.length"><b>常见问题:</b>{{ card.frequentProblems.map((item) => `${item.name}(${item.count}次)`).join("、") }}</p>
<template v-if="card.standards?.length"><p><b>关联标准:</b></p><ul class="report-card-detail__standards"><li v-for="standard in card.standards" :key="`${standard.category}-${standard.riskPoint}`">{{ standard.category }} · {{ standard.riskPoint }}:{{ standard.rule }}({{ standard.source || "标准文件.xlsx" }})· {{ standard.status }}</li></ul></template>
<template v-if="card.similarCases?.length"><p><b>相似案例:</b></p><ul class="report-card-detail__standards"><li v-for="(item, caseIndex) in card.similarCases" :key="`${item.date || 'case'}-${caseIndex}`">{{ item.date || "日期未知" }} · {{ item.product || "未命名产品" }} · {{ item.problem || item.description || "异常问题未填写" }} → {{ item.solution || "解决方案未填写" }}<template v-if="item.similarity != null">(相似度 {{ Math.round(item.similarity * 100) }}%)</template></li></ul></template>
<ul v-if="card.risks?.length"><li v-for="risk in card.risks" :key="`${risk.title}-${risk.detail}`"><b>{{ risk.level }} · {{ risk.title }}:</b>{{ risk.detail }}</li></ul>
<ol v-if="card.suggestions?.length"><li v-for="suggestion in card.suggestions" :key="suggestion">{{ suggestion }}</li></ol>
<p v-if="card.note"><b>备注:</b>{{ card.note }}</p>
<p v-if="card.warning" class="report-card-detail__warning">{{ card.warning }}</p>
</section>
</template>
<footer class="report-footer"> <footer class="report-footer">
<span>编制:蝶雅风险预估系统</span <span>编制:蝶雅风险预估系统</span
><span>生成时间:{{ generatedAt }}</span> ><span>生成时间:{{ generatedAt }}</span>