237 lines
10 KiB
HTML
237 lines
10 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>路由拓扑</title>
|
||
<link rel="stylesheet" href="web/style.css">
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
|
||
<header class="topbar">
|
||
<div class="brand"><div class="logo" id="brandLogo">R</div><h1 id="brandName">路由拓扑</h1></div>
|
||
<div class="tb-group">
|
||
<button class="btn" id="btnUndo" disabled title="撤销上一次操作">↶<span class="lbl">撤销</span></button>
|
||
<button class="btn" id="btnAuto" title="自动整理布局">⌗<span class="lbl">自动整理</span></button>
|
||
<button class="btn" id="btnFit" title="适应窗口">⤢<span class="lbl">适应窗口</span></button>
|
||
</div>
|
||
<span class="sep"></span>
|
||
<div class="tb-group">
|
||
<button class="btn" id="btnImport" title="导入拓扑">⇩<span class="lbl">导入</span></button>
|
||
<button class="btn" id="btnExport" title="导出拓扑">⇧<span class="lbl">导出</span></button>
|
||
<button class="btn danger" id="btnClear" title="清空画布">⌫<span class="lbl">清空</span></button>
|
||
</div>
|
||
<div class="topbar-right">
|
||
<span class="save-state" id="saveState"></span>
|
||
<button class="btn" id="btnShare" title="共享与协作" hidden>⇗<span class="lbl">共享</span></button>
|
||
<button class="btn primary" id="btnSave">保存</button>
|
||
<button class="btn" id="btnMyTopos">我的拓扑</button>
|
||
<div class="user-wrap">
|
||
<button class="btn" id="btnUser">登录</button>
|
||
</div>
|
||
</div>
|
||
<input type="file" id="fileInput" accept=".json,application/json" hidden>
|
||
</header>
|
||
|
||
<div class="banner" id="banner" hidden></div>
|
||
|
||
<main class="main">
|
||
<div class="canvas-wrap">
|
||
<svg id="svg" xmlns="http://www.w3.org/2000/svg">
|
||
<defs>
|
||
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5"
|
||
markerWidth="6.5" markerHeight="6.5" orient="auto">
|
||
<path d="M 0 0.7 L 10 5 L 0 9.3 L 2.6 5 Z" fill="#94a3b8"/>
|
||
</marker>
|
||
</defs>
|
||
<g id="viewport"></g>
|
||
</svg>
|
||
<div class="hint">拖动空白平移 · 滚轮缩放 · 拖节点右侧蓝点连线 · 拖底部绿点加子节点 · 双击空白新建</div>
|
||
<div class="stats" id="stats"></div>
|
||
</div>
|
||
|
||
<aside class="inspector">
|
||
<div class="insp-empty" id="inspEmpty">
|
||
<b>未选中节点</b>
|
||
点击图中节点进行编辑
|
||
</div>
|
||
<div id="inspBody" hidden>
|
||
<div class="insp-section" data-sec="basic">
|
||
<div class="insp-sec-head"><span>基本信息</span><span class="insp-chevron">▾</span></div>
|
||
<div class="insp-sec-body">
|
||
<label>名称 / 地址
|
||
<input id="fLabel" type="text" placeholder="192.168.1.10 / dc.corp.local" spellcheck="false">
|
||
</label>
|
||
<label>类型
|
||
<select id="fType">
|
||
<option value="net">网段 (CIDR)</option>
|
||
<option value="host">主机 (IP)</option>
|
||
<option value="domain">域名</option>
|
||
<option value="other">其他</option>
|
||
</select>
|
||
</label>
|
||
<label>状态
|
||
<select id="fStatus"></select>
|
||
</label>
|
||
<label>端口 / 服务
|
||
<input id="fPorts" type="text" placeholder="22, 80, 445 / ssh、smb" spellcheck="false">
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="insp-section" data-sec="note">
|
||
<div class="insp-sec-head"><span>备注</span><span class="insp-chevron">▾</span></div>
|
||
<div class="insp-sec-body">
|
||
<label>备注
|
||
<textarea id="fNote" placeholder="凭据、漏洞、横向思路…"></textarea>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="insp-section" data-sec="actions">
|
||
<div class="insp-sec-head"><span>操作</span><span class="insp-chevron">▾</span></div>
|
||
<div class="insp-sec-body">
|
||
<div class="btn-row">
|
||
<button class="btn" id="btnAddChild">+ 子节点</button>
|
||
<button class="btn danger" id="btnDelete">删除节点</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="meta" id="inspMeta"></div>
|
||
</div>
|
||
<div class="legend" id="legend"></div>
|
||
</aside>
|
||
</main>
|
||
</div>
|
||
|
||
<!-- ================= 登录 / 注册 ================= -->
|
||
<div class="overlay" id="loginOverlay" hidden>
|
||
<div class="dialog">
|
||
<div class="dlg-head"><h2 id="loginTitle">登录</h2><span class="dlg-close" id="btnCloseLogin">×</span></div>
|
||
<label>用户名
|
||
<input id="loginUser" type="text" spellcheck="false" autocomplete="username" placeholder="用户名">
|
||
</label>
|
||
<label>密码
|
||
<input id="loginPass" type="password" autocomplete="current-password" placeholder="密码">
|
||
</label>
|
||
<label id="loginPass2Wrap" hidden>确认密码
|
||
<input id="loginPass2" type="password" autocomplete="new-password" placeholder="请再次输入密码">
|
||
</label>
|
||
<label class="pwd-show"><input type="checkbox" id="loginShow"> 显示密码</label>
|
||
<div class="dlg-err" id="loginErr"></div>
|
||
<div class="dlg-actions">
|
||
<button class="btn primary" id="btnDoLogin">登录</button>
|
||
<button class="btn" id="btnRegister">注册新账号</button>
|
||
</div><br>
|
||
<div class="dlg-tip" id="loginTip">默认管理员 admin / admin123</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ================= 我的拓扑 ================= -->
|
||
<div class="overlay" id="topoOverlay" hidden>
|
||
<div class="dialog wide">
|
||
<div class="dlg-head"><h2>我的拓扑</h2><span class="dlg-close" id="btnCloseTopo">×</span></div>
|
||
<div class="new-row">
|
||
<input id="newTopoName" type="text" placeholder="新建拓扑名称,例如:某内网横向拓扑" spellcheck="false">
|
||
<select id="newTopoTemplate" title="选择初始化模板"></select>
|
||
<button class="btn primary" id="btnCreateTopo">新建</button>
|
||
</div>
|
||
<div class="seg" id="topoScope">
|
||
<button class="seg-btn active" type="button" data-scope="mine">我的拓扑</button>
|
||
<button class="seg-btn" type="button" data-scope="shared">共享拓扑</button>
|
||
<button class="seg-btn" type="button" data-scope="public">公开拓扑</button>
|
||
</div>
|
||
<div class="topo-list" id="topoList"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ================= 共享与协作 ================= -->
|
||
<div class="overlay" id="shareOverlay" hidden>
|
||
<div class="dialog wide share-dialog">
|
||
<div class="dlg-head"><h2>共享与协作</h2><span class="dlg-close" id="btnCloseShare">×</span></div>
|
||
<div class="dlg-msg" id="shareMsg"></div>
|
||
<label class="set-row">
|
||
<input type="checkbox" id="shareMode"> <span>开启共享模式(允许以下成员 / 分组访问与协同编辑)</span>
|
||
</label>
|
||
<div class="share-cols">
|
||
<div class="share-col">
|
||
<div class="share-col-title">成员</div>
|
||
<input class="search" id="shareUserFilter" placeholder="搜索成员" spellcheck="false">
|
||
<div class="share-list" id="shareUserList"></div>
|
||
</div>
|
||
<div class="share-col">
|
||
<div class="share-col-title">分组</div>
|
||
<input class="search" id="shareGroupFilter" placeholder="搜索分组" spellcheck="false">
|
||
<div class="share-list" id="shareGroupList"></div>
|
||
</div>
|
||
</div>
|
||
<div class="dlg-err" id="shareErr"></div>
|
||
<div class="dlg-actions">
|
||
<button class="btn" id="btnCancelShare" type="button">取消</button>
|
||
<button class="btn primary" id="btnSaveShare" type="button">保存共享</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 管理员控制台已拆分为独立页面:web/admin.html -->
|
||
|
||
<!-- ================= 修改密码 ================= -->
|
||
<div class="overlay" id="pwdOverlay" hidden>
|
||
<div class="dialog">
|
||
<div class="dlg-head"><h2 id="pwdTitle">修改密码</h2><span class="dlg-close" id="btnClosePwd">×</span></div>
|
||
<div class="dlg-tip" id="pwdHintWrap" hidden>首次登录必须修改密码后才能继续使用</div>
|
||
<label>原密码<input id="pwdOld" type="password" autocomplete="current-password" placeholder="当前使用的密码"></label>
|
||
<label>新密码<input id="pwdNew" type="password" autocomplete="new-password" placeholder="至少 8 位,需含字母和数字"></label>
|
||
<label>确认新密码<input id="pwdNew2" type="password" autocomplete="new-password" placeholder="请再次输入新密码"></label>
|
||
<label class="pwd-show"><input type="checkbox" id="pwdShow"> 显示密码</label>
|
||
<div class="dlg-err" id="pwdErr"></div>
|
||
<div class="dlg-actions"><button class="btn primary" id="btnDoPwd">确认修改</button></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ================= 站内确认 / 输入弹窗 ================= -->
|
||
<div class="overlay" id="confirmOverlay" hidden>
|
||
<div class="dialog">
|
||
<div class="dlg-head"><h2 id="confirmTitle">请确认</h2><span class="dlg-close" id="confirmClose">×</span></div>
|
||
<div class="dlg-msg" id="confirmMsg"></div>
|
||
<label id="confirmFieldWrap" hidden><span id="confirmFieldLabel"></span>
|
||
<input id="confirmField" type="text" spellcheck="false" autocomplete="off">
|
||
</label>
|
||
<div class="dlg-err" id="confirmErr"></div>
|
||
<div class="dlg-actions">
|
||
<button class="btn" id="confirmCancel" type="button">取消</button>
|
||
<button class="btn primary" id="confirmOk" type="button">确定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ================= 导出 / 导入 格式选择 ================= -->
|
||
<div class="overlay" id="formatOverlay" hidden>
|
||
<div class="dialog">
|
||
<div class="dlg-head"><h2 id="formatTitle">导出拓扑</h2><span class="dlg-close" id="btnCloseFormat">×</span></div>
|
||
<div class="dlg-msg" id="formatMsg"></div>
|
||
<div class="format-list" id="formatList"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户菜单置于 body 直属并使用 fixed 定位,避免被顶部栏的 overflow / backdrop-filter 裁剪 -->
|
||
<div class="dropdown" id="userMenu" hidden>
|
||
<div class="dd-name" id="ddName"></div>
|
||
<div class="dd-item" id="ddWorkspace">我的工作台</div>
|
||
<div class="dd-item" id="ddPwd">修改密码</div>
|
||
<div class="dd-item" id="ddLogout">退出登录</div>
|
||
</div>
|
||
|
||
<!-- 右下角状态区:当前正在编辑的拓扑 + 在线协作(始终固定右下角) -->
|
||
<div class="corner-status" id="cornerStatus">
|
||
<div class="topo-current" id="topoCurrent">
|
||
<i class="tc-dot"></i><span class="tc-label">当前拓扑</span><span class="tc-name">—</span>
|
||
</div>
|
||
<div class="collab-bar" id="collabBar" hidden></div>
|
||
</div>
|
||
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<script src="web/app.js"></script>
|
||
</body>
|
||
</html>
|