Enhance ComponentsModelsPage with FieldLabel and HelpHint components for improved form usability

Refactored the ComponentsModelsPage to introduce FieldLabel and HelpHint components, enhancing the user experience by providing contextual hints for form fields. Updated the layout to replace traditional labels with these new components, ensuring better accessibility and clarity for users when entering model details. This change aims to streamline the form interface and improve overall usability.
This commit is contained in:
Xin Wang
2026-06-08 10:21:43 +08:00
parent edf2148473
commit e2a0eedd63

View File

@@ -7,6 +7,7 @@ import {
ChevronRight,
Eye,
EyeOff,
HelpCircle,
Loader2,
MoreHorizontal,
Pencil,
@@ -41,7 +42,12 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { useState } from "react";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { useState, type ReactNode } from "react";
type ModelType = "LLM" | "ASR" | "TTS" | "Realtime" | "Embedding";
@@ -529,24 +535,40 @@ export function ComponentsModelsPage() {
</DialogHeader>
<div className="space-y-5">
<label className="block">
<div className="mb-2 text-sm font-medium text-foreground">
<div className="block">
<FieldLabel
htmlFor="model-name"
hint={{
description:
"在控制台中展示的资源别名,便于识别和管理,可与模型 ID 不同。",
example: "DeepSeek-V3、Qwen-Max",
}}
>
</div>
</FieldLabel>
<Input
id="model-name"
value={form.name}
autoFocus
onChange={(event) => updateForm("name", event.target.value)}
placeholder="请输入资源名称"
className="border-hairline-strong bg-background text-foreground placeholder:text-muted-soft"
/>
</label>
</div>
<label className="block">
<div className="mb-2 text-sm font-medium text-foreground">
<div className="block">
<FieldLabel
htmlFor="model-id"
hint={{
description:
"调用 API 时传入的模型标识,需与服务商文档中的名称完全一致。",
example: "deepseek-chat、qwen-max、paraformer-realtime-v2",
}}
>
ID
</div>
</FieldLabel>
<Input
id="model-id"
value={form.modelId}
onChange={(event) =>
updateForm("modelId", event.target.value)
@@ -554,13 +576,19 @@ export function ComponentsModelsPage() {
placeholder="例如 deepseek-chat"
className="border-hairline-strong bg-background text-foreground placeholder:text-muted-soft"
/>
</label>
</div>
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div className="block">
<div className="mb-2 text-sm font-medium text-foreground">
<FieldLabel
hint={{
description:
"模型在语音管线中的能力分类,决定可选的接口类型与后续编排用途。",
example: "LLM、ASR、TTS、Realtime、Embedding",
}}
>
</div>
</FieldLabel>
<Select
value={form.type}
onValueChange={(value) => handleTypeChange(value as ModelType)}
@@ -579,9 +607,15 @@ export function ComponentsModelsPage() {
</div>
<div className="block">
<div className="mb-2 text-sm font-medium text-foreground">
<FieldLabel
hint={{
description:
"服务商 API 的协议或适配器类型,需与所选资源类型匹配。",
example: "openai、xfyun、dashscope、gemini",
}}
>
</div>
</FieldLabel>
<Select
value={form.interfaceType}
onValueChange={(value) =>
@@ -602,24 +636,40 @@ export function ComponentsModelsPage() {
</div>
</div>
<label className="block">
<div className="mb-2 text-sm font-medium text-foreground">
<div className="block">
<FieldLabel
htmlFor="model-api-url"
hint={{
description:
"模型服务的 Base URL 或接口根地址,通常以 /v1 结尾,不含具体路径参数。",
example: "https://api.deepseek.com/v1",
}}
>
API URL
</div>
</FieldLabel>
<Input
id="model-api-url"
value={form.apiUrl}
onChange={(event) => updateForm("apiUrl", event.target.value)}
placeholder="https://api.example.com/v1"
className="border-hairline-strong bg-background text-foreground placeholder:text-muted-soft"
/>
</label>
</div>
<label className="block">
<div className="mb-2 text-sm font-medium text-foreground">
<div className="block">
<FieldLabel
htmlFor="model-api-key"
hint={{
description:
"访问模型服务的鉴权密钥,由服务商控制台生成,请妥善保管勿泄露。",
example: "sk-xxxxxxxx",
}}
>
API Key
</div>
</FieldLabel>
<div className="relative">
<Input
id="model-api-key"
value={form.apiKey}
type={showKey ? "text" : "password"}
onChange={(event) => updateForm("apiKey", event.target.value)}
@@ -635,7 +685,7 @@ export function ComponentsModelsPage() {
{showKey ? <EyeOff size={16} /> : <Eye size={16} />}
</button>
</div>
</label>
</div>
</div>
<DialogFooter className="sm:items-center sm:justify-between">
@@ -691,3 +741,54 @@ export function ComponentsModelsPage() {
</div>
);
}
function FieldLabel({
htmlFor,
children,
hint,
}: {
htmlFor?: string;
children: ReactNode;
hint: { description: string; example?: string };
}) {
return (
<div className="mb-2 flex items-center gap-1.5 text-sm font-medium text-foreground">
<label htmlFor={htmlFor}>{children}</label>
<HelpHint {...hint} />
</div>
);
}
function HelpHint({
description,
example,
}: {
description: string;
example?: string;
}) {
return (
<Popover>
<PopoverTrigger asChild>
<button
type="button"
aria-label="查看说明"
className="flex h-5 w-5 items-center justify-center rounded-full text-muted-soft transition-colors hover:bg-surface-strong hover:text-foreground"
>
<HelpCircle size={14} />
</button>
</PopoverTrigger>
<PopoverContent
align="start"
className="w-72 space-y-2 text-sm leading-6 text-muted-foreground"
>
<p>{description}</p>
{example && (
<p className="text-xs leading-5 text-muted-soft">
<span className="font-medium text-muted-foreground"></span>
{example}
</p>
)}
</PopoverContent>
</Popover>
);
}