chore: commit pending gateway changes

This commit is contained in:
2026-05-10 22:34:15 +08:00
parent 53f8edfb67
commit d59756a27c
71 changed files with 15106 additions and 656 deletions
+3 -2
View File
@@ -2,6 +2,7 @@ import type { BaseModelCatalogItem, IntegrationPlatform, PlatformModel, RateLimi
import { adminPages, apiDocPages, primaryModules, workspacePages } from '../navigation';
import { DataPanel } from './DataPanel';
import { ModuleList } from './ModuleList';
import { baseModelTypeText } from '../pages/admin/platform-form';
export function Dashboard(props: {
baseModels: BaseModelCatalogItem[];
@@ -66,7 +67,7 @@ export function Dashboard(props: {
<DataPanel
columns={['Provider', '名称', '状态', '优先级']}
empty="暂无平台数据"
rows={props.platforms.map((item) => [item.provider, item.name, item.status, String(item.priority)])}
rows={props.platforms.map((item) => [item.provider, item.internalName || item.name, item.status, String(item.priority)])}
title="平台"
/>
<DataPanel
@@ -81,7 +82,7 @@ export function Dashboard(props: {
<DataPanel
columns={['Provider', '模型', '类型', '版本']}
empty="暂无基准模型"
rows={props.baseModels.map((item) => [item.providerKey, item.canonicalModelKey, item.modelType, String(item.pricingVersion)])}
rows={props.baseModels.map((item) => [item.providerKey, item.canonicalModelKey, baseModelTypeText(item), String(item.pricingVersion)])}
title="基准模型库"
/>
<DataPanel
+6 -2
View File
@@ -13,14 +13,18 @@ const buttonVariants = cva('shButton', {
destructive: 'shButtonDestructive',
},
size: {
default: 'shButtonDefaultSize',
xs: 'shButtonXs',
sm: 'shButtonSm',
md: 'shButtonMd',
default: 'shButtonMd',
lg: 'shButtonLg',
xl: 'shButtonXl',
icon: 'shButtonIcon',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
size: 'md',
},
});
+24
View File
@@ -0,0 +1,24 @@
import * as React from 'react';
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
import { CheckIcon } from 'lucide-react';
import { cn } from '../../lib/utils';
export const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
'peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive h-3.5 w-3.5 shrink-0 rounded-[3px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...props}
>
<CheckboxPrimitive.Indicator className="flex items-center justify-center text-primary-foreground transition-none">
<CheckIcon className="h-3 w-3" strokeWidth={2.5} />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
@@ -0,0 +1,62 @@
import * as React from 'react';
import { AlertTriangle } from 'lucide-react';
import { cn } from '../../lib/utils';
import { Button } from './button';
export interface ConfirmDialogProps {
cancelLabel?: string;
children?: React.ReactNode;
className?: string;
confirmLabel?: string;
confirmVariant?: 'default' | 'destructive';
description?: string;
loading?: boolean;
open: boolean;
title: string;
onCancel: () => void;
onConfirm: () => void | Promise<void>;
}
export function ConfirmDialog(props: ConfirmDialogProps) {
const { onCancel, open } = props;
React.useEffect(() => {
if (!open) return undefined;
function onKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') onCancel();
}
window.addEventListener('keydown', onKeyDown);
return () => window.removeEventListener('keydown', onKeyDown);
}, [onCancel, open]);
if (!open) return null;
return (
<div className="confirmDialogBackdrop" role="presentation">
<section className={cn('confirmDialog', props.className)} role="alertdialog" aria-modal="true" aria-labelledby="confirm-dialog-title">
<div className="confirmDialogIcon">
<AlertTriangle size={18} />
</div>
<div className="confirmDialogBody">
<strong id="confirm-dialog-title">{props.title}</strong>
{props.description && <p>{props.description}</p>}
{props.children}
</div>
<footer className="confirmDialogActions">
<Button type="button" variant="outline" size="sm" disabled={props.loading} onClick={props.onCancel}>
{props.cancelLabel ?? '取消'}
</Button>
<Button
type="button"
variant={props.confirmVariant ?? 'destructive'}
size="sm"
disabled={props.loading}
onClick={() => void props.onConfirm()}
>
{props.confirmLabel ?? '确认'}
</Button>
</footer>
</section>
</div>
);
}
+17
View File
@@ -0,0 +1,17 @@
import type { ReactNode } from 'react';
import { cn } from '../../lib/utils';
export function FormItem(props: {
children: ReactNode;
className?: string;
description?: ReactNode;
label: ReactNode;
}) {
return (
<label className={cn('shFormItem', props.className)}>
<span className="shFormItemLabel">{props.label}</span>
{props.children}
{props.description ? <span className="shFormItemDescription">{props.description}</span> : null}
</label>
);
}
+4
View File
@@ -1,9 +1,13 @@
export * from './badge';
export * from './button';
export * from './card';
export * from './checkbox';
export * from './confirm-dialog';
export * from './dialog';
export * from './form-item';
export * from './input';
export * from './label';
export * from './message';
export * from './select';
export * from './separator';
export * from './table';
+22 -2
View File
@@ -1,8 +1,28 @@
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '../../lib/utils';
export const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
({ className, ...props }, ref) => <input ref={ref} className={cn('shInput', className)} {...props} />,
const inputVariants = cva('shInput', {
variants: {
size: {
xs: 'shControlXs',
sm: 'shControlSm',
md: 'shControlMd',
lg: 'shControlLg',
xl: 'shControlXl',
},
},
defaultVariants: {
size: 'md',
},
});
export interface InputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>,
VariantProps<typeof inputVariants> {}
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, size, ...props }, ref) => <input ref={ref} className={cn(inputVariants({ size, className }))} {...props} />,
);
Input.displayName = 'Input';
+47
View File
@@ -0,0 +1,47 @@
import * as React from 'react';
import { AlertCircle, CheckCircle2, Info, X } from 'lucide-react';
import { cn } from '../../lib/utils';
import { Button } from './button';
export type ScreenMessageVariant = 'info' | 'success' | 'error';
export interface ScreenMessageProps {
className?: string;
duration?: number;
message: string;
open?: boolean;
variant?: ScreenMessageVariant;
onClose?: () => void;
}
const iconMap: Record<ScreenMessageVariant, React.ReactNode> = {
error: <AlertCircle size={16} />,
info: <Info size={16} />,
success: <CheckCircle2 size={16} />,
};
export function ScreenMessage(props: ScreenMessageProps) {
const { duration = 3600, message, onClose, open = Boolean(message), variant = 'info' } = props;
React.useEffect(() => {
if (!open || !message || duration <= 0 || !onClose) return undefined;
const timer = window.setTimeout(onClose, duration);
return () => window.clearTimeout(timer);
}, [duration, message, onClose, open]);
if (!open || !message) return null;
return (
<div className="screenMessageViewport" role="presentation">
<div className={cn('screenMessage', `screenMessage-${variant}`, props.className)} role="alert">
<span className="screenMessageIcon">{iconMap[variant]}</span>
<span className="screenMessageText">{message}</span>
{onClose && (
<Button type="button" variant="ghost" size="icon" className="screenMessageClose" aria-label="关闭提示" onClick={onClose}>
<X size={14} />
</Button>
)}
</div>
</div>
);
}
+22 -2
View File
@@ -1,8 +1,28 @@
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '../../lib/utils';
export const Select = React.forwardRef<HTMLSelectElement, React.SelectHTMLAttributes<HTMLSelectElement>>(
({ className, ...props }, ref) => <select ref={ref} className={cn('shInput', className)} {...props} />,
const selectVariants = cva('shInput shSelect', {
variants: {
size: {
xs: 'shControlXs',
sm: 'shControlSm',
md: 'shControlMd',
lg: 'shControlLg',
xl: 'shControlXl',
},
},
defaultVariants: {
size: 'md',
},
});
export interface SelectProps
extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size'>,
VariantProps<typeof selectVariants> {}
export const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
({ className, size, ...props }, ref) => <select ref={ref} className={cn(selectVariants({ size, className }))} {...props} />,
);
Select.displayName = 'Select';
+22 -2
View File
@@ -1,8 +1,28 @@
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '../../lib/utils';
export const Textarea = React.forwardRef<HTMLTextAreaElement, React.TextareaHTMLAttributes<HTMLTextAreaElement>>(
({ className, ...props }, ref) => <textarea ref={ref} className={cn('shTextarea', className)} {...props} />,
const textareaVariants = cva('shTextarea', {
variants: {
size: {
xs: 'shTextareaXs',
sm: 'shTextareaSm',
md: 'shTextareaMd',
lg: 'shTextareaLg',
xl: 'shTextareaXl',
},
},
defaultVariants: {
size: 'md',
},
});
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement>,
VariantProps<typeof textareaVariants> {}
export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, size, ...props }, ref) => <textarea ref={ref} className={cn(textareaVariants({ size, className }))} {...props} />,
);
Textarea.displayName = 'Textarea';