Files
Edu/apps/portal-shell/src/features/admin/ai-settings/transformations.ts
SpecialX 039db5efdd fix(portal-shell): 管理域 UI 规范合规与 TypeScript 修复
- 替换 41 处原生 select 为 Select 组件封装

- 替换 5 处 window.confirm 为 shadcn AlertDialog

- 修复 lesson-plans delete-confirm-dialog 为 AlertDialog

- 修复 5 处 Tailwind 任意值 text-[10px]

- 修复 graphql-data.ts mutation case 缺少 id 定义

- 修复 use-position-persistence.ts eslint 规则引用
2026-08-01 05:50:25 +08:00

135 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* AI Settings 数据变换工具ARCHITECTURE.md §11.3 DoD - 纯函数单测)
*
* 所有格式化/映射函数均为纯函数,便于 vitest 单测。
* 关联ARCHITECTURE.md §11.3 DoD "数据变换/权限判断等纯函数有 vitest 单测"
*/
/** AI Provider 类型标签映射(对齐 CICDzhipu/openai/gemini/ollama/custom */
export const PROVIDER_TYPE_LABEL: Record<string, string> = {
zhipu: "智谱 AI",
openai: "OpenAI",
gemini: "Google Gemini",
ollama: "Ollama本地",
custom: "自定义",
};
/** AI Provider 类型支持的取值列表 */
export const PROVIDER_TYPES: readonly string[] = [
"zhipu",
"openai",
"gemini",
"ollama",
"custom",
] as const;
/** AI Provider 可见性枚举值列表(@contract-pendingMSW 兜底) */
export const PROVIDER_VISIBILITY_OPTIONS: readonly string[] = [
"private",
"shared",
"public",
] as const;
/** AI Provider 可见性标签映射 */
export const PROVIDER_VISIBILITY_LABEL: Record<string, string> = {
private: "仅自己可见",
shared: "组织共享",
public: "全员可见",
};
/**
* 将 Provider 类型代码映射为展示标签。未知值回退为原始值。
*/
export function formatProviderType(type: string): string {
return PROVIDER_TYPE_LABEL[type] ?? type;
}
/**
* 判断字符串是否为受支持的 Provider 类型。
*/
export function isValidProviderType(type: string): boolean {
return PROVIDER_TYPES.includes(type);
}
/**
* 将 Provider 可见性代码映射为展示标签。未知值回退为原始值。
*/
export function formatProviderVisibility(visibility: string): string {
return PROVIDER_VISIBILITY_LABEL[visibility] ?? visibility;
}
/**
* 根据 isActive 返回 Tailwind 徽章语义类名。
*/
export function activeToBadgeClass(isActive: boolean): string {
return isActive
? "bg-success/10 text-success"
: "bg-muted text-muted-foreground";
}
/**
* 根据 isActive 返回展示标签。
*/
export function formatActiveLabel(isActive: boolean): string {
return isActive ? "active" : "inactive";
}
/**
* 格式化 AI 用量费用(分)为元展示字符串。
* 输入无效返回 "¥0.00"。
*/
export function formatCostCents(costCents: number): string {
if (!Number.isFinite(costCents) || costCents < 0) return "¥0.00";
const yuan = costCents / 100;
return `¥${yuan.toFixed(2)}`;
}
/**
* 格式化数字为千分位展示字符串。
* 输入无效返回 "0"。
*/
export function formatNumber(value: number): string {
if (!Number.isFinite(value) || value < 0) return "0";
return value.toLocaleString("zh-CN");
}
/**
* 截断 API Key 用于列表展示(仅保留前 4 + 后 4 字符,中间以 ... 占位)。
* 输入为空返回占位符 "--"。
*/
export function maskApiKey(apiKey: string | null | undefined): string {
if (!apiKey || apiKey.trim().length === 0) return "--";
const text = apiKey.trim();
if (text.length <= 8) return text;
return `${text.slice(0, 4)}...${text.slice(-4)}`;
}
/**
* 截断 BaseUrl 用于列表展示(超过 maxLen 字符时截断并加省略号)。
* maxLen 默认 40。
*/
export function truncateBaseUrl(
url: string | null | undefined,
maxLen = 40,
): string {
if (!url) return "--";
const text = url.trim();
if (text.length <= maxLen) return text;
return `${text.slice(0, maxLen)}...`;
}
/**
* 格式化 ISO 日期字符串为本地化展示zh-CN仅年月日
* 输入无效时返回占位符。
*/
export function formatAiDate(isoDate: string | null | undefined): string {
if (!isoDate) return "--";
const d = new Date(isoDate);
if (Number.isNaN(d.getTime())) return "--";
return d.toLocaleDateString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
});
}