kx_guo 32739944c7 优化风险卡片与历史会话加载
- 展示数据库七维风险维度、默认值和数据说明\n- 增加标准兜底校验卡片及无历史案例提示\n- 支持 AI 风险建议和案例推理卡片的新数据结构\n- 进入会话默认定位最新内容\n- 上划到顶部自动加载更早的 20 条消息并保持阅读位置\n- 移除手动加载更早消息按钮\n- 更新风险评估表单和前端类型定义
2026-09-04 18:02:39 +08:00

235 lines
4.5 KiB
Vue

<script setup lang="ts">
import { BarChart3 } from "@lucide/vue";
import { computed } from "vue";
import RiskBadge from "@/components/common/RiskBadge.vue";
import type { RiskAssessmentResult, RiskDimensionScore } from "@/types/risk";
const props = defineProps<{
result: RiskAssessmentResult | null;
error: string | null;
}>();
const dimensions = computed<RiskDimensionScore[]>(() =>
props.result?.dimensionScores?.length
? props.result.dimensionScores
: (props.result?.factors ?? []).map((factor) => ({
dimension: factor.name,
score: Math.max(0, Math.round(100 - factor.value)),
weight: factor.weight,
})),
);
</script>
<template>
<section class="result-panel" aria-live="polite">
<div class="result-panel__heading">
<BarChart3 :size="18" />
<h2>评估结果</h2>
</div>
<p v-if="error" class="result-error">{{ error }}</p>
<div v-else-if="result" class="result-content">
<div class="score-line">
<div>
<span>综合安全分</span>
<strong>{{ result.score.toFixed(1) }}</strong>
</div>
<RiskBadge :level="result.level" />
</div>
<dl class="result-meta">
<div>
<dt>对象</dt>
<dd>{{ result.subjectId }}</dd>
</div>
<div>
<dt>模型</dt>
<dd>{{ result.modelVersion }}</dd>
</div>
<div>
<dt>数据口径</dt>
<dd>异常案例库 + 缺失值兜底</dd>
</div>
</dl>
<div class="factor-list">
<div
v-for="factor in dimensions"
:key="factor.dimension"
class="factor-row"
:title="factor.reason || undefined"
>
<span>{{ factor.dimension }}</span>
<div class="factor-bar">
<i :style="{ width: `${factor.score}%` }"></i>
</div>
<strong>{{ Math.round(factor.score) }}<small>分</small></strong>
<em v-if="factor.status === 'default'">默认</em>
</div>
</div>
</div>
<div v-else class="result-empty">
<span>--</span>
<p>暂无评估结果</p>
</div>
</section>
</template>
<style scoped>
.result-panel {
min-height: 420px;
border-left: 1px solid var(--color-border);
padding-left: 28px;
}
.result-panel__heading {
display: flex;
align-items: center;
gap: 8px;
color: var(--color-brand);
}
h2 {
margin: 0;
color: var(--color-text);
font-size: 15px;
}
.result-content {
display: grid;
gap: 24px;
margin-top: 24px;
}
.score-line {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
border-bottom: 1px solid var(--color-border);
padding-bottom: 22px;
}
.score-line div {
display: grid;
gap: 4px;
}
.score-line span,
dt {
color: var(--color-text-muted);
font-size: 12px;
}
.score-line strong {
font-size: 36px;
line-height: 1;
}
.result-meta {
display: grid;
margin: 0;
gap: 12px;
}
.result-meta div {
display: flex;
justify-content: space-between;
gap: 16px;
}
dd {
margin: 0;
overflow-wrap: anywhere;
font-weight: 600;
text-align: right;
}
.factor-list {
display: grid;
gap: 12px;
}
.factor-row {
display: grid;
grid-template-columns: minmax(100px, 1fr) minmax(80px, 1.3fr) 42px;
align-items: center;
gap: 10px;
font-size: 12px;
}
.factor-row > span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.factor-row > strong {
text-align: right;
}
.factor-row > strong small {
margin-left: 2px;
color: var(--color-text-muted);
font-size: 10px;
font-weight: 500;
}
.factor-row > em {
border-radius: 3px;
padding: 2px 4px;
background: var(--color-warning-soft);
color: var(--color-warning);
font-size: 9px;
font-style: normal;
}
.factor-bar {
height: 6px;
overflow: hidden;
border-radius: 3px;
background: var(--color-border);
}
.factor-bar i {
display: block;
height: 100%;
border-radius: inherit;
background: var(--color-brand);
}
.result-empty {
display: grid;
min-height: 320px;
place-content: center;
color: var(--color-text-muted);
text-align: center;
}
.result-empty span {
color: var(--color-border-strong);
font-size: 46px;
}
.result-empty p,
.result-error {
margin: 8px 0 0;
}
.result-error {
border: 1px solid #f3c4c0;
border-radius: 6px;
padding: 12px;
background: var(--color-danger-soft);
color: var(--color-danger);
}
@media (max-width: 980px) {
.result-panel {
min-height: auto;
border-top: 1px solid var(--color-border);
border-left: 0;
padding-top: 24px;
padding-left: 0;
}
}
</style>