fix(aiops): 保存配置后分析页状态即时同步;分析页布局重排
- enabled 状态在 saveConfig 成功后立即 setEnabled(此前只更新 config, 导致'AI 分析未启用'横幅要等下次请求才消失) - 布局:工具栏改 4 列网格(数据源/时间/应用/问题),执行按钮与提示移到 分隔线下右对齐;未启用横幅加 ✦ 图标 + 「去配置」直达按钮;空态/加载态 加图标居中;消耗行改统计卡风格(数值 tabular 高亮)
This commit is contained in:
1 parent
5d898e3592
commit
2efbed882a
3 files changed
+39
-27
No files matched your search
@@ -20,6 +20,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
analyze: {
|
analyze: {
|
||||||
|
goConfig: 'Open config',
|
||||||
appScope: 'App scope',
|
appScope: 'App scope',
|
||||||
allApps: 'All apps',
|
allApps: 'All apps',
|
||||||
appRequired: 'Required (over 1h)',
|
appRequired: 'Required (over 1h)',
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
analyze: {
|
analyze: {
|
||||||
|
goConfig: '去配置',
|
||||||
appScope: '应用范围',
|
appScope: '应用范围',
|
||||||
allApps: '全部应用',
|
allApps: '全部应用',
|
||||||
appRequired: '必选(超过 1 小时)',
|
appRequired: '必选(超过 1 小时)',
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ export default function AiopsPage() {
|
|||||||
.filter(Boolean),
|
.filter(Boolean),
|
||||||
})
|
})
|
||||||
setConfig(res.config)
|
setConfig(res.config)
|
||||||
|
setEnabled(Boolean(res.config?.enabled))
|
||||||
setApiKeyInput('')
|
setApiKeyInput('')
|
||||||
toast.success('配置已保存')
|
toast.success('配置已保存')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -245,13 +246,16 @@ export default function AiopsPage() {
|
|||||||
{/* ─────────── 分析 ─────────── */}
|
{/* ─────────── 分析 ─────────── */}
|
||||||
<TabsContent value="analyze" className="space-y-4">
|
<TabsContent value="analyze" className="space-y-4">
|
||||||
{!enabled && (
|
{!enabled && (
|
||||||
<div className="rounded-md border border-warning/40 bg-warning/10 px-3 py-2 text-sm">
|
<div className="flex flex-wrap items-center gap-3 rounded-lg border border-warning/40 bg-warning/10 px-4 py-3 text-sm">
|
||||||
{t('analyze.notEnabled')}
|
<Sparkles className="size-4 shrink-0 text-warning" />
|
||||||
|
<span className="flex-1">{t('analyze.notEnabled')}</span>
|
||||||
|
<Button size="sm" variant="outline" onClick={() => setTab('config')}>{t('analyze.goConfig')}</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="flex flex-wrap items-end gap-3 pt-4">
|
<CardContent className="pt-4">
|
||||||
|
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||||
<div className="min-w-40 space-y-1">
|
<div className="min-w-40 space-y-1">
|
||||||
<Label className="text-xs">{t('analyze.source')}</Label>
|
<Label className="text-xs">{t('analyze.source')}</Label>
|
||||||
<select
|
<select
|
||||||
@@ -299,34 +303,41 @@ export default function AiopsPage() {
|
|||||||
</select>
|
</select>
|
||||||
{needApp && <p className="text-[11px] text-warning">{t('analyze.appScopeHint')}</p>}
|
{needApp && <p className="text-[11px] text-warning">{t('analyze.appScopeHint')}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-64 flex-1 space-y-1">
|
<div className="space-y-1 xl:col-span-2">
|
||||||
<Label className="text-xs">{t('analyze.question')}</Label>
|
<Label className="text-xs">{t('analyze.question')}</Label>
|
||||||
<Input
|
<Input
|
||||||
value={question}
|
value={question}
|
||||||
placeholder={t('analyze.questionPlaceholder')}
|
placeholder={t('analyze.questionPlaceholder')}
|
||||||
onChange={e => setQuestion(e.target.value)}
|
onChange={e => setQuestion(e.target.value)}
|
||||||
className="h-9"
|
className="h-9"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex flex-wrap items-center justify-between gap-2 border-t pt-3">
|
||||||
|
<p className="text-xs text-muted-foreground">{t('analyze.runHint')}</p>
|
||||||
|
<Button onClick={runAnalysis} disabled={running || !enabled || (needApp && !app)} className="h-9">
|
||||||
|
{running ? <Loader2 className="mr-1.5 h-4 w-4 animate-spin" /> : <Play className="mr-1.5 h-4 w-4" />}
|
||||||
|
{running ? t('analyze.running') : t('analyze.run')}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={runAnalysis} disabled={running || !enabled || (needApp && !app)} className="h-9">
|
|
||||||
{running ? <Loader2 className="mr-1.5 h-4 w-4 animate-spin" /> : <Play className="mr-1.5 h-4 w-4" />}
|
|
||||||
{running ? t('analyze.running') : t('analyze.run')}
|
|
||||||
</Button>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<div className="px-4 pb-3 text-xs text-muted-foreground">{t('analyze.runHint')}</div>
|
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{!outcome && !running && (
|
{!outcome && !running && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="py-10 text-center text-sm text-muted-foreground">{t('analyze.empty')}</CardContent>
|
<CardContent className="flex flex-col items-center justify-center gap-2 py-14 text-center">
|
||||||
|
<Sparkles className="size-8 text-muted-foreground/40" />
|
||||||
|
<p className="text-sm text-muted-foreground">{t('analyze.empty')}</p>
|
||||||
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{running && (
|
{running && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="flex items-center justify-center gap-2 py-10 text-sm text-muted-foreground">
|
<CardContent className="flex flex-col items-center justify-center gap-2 py-14 text-center">
|
||||||
<Loader2 className="h-4 w-4 animate-spin" />
|
<Loader2 className="size-7 animate-spin text-primary" />
|
||||||
{t('analyze.running')}
|
<p className="text-sm font-medium">{t('analyze.running')}</p>
|
||||||
|
<p className="text-xs text-muted-foreground">{t('analyze.runHint')}</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
@@ -334,13 +345,12 @@ export default function AiopsPage() {
|
|||||||
{outcome && (
|
{outcome && (
|
||||||
<>
|
<>
|
||||||
{/* 本次消耗:让成本和延迟是可见的 */}
|
{/* 本次消耗:让成本和延迟是可见的 */}
|
||||||
<div className="flex flex-wrap items-center gap-4 rounded-md border bg-muted/30 px-3 py-2 text-xs text-muted-foreground">
|
<div className="flex flex-wrap items-center gap-x-5 gap-y-1 rounded-lg border bg-muted/30 px-4 py-2.5 text-xs">
|
||||||
<span>
|
<span className="font-medium text-foreground">{t('analyze.lastRun')}</span>
|
||||||
{t('analyze.lastRun')}:{t('analyze.promptTokens')} {fmtNum(outcome.run.promptTokens)} ·{' '}
|
<span className="text-muted-foreground">{t('analyze.promptTokens')} <span className="tabular-nums text-foreground">{fmtNum(outcome.run.promptTokens)}</span></span>
|
||||||
{t('analyze.completionTokens')} {fmtNum(outcome.run.completionTokens)} · {t('analyze.duration')}{' '}
|
<span className="text-muted-foreground">{t('analyze.completionTokens')} <span className="tabular-nums text-foreground">{fmtNum(outcome.run.completionTokens)}</span></span>
|
||||||
{(outcome.run.durationMs / 1000).toFixed(1)}s
|
<span className="text-muted-foreground">{t('analyze.duration')} <span className="tabular-nums text-foreground">{(outcome.run.durationMs / 1000).toFixed(1)}s</span></span>
|
||||||
</span>
|
<Button variant="link" size="sm" className="ml-auto h-auto p-0 text-xs" onClick={() => setPromptOpen(true)}>
|
||||||
<Button variant="link" size="sm" className="h-auto p-0 text-xs" onClick={() => setPromptOpen(true)}>
|
|
||||||
{t('analyze.viewPrompt')}
|
{t('analyze.viewPrompt')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user