feat: 第一季九章完整剧本与引擎(chapters 4-9、相处线索报告替代七维评分、采样覆盖驱动支线插入、consent v2、报告页隐私区块完善)+ 跨 agent 断点修复
This commit is contained in:
+25
-25
@@ -1,22 +1,23 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { BADGE_MAP, PROFILE_DIMENSIONS } from './badges';
|
||||
import type { DimensionScore } from './ReportPage';
|
||||
import { AREA_BADGE_MAP, DEFAULT_BADGE } from './badges';
|
||||
import type { RelationshipArea } from '../../engine/scoring';
|
||||
|
||||
interface Props {
|
||||
badgeDimension: string;
|
||||
scores: DimensionScore[];
|
||||
/** 勋章领域(area id);null = 证据不足,用默认称号 */
|
||||
badgeArea: string | null;
|
||||
areas: RelationshipArea[];
|
||||
/** 玩家自取称呼(C1-01 profile.name),空串时只显示编号 */
|
||||
playerName?: string;
|
||||
}
|
||||
|
||||
/** 分享卡:canvas 生成 PNG 下载(勋章 + 7 维分数) */
|
||||
export function ShareCard({ badgeDimension, scores, playerName }: Props) {
|
||||
/** 分享卡:canvas 生成 PNG 下载(勋章 + 六领域相处线索) */
|
||||
export function ShareCard({ badgeArea, areas, playerName }: Props) {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
draw();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [scores, badgeDimension, playerName]);
|
||||
}, [areas, badgeArea, playerName]);
|
||||
|
||||
function draw(): HTMLCanvasElement {
|
||||
const canvas = canvasRef.current!;
|
||||
@@ -26,8 +27,7 @@ export function ShareCard({ badgeDimension, scores, playerName }: Props) {
|
||||
canvas.width = W;
|
||||
canvas.height = H;
|
||||
|
||||
const badge = BADGE_MAP[badgeDimension] ?? { title: '档案采集员', flavor: '' };
|
||||
const dim = PROFILE_DIMENSIONS.find((d) => d.id === badgeDimension);
|
||||
const badge = badgeArea ? (AREA_BADGE_MAP[badgeArea] ?? DEFAULT_BADGE) : DEFAULT_BADGE;
|
||||
|
||||
ctx.fillStyle = '#14231d';
|
||||
ctx.fillRect(0, 0, W, H);
|
||||
@@ -38,7 +38,7 @@ export function ShareCard({ badgeDimension, scores, playerName }: Props) {
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillStyle = '#d0dfcd';
|
||||
ctx.font = '28px serif';
|
||||
ctx.fillText('苍梧疑冢 · 人格画像', W / 2, 80);
|
||||
ctx.fillText('苍梧疑冢 · 相处线索', W / 2, 80);
|
||||
ctx.font = '22px serif';
|
||||
ctx.fillText(`C-2049${playerName ? `「${playerName}」` : ''}的档案`, W / 2, 118);
|
||||
|
||||
@@ -57,30 +57,30 @@ export function ShareCard({ badgeDimension, scores, playerName }: Props) {
|
||||
ctx.fillStyle = '#b3c6b4';
|
||||
ctx.font = '22px serif';
|
||||
ctx.fillText(badge.flavor, W / 2, 400);
|
||||
if (dim) {
|
||||
ctx.font = '20px serif';
|
||||
ctx.fillText(`—— 实测维度:${dim.label} ——`, W / 2, 440);
|
||||
}
|
||||
|
||||
// 7 维分数条
|
||||
// 六领域线索行
|
||||
ctx.textAlign = 'left';
|
||||
scores.forEach((s, i) => {
|
||||
const y = 510 + i * 56;
|
||||
areas.forEach((a, i) => {
|
||||
const y = 480 + i * 64;
|
||||
const signal = a.candidates.reduce((n, c) => n + c.pointIds.length, 0);
|
||||
ctx.fillStyle = '#dce7d9';
|
||||
ctx.font = '22px serif';
|
||||
ctx.fillText(s.label, 80, y);
|
||||
ctx.fillStyle = '#385044';
|
||||
ctx.fillRect(260, y - 16, 240, 18);
|
||||
ctx.fillStyle = '#a8c1a3';
|
||||
ctx.fillRect(260, y - 16, (240 * s.score) / s.max, 18);
|
||||
ctx.fillStyle = '#dce7d9';
|
||||
ctx.fillText(s.score.toFixed(1), 520, y);
|
||||
ctx.fillText(a.label, 72, y);
|
||||
ctx.fillStyle = a.status === '证据不足' ? '#7d9084' : '#a8c1a3';
|
||||
ctx.font = '19px serif';
|
||||
const detail =
|
||||
a.status === '证据不足'
|
||||
? '情境不足,暂不推断'
|
||||
: signal > 0
|
||||
? a.candidates.map((c) => c.label).join(' / ')
|
||||
: '暂未出现明确倾向';
|
||||
ctx.fillText(detail, 72, y + 28);
|
||||
});
|
||||
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillStyle = '#acc0af';
|
||||
ctx.font = '18px serif';
|
||||
ctx.fillText('每个选择都是实测,没有自报。', W / 2, H - 40);
|
||||
ctx.fillText('每条线索都来自剧情实测,等待本人核对。', W / 2, H - 40);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user