mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-18 21:47:17 +02:00
* feat: integrate the mobile UI runtime layer from the migration package 迁移包给出的移动端改造是「运行时注入」架构:mobile.css 只写 767.98px 以下的 规则,mobile-ui.js 在末位加载并包装 switchPage、注入抽屉/底部标签栏/更多面板。 模板侧只有 4 处接线(viewport-fit + interactive-widget、样式层链接、ELK/SheetJS 移动端延迟加载、交互层末位脚本),其余保持上游原样,桌面端零影响。 * feat: 移动端注入层接入 i18n,并把触控下限统一到 44px 迁移包的交互层把标签栏、抽屉、更多面板的文案全写死成中文,en-US/ru-RU 下会中英 混杂;现在所有注入节点统一挂 data-i18n,交给 i18n.js 的 applyTranslations 在语言 切换时自动重译,本层只保留中文兜底。语言项改为按桌面端下拉里真实存在的语言循环 (原来只在 zh/en 之间来回,俄语用户点不到),面板里的 API 文档/GitHub 不再抄一份 URL,改为触发桌面端既有控件;iOS 上没有全屏 API 时给出提示而不是静默无反应; 不在四个标签里的页面点亮"更多",否则三分之一的页面底部栏无任何高亮。 同时补齐 PWA manifest、api-docs 页移动端样式层,并把通知铃铛与头像按钮从 38px 提到本层自己的 --m-touch(44px) 口径。配 web/static/js/mobile-ui-i18n.test.cjs: 键集三方一致、引用键必须三语都有译文、helper 区之外不许出现裸写文案的 API。 * style: 移动端外壳改 iOS 观感,并修掉抽屉页脚错位 观感:顶栏/底栏改透出材质+发丝分隔线,图标按钮一律正圆去底框,文字按钮一律胶囊, 卡片 18、面板顶角 22,按压给 scale 反馈并在 prefers-reduced-motion 下关掉;焦点环 从 2px 硬描边换成 3px 柔光环(forced-colors 下把硬描边还回去)。材质一律用 color-mix 从主题自己的 --bg-primary/--border-color 派生,浅色暗色都跟随。 两处不是"不好看"而是真错: - 抽屉页脚被 insertBefore 插到菜单上面,"返回顶部"出现在列表上方; - 顶栏材质在暗色下被 style.css 的 html[data-theme="dark"] header 不透明渐变压住, 只写 header 选择器不生效,改成同特异性后暗色才真正透出。 圆角与胶囊走 --m-r-icon/--m-r-btn 令牌(24 节那批 !important 规则读的就是它们), 不再往下补选择器与 !important 对轰。 配 scripts/mobile-shot.mjs:手机视口下抓外壳图,--why 用 CDP 匹配样式表回答 "规则写了没生效是没命中还是被谁压住",--eval 量真实计算值。 * fix: 抽屉页脚与底部固定件被 Safari 工具栏压在后面 iOS 的 100dvh 是「工具栏收起时」的最大视口高度,工具栏展开时可见高度只有 innerHeight。两个全屏抽屉写死 height:100dvh,于是页脚那一截正好落在 Safari 底栏后面;bottom:0 的底栏与贴底面板同理。现在抽屉改用 mobile-ui.js 每次 resize 重写的 --m-app-height,贴底固定件按 max(0px, 100dvh - 可见高度) 抬升;不支持 dvh 或桌面端抬升量恒为 0,实测默认态底栏/抽屉位置零位移。 * fix: 去掉底部标签栏里重复的「更多」入口 顶栏的 ··· 与标签栏的「更多」打开的是同一个面板。按一个入口一份实现收掉底部那枚, 标签栏只留四个页面跳转;随之清掉 more2 图标、mobile.tabMore 键与门禁里的点击断言。 落点在四个标签之外的页面不再高亮任何标签——没有「更多」可点了。 * fix: 清掉另外两处重复入口,并把同类视口缺陷扫干净 顺着「底部更多与顶栏 ··· 重复」这条线扫了一遍,还有两处同类: - 面板里的「退出登录」与头像菜单里的退出是同一个动作两个入口,头像菜单是桌面端 一贯的位置,去掉面板那枚;连带删掉只有它能触发的 m-danger 样式与 logout 图标。 - 抽屉页脚的「返回顶部」与面板里的返回顶部是同一个动作两个入口,且抽屉是导航容器, 动作按钮不该住在里面;去掉整个页脚(那句"移动端导航"文案也没有信息量)。 同类视口缺陷两处: - #m-toast 的 bottom 只算到 tabbar-h + safe,底栏按 --m-bar-bottom 抬升时提示条不抬, 工具栏展开时会压在底栏上;实测补前后间距 16px -> 16px。 - api-docs 页容器写 min-height:100dvh,恒定比可见区高出一截,最后一行落在 Safari 底栏后面;改成 100svh(最小视口)。 i18n 契约测试同步:header.logout / mobile.navFooter 不再被引用,mobile 命名空间 18 键。 * style: 手机端对齐 new-api 的卡片密度与 iOS 的填充式表单 仪表盘那排 KPI 在 ≤480px 被桌面样式拍成一列 116px 高的长条,手机上要滚两屏才看完 四个数字。收成两列紧凑网格(奇数张末卡通栏不留空洞),数字 26px/600 + tabular-nums 做主角,标签 12px 弱化,图标块 28px。卡片表面统一成发丝边 + 16 圆角 + 几乎看不见的 投影——桌面的双层阴影在小屏上一屏四张会散。表单改 iOS 填充式(靠底色区分字段), 聚焦只留一圈柔光环(登录框实测叠过两圈),登录卡 20 圆角 + 满宽胶囊主按钮。 KPI 按 nth-child 分的淡彩渐变是产品自己的身份,这一节不跟它抢。 * fix: 补生产侧俄语、修掉白底白字的提示条,并把逐按钮门禁跑到 PASS 用户报的两件事: - 面板里「切换语言」只在中英之间跳 —— 不是逻辑问题,是 ~/csai-生产版 缺俄语资产 (模板没有 ru-RU 选项、i18n/ 只有两个 json、i18n.js 是不认 ru 的旧版)。补齐后 真手指连点验通 中→英→俄→中,且该行文字自身跟着换。 - 点面板按钮时屏幕中间冒出一块白的 —— 是我自己造成的:25.6 把提示条改成浅色材质, 字色还是基底的 #fff,等于白底白字。回到深色 HUD 胶囊,只留一层投影。 逐按钮门禁(需要登录态,本轮首次跑通全量 34 个界面 = 31 页 + 抽屉/面板/会话抽屉展开态): - 硬缺陷 covered/offscreen/zero/pointer-events 全 0;11 条外壳真手指点击全过; - 小于 44px 从 79 降到 3,残留是桌面密集表格里的控件,钉进基线只降不升; - 修掉的真缺陷:子菜单项 42px(本层自己漏的下限)、.ai-channel-manager 的 overflow:hidden 把 4 个通道选择按钮裁到滚不进来、会话抽屉里搜索框 42/分组按钮 24 宽/ 项目行 38/折叠头 30、工具条里 <a onclick> 只有 21px 高(这些链接既没 href 也没 onclick 属性可依赖,判据要放宽到 <a>);复选框口径统一到 24.11 一处并放大到 30px。 - 门禁自己的两个错也修了:scrollIntoView 在「外层 hidden + 内层 auto」的嵌套里根本不滚, 造成 59 个 offscreen 假红,改成逐层滚可滚祖先模拟手指;touch+mouse 双发会让一次点击 变成两次(抽屉开了又关),只发 touch。 - 退出码口径写死:够不到/遮挡/零尺寸/pointer-events 与自家外壳必须 0,桌面遗留小命中区 走基线;基线被压低时注入探针验红(RATCHET FAIL ×2)后撤销复验绿。 * style: 手机端玻璃质感升级,并补上对话上传的安卓适配 观感目标从"iOS 化的平面灰底"升到玻璃质感。平底上做半透明无物可透,玻璃读不出来, 所以先造画布再上材质(mobile.css 第 28 节): - .container 铺环境光(浅色=近白底+右上 accent/左下紫柔光,暗色=深底+更亮柔光), .content-area / .chat-messages 交出实心底色,半透明表面从此有色可透; - 模糊只给小面积/瞬时件:顶栏、底栏、底部面板、抽屉、登录卡、弹窗;常驻卡片 只做半透明+发丝边+顶缘高光,不挂模糊——固定在滚动内容上的大面积模糊是掉帧主因 (25 节实测口径不变);浅深色全用 color-mix 从主题令牌派生,只有顶缘高光按主题 换向;prefers-reduced-transparency 与不支持模糊的环境退回实心。 - index.html 的 mobile.css 版本号递增,防 iPhone Safari 吃旧缓存。 安卓适配:对话上传的 accept="*" 不是合法 MIME 通配(各浏览器/WebView 解析不一致), 改为 "*/*";mobile-tap-audit.mjs 新增 --ua=iphone|android,同一套门禁可换安卓 UA 复跑 (壳层按视口宽度判定、与 UA 无关,换 UA 是给"安卓浏览器下同样合格"留可复现证据)。 验证(都在生产版文件夹跑):iPhone/Android 双 UA 端到端上传(按钮 44px → 触达 选择器 → POST /api/chat-uploads 200 落盘 → 附件条渲染)PASS;双 UA 全量门禁各 34 页 PASS(硬缺陷 0、点击失败 0、44px 基线无回潮)。
1085 lines
36 KiB
HTML
1085 lines
36 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||
<meta name="theme-color" content="#f8fafc" media="(prefers-color-scheme: light)">
|
||
<meta name="theme-color" content="#0b1120" media="(prefers-color-scheme: dark)">
|
||
<title data-i18n="apiDocs.pageTitle">API 文档 - CyberStrikeAI</title>
|
||
<link rel="icon" type="image/png" href="/static/logo.png">
|
||
<link rel="stylesheet" href="/static/css/style.css">
|
||
<script src="/static/js/theme.js"></script>
|
||
<style>
|
||
/* 覆盖主CSS的overflow限制,允许API文档页面滚动 */
|
||
body {
|
||
overflow: auto !important;
|
||
height: auto !important;
|
||
}
|
||
|
||
.api-docs-container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
padding: 24px;
|
||
background: var(--bg-primary);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.api-docs-header {
|
||
position: relative;
|
||
margin-bottom: 32px;
|
||
padding-bottom: 24px;
|
||
padding-right: 280px;
|
||
border-bottom: 2px solid var(--border-color);
|
||
}
|
||
|
||
.api-docs-header-actions {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.api-docs-header-actions .theme-toggle-btn,
|
||
.api-docs-header-actions .lang-switcher-btn {
|
||
min-height: 36px;
|
||
}
|
||
|
||
.api-docs-header h1 {
|
||
font-size: 2rem;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
margin-bottom: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.api-docs-header p {
|
||
color: var(--text-secondary);
|
||
font-size: 0.9375rem;
|
||
margin: 0;
|
||
}
|
||
|
||
.auth-info-section {
|
||
background: var(--bg-secondary);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.auth-info-content {
|
||
max-width: 100%;
|
||
}
|
||
|
||
.auth-info-section pre {
|
||
margin: 0;
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
|
||
}
|
||
|
||
.auth-info-section code {
|
||
font-family: inherit;
|
||
font-size: inherit;
|
||
}
|
||
|
||
.auth-info-header {
|
||
padding: 4px 0;
|
||
}
|
||
|
||
.auth-info-header:hover {
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.auth-info-arrow {
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.auth-info-body {
|
||
animation: slideDown 0.2s ease;
|
||
}
|
||
|
||
@keyframes slideDown {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.api-docs-content {
|
||
display: grid;
|
||
grid-template-columns: 280px 1fr;
|
||
gap: 24px;
|
||
}
|
||
|
||
.api-docs-sidebar {
|
||
background: var(--bg-secondary);
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
height: fit-content;
|
||
max-height: calc(100vh - 100px);
|
||
position: sticky;
|
||
top: 24px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.api-docs-sidebar h3 {
|
||
font-size: 0.875rem;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
margin-bottom: 12px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.api-group-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
}
|
||
|
||
.api-group-item {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.api-group-link {
|
||
display: block;
|
||
padding: 8px 12px;
|
||
color: var(--text-secondary);
|
||
text-decoration: none;
|
||
border-radius: 6px;
|
||
font-size: 0.875rem;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.api-group-link:hover {
|
||
background: var(--bg-tertiary);
|
||
color: var(--accent-color);
|
||
}
|
||
|
||
.api-group-link.active {
|
||
background: rgba(0, 102, 255, 0.1);
|
||
color: var(--accent-color);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.api-docs-main {
|
||
background: var(--bg-primary);
|
||
}
|
||
|
||
.api-endpoint {
|
||
background: var(--bg-primary);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 8px;
|
||
margin-bottom: 24px;
|
||
overflow: hidden;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.api-endpoint:hover {
|
||
box-shadow: var(--shadow-md);
|
||
}
|
||
|
||
.api-endpoint-header {
|
||
padding: 20px 24px;
|
||
background: var(--bg-secondary);
|
||
border-bottom: 1px solid var(--border-color);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.api-endpoint-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
flex: 1;
|
||
}
|
||
|
||
.api-method {
|
||
display: inline-block;
|
||
padding: 4px 12px;
|
||
border-radius: 4px;
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.api-method.post {
|
||
background: #49cc90;
|
||
color: white;
|
||
}
|
||
|
||
.api-method.get {
|
||
background: #61affe;
|
||
color: white;
|
||
}
|
||
|
||
.api-method.put {
|
||
background: #fca130;
|
||
color: white;
|
||
}
|
||
|
||
.api-method.delete {
|
||
background: #f93e3e;
|
||
color: white;
|
||
}
|
||
|
||
.api-path {
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||
font-size: 0.9375rem;
|
||
color: var(--text-primary);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.api-summary {
|
||
color: var(--text-secondary);
|
||
font-size: 0.875rem;
|
||
margin-left: 8px;
|
||
}
|
||
|
||
.api-endpoint-body {
|
||
padding: 24px;
|
||
}
|
||
|
||
.api-section {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.api-table-wrapper {
|
||
overflow-x: auto;
|
||
width: 100%;
|
||
-webkit-overflow-scrolling: touch;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* 确保表格在移动设备上也能正常显示 */
|
||
@media (max-width: 768px) {
|
||
.api-params-table {
|
||
font-size: 0.8125rem;
|
||
}
|
||
|
||
.api-params-table th,
|
||
.api-params-table td {
|
||
padding: 8px;
|
||
}
|
||
|
||
/* 移动设备上描述列可以更宽 */
|
||
.api-params-table th:nth-child(3),
|
||
.api-params-table td:nth-child(3) {
|
||
min-width: 150px;
|
||
}
|
||
}
|
||
|
||
.api-section:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.api-section-title {
|
||
font-size: 0.875rem;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
margin-bottom: 12px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.api-description {
|
||
color: var(--text-secondary);
|
||
font-size: 0.9375rem;
|
||
line-height: 1.6;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.api-description strong {
|
||
color: var(--text-primary);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.api-description code {
|
||
background: var(--bg-secondary);
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||
font-size: 0.875em;
|
||
color: var(--accent-color);
|
||
}
|
||
|
||
.api-description pre {
|
||
background: var(--bg-secondary);
|
||
padding: 12px;
|
||
border-radius: 6px;
|
||
overflow-x: auto;
|
||
margin: 8px 0;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.api-description pre code {
|
||
background: transparent;
|
||
padding: 0;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.api-description ul {
|
||
margin: 8px 0;
|
||
padding-left: 24px;
|
||
list-style-type: disc;
|
||
}
|
||
|
||
.api-description li {
|
||
margin: 4px 0;
|
||
}
|
||
|
||
/* Markdown样式 */
|
||
.api-description-detail .md-h1 {
|
||
font-size: 1.5rem;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
margin: 16px 0 12px 0;
|
||
padding-bottom: 8px;
|
||
border-bottom: 2px solid var(--border-color);
|
||
}
|
||
|
||
.api-description-detail .md-h2 {
|
||
font-size: 1.25rem;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
margin: 14px 0 10px 0;
|
||
padding-bottom: 6px;
|
||
border-bottom: 1px solid var(--border-color);
|
||
}
|
||
|
||
.api-description-detail .md-h3 {
|
||
font-size: 1.125rem;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
margin: 12px 0 8px 0;
|
||
}
|
||
|
||
.api-description-detail .md-paragraph {
|
||
margin: 8px 0;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.api-description-detail .md-list {
|
||
margin: 8px 0;
|
||
padding-left: 24px;
|
||
list-style-type: disc;
|
||
}
|
||
|
||
.api-description-detail .md-list ol {
|
||
list-style-type: decimal;
|
||
margin: 4px 0;
|
||
padding-left: 24px;
|
||
}
|
||
|
||
.api-description-detail .md-list-item {
|
||
margin: 4px 0;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.api-description-detail .md-link {
|
||
color: var(--accent-color);
|
||
text-decoration: none;
|
||
border-bottom: 1px solid transparent;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.api-description-detail .md-link:hover {
|
||
color: var(--accent-hover);
|
||
border-bottom-color: var(--accent-hover);
|
||
}
|
||
|
||
.api-description-detail .inline-code {
|
||
background: var(--bg-secondary);
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||
font-size: 0.875em;
|
||
color: var(--accent-color);
|
||
}
|
||
|
||
.api-description-detail .code-block {
|
||
background: var(--bg-secondary);
|
||
padding: 12px;
|
||
border-radius: 6px;
|
||
overflow-x: auto;
|
||
margin: 12px 0;
|
||
font-size: 0.875rem;
|
||
border: 1px solid var(--border-color);
|
||
}
|
||
|
||
.api-description-detail .code-block code {
|
||
background: transparent;
|
||
padding: 0;
|
||
color: var(--text-secondary);
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||
display: block;
|
||
white-space: pre;
|
||
}
|
||
|
||
.api-description-detail .code-lang {
|
||
display: block;
|
||
font-size: 0.75rem;
|
||
color: var(--text-muted);
|
||
margin-bottom: 8px;
|
||
text-transform: uppercase;
|
||
font-weight: 600;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.api-description-detail strong {
|
||
color: var(--text-primary);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.api-description-detail em {
|
||
font-style: italic;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.api-description-toggle {
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.description-toggle-btn {
|
||
background: none;
|
||
border: none;
|
||
color: var(--accent-color);
|
||
cursor: pointer;
|
||
padding: 6px 0;
|
||
font-size: 0.875rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
transition: all 0.2s ease;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.description-toggle-btn:hover {
|
||
color: var(--accent-hover);
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.description-toggle-icon {
|
||
transition: transform 0.2s ease;
|
||
}
|
||
|
||
.api-description-detail {
|
||
margin-top: 12px;
|
||
padding-top: 12px;
|
||
border-top: 1px solid var(--border-color);
|
||
animation: slideDown 0.2s ease;
|
||
}
|
||
|
||
@keyframes slideDown {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.api-params-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 0.875rem;
|
||
table-layout: fixed;
|
||
}
|
||
|
||
.api-params-table th,
|
||
.api-params-table td {
|
||
padding: 12px;
|
||
text-align: left;
|
||
border-bottom: 1px solid var(--border-color);
|
||
vertical-align: top;
|
||
}
|
||
|
||
/* 设置列宽 */
|
||
.api-params-table th:nth-child(1),
|
||
.api-params-table td:nth-child(1) {
|
||
width: 15%;
|
||
min-width: 120px;
|
||
}
|
||
|
||
.api-params-table th:nth-child(2),
|
||
.api-params-table td:nth-child(2) {
|
||
width: 12%;
|
||
min-width: 100px;
|
||
}
|
||
|
||
.api-params-table th:nth-child(3),
|
||
.api-params-table td:nth-child(3) {
|
||
width: 45%;
|
||
min-width: 200px;
|
||
}
|
||
|
||
.api-params-table th:nth-child(4),
|
||
.api-params-table td:nth-child(4) {
|
||
width: 10%;
|
||
min-width: 80px;
|
||
}
|
||
|
||
.api-params-table th:nth-child(5),
|
||
.api-params-table td:nth-child(5) {
|
||
width: 18%;
|
||
min-width: 150px;
|
||
}
|
||
|
||
/* 参数名、类型、必需列不换行 */
|
||
.api-params-table th:nth-child(1),
|
||
.api-params-table td:nth-child(1),
|
||
.api-params-table th:nth-child(2),
|
||
.api-params-table td:nth-child(2),
|
||
.api-params-table th:nth-child(4),
|
||
.api-params-table td:nth-child(4) {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
/* 示例列允许换行,完整显示 */
|
||
.api-params-table th:nth-child(5),
|
||
.api-params-table td:nth-child(5) {
|
||
white-space: normal;
|
||
word-wrap: break-word;
|
||
overflow-wrap: break-word;
|
||
overflow: visible;
|
||
}
|
||
|
||
/* 确保示例列中的code标签也能完整显示 */
|
||
.api-params-table td:nth-child(5) code {
|
||
white-space: normal;
|
||
word-wrap: break-word;
|
||
overflow-wrap: break-word;
|
||
display: inline-block;
|
||
max-width: 100%;
|
||
}
|
||
|
||
/* 描述列允许换行,但保持水平方向 */
|
||
.api-params-table th:nth-child(3),
|
||
.api-params-table td:nth-child(3) {
|
||
white-space: normal;
|
||
word-wrap: break-word;
|
||
word-break: normal;
|
||
overflow-wrap: break-word;
|
||
writing-mode: horizontal-tb !important;
|
||
direction: ltr !important;
|
||
text-align: left;
|
||
line-height: 1.6;
|
||
max-width: none;
|
||
}
|
||
|
||
/* 确保描述单元格内的内容正常显示 */
|
||
.api-params-table td:nth-child(3) * {
|
||
display: inline;
|
||
writing-mode: horizontal-tb !important;
|
||
}
|
||
|
||
.api-params-table th {
|
||
background: var(--bg-secondary);
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
writing-mode: horizontal-tb !important;
|
||
direction: ltr !important;
|
||
}
|
||
|
||
.api-params-table td {
|
||
color: var(--text-secondary);
|
||
writing-mode: horizontal-tb !important;
|
||
direction: ltr !important;
|
||
}
|
||
|
||
.api-param-name {
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||
color: var(--accent-color);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.api-param-type {
|
||
color: var(--text-muted);
|
||
font-size: 0.8125rem;
|
||
}
|
||
|
||
.api-param-required {
|
||
color: var(--error-color);
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.api-param-optional {
|
||
color: var(--text-muted);
|
||
font-size: 0.75rem;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.api-test-section {
|
||
margin-top: 24px;
|
||
padding-top: 24px;
|
||
border-top: 1px solid var(--border-color);
|
||
}
|
||
|
||
.api-test-form {
|
||
background: var(--bg-secondary);
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
}
|
||
|
||
.api-test-input-group {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.api-test-input-group label {
|
||
display: block;
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
color: var(--text-primary);
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.api-test-input-group input,
|
||
.api-test-input-group textarea {
|
||
width: 100%;
|
||
padding: 10px 12px;
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 6px;
|
||
font-size: 0.875rem;
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||
background: var(--bg-primary);
|
||
color: var(--text-primary);
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.api-test-input-group input:focus,
|
||
.api-test-input-group textarea:focus {
|
||
outline: none;
|
||
border-color: var(--accent-color);
|
||
box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
|
||
}
|
||
|
||
.api-test-input-group textarea {
|
||
min-height: 120px;
|
||
resize: vertical;
|
||
}
|
||
|
||
.api-test-buttons {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.api-test-btn {
|
||
padding: 10px 20px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.api-test-btn.primary {
|
||
background: var(--accent-color);
|
||
color: white;
|
||
}
|
||
|
||
.api-test-btn.primary:hover {
|
||
background: var(--accent-hover);
|
||
}
|
||
|
||
.api-test-btn.secondary {
|
||
background: var(--bg-tertiary);
|
||
color: var(--text-primary);
|
||
border: 1px solid var(--border-color);
|
||
}
|
||
|
||
.api-test-btn.secondary:hover {
|
||
background: var(--bg-secondary);
|
||
}
|
||
|
||
/* 复制curl按钮 - 黄色主题 */
|
||
.api-test-btn.copy-curl {
|
||
background: #ffc107;
|
||
color: white;
|
||
border: 1px solid #ffb300;
|
||
}
|
||
|
||
.api-test-btn.copy-curl:hover {
|
||
background: #ffb300;
|
||
border-color: #ffa000;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 2px 8px rgba(255, 193, 7, 0.3);
|
||
}
|
||
|
||
.api-test-btn.copy-curl:active {
|
||
transform: translateY(0);
|
||
box-shadow: 0 1px 4px rgba(255, 193, 7, 0.2);
|
||
}
|
||
|
||
/* 清除结果按钮 - 红色/橙色主题 */
|
||
.api-test-btn.clear-result {
|
||
background: #ff6b6b;
|
||
color: white;
|
||
border: 1px solid #ff5252;
|
||
}
|
||
|
||
.api-test-btn.clear-result:hover {
|
||
background: #ff5252;
|
||
border-color: #ff4444;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 2px 8px rgba(255, 107, 107, 0.3);
|
||
}
|
||
|
||
.api-test-btn.clear-result:active {
|
||
transform: translateY(0);
|
||
box-shadow: 0 1px 4px rgba(255, 107, 107, 0.2);
|
||
}
|
||
|
||
.api-test-result {
|
||
margin-top: 20px;
|
||
padding: 16px;
|
||
border-radius: 8px;
|
||
font-size: 0.875rem;
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.api-test-result.success {
|
||
background: #d4edda;
|
||
color: #155724;
|
||
border: 1px solid #c3e6cb;
|
||
}
|
||
|
||
.api-test-result.error {
|
||
background: #f8d7da;
|
||
color: #721c24;
|
||
border: 1px solid #f5c6cb;
|
||
}
|
||
|
||
.api-test-result.loading {
|
||
background: var(--bg-secondary);
|
||
color: var(--text-secondary);
|
||
border: 1px solid var(--border-color);
|
||
}
|
||
|
||
.api-response-example {
|
||
background: var(--bg-secondary);
|
||
border-radius: 6px;
|
||
padding: 16px;
|
||
margin-top: 12px;
|
||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||
font-size: 0.8125rem;
|
||
color: var(--text-secondary);
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.api-response-example pre {
|
||
margin: 0;
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.api-tag {
|
||
display: inline-block;
|
||
padding: 4px 8px;
|
||
background: var(--bg-tertiary);
|
||
color: var(--text-secondary);
|
||
border-radius: 4px;
|
||
font-size: 0.75rem;
|
||
margin-left: 8px;
|
||
}
|
||
|
||
.loading-spinner {
|
||
display: inline-block;
|
||
width: 16px;
|
||
height: 16px;
|
||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||
border-top-color: white;
|
||
border-radius: 50%;
|
||
animation: spin 0.6s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.empty-state svg {
|
||
width: 64px;
|
||
height: 64px;
|
||
margin-bottom: 16px;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.empty-state h3 {
|
||
font-size: 1.125rem;
|
||
margin-bottom: 8px;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.empty-state p {
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
html[data-theme="dark"] body {
|
||
background: var(--bg-secondary);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-docs-container {
|
||
background: var(--bg-secondary);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-docs-sidebar,
|
||
html[data-theme="dark"] .auth-info-section,
|
||
html[data-theme="dark"] .api-endpoint,
|
||
html[data-theme="dark"] .api-endpoint-header,
|
||
html[data-theme="dark"] .api-test-form,
|
||
html[data-theme="dark"] .api-response-example,
|
||
html[data-theme="dark"] .api-description pre,
|
||
html[data-theme="dark"] .api-description-detail .code-block,
|
||
html[data-theme="dark"] .api-description-detail .inline-code,
|
||
html[data-theme="dark"] .api-description code {
|
||
background: #111827;
|
||
border-color: #263244;
|
||
}
|
||
|
||
html[data-theme="dark"] .api-endpoint:hover {
|
||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.38);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-group-link:hover {
|
||
background: rgba(96, 165, 250, 0.1);
|
||
color: var(--accent-hover);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-group-link.active {
|
||
background: rgba(96, 165, 250, 0.16);
|
||
color: var(--accent-hover);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-params-table th {
|
||
background: #0f172a;
|
||
}
|
||
|
||
html[data-theme="dark"] .api-test-input-group input,
|
||
html[data-theme="dark"] .api-test-input-group textarea {
|
||
background: #0f172a;
|
||
border-color: #2b374b;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-test-input-group input:focus,
|
||
html[data-theme="dark"] .api-test-input-group textarea:focus {
|
||
box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.16);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-test-result.success {
|
||
background: rgba(52, 211, 153, 0.14);
|
||
color: #6ee7b7;
|
||
border-color: rgba(52, 211, 153, 0.28);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-test-result.error {
|
||
background: rgba(248, 113, 113, 0.14);
|
||
color: #fca5a5;
|
||
border-color: rgba(248, 113, 113, 0.28);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-test-btn.secondary {
|
||
background: #111827;
|
||
border-color: #2b374b;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-test-btn.secondary:hover {
|
||
background: #1f2937;
|
||
border-color: var(--accent-color);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-docs-header-actions .theme-toggle-btn,
|
||
html[data-theme="dark"] .api-docs-header-actions .lang-switcher-btn {
|
||
background: #111827;
|
||
border-color: #2b374b;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
html[data-theme="dark"] .api-docs-header-actions .theme-toggle-btn:hover,
|
||
html[data-theme="dark"] .api-docs-header-actions .lang-switcher-btn:hover {
|
||
background: #1f2937;
|
||
border-color: var(--accent-color);
|
||
}
|
||
|
||
html[data-theme="dark"] #token-status {
|
||
background: rgba(96, 165, 250, 0.12) !important;
|
||
border-left-color: var(--accent-color) !important;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.api-docs-header {
|
||
padding-right: 0;
|
||
padding-bottom: 72px;
|
||
}
|
||
|
||
.api-docs-header-actions {
|
||
top: auto;
|
||
bottom: 16px;
|
||
left: 0;
|
||
right: 0;
|
||
justify-content: flex-end;
|
||
}
|
||
}
|
||
</style>
|
||
<!-- 移动端样式层:只覆盖 767.98px 以下,写在页内样式之后才能同特异性胜出 -->
|
||
<link rel="stylesheet" href="/static/css/api-docs-mobile.css?v=20261006-1">
|
||
</head>
|
||
<body>
|
||
<div class="api-docs-container">
|
||
<div class="api-docs-header">
|
||
<h1>
|
||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<polyline points="14 2 14 8 20 8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<line x1="16" y1="13" x2="8" y2="13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
<line x1="16" y1="17" x2="8" y2="17" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span data-i18n="apiDocs.title">API 文档</span>
|
||
</h1>
|
||
<p data-i18n="apiDocs.subtitle">CyberStrikeAI 平台 API 接口文档,支持在线测试</p>
|
||
<div class="api-docs-header-actions">
|
||
<button id="theme-toggle-btn" class="theme-toggle-btn btn-secondary" type="button" onclick="window.cycleThemePreference && window.cycleThemePreference()" data-i18n="theme.toggle" data-i18n-attr="title" title="切换主题" aria-label="切换主题">
|
||
<svg class="theme-toggle-icon theme-toggle-icon--system" width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
|
||
<rect x="3" y="4" width="18" height="13" rx="2" stroke="currentColor" stroke-width="2"/>
|
||
<path d="M8 21h8M12 17v4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
<svg class="theme-toggle-icon theme-toggle-icon--light" width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="4" stroke="currentColor" stroke-width="2"/>
|
||
<path d="M12 2v2M12 20v2M4 12H2M22 12h-2M5 5l1.5 1.5M17.5 17.5L19 19M19 5l-1.5 1.5M6.5 17.5L5 19" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||
</svg>
|
||
<svg class="theme-toggle-icon theme-toggle-icon--dark" width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M21 14.5A8.5 8.5 0 0 1 9.5 3a7 7 0 1 0 11.5 11.5z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||
</svg>
|
||
<span id="theme-toggle-label">跟随系统</span>
|
||
</button>
|
||
<div class="lang-switcher">
|
||
<button type="button" class="btn-secondary lang-switcher-btn" onclick="typeof toggleLangDropdown === 'function' && toggleLangDropdown()" title="界面语言">
|
||
<span class="lang-switcher-icon">🌐</span>
|
||
<span id="current-lang-label">中文</span>
|
||
</button>
|
||
<div id="lang-dropdown" class="lang-dropdown" style="display: none;">
|
||
<div class="lang-option" data-lang="zh-CN" onclick="typeof onLanguageSelect === 'function' && onLanguageSelect('zh-CN')">中文</div>
|
||
<div class="lang-option" data-lang="en-US" onclick="typeof onLanguageSelect === 'function' && onLanguageSelect('en-US')">English</div>
|
||
<div class="lang-option" data-lang="ru-RU" onclick="typeof onLanguageSelect === 'function' && onLanguageSelect('ru-RU')">Русский</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="auth-info-section" class="auth-info-section" style="display: none;">
|
||
<div class="auth-info-content">
|
||
<div class="auth-info-header" onclick="toggleAuthInfo()" style="display: flex; align-items: center; justify-content: space-between; cursor: pointer; user-select: none;">
|
||
<div style="display: flex; align-items: center; gap: 8px;">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
||
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||
</svg>
|
||
<h3 style="margin: 0; font-size: 1rem; font-weight: 600; color: var(--text-primary);" data-i18n="apiDocs.authTitle">API 认证说明</h3>
|
||
</div>
|
||
<svg id="auth-info-arrow" class="auth-info-arrow" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="transition: transform 0.2s ease;">
|
||
<polyline points="6 9 12 15 18 9"/>
|
||
</svg>
|
||
</div>
|
||
<div id="auth-info-body" class="auth-info-body" style="display: none; color: var(--text-secondary); font-size: 0.875rem; line-height: 1.6; margin-top: 16px;">
|
||
<p style="margin: 0 0 12px 0;"><strong data-i18n="apiDocs.authAllNeedToken">所有 API 接口都需要 Token 认证。</strong></p>
|
||
<div style="background: var(--bg-secondary); padding: 12px; border-radius: 6px; margin-bottom: 12px;">
|
||
<p style="margin: 0 0 8px 0; font-weight: 500;" data-i18n="apiDocs.authGetToken">1. 获取 Token:</p>
|
||
<p style="margin: 0 0 8px 0;" data-i18n="apiDocs.authGetTokenDesc">在前端页面登录后,Token 会自动保存。您也可以通过以下方式获取:</p>
|
||
<pre style="background: var(--bg-primary); padding: 8px; border-radius: 4px; margin: 8px 0; overflow-x: auto; font-size: 0.8125rem;"><code>POST /api/auth/login
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"password": "your_password"
|
||
}
|
||
|
||
响应: {
|
||
"token": "eyJhbGc...",
|
||
"expires_at": "2026-01-27T...",
|
||
"session_duration_hr": 24
|
||
}</code></pre>
|
||
</div>
|
||
<div style="background: var(--bg-secondary); padding: 12px; border-radius: 6px; margin-bottom: 12px;">
|
||
<p style="margin: 0 0 8px 0; font-weight: 500;" data-i18n="apiDocs.authUseToken">2. 使用 Token:</p>
|
||
<p style="margin: 0 0 8px 0;" data-i18n="apiDocs.authUseTokenDesc">在请求头中添加 Authorization 字段:</p>
|
||
<pre style="background: var(--bg-primary); padding: 8px; border-radius: 4px; margin: 8px 0; overflow-x: auto; font-size: 0.8125rem;"><code>Authorization: Bearer your_token_here</code></pre>
|
||
<p style="margin: 8px 0 0 0; font-size: 0.8125rem; color: var(--text-muted);" data-i18n="apiDocs.authTip">💡 提示:本页面会自动使用您已登录的 Token,无需手动填写。</p>
|
||
</div>
|
||
<div id="token-status" style="display: none; background: rgba(0, 102, 255, 0.1); padding: 8px 12px; border-radius: 6px; border-left: 3px solid var(--accent-color);">
|
||
<p style="margin: 0; font-size: 0.8125rem; color: var(--accent-color);" data-i18n="apiDocs.tokenDetected">
|
||
<strong>✓ 已检测到 Token</strong> - 您可以直接测试 API 接口
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function toggleAuthInfo() {
|
||
const body = document.getElementById('auth-info-body');
|
||
const arrow = document.getElementById('auth-info-arrow');
|
||
if (body && arrow) {
|
||
const isExpanded = body.style.display !== 'none';
|
||
body.style.display = isExpanded ? 'none' : 'block';
|
||
arrow.style.transform = isExpanded ? 'rotate(0deg)' : 'rotate(180deg)';
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<div class="api-docs-content">
|
||
<div class="api-docs-sidebar">
|
||
<h3 data-i18n="apiDocs.sidebarGroupTitle">API 分组</h3>
|
||
<ul class="api-group-list" id="api-group-list">
|
||
<li class="api-group-item">
|
||
<a href="#" class="api-group-link active" data-group="all" data-i18n="apiDocs.allApis">全部接口</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="api-docs-main" id="api-docs-main">
|
||
<div class="empty-state">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<circle cx="12" cy="12" r="10"/>
|
||
<line x1="12" y1="8" x2="12" y2="12"/>
|
||
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
||
</svg>
|
||
<h3 data-i18n="apiDocs.loading">加载中...</h3>
|
||
<p data-i18n="apiDocs.loadingDesc">正在加载 API 文档</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/static/vendor/i18next.min.js"></script>
|
||
<script src="/static/js/i18n.js"></script>
|
||
<script src="/static/js/api-docs.js"></script>
|
||
</body>
|
||
</html>
|