refactor: standardize dialog component styling and layout for improved consistency and responsiveness.
This commit is contained in:
parent
7d29fe0f2a
commit
e531c8808d
|
|
@ -65,9 +65,7 @@ services:
|
|||
context: ./frontend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- VITE_API_BASE_URL=http://localhost:8000/api/v1
|
||||
- "3000:80" # Nginx 监听 80 端口
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
# =============================================
|
||||
# DeepAudit Frontend Docker 构建
|
||||
# =============================================
|
||||
# 使用 Nginx 提供静态文件和反向代理 (支持 SSE 流式传输)
|
||||
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
|
@ -20,38 +25,23 @@ RUN unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY all_proxy ALL_PROXY && \
|
|||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 构建时使用占位符,运行时替换
|
||||
ENV VITE_API_BASE_URL=__API_BASE_URL__
|
||||
# 🔥 构建时使用相对路径 /api - Nginx 会处理代理
|
||||
ENV VITE_API_BASE_URL=/api/v1
|
||||
|
||||
# 构建生产版本
|
||||
RUN pnpm build
|
||||
|
||||
# 生产镜像
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 清除代理设置
|
||||
ENV http_proxy=
|
||||
ENV https_proxy=
|
||||
ENV HTTP_PROXY=
|
||||
ENV HTTPS_PROXY=
|
||||
|
||||
# 安装 serve(确保无代理)
|
||||
RUN unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY all_proxy ALL_PROXY && \
|
||||
npm install -g serve
|
||||
# =============================================
|
||||
# 生产镜像 - 使用 Nginx (支持 SSE 反向代理)
|
||||
# =============================================
|
||||
FROM nginx:alpine
|
||||
|
||||
# 复制构建产物
|
||||
COPY --from=builder /app/dist ./dist
|
||||
|
||||
# 复制启动脚本
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["serve", "-s", "dist", "-l", "3000"]
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# 复制 Nginx 配置 (包含 SSE 反向代理配置)
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
|
|
|||
|
|
@ -16,6 +16,26 @@ server {
|
|||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# ========== SSE 流式传输必需配置 ==========
|
||||
# 禁用代理缓冲,确保事件实时推送
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
|
||||
# 明确告知 Nginx 不要缓冲 (对上游 FastAPI 的 X-Accel-Buffering 头也有效)
|
||||
proxy_set_header X-Accel-Buffering no;
|
||||
|
||||
# 支持 chunked 编码
|
||||
chunked_transfer_encoding on;
|
||||
|
||||
# SSE 长连接超时配置
|
||||
proxy_read_timeout 300s; # 5 分钟读取超时 (与后端 max_idle 一致)
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_send_timeout 60s;
|
||||
|
||||
# 保持连接
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection '';
|
||||
}
|
||||
|
||||
# 缓存静态资源
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { Input } from '@/components/ui/input';
|
|||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
|
|
@ -435,12 +435,16 @@ export default function AuditRules() {
|
|||
|
||||
{/* Create Rule Set Dialog */}
|
||||
<Dialog open={showCreateDialog} onOpenChange={setShowCreateDialog}>
|
||||
<DialogContent className="max-w-lg cyber-card !fixed p-0 bg-[#0c0c12]">
|
||||
<DialogHeader className="cyber-card-header">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">新建规则集</DialogTitle>
|
||||
<DialogContent className="!w-[min(90vw,500px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">新建规则集</span>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">名称 *</Label>
|
||||
<Input value={ruleSetForm.name} onChange={e => setRuleSetForm({ ...ruleSetForm, name: e.target.value })} placeholder="规则集名称" className="cyber-input" />
|
||||
|
|
@ -470,7 +474,7 @@ export default function AuditRules() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="p-4 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<Button variant="outline" onClick={() => setShowCreateDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={handleCreateRuleSet} className="cyber-btn-primary">创建</Button>
|
||||
</DialogFooter>
|
||||
|
|
@ -479,12 +483,16 @@ export default function AuditRules() {
|
|||
|
||||
{/* Edit Rule Set Dialog */}
|
||||
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
|
||||
<DialogContent className="max-w-lg cyber-card !fixed p-0 bg-[#0c0c12]">
|
||||
<DialogHeader className="cyber-card-header">
|
||||
<Edit className="w-5 h-5 text-primary" />
|
||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">编辑规则集</DialogTitle>
|
||||
<DialogContent className="!w-[min(90vw,500px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Edit className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">编辑规则集</span>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">名称</Label>
|
||||
<Input value={ruleSetForm.name} onChange={e => setRuleSetForm({ ...ruleSetForm, name: e.target.value })} className="cyber-input" />
|
||||
|
|
@ -510,7 +518,7 @@ export default function AuditRules() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="p-4 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<Button variant="outline" onClick={() => setShowEditDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={handleUpdateRuleSet} className="cyber-btn-primary">保存</Button>
|
||||
</DialogFooter>
|
||||
|
|
@ -519,12 +527,16 @@ export default function AuditRules() {
|
|||
|
||||
{/* Rule Edit Dialog */}
|
||||
<Dialog open={showRuleDialog} onOpenChange={setShowRuleDialog}>
|
||||
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto cyber-card !fixed p-0 bg-[#0c0c12]">
|
||||
<DialogHeader className="cyber-card-header">
|
||||
<Code className="w-5 h-5 text-primary" />
|
||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">{selectedRule ? '编辑规则' : '添加规则'}</DialogTitle>
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Code className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">{selectedRule ? '编辑规则' : '添加规则'}</span>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">规则代码 *</Label>
|
||||
|
|
@ -568,7 +580,7 @@ export default function AuditRules() {
|
|||
<Input value={ruleForm.reference_url} onChange={e => setRuleForm({ ...ruleForm, reference_url: e.target.value })} placeholder="如 https://owasp.org/..." className="cyber-input" />
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="p-4 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<Button variant="outline" onClick={() => setShowRuleDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={selectedRule ? handleUpdateRule : handleAddRule} className="cyber-btn-primary">{selectedRule ? '保存' : '添加'}</Button>
|
||||
</DialogFooter>
|
||||
|
|
@ -577,16 +589,22 @@ export default function AuditRules() {
|
|||
|
||||
{/* Import Dialog */}
|
||||
<Dialog open={showImportDialog} onOpenChange={setShowImportDialog}>
|
||||
<DialogContent className="max-w-2xl cyber-card !fixed p-0 bg-[#0c0c12]">
|
||||
<DialogHeader className="cyber-card-header">
|
||||
<Upload className="w-5 h-5 text-primary" />
|
||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">导入规则集</DialogTitle>
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Upload className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">导入规则集</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">粘贴导出的 JSON 内容</p>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<DialogDescription className="px-6 pt-4 text-gray-400">粘贴导出的 JSON 内容</DialogDescription>
|
||||
<div className="p-6">
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<Textarea value={importJson} onChange={e => setImportJson(e.target.value)} placeholder='{"name": "...", "rules": [...]}' rows={15} className="cyber-input font-mono text-sm text-emerald-400" />
|
||||
</div>
|
||||
<DialogFooter className="p-4 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<Button variant="outline" onClick={() => setShowImportDialog(false)} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={handleImport} className="cyber-btn-primary">导入</Button>
|
||||
</DialogFooter>
|
||||
|
|
|
|||
|
|
@ -423,9 +423,9 @@ export default function Projects() {
|
|||
初始化项目
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-2xl max-h-[85vh] !overflow-y-auto cyber-card border-gray-700 bg-[#0c0c12] p-0 !fixed">
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
{/* Terminal Header */}
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50">
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50 flex-shrink-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
|
|
@ -436,14 +436,14 @@ export default function Projects() {
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<DialogHeader className="px-6 pt-4">
|
||||
<DialogHeader className="px-6 pt-4 flex-shrink-0">
|
||||
<DialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-white">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
初始化新项目
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="p-6">
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<Tabs defaultValue="repository" className="w-full">
|
||||
<TabsList className="flex w-full bg-gray-900/50 border border-gray-800 p-1 h-auto gap-1 rounded">
|
||||
<TabsTrigger
|
||||
|
|
@ -934,9 +934,9 @@ export default function Projects() {
|
|||
|
||||
{/* Edit Dialog */}
|
||||
<Dialog open={showEditDialog} onOpenChange={setShowEditDialog}>
|
||||
<DialogContent className="max-w-2xl cyber-card border-gray-700 bg-[#0c0c12] p-0 !fixed">
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
{/* Terminal Header */}
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50">
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-[#0a0a0f] border-b border-gray-800/50 flex-shrink-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/80" />
|
||||
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
|
||||
|
|
@ -947,7 +947,7 @@ export default function Projects() {
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<DialogHeader className="px-6 pt-4">
|
||||
<DialogHeader className="px-6 pt-4 flex-shrink-0">
|
||||
<DialogTitle className="font-mono text-lg uppercase tracking-wider flex items-center gap-2 text-white">
|
||||
<Edit className="w-5 h-5 text-primary" />
|
||||
编辑项目配置
|
||||
|
|
@ -959,7 +959,7 @@ export default function Projects() {
|
|||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="p-6 flex flex-col gap-6 max-h-[70vh] overflow-y-auto custom-scrollbar">
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-6">
|
||||
{/* 基本信息 */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-mono font-bold uppercase text-sm text-gray-400 border-b border-gray-800 pb-2">基本信息</h3>
|
||||
|
|
@ -1172,7 +1172,7 @@ export default function Projects() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end space-x-3 p-4 border-t border-gray-800 bg-gray-900/30">
|
||||
<div className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<Button variant="outline" onClick={() => setShowEditDialog(false)} className="cyber-btn-outline">
|
||||
取消
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { Input } from '@/components/ui/input';
|
|||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { toast } from 'sonner';
|
||||
|
|
@ -28,7 +28,7 @@ import {
|
|||
Terminal,
|
||||
MessageSquare,
|
||||
Shield,
|
||||
Zap,
|
||||
|
||||
Code,
|
||||
AlertTriangle,
|
||||
Activity,
|
||||
|
|
@ -344,14 +344,23 @@ export default function PromptManager() {
|
|||
|
||||
{/* Create/Edit Dialog */}
|
||||
<Dialog open={showCreateDialog || showEditDialog} onOpenChange={(open) => { if (!open) { setShowCreateDialog(false); setShowEditDialog(false); } }}>
|
||||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto cyber-card p-0 bg-[#0c0c12]">
|
||||
<DialogHeader className="cyber-card-header">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
||||
{showEditDialog ? '编辑模板' : '新建模板'}
|
||||
<DialogContent className="!w-[min(90vw,700px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<Terminal className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">
|
||||
{showEditDialog ? '编辑模板' : '新建模板'}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">
|
||||
{showEditDialog ? 'Edit Template' : 'Create Template'}
|
||||
</p>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs font-bold text-gray-500 uppercase">模板名称 *</Label>
|
||||
|
|
@ -381,10 +390,10 @@ export default function PromptManager() {
|
|||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="zh" className="mt-4">
|
||||
<Textarea value={form.content_zh} onChange={e => setForm({ ...form, content_zh: e.target.value })} placeholder="输入中文提示词内容..." rows={15} className="cyber-input font-mono text-sm text-emerald-400" />
|
||||
<Textarea value={form.content_zh} onChange={e => setForm({ ...form, content_zh: e.target.value })} placeholder="输入中文提示词内容..." rows={12} className="cyber-input font-mono text-sm text-emerald-400" />
|
||||
</TabsContent>
|
||||
<TabsContent value="en" className="mt-4">
|
||||
<Textarea value={form.content_en} onChange={e => setForm({ ...form, content_en: e.target.value })} placeholder="Enter English prompt content..." rows={15} className="cyber-input font-mono text-sm text-emerald-400" />
|
||||
<Textarea value={form.content_en} onChange={e => setForm({ ...form, content_en: e.target.value })} placeholder="Enter English prompt content..." rows={12} className="cyber-input font-mono text-sm text-emerald-400" />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -392,7 +401,7 @@ export default function PromptManager() {
|
|||
<Label className="text-xs font-bold text-gray-400 uppercase">启用此模板</Label>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="p-4 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<Button variant="outline" onClick={() => { setShowCreateDialog(false); setShowEditDialog(false); }} className="cyber-btn-outline">取消</Button>
|
||||
<Button onClick={showEditDialog ? handleUpdate : handleCreate} className="cyber-btn-primary">{showEditDialog ? '保存' : '创建'}</Button>
|
||||
</DialogFooter>
|
||||
|
|
@ -401,15 +410,21 @@ export default function PromptManager() {
|
|||
|
||||
{/* Test Dialog */}
|
||||
<Dialog open={showTestDialog} onOpenChange={setShowTestDialog}>
|
||||
<DialogContent className="!max-w-6xl w-[90vw] max-h-[90vh] !overflow-y-auto cyber-card p-0 bg-[#0c0c12] !fixed">
|
||||
<DialogHeader className="cyber-card-header">
|
||||
<Sparkles className="w-5 h-5 text-violet-400" />
|
||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
||||
测试提示词: {selectedTemplate?.name}
|
||||
<DialogContent className="!w-[min(95vw,1200px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<div className="p-2 bg-violet-500/20 rounded border border-violet-500/30">
|
||||
<Sparkles className="w-5 h-5 text-violet-400" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">
|
||||
测试提示词: {selectedTemplate?.name}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">使用示例代码测试提示词效果</p>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<DialogDescription className="px-6 pt-4 text-gray-400">使用示例代码测试提示词效果</DialogDescription>
|
||||
<div className="p-6 grid grid-cols-2 gap-6">
|
||||
<div className="flex-1 overflow-y-auto p-6 grid grid-cols-2 gap-6">
|
||||
{/* Left: Input */}
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
|
|
@ -470,10 +485,9 @@ export default function PromptManager() {
|
|||
<div className="p-3 bg-gray-900/50 border-b border-gray-800 flex items-center justify-between">
|
||||
<span className="text-xs font-bold uppercase text-gray-500">质量评分</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`text-2xl font-bold ${
|
||||
testResult.result.quality_score >= 80 ? 'text-emerald-400' :
|
||||
<div className={`text-2xl font-bold ${testResult.result.quality_score >= 80 ? 'text-emerald-400' :
|
||||
testResult.result.quality_score >= 60 ? 'text-amber-400' : 'text-rose-400'
|
||||
}`}>
|
||||
}`}>
|
||||
{testResult.result.quality_score}
|
||||
</div>
|
||||
<span className="text-xs text-gray-500">/ 100</span>
|
||||
|
|
@ -493,11 +507,10 @@ export default function PromptManager() {
|
|||
</div>
|
||||
{testResult.result.issues.map((issue: any, idx: number) => (
|
||||
<div key={idx} className="cyber-card p-0 overflow-hidden">
|
||||
<div className={`px-3 py-2 border-b border-gray-800 flex items-center justify-between ${
|
||||
issue.severity === 'critical' ? 'bg-rose-500/20 text-rose-400' :
|
||||
<div className={`px-3 py-2 border-b border-gray-800 flex items-center justify-between ${issue.severity === 'critical' ? 'bg-rose-500/20 text-rose-400' :
|
||||
issue.severity === 'high' ? 'bg-orange-500/20 text-orange-400' :
|
||||
issue.severity === 'medium' ? 'bg-amber-500/20 text-amber-400' : 'bg-sky-500/20 text-sky-400'
|
||||
}`}>
|
||||
issue.severity === 'medium' ? 'bg-amber-500/20 text-amber-400' : 'bg-sky-500/20 text-sky-400'
|
||||
}`}>
|
||||
<span className="font-bold text-xs uppercase">{issue.severity}</span>
|
||||
{issue.line && <span className="text-xs opacity-80">行 {issue.line}</span>}
|
||||
</div>
|
||||
|
|
@ -565,7 +578,7 @@ export default function PromptManager() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter className="p-4 border-t border-gray-800">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<Button variant="outline" onClick={() => setShowTestDialog(false)} className="cyber-btn-outline">关闭</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
|
@ -573,15 +586,21 @@ export default function PromptManager() {
|
|||
|
||||
{/* View Dialog */}
|
||||
<Dialog open={showViewDialog} onOpenChange={setShowViewDialog}>
|
||||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto cyber-card p-0 bg-[#0c0c12]">
|
||||
<DialogHeader className="cyber-card-header">
|
||||
<FileText className="w-5 h-5 text-primary" />
|
||||
<DialogTitle className="text-lg font-bold uppercase tracking-wider text-white">
|
||||
{viewTemplate?.name}
|
||||
<DialogContent className="!w-[min(90vw,800px)] !max-w-none max-h-[85vh] flex flex-col p-0 gap-0 bg-[#0c0c12] border border-gray-800 rounded-lg">
|
||||
<DialogHeader className="px-6 py-4 border-b border-gray-800 flex-shrink-0 bg-gray-900/50">
|
||||
<DialogTitle className="flex items-center gap-3 font-mono text-white">
|
||||
<div className="p-2 bg-primary/20 rounded border border-primary/30">
|
||||
<FileText className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-base font-bold uppercase tracking-wider">
|
||||
{viewTemplate?.name}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500 font-normal mt-0.5">{viewTemplate?.description || 'View Template'}</p>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<DialogDescription className="px-6 pt-4 text-gray-400">{viewTemplate?.description}</DialogDescription>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
<div className="flex flex-wrap gap-2 mb-4">
|
||||
{viewTemplate?.is_system && <Badge className="cyber-badge-info">系统模板</Badge>}
|
||||
{viewTemplate?.is_default && <Badge className="cyber-badge-success">默认</Badge>}
|
||||
|
|
@ -614,7 +633,7 @@ export default function PromptManager() {
|
|||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
<DialogFooter className="p-4 border-t border-gray-800 flex gap-2">
|
||||
<DialogFooter className="flex-shrink-0 flex justify-end gap-3 px-6 py-4 bg-gray-900/50 border-t border-gray-800">
|
||||
<Button variant="outline" onClick={() => copyToClipboard(viewTemplate?.content_zh || viewTemplate?.content_en || '')} className="cyber-btn-outline">
|
||||
<Copy className="w-4 h-4 mr-2" />
|
||||
复制内容
|
||||
|
|
|
|||
Loading…
Reference in New Issue