refactor(web): simplify models page header

This commit is contained in:
2026-05-11 11:02:08 +08:00
parent 9243db2787
commit ec87816c95
2 changed files with 27 additions and 54 deletions
+5 -28
View File
@@ -1,5 +1,5 @@
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { Boxes, Search, Sparkles } from 'lucide-react'; import { Boxes, Search } from 'lucide-react';
import type { import type {
BaseModelCatalogItem, BaseModelCatalogItem,
BillingConfig, BillingConfig,
@@ -7,7 +7,6 @@ import type {
PlatformModel, PlatformModel,
} from '@easyai-ai-gateway/contracts'; } from '@easyai-ai-gateway/contracts';
import type { ConsoleData } from '../app-state'; import type { ConsoleData } from '../app-state';
import { PageHeader } from '../components/PageHeader';
import { Badge, Card, CardContent, Input } from '../components/ui'; import { Badge, Card, CardContent, Input } from '../components/ui';
import { stableModelAlias } from './admin/platform-form'; import { stableModelAlias } from './admin/platform-form';
@@ -146,15 +145,12 @@ export function ModelsPage(props: { data: ConsoleData }) {
const filteredModels = useMemo(() => { const filteredModels = useMemo(() => {
const normalizedQuery = query.trim().toLowerCase(); const normalizedQuery = query.trim().toLowerCase();
return sourceModels.filter((model) => { return sourceModels.filter((model) => {
const providerInfo = providerMap.get(model.providerKey);
const matchedProvider = provider === 'all' || model.providerKey === provider; const matchedProvider = provider === 'all' || model.providerKey === provider;
const matchedCapability = modelMatchesCapability(model.modelType, capability); const matchedCapability = modelMatchesCapability(model.modelType, capability);
const matchedQuery = [ const matchedQuery = [
model.modelName, model.modelName,
model.modelAlias, model.modelAlias,
model.displayName, model.displayName,
providerInfo?.displayName,
providerInfo?.code,
] ]
.filter(Boolean) .filter(Boolean)
.join(' ') .join(' ')
@@ -162,9 +158,7 @@ export function ModelsPage(props: { data: ConsoleData }) {
.includes(normalizedQuery); .includes(normalizedQuery);
return matchedProvider && matchedCapability && matchedQuery; return matchedProvider && matchedCapability && matchedQuery;
}); });
}, [capability, provider, providerMap, query, sourceModels]); }, [capability, provider, query, sourceModels]);
const selectedProvider = provider === 'all' ? undefined : providerMap.get(provider);
return ( return (
<div className="modelsPage"> <div className="modelsPage">
@@ -184,31 +178,14 @@ export function ModelsPage(props: { data: ConsoleData }) {
</aside> </aside>
<main className="modelsContent"> <main className="modelsContent">
<PageHeader <div className="modelsToolbar">
eyebrow="Models" <p> {sourceModels.length} {filteredModels.length} </p>
title="模型" <div className="searchField modelHeaderSearch">
description={`${sourceModels.length} 个模型,当前显示 ${filteredModels.length}`}
/>
<div className="modelSearchBar">
<div className="searchField">
<Search size={16} /> <Search size={16} />
<Input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="模型名称模糊搜索" /> <Input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="模型名称模糊搜索" />
</div> </div>
</div> </div>
<section className="providerHero">
{selectedProvider ? <ProviderIcon provider={selectedProvider} /> : (
<div className="providerLogo">
<Sparkles size={22} />
</div>
)}
<div>
<strong>{selectedProvider?.displayName ?? '全部模型厂商'}</strong>
<span>{selectedProvider ? `${selectedProvider.code} · ${selectedProvider.providerType}` : `${sourceProviders.length} 个厂商来自 integration-platform 目录`}</span>
</div>
<Badge variant="success">{filteredModels.length} </Badge>
</section>
<section className="modelCards"> <section className="modelCards">
{filteredModels.map((model) => ( {filteredModels.map((model) => (
<ModelCard model={model} provider={providerMap.get(model.providerKey)} key={model.id} /> <ModelCard model={model} provider={providerMap.get(model.providerKey)} key={model.id} />
+22 -26
View File
@@ -79,9 +79,17 @@
gap: 14px; gap: 14px;
} }
.modelSearchBar { .modelsToolbar {
display: flex; display: flex;
align-items: center;
justify-content: flex-end; justify-content: flex-end;
gap: 16px;
}
.modelsToolbar p {
margin-right: auto;
color: var(--text-soft);
font-size: var(--font-size-base);
} }
.searchField { .searchField {
@@ -95,6 +103,10 @@
color: var(--muted-foreground); color: var(--muted-foreground);
} }
.searchField.modelHeaderSearch {
width: min(520px, 42vw);
}
.searchField svg { .searchField svg {
margin-left: 12px; margin-left: 12px;
} }
@@ -105,27 +117,6 @@
box-shadow: none; box-shadow: none;
} }
.providerHero {
display: flex;
min-height: 86px;
align-items: center;
gap: 14px;
padding: 18px;
border: 1px solid var(--border);
border-radius: 10px;
background: #fff;
}
.providerHero strong {
display: block;
font-size: 18px;
}
.providerHero span {
color: var(--muted-foreground);
font-size: 13px;
}
.providerLogo, .providerLogo,
.modelIcon { .modelIcon {
display: grid; display: grid;
@@ -149,10 +140,6 @@
object-fit: contain; object-fit: contain;
} }
.providerHero .shBadge {
margin-left: auto;
}
.modelCards { .modelCards {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(260px, 1fr)); grid-template-columns: repeat(3, minmax(260px, 1fr));
@@ -1423,6 +1410,15 @@
flex-direction: column; flex-direction: column;
} }
.modelsToolbar {
align-items: flex-start;
flex-direction: column;
}
.searchField.modelHeaderSearch {
width: 100%;
}
.providerCatalogActions { .providerCatalogActions {
grid-column: 1 / -1; grid-column: 1 / -1;
justify-content: flex-start; justify-content: flex-start;