规范风险报告生成与方案追加规则

This commit is contained in:
kx_guo 2026-09-15 14:08:58 +08:00
parent 173e05eda6
commit 7f74f5a041
2 changed files with 42 additions and 22 deletions

View File

@ -17,7 +17,10 @@ import { useRouter } from "vue-router";
import type { AssistantResponseCard } from "@/types/conversation";
import type { ReportCard } from "@/types/risk";
const props = defineProps<{ card: AssistantResponseCard }>();
const props = defineProps<{
card: AssistantResponseCard;
canAppendReport?: boolean;
}>();
const router = useRouter();
const emit = defineEmits<{
generateReport: [card: ReportCard];
@ -381,12 +384,13 @@ function generateReport() {
<div class="response-actions">
<button
v-if="card.type === 'solution' && canAppendReport"
type="button"
class="response-actions__primary"
title="追加到当前风险报告"
@click="generateReport"
>
<CheckCircle2 :size="14" />生成报告
<CheckCircle2 :size="14" />追加到报告
</button>
<button
type="button"

View File

@ -47,6 +47,7 @@ const toolInput = ref("");
const toolError = ref("");
const toolFile = ref<File | null>(null);
const expandedReplies = ref(new Set<number>());
const primaryReportReady = ref(false);
const assessmentFields = ref({
category: "",
specification: "",
@ -116,15 +117,22 @@ onActivated(async () => {
// MainLayout keeps this page alive. Returning from report/dashboard therefore
// does not remount it, so explicitly reveal the newest message on re-entry.
if (conversation.value?.messages.length) await scrollToBottom("auto");
refreshPrimaryReportState();
});
watch(
() => route.query.conversation,
async () => {
await openFromRoute();
refreshPrimaryReportState();
},
);
watch(
() => conversation.value?.id,
() => refreshPrimaryReportState(),
);
async function usePrompt(value: string) {
const current = prompt.value.trim();
prompt.value = current ? `${current}\n${value}` : value;
@ -356,6 +364,28 @@ function legacySolutionCards(result: RiskAssessmentResult): ReportCard[] {
}));
}
function isPrimaryRiskReport(
report: RiskAssessmentResult | null,
): report is RiskAssessmentResult {
return Boolean(
report &&
(report.dimensionScores?.length ||
report.factors?.length ||
report.categoryWarning ||
report.supplierFreq),
);
}
function refreshPrimaryReportState() {
const conversationId = conversation.value?.id;
primaryReportReady.value = Boolean(
conversationId &&
isPrimaryRiskReport(
readConversationReport(conversationId, auth.user?.id),
),
);
}
function openReport(result: RiskAssessmentResult) {
const conversationId = conversation.value?.id;
if (!conversationId) return;
@ -376,6 +406,7 @@ function openReport(result: RiskAssessmentResult) {
}
: cloneReport(result);
writeConversationReport(conversationId, merged, auth.user?.id);
primaryReportReady.value = true;
void router.push({
name: "report",
query: { conversation: String(conversationId) },
@ -384,27 +415,11 @@ function openReport(result: RiskAssessmentResult) {
function openSolutionReport(card: ReportCard) {
const conversationId = conversation.value?.id;
if (!conversationId) return;
const current = conversation.value?.messages
.slice()
.reverse()
.find((message) => message.riskResult)?.riskResult;
if (!conversationId || card.type !== "solution" || !primaryReportReady.value)
return;
const stored = readConversationReport(conversationId, auth.user?.id);
const sameReport = Boolean(
current && stored && current.subjectId === stored.subjectId,
);
const base: RiskAssessmentResult =
sameReport && stored
? cloneReport(stored)
: current
? cloneReport(current)
: stored || {
subjectId: "AI 案例推理方案",
score: 50,
level: "medium",
modelVersion: "ai-solution-card",
factors: [],
};
if (!isPrimaryRiskReport(stored)) return;
const base = cloneReport(stored);
const existingCards = base.reportCards ?? [];
const merged: RiskAssessmentResult = {
...base,
@ -571,6 +586,7 @@ async function scrollToBottom(behavior: "smooth" | "auto" = "smooth") {
<AssistantResponseCard
v-if="message.responseCard"
:card="message.responseCard"
:can-append-report="primaryReportReady"
@generate-report="openSolutionReport"
/>
<time class="message-time">{{