feat(admin): 优化后台运维与任务管理
完善平台、基准模型、定价规则和实时负载的紧凑查询与滚动展示,并固定关键操作列。 新增管理员任务记录、批量计费结算和用户组限流、额度及模型权限维护能力,同时补充任务脱敏、查询索引与 OpenAPI 契约。 验证:pnpm openapi、Go 全量测试、pnpm lint、pnpm test、pnpm build、gofmt 与差异格式检查均通过。
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useMemo, useState, type FormEvent } from 'react';
|
||||
import { FileText, Image, Pencil, Plus, RotateCcw, Trash2, Video } from 'lucide-react';
|
||||
import { FileText, Image, Pencil, Plus, RotateCcw, Search, Trash2, Video } from 'lucide-react';
|
||||
import type { PricingRule, PricingRuleInput, PricingRuleSet, PricingRuleSetUpsertRequest } from '@easyai-ai-gateway/contracts';
|
||||
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, ConfirmDialog, FormDialog, Input, Label, Select } from '../../components/ui';
|
||||
import { Badge, Button, Card, CardContent, ConfirmDialog, FormDialog, Input, Label, Select } from '../../components/ui';
|
||||
import type { LoadState } from '../../types';
|
||||
import { unitLabel } from './PricingEditorControls';
|
||||
import { createDefaultPricingRules, KeyValueEditor, PricingRuleVisualEditor } from './PricingRuleVisualEditor';
|
||||
@@ -92,68 +92,74 @@ export function PricingRulesPanel(props: {
|
||||
|
||||
return (
|
||||
<div className="pageStack">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>定价规则</CardTitle>
|
||||
<Badge variant="secondary">{props.pricingRuleSets.length}</Badge>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="providerToolbar">
|
||||
<p>规则集可包含文本、图像、视频等多个计价条目,平台或平台模型绑定规则集后再通过折扣率整体调价。</p>
|
||||
<Button type="button" onClick={openCreateDialog}><Plus size={15} />新增规则</Button>
|
||||
</div>
|
||||
<div className="modelCatalogFilters">
|
||||
<Input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="搜索规则名称 / key" />
|
||||
<Card className="compactAdminTableCard">
|
||||
<CardContent className="compactAdminTableContent">
|
||||
<div className="compactAdminToolbar catalogCompactToolbar">
|
||||
<span className="platformSearchBox compactAdminSearch">
|
||||
<Search size={15} />
|
||||
<Input
|
||||
aria-label="搜索定价规则"
|
||||
value={query}
|
||||
placeholder="规则名称、Key 或说明"
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
/>
|
||||
</span>
|
||||
<div className="compactAdminToolbarActions">
|
||||
<Button type="button" variant="outline" size="sm" disabled={!query} onClick={() => setQuery('')}>
|
||||
<RotateCcw size={14} />
|
||||
重置
|
||||
</Button>
|
||||
<Button type="button" size="sm" onClick={openCreateDialog}><Plus size={15} />新增规则</Button>
|
||||
</div>
|
||||
</div>
|
||||
{(props.message || localError) && <p className="formMessage">{localError || props.message}</p>}
|
||||
<section className="pricingRuleGrid">
|
||||
{filtered.map((ruleSet) => (
|
||||
<article className="pricingRuleCard" key={ruleSet.id}>
|
||||
<header>
|
||||
<div>
|
||||
<strong>{ruleSet.name}</strong>
|
||||
<span>{ruleSet.ruleSetKey}</span>
|
||||
</div>
|
||||
<Badge variant={ruleSet.status === 'active' ? 'success' : 'secondary'}>{ruleSet.status}</Badge>
|
||||
</header>
|
||||
<p>{ruleSet.description || '未填写说明'}</p>
|
||||
<div className="providerCatalogMeta">
|
||||
<span>{ruleSet.category}</span>
|
||||
<span>{ruleSet.currency}</span>
|
||||
<span>{pricingRuleSummaries(ruleSet).length} 条计价规则</span>
|
||||
</div>
|
||||
<div className="pricingRuleItems">
|
||||
{pricingRuleSummaries(ruleSet).map((item) => (
|
||||
<span key={item.label}>
|
||||
<strong>{pricingRuleSummaryIcon(item.kind)}{item.label}</strong>
|
||||
<em>{item.value}</em>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="providerCatalogActions">
|
||||
<Button type="button" variant="outline" size="sm" onClick={() => editRuleSet(ruleSet)}><Pencil size={14} />修改</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
disabled={isDefaultRuleSet(ruleSet)}
|
||||
title={isDefaultRuleSet(ruleSet) ? '默认计价规则不能删除' : undefined}
|
||||
onClick={() => setPendingDeleteRuleSet(ruleSet)}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
{!filtered.length && (
|
||||
<div className="emptyState"><strong>暂无匹配的定价规则</strong></div>
|
||||
)}
|
||||
</section>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<section className="pricingRuleGrid">
|
||||
{filtered.map((ruleSet) => (
|
||||
<article className="pricingRuleCard" key={ruleSet.id}>
|
||||
<header>
|
||||
<div>
|
||||
<strong>{ruleSet.name}</strong>
|
||||
<span>{ruleSet.ruleSetKey}</span>
|
||||
</div>
|
||||
<Badge variant={ruleSet.status === 'active' ? 'success' : 'secondary'}>{ruleSet.status}</Badge>
|
||||
</header>
|
||||
<p>{ruleSet.description || '未填写说明'}</p>
|
||||
<div className="providerCatalogMeta">
|
||||
<span>{ruleSet.category}</span>
|
||||
<span>{ruleSet.currency}</span>
|
||||
<span>{pricingRuleSummaries(ruleSet).length} 条计价规则</span>
|
||||
</div>
|
||||
<div className="pricingRuleItems">
|
||||
{pricingRuleSummaries(ruleSet).map((item) => (
|
||||
<span key={item.label}>
|
||||
<strong>{pricingRuleSummaryIcon(item.kind)}{item.label}</strong>
|
||||
<em>{item.value}</em>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="providerCatalogActions">
|
||||
<Button type="button" variant="outline" size="sm" onClick={() => editRuleSet(ruleSet)}><Pencil size={14} />修改</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
disabled={isDefaultRuleSet(ruleSet)}
|
||||
title={isDefaultRuleSet(ruleSet) ? '默认计价规则不能删除' : undefined}
|
||||
onClick={() => setPendingDeleteRuleSet(ruleSet)}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
{!filtered.length && (
|
||||
<Card><CardContent className="emptyState"><strong>暂无定价规则</strong></CardContent></Card>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<FormDialog
|
||||
ariaLabel={editingId ? '编辑定价规则' : '新增定价规则'}
|
||||
bodyClassName="pricingRuleFormBody"
|
||||
|
||||
Reference in New Issue
Block a user