Files
ai-worker-platform/static/style.css
T
hz4th_coder 78ccca61c3 DAG画布连线编辑:新建/重连/删除 + 环检测
- 连线操作三件套:
  * 新建:拖节点右侧输出口到目标节点(幽灵线预览 + 合法/非法高亮)
  * 重连:单击连线选中 → 操作条「🔗重连」→ 点击新目标节点(自动移除旧边)
  * 删除:双击连线 / 悬停中点✕按钮 / 选中后 Delete 键 / 操作条删除按钮
- 单击连线选中高亮 + 顶部操作条(重连/删除/取消),点击空白取消选中
- 前端防环校验(DFS)+ 非法目标标红,后端 PUT/POST depends_on 全面校验:
  依赖存在性、非自依赖、全图环检测(返回环路径)
- 节点新增左右连接端口(悬停/连线模式显示);拖节点时连线与端口同步更新
- 修复:控件条点击不再误触发取消选中/视图复位
2026-08-13 11:31:40 +08:00

201 lines
14 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:root{
--bg:#0f1420; --panel:#171e2e; --panel2:#1d2638; --border:#2a3550;
--text:#e6ebf5; --muted:#8b97ad; --accent:#4f8cff; --accent2:#38d39f;
--warn:#ffb454; --danger:#ff6b6b; --purple:#a78bfa;
}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:"PingFang SC","Microsoft YaHei",system-ui,sans-serif;font-size:14px}
#app{display:flex;min-height:100vh}
#sidebar{width:220px;background:var(--panel);border-right:1px solid var(--border);display:flex;flex-direction:column;position:fixed;top:0;bottom:0}
.logo{padding:20px 18px;font-size:18px;font-weight:700;border-bottom:1px solid var(--border)}
.logo span{color:var(--accent)}
.logo small{display:block;font-size:11px;color:var(--muted);font-weight:400;margin-top:2px}
nav{padding:12px 0;flex:1}
nav a{display:block;padding:11px 20px;color:var(--muted);text-decoration:none;border-left:3px solid transparent}
nav a:hover{color:var(--text);background:var(--panel2)}
nav a.active{color:var(--accent);border-left-color:var(--accent);background:var(--panel2)}
.sidebar-foot{padding:14px 18px;border-top:1px solid var(--border);font-size:12px}
.conn.ok{color:var(--accent2)} .conn.err{color:var(--danger)}
.logout{color:var(--muted);text-decoration:none;display:block;margin-top:6px}
.logout:hover{color:var(--danger)}
#main{flex:1;margin-left:220px;padding:24px 28px;max-width:1400px}
h1.page-title{font-size:20px;margin-bottom:18px}
h1.page-title small{color:var(--muted);font-weight:400;font-size:13px;margin-left:8px}
.toolbar{display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap}
.btn{background:var(--panel2);border:1px solid var(--border);color:var(--text);padding:8px 14px;border-radius:8px;cursor:pointer;font-size:13px}
.btn:hover{border-color:var(--accent)}
.btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}
.btn.primary:hover{filter:brightness(1.15)}
.btn.success{background:var(--accent2);border-color:var(--accent2);color:#06281c}
.btn.danger{background:transparent;border-color:var(--danger);color:var(--danger)}
.btn.warn{background:transparent;border-color:var(--warn);color:var(--warn)}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn.sm{padding:4px 10px;font-size:12px;border-radius:6px}
input,select,textarea{background:var(--panel2);border:1px solid var(--border);color:var(--text);padding:8px 10px;border-radius:8px;font-size:13px;outline:none;width:100%}
input:focus,select:focus,textarea:focus{border-color:var(--accent)}
textarea{min-height:90px;resize:vertical;font-family:inherit}
label{display:block;font-size:12px;color:var(--muted);margin:12px 0 5px}
.row{display:flex;gap:10px}.row>*{flex:1}
.card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:16px}
.grid{display:grid;gap:14px}
.g4{grid-template-columns:repeat(4,1fr)}
.g3{grid-template-columns:repeat(3,1fr)}
.g2{grid-template-columns:repeat(2,1fr)}
@media(max-width:1100px){.g4{grid-template-columns:repeat(2,1fr)}.g3,.g2{grid-template-columns:1fr}}
.stat{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:16px 18px}
.stat .num{font-size:26px;font-weight:700;margin-top:6px}
.stat .lbl{color:var(--muted);font-size:12px}
.stat .sub{color:var(--muted);font-size:11px;margin-top:4px}
table{width:100%;border-collapse:collapse;font-size:13px}
th{color:var(--muted);text-align:left;font-weight:500;padding:10px 12px;border-bottom:1px solid var(--border);white-space:nowrap}
td{padding:10px 12px;border-bottom:1px solid var(--border);vertical-align:middle}
tr:hover td{background:var(--panel2)}
.mono{font-family:ui-monospace,Consolas,monospace}
.badge{display:inline-block;padding:2px 9px;border-radius:20px;font-size:12px;white-space:nowrap}
.st-todo{background:#2a3550;color:#aeb9d4}
.st-running{background:#173b6b;color:#6db3ff}
.st-review{background:#4a3a10;color:#ffd166}
.st-done{background:#0f3d2e;color:#38d39f}
.st-rejected{background:#4a1c1c;color:#ff8f8f}
.st-failed{background:#3d1c2e;color:#ff7ac2}
.st-cancelled{background:#333;color:#999}
.pri-high{color:#ff6b6b}.pri-medium{color:#ffb454}.pri-low{color:#8b97ad}
.kanban{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;align-items:start;min-width:1200px}
.kb-col{background:var(--panel);border:1px solid var(--border);border-radius:12px;min-height:200px}
.kb-head{padding:10px 12px;font-weight:600;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--border)}
.kb-head .cnt{color:var(--muted);font-size:12px}
.kb-body{padding:10px;display:flex;flex-direction:column;gap:8px;min-height:120px}
.kb-card{background:var(--panel2);border:1px solid var(--border);border-radius:10px;padding:10px 12px;cursor:grab;transition:border .15s}
.kb-card:hover{border-color:var(--accent)}
.kb-card.dragging{opacity:.4}
.kb-col.drag-over{border-color:var(--accent);background:var(--panel2)}
.kb-card .t{font-size:13px;font-weight:500;margin-bottom:6px}
.kb-card .m{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.kb-card .cost{color:var(--muted);font-size:11px;margin-top:6px}
.kb-card .out{font-size:11px;color:var(--accent2);margin-top:4px;display:none}
.list-item{display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-bottom:1px solid var(--border)}
.list-item:last-child{border-bottom:none}
.list-item .title{font-weight:600}
.list-item .meta{color:var(--muted);font-size:12px;margin-top:3px}
.modal{width:640px;max-width:94vw;max-height:86vh;overflow-y:auto;background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:22px;position:relative}
.modal h3{font-size:16px;margin-bottom:14px}
.modal .close{position:absolute;top:14px;right:16px;background:none;border:none;color:var(--muted);font-size:20px;cursor:pointer}
.modal .close:hover{color:var(--text)}
#modal-mask{position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;z-index:100}
.modal-actions{display:flex;gap:10px;margin-top:18px;justify-content:flex-end;flex-wrap:wrap}
#login-mask{position:fixed;inset:0;background:var(--bg);z-index:200;display:flex;align-items:center;justify-content:center}
.login-box{width:340px;background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:30px;text-align:center}
.login-box h2{font-size:17px;margin-bottom:6px}
.login-box p{color:var(--muted);font-size:13px;margin-bottom:16px}
.login-box .btn{width:100%;margin-top:12px;padding:10px}
.err{color:var(--danger);font-size:12px;margin-top:10px;min-height:16px}
#toast-wrap{position:fixed;top:20px;right:20px;z-index:300;display:flex;flex-direction:column;gap:8px}
.toast{background:var(--panel2);border:1px solid var(--border);border-left:3px solid var(--accent);padding:10px 16px;border-radius:8px;font-size:13px;animation:slidein .25s}
.toast.ok{border-left-color:var(--accent2)}
.toast.err{border-left-color:var(--danger)}
@keyframes slidein{from{transform:translateX(30px);opacity:0}to{transform:none;opacity:1}}
.log-line{font-size:12px;padding:7px 10px;border-left:2px solid var(--border);margin-bottom:6px;background:var(--panel2);border-radius:0 8px 8px 0;font-family:ui-monospace,Consolas,monospace;word-break:break-all}
.log-line.info{border-left-color:var(--accent)}
.log-line.success{border-left-color:var(--accent2)}
.log-line.warn{border-left-color:var(--warn)}
.log-line.error{border-left-color:var(--danger)}
.log-line .ts{color:var(--muted);margin-right:8px}
.output-box{background:#0b0f18;border:1px solid var(--border);border-radius:10px;padding:14px;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,Consolas,monospace;font-size:13px;max-height:300px;overflow-y:auto}
.kv{display:grid;grid-template-columns:90px 1fr;gap:6px 12px;font-size:13px}
.kv dt{color:var(--muted)}
.kv dd{margin:0}
.sec-title{font-size:14px;font-weight:600;margin:20px 0 10px;color:var(--accent)}
.bar{height:8px;background:var(--panel2);border-radius:6px;overflow:hidden;margin-top:6px}
.bar>div{height:100%;background:var(--accent);border-radius:6px}
.empty{color:var(--muted);text-align:center;padding:40px 0}
.spin{display:inline-block;width:14px;height:14px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:rot .8s linear infinite;vertical-align:-2px;margin-right:6px}
@keyframes rot{to{transform:rotate(360deg)}}
.proj-card{cursor:pointer;transition:border .15s}
.proj-card:hover{border-color:var(--accent)}
.pager-tip{color:var(--muted);font-size:12px}
.pill{display:inline-block;background:var(--panel2);border:1px solid var(--border);border-radius:6px;padding:2px 8px;font-size:12px;margin:2px}
/* V1tabs / DAG / 告警 / 开关 */
.tabs{display:flex;gap:4px;margin-bottom:16px;border-bottom:1px solid var(--border)}
.tabs a{padding:9px 16px;color:var(--muted);text-decoration:none;border-bottom:2px solid transparent;font-size:13px}
.tabs a.active{color:var(--accent);border-bottom-color:var(--accent)}
.dag-box{background:var(--panel);border:1px solid var(--border);border-radius:12px;overflow:hidden;height:520px;position:relative;touch-action:none;user-select:none;cursor:grab}
.dag-box svg{display:block;transition:transform .05s linear;overflow:visible}
.dag-node{stroke:var(--border);stroke-width:1.5;rx:10;cursor:grab}
.dag-node:hover{stroke:var(--accent)}
.dag-node rect{stroke-width:1.5;rx:10}
.dag-label{font-size:13px;font-weight:600;fill:#ffffff;stroke:none;text-anchor:middle;pointer-events:none}
.dag-sub{font-size:11px;fill:rgba(255,255,255,.82);stroke:none;text-anchor:middle;pointer-events:none}
.dag-edge{stroke:var(--border);stroke-width:1.5;fill:none;marker-end:url(#arrow);pointer-events:none}
.dag-edge.act{stroke:var(--accent)}
.dag-edge-g{cursor:pointer}
.dag-edge-hit{fill:none;stroke:transparent;stroke-width:14;pointer-events:stroke;cursor:pointer}
.dag-edge-g:hover .dag-edge{stroke:var(--accent)}
.dag-edge.sel{stroke:var(--accent);stroke-width:2.5}
.dag-edge-x{opacity:0;fill:var(--danger);stroke:#fff;stroke-width:1.5;pointer-events:none;cursor:pointer;transition:opacity .12s}
.dag-edge-xt{opacity:0;font-size:10px;fill:#fff;text-anchor:middle;pointer-events:none;transition:opacity .12s}
.dag-edge-g:hover .dag-edge-x,.dag-edge-g:hover .dag-edge-xt,.dag-edge-g.sel .dag-edge-x,.dag-edge-g.sel .dag-edge-xt{opacity:1}
.dag-edge-g:hover .dag-edge-x,.dag-edge-g.sel .dag-edge-x{pointer-events:all}
.dag-port{fill:var(--panel2);stroke:var(--accent);stroke-width:2;opacity:0;pointer-events:all;transition:opacity .15s}
.dag-node:hover .dag-port{opacity:1}
.dag-connecting .dag-port{opacity:1}
.dag-port.out{cursor:crosshair}
.dag-port.in{cursor:crosshair}
.dag-node.drop-ok rect{stroke:var(--accent2);stroke-width:2.5}
.dag-node.drop-bad rect{stroke:var(--danger);stroke-width:2.5}
.dag-ghost{stroke:var(--accent);stroke-width:2;stroke-dasharray:6 4;fill:none;marker-end:url(#arrow);pointer-events:none}
.dag-ghost.bad{stroke:var(--danger)}
.dag-edge-actions{position:absolute;top:14px;left:50%;transform:translateX(-50%);background:rgba(23,30,46,.92);border:1px solid var(--border);border-radius:8px;padding:4px 8px;font-size:12px;color:var(--text);display:flex;align-items:center;gap:6px;z-index:7;backdrop-filter:blur(2px);box-shadow:0 4px 14px rgba(0,0,0,.35)}
.dag-edge-actions span{color:var(--muted);white-space:nowrap}
.dag-edge-actions b{color:var(--accent)}
.dag-trash{position:absolute;left:14px;bottom:14px;background:rgba(255,107,107,.1);border:1.5px dashed var(--danger);color:var(--danger);border-radius:10px;padding:8px 14px;font-size:12px;cursor:pointer;z-index:5;display:flex;align-items:center;gap:6px;backdrop-filter:blur(2px)}
.dag-trash:hover,.dag-trash.hover{background:rgba(255,107,107,.25);border-style:solid}
.dag-trash .cnt{background:var(--danger);color:#fff;border-radius:8px;padding:0 6px;font-size:11px}
.dag-zoom{position:absolute;right:14px;top:14px;background:rgba(23,30,46,.88);border:1px solid var(--border);border-radius:8px;padding:4px 10px;font-size:12px;color:var(--muted);display:flex;align-items:center;gap:6px;z-index:6}
.dag-zoom b{color:var(--text);font-weight:600;min-width:42px;text-align:center}
.dag-zoom button{background:var(--panel2);border:1px solid var(--border);color:var(--text);border-radius:5px;padding:2px 8px;font-size:11px;cursor:pointer}
.dag-zoom button:hover{border-color:var(--accent);color:var(--accent)}
.alert-row{border-left:3px solid var(--border);padding:12px 14px;margin-bottom:8px;background:var(--panel2);border-radius:0 10px 10px 0;cursor:pointer}
.alert-row.unread{border-left-color:var(--warn)}
.alert-row.critical{border-left-color:var(--danger)}
.alert-row .t{font-weight:600;font-size:13px}
.alert-row .d{color:var(--muted);font-size:12px;margin-top:4px}
.alert-row .tm{color:var(--muted);font-size:11px;margin-top:4px}
.alert-row .badge{margin-right:8px}
.check-group{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}
.check-group label{display:flex;align-items:center;gap:5px;font-size:12px;color:var(--text);margin:0;padding:5px 10px;background:var(--panel2);border:1px solid var(--border);border-radius:20px;cursor:pointer}
.check-group input{width:auto}
.switch{display:inline-flex;align-items:center;gap:8px;cursor:pointer;font-size:13px}
.switch input{width:auto}
code{background:var(--panel2);border:1px solid var(--border);border-radius:6px;padding:2px 7px;font-size:12px;color:var(--accent2);font-family:ui-monospace,Consolas,monospace}
.doc-item{display:flex;justify-content:space-between;align-items:center;padding:12px 0;border-bottom:1px solid var(--border)}
.wbs-row{border:1px solid var(--border);border-radius:10px;padding:12px;margin-bottom:10px;background:var(--panel2)}
.wbs-row .idx{display:inline-block;width:26px;height:26px;line-height:26px;text-align:center;background:var(--accent);color:#fff;border-radius:50%;font-size:13px;margin-right:8px}
.copy-btn{cursor:pointer;color:var(--accent)}
.tab-note{color:var(--muted);font-size:12px;margin-bottom:12px}
/* ---------- V2 新增样式 ---------- */
.mode-card{border-left:3px solid var(--accent);transition:transform .15s}
.mode-card:hover{transform:translateY(-2px);box-shadow:0 4px 14px rgba(0,0,0,.08)}
.agent-step{border:1px solid var(--line);border-radius:8px;padding:8px;margin-bottom:6px;font-size:12px}
.agent-step pre{margin:6px 0 0;font-size:12px;color:#444}
.empty-td{text-align:center;color:var(--muted);padding:16px}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.form-grid label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--muted)}
.form-grid input,.form-grid select,.form-grid textarea{font-size:13px}
.form-grid textarea{resize:vertical}
#main .tabs a{cursor:pointer}
.tag{display:inline-block;background:var(--accent-soft,#eef2ff);color:#4f5bd5;border-radius:4px;padding:0 6px;font-size:11px;margin-right:4px}