Files
ln-bi/src/modules/assets/assistant/generative-presentation.ts
T
2026-09-23 17:52:33 +08:00

130 lines
3.8 KiB
TypeScript

import type {
AssistantAction,
AssistantResult,
Dimension,
PresentationBlock,
PresentationInteraction,
} from "../../../shared/assets/assistant";
export type PresentationDatum = Record<string, unknown>;
const fields: Record<NonNullable<PresentationBlock["dataset"]>, string[]> = {
groups: [
"name",
"count",
"operating",
"rate",
"abnormal",
"pending",
"share",
],
daily: ["date", "delivered", "returned", "replaced", "total"],
comparison: ["label", "current", "previous", "delta", "rate"],
rows: [
"id",
"plate",
"vehicleClass",
"type",
"model",
"region",
"city",
"department",
"status",
"inventory",
"abnormal",
"pending",
"abnormalType",
"targetCompletionTime",
"actualCompletionTime",
"date",
"event",
],
};
export function presentationData(
result: AssistantResult,
dataset: PresentationBlock["dataset"],
): PresentationDatum[] {
return dataset ? (result[dataset] || []).map((row) => ({ ...row })) : [];
}
export function presentationFields(block: PresentationBlock) {
const allowed = block.dataset ? fields[block.dataset] : [];
const defaults =
block.dataset === "daily"
? [
{ field: "date", label: "日期" },
{ field: "total", label: "车次" },
]
: block.dataset === "comparison"
? [
{ field: "label", label: "指标" },
{ field: "current", label: "本期" },
{ field: "previous", label: "上期" },
]
: block.dataset === "rows"
? [
{ field: "plate", label: "车牌" },
{ field: "status", label: "状态" },
]
: [
{ field: "name", label: "分组" },
{ field: "count", label: "数量" },
];
return (block.columns?.length ? block.columns : defaults).filter((column) =>
allowed.includes(column.field),
);
}
export function presentationSeries(block: PresentationBlock) {
const allowed = block.dataset ? fields[block.dataset] : [];
const fallback =
block.dataset === "daily"
? { field: "total", label: "车次" }
: block.dataset === "comparison"
? { field: "current", label: "本期" }
: { field: "count", label: "数量" };
return (block.series?.length ? block.series : [fallback]).filter(
(series) =>
allowed.includes(series.field) &&
!["name", "date", "label"].includes(series.field),
);
}
export function presentationMetrics(
result: AssistantResult,
labels?: string[],
) {
return labels?.length
? labels.flatMap((label) => {
const metric = result.metrics.find((item) => item.label === label);
return metric ? [metric] : [];
})
: result.metrics;
}
export function presentationValue(value: unknown, field: string) {
if (value === undefined || value === null || value === "") return "—";
if (typeof value === "number")
return Number.isFinite(value)
? `${value.toLocaleString("zh-CN")}${["rate", "share"].includes(field) ? "%" : ""}`
: "—";
if (typeof value === "boolean") return value ? "是" : "否";
return typeof value === "string" ? value : "—";
}
export function presentationAction(
interaction: PresentationInteraction | undefined,
datum?: PresentationDatum,
): AssistantAction | undefined {
if (!interaction) return;
if (interaction.kind === "followup")
return { type: "explore", interactionId: interaction.id };
if (
interaction.kind === "group" &&
datum?.keys &&
typeof datum.keys === "object" &&
Object.keys(datum.keys).length
)
return {
type: "explore",
interactionId: interaction.id,
keys: datum.keys as Partial<Record<Dimension, string>>,
};
if (interaction.kind === "point" && typeof datum?.date === "string")
return { type: "explore", interactionId: interaction.id, date: datum.date };
}