output.html 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <title>工序可视化 · 共 1 条</title>
  7. <style>
  8. * { box-sizing: border-box; margin: 0; padding: 0; }
  9. html { scroll-behavior: smooth; }
  10. body {
  11. font-family: -apple-system, BlinkMacSystemFont, "PingFang SC",
  12. "Microsoft YaHei", "Segoe UI", sans-serif;
  13. background: #f6f7f9;
  14. color: #18181b;
  15. line-height: 1.55;
  16. padding: 0;
  17. }
  18. /* 容器:所有 section / header 共享同一宽度,左右边界整齐对齐 */
  19. .container { width: 100%; margin: 0 auto; }
  20. /* ========= 整页布局:左侧 sidebar + 右侧 detail ========= */
  21. .app-layout {
  22. display: grid;
  23. grid-template-columns: 320px minmax(0, 1fr);
  24. gap: 18px;
  25. align-items: start;
  26. padding: 18px 18px 64px 18px;
  27. }
  28. .sidebar {
  29. background: #fff;
  30. border: 1px solid #e4e4e7;
  31. border-radius: 10px;
  32. position: sticky;
  33. top: 18px;
  34. max-height: calc(100vh - 36px);
  35. display: flex;
  36. flex-direction: column;
  37. overflow: hidden;
  38. }
  39. .sidebar-head {
  40. display: flex; justify-content: space-between; align-items: center;
  41. gap: 8px;
  42. padding: 12px 14px;
  43. border-bottom: 1px solid #e4e4e7;
  44. }
  45. .sidebar-title {
  46. font-size: 13px; font-weight: 700; color: #18181b;
  47. letter-spacing: 0.04em; text-transform: uppercase;
  48. flex: 1; min-width: 0;
  49. white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  50. }
  51. .sidebar-count {
  52. font-size: 12px; color: #71717a;
  53. font-family: "SF Mono", "Menlo", "Consolas", monospace;
  54. white-space: nowrap;
  55. }
  56. .sidebar-toggle {
  57. flex: 0 0 auto;
  58. width: 26px; height: 26px;
  59. border-radius: 5px;
  60. background: transparent;
  61. border: 1px solid #e4e4e7;
  62. color: #52525b;
  63. cursor: pointer;
  64. display: inline-flex; align-items: center; justify-content: center;
  65. padding: 0;
  66. transition: background 0.15s, color 0.15s, transform 0.2s ease;
  67. }
  68. .sidebar-toggle:hover { background: #f4f4f5; color: #18181b; }
  69. .sidebar-toggle:active { transform: scale(0.94); }
  70. .sidebar-toggle svg { width: 14px; height: 14px; display: block; }
  71. /* ========= 折叠态 ========= */
  72. .app-layout.sidebar-collapsed {
  73. grid-template-columns: 84px minmax(0, 1fr);
  74. }
  75. .app-layout.sidebar-collapsed .sidebar-title,
  76. .app-layout.sidebar-collapsed .sidebar-count {
  77. display: none;
  78. }
  79. .app-layout.sidebar-collapsed .sidebar-head {
  80. justify-content: center;
  81. padding: 10px 8px;
  82. }
  83. .app-layout.sidebar-collapsed .sidebar-toggle {
  84. transform: rotate(180deg); /* 箭头反向 → 指向"展开" */
  85. }
  86. .app-layout.sidebar-collapsed .sidebar-toggle:active {
  87. transform: rotate(180deg) scale(0.94);
  88. }
  89. .app-layout.sidebar-collapsed .sidebar-list {
  90. padding: 6px;
  91. }
  92. .app-layout.sidebar-collapsed .sidebar-item {
  93. justify-content: center;
  94. padding: 5px;
  95. gap: 0;
  96. }
  97. .app-layout.sidebar-collapsed .sidebar-item .meta {
  98. display: none;
  99. }
  100. .app-layout.sidebar-collapsed .sidebar-item .cover {
  101. flex: 0 0 60px;
  102. width: 60px; height: 60px;
  103. }
  104. .sidebar-list {
  105. overflow-y: auto;
  106. padding: 8px;
  107. flex: 1;
  108. scrollbar-width: thin;
  109. scrollbar-color: #d4d4d8 transparent;
  110. }
  111. .sidebar-list::-webkit-scrollbar { width: 8px; }
  112. .sidebar-list::-webkit-scrollbar-thumb { background: #d4d4d8; border-radius: 4px; }
  113. .sidebar-list::-webkit-scrollbar-thumb:hover { background: #a1a1aa; }
  114. .sidebar-item {
  115. display: flex;
  116. gap: 10px;
  117. padding: 9px;
  118. border-radius: 8px;
  119. cursor: pointer;
  120. text-decoration: none;
  121. color: inherit;
  122. align-items: flex-start;
  123. transition: background 0.15s, border-color 0.15s;
  124. border: 1px solid transparent;
  125. margin-bottom: 4px;
  126. }
  127. .sidebar-item:hover { background: #f4f4f5; }
  128. .sidebar-item.active {
  129. background: #eef2ff;
  130. border-color: #c7d2fe;
  131. }
  132. .sidebar-item .cover {
  133. flex: 0 0 56px;
  134. width: 56px; height: 56px;
  135. border-radius: 6px;
  136. overflow: hidden;
  137. background: #f4f4f5;
  138. border: 1px solid #e4e4e7;
  139. display: flex; align-items: center; justify-content: center;
  140. color: #a1a1aa;
  141. font-size: 11px;
  142. }
  143. .sidebar-item .cover img {
  144. width: 100%; height: 100%; object-fit: cover; display: block;
  145. }
  146. .sidebar-item .meta {
  147. flex: 1; min-width: 0;
  148. display: flex; flex-direction: column; gap: 4px;
  149. }
  150. .sidebar-item .item-title {
  151. font-size: 13px;
  152. font-weight: 500;
  153. line-height: 1.4;
  154. color: #18181b;
  155. display: -webkit-box;
  156. -webkit-line-clamp: 2;
  157. -webkit-box-orient: vertical;
  158. overflow: hidden;
  159. word-break: break-word;
  160. }
  161. .sidebar-item.active .item-title { color: #3730a3; font-weight: 600; }
  162. .sidebar-item .item-meta {
  163. font-size: 11px;
  164. color: #71717a;
  165. display: flex; gap: 6px; align-items: center;
  166. }
  167. .sidebar-item .item-status {
  168. padding: 1px 7px;
  169. border-radius: 4px;
  170. font-weight: 600;
  171. font-size: 10px;
  172. }
  173. .sidebar-item .item-steps {
  174. font-family: "SF Mono", "Menlo", "Consolas", monospace;
  175. }
  176. .detail {
  177. min-width: 0; /* 让里面的横向滚动条正确生效 */
  178. }
  179. .detail .workflow-panel + .workflow-panel { margin-top: 0; }
  180. .detail .workflow-panel[hidden] { display: none; }
  181. @media (max-width: 960px) {
  182. .app-layout {
  183. grid-template-columns: 1fr;
  184. }
  185. .sidebar {
  186. position: static;
  187. max-height: 280px;
  188. }
  189. }
  190. /* 内部长正文需要可读宽度,避免占满整个表格的横向区域 */
  191. .body-text { max-width: 1100px; }
  192. .mono { font-family: "SF Mono", "Menlo", "Consolas", monospace; font-size: 0.9em; }
  193. .dim { color: #71717a; }
  194. /* 顶部 —— 左 padding 与下方 .card 的内边距对齐,
  195. * 让 h1 文字与各卡片标题("原始内容" / "完整工序序列" / "工序摘要")左对齐 */
  196. .page-header { margin-bottom: 22px; padding: 0 28px; }
  197. .page-header h1 {
  198. font-size: 24px; font-weight: 700; color: #18181b; margin-bottom: 6px;
  199. letter-spacing: -0.01em;
  200. }
  201. .page-header .meta {
  202. display: flex; gap: 14px; font-size: 13px; align-items: center; flex-wrap: wrap;
  203. }
  204. .status {
  205. padding: 3px 12px; border-radius: 999px;
  206. font-weight: 600; font-size: 12px;
  207. }
  208. .status-finalized { background: #d1fae5; color: #065f46; }
  209. .status-in_progress { background: #fef3c7; color: #92400e; }
  210. /* 卡片 */
  211. .card {
  212. background: #fff;
  213. border: 1px solid #e4e4e7;
  214. border-radius: 10px;
  215. padding: 20px 24px;
  216. margin-bottom: 18px;
  217. }
  218. .card h2 {
  219. font-size: 12px; font-weight: 700;
  220. color: #71717a; margin-bottom: 14px;
  221. letter-spacing: 0.08em; text-transform: uppercase;
  222. }
  223. /* 原始内容 */
  224. .source-title { font-size: 16px; font-weight: 500; line-height: 1.5; color: #18181b; }
  225. .source-body { margin-top: 10px; }
  226. .source-body summary {
  227. cursor: pointer; color: #71717a; font-size: 13px; padding: 4px 0; user-select: none;
  228. }
  229. .source-body summary:hover { color: #2563eb; }
  230. .body-text {
  231. white-space: pre-wrap; background: #f4f4f5; padding: 14px; border-radius: 6px;
  232. font-size: 13px; margin-top: 8px; max-height: 320px; overflow-y: auto;
  233. color: #3f3f46; line-height: 1.7;
  234. }
  235. .image-strip {
  236. display: flex;
  237. flex-wrap: nowrap; /* 强制单行 */
  238. gap: 8px;
  239. margin-top: 14px;
  240. overflow-x: auto; /* 单行 + 横向滚动 */
  241. overflow-y: hidden;
  242. padding-bottom: 8px; /* 给滚动条留空 */
  243. scrollbar-width: thin;
  244. scrollbar-color: #d4d4d8 transparent;
  245. }
  246. .image-strip::-webkit-scrollbar { height: 8px; }
  247. .image-strip::-webkit-scrollbar-track { background: transparent; }
  248. .image-strip::-webkit-scrollbar-thumb {
  249. background: #d4d4d8; border-radius: 4px;
  250. }
  251. .image-strip::-webkit-scrollbar-thumb:hover { background: #a1a1aa; }
  252. .image-strip .thumb {
  253. flex: 0 0 auto;
  254. position: relative;
  255. width: 96px; /* 3:4 → 96 × 128 */
  256. height: 128px;
  257. border-radius: 6px;
  258. overflow: hidden;
  259. border: 1px solid #e4e4e7;
  260. background: #f4f4f5;
  261. cursor: pointer;
  262. padding: 0;
  263. transition: transform 0.15s, box-shadow 0.15s, border-color 0.15s;
  264. }
  265. .image-strip .thumb:hover {
  266. transform: translateY(-2px);
  267. box-shadow: 0 4px 12px rgba(0,0,0,0.10);
  268. border-color: #a1a1aa;
  269. }
  270. .image-strip .thumb:focus-visible {
  271. outline: 2px solid #2563eb;
  272. outline-offset: 2px;
  273. }
  274. .image-strip .thumb img {
  275. width: 100%;
  276. height: 100%;
  277. object-fit: cover; /* 裁剪以适配 3:4 */
  278. display: block;
  279. }
  280. .thumb-label {
  281. position: absolute; bottom: 0; left: 0; right: 0;
  282. background: linear-gradient(transparent, rgba(0,0,0,0.65));
  283. color: #fff;
  284. font-size: 10.5px; padding: 8px 4px 3px; text-align: center;
  285. font-family: "SF Mono", "Menlo", "Consolas", monospace;
  286. pointer-events: none;
  287. }
  288. /* ========== Lightbox ========== */
  289. .lightbox {
  290. position: fixed; inset: 0;
  291. background: rgba(0,0,0,0.88);
  292. z-index: 1000;
  293. display: flex; align-items: center; justify-content: center;
  294. user-select: none;
  295. -webkit-tap-highlight-color: transparent;
  296. animation: lb-fade-in 0.18s ease-out;
  297. }
  298. .lightbox[hidden] { display: none; }
  299. @keyframes lb-fade-in {
  300. from { background: rgba(0,0,0,0); }
  301. to { background: rgba(0,0,0,0.88); }
  302. }
  303. .lb-stage {
  304. max-width: 92vw;
  305. max-height: 84vh;
  306. display: flex; align-items: center; justify-content: center;
  307. }
  308. .lb-stage img {
  309. max-width: 100%;
  310. max-height: 100%;
  311. display: block;
  312. object-fit: contain;
  313. border-radius: 4px;
  314. box-shadow: 0 12px 40px rgba(0,0,0,0.5);
  315. animation: lb-pop 0.2s ease-out;
  316. }
  317. @keyframes lb-pop {
  318. from { opacity: 0; transform: scale(0.97); }
  319. to { opacity: 1; transform: scale(1); }
  320. }
  321. .lb-btn {
  322. position: absolute;
  323. background: rgba(255,255,255,0.10);
  324. color: #fff;
  325. border: 1px solid rgba(255,255,255,0.18);
  326. cursor: pointer;
  327. font-family: -apple-system, sans-serif;
  328. line-height: 1;
  329. display: flex; align-items: center; justify-content: center;
  330. transition: background 0.15s, transform 0.1s;
  331. padding: 0;
  332. }
  333. .lb-btn:hover { background: rgba(255,255,255,0.22); }
  334. .lb-btn:active { transform: scale(0.96); }
  335. .lb-close {
  336. top: 18px; right: 18px;
  337. width: 42px; height: 42px;
  338. border-radius: 50%;
  339. font-size: 22px; font-weight: 300;
  340. }
  341. .lb-prev, .lb-next {
  342. top: 50%;
  343. transform: translateY(-50%);
  344. width: 50px; height: 80px;
  345. border-radius: 8px;
  346. font-size: 34px;
  347. font-weight: 200;
  348. }
  349. .lb-prev:hover, .lb-next:hover { transform: translateY(-50%) scale(1); }
  350. .lb-prev:active, .lb-next:active { transform: translateY(-50%) scale(0.96); }
  351. .lb-prev { left: 18px; }
  352. .lb-next { right: 18px; }
  353. .lb-footer {
  354. position: absolute; bottom: 18px; left: 0; right: 0;
  355. text-align: center;
  356. color: rgba(255,255,255,0.75);
  357. font-size: 13px;
  358. pointer-events: none;
  359. display: flex; flex-direction: column; gap: 4px;
  360. }
  361. #lb-counter {
  362. font-family: "SF Mono", "Menlo", "Consolas", monospace;
  363. font-weight: 600;
  364. color: #fff;
  365. }
  366. .lb-hint { font-size: 11px; color: rgba(255,255,255,0.5); }
  367. @media (max-width: 720px) {
  368. .lb-prev, .lb-next { width: 40px; height: 60px; font-size: 26px; }
  369. .lb-close { top: 12px; right: 12px; width: 36px; height: 36px; }
  370. .lb-hint { display: none; }
  371. }
  372. /* ========= 工序表 ========= */
  373. .workflow-card { padding: 0; overflow: hidden; }
  374. .workflow-card .card-head {
  375. padding: 16px 24px 12px;
  376. border-bottom: 1px solid #e4e4e7;
  377. }
  378. .workflow-card .card-head h2 { margin: 0; }
  379. .table-wrap { overflow-x: auto; }
  380. table.workflow {
  381. width: 100%;
  382. border-collapse: separate;
  383. border-spacing: 0;
  384. font-size: 13px;
  385. min-width: 1760px;
  386. background: #fff;
  387. }
  388. /* 表头:两层 */
  389. table.workflow thead th {
  390. font-weight: 600;
  391. text-align: center;
  392. vertical-align: middle;
  393. padding: 9px 10px;
  394. background: #1f2937;
  395. color: #f1f5f9;
  396. border-right: 1px solid #374151;
  397. border-bottom: 1px solid #374151;
  398. position: sticky; top: 0; z-index: 3;
  399. white-space: nowrap;
  400. font-size: 12.5px;
  401. letter-spacing: 0.02em;
  402. }
  403. table.workflow thead th:last-child { border-right: none; }
  404. table.workflow thead .head-l1 th.col-grp-in {
  405. background: #4338ca;
  406. color: #eef2ff;
  407. }
  408. table.workflow thead .head-l1 th.col-grp-out {
  409. background: #b45309;
  410. color: #fef3c7;
  411. }
  412. table.workflow thead .head-l2 th {
  413. top: 38px;
  414. background: #374151;
  415. font-weight: 500;
  416. font-size: 12px;
  417. }
  418. table.workflow thead .head-l2 th.col-sub-in { background: #4f46e5; color: #eef2ff; }
  419. table.workflow thead .head-l2 th.col-sub-out { background: #c2410c; color: #fff7ed; }
  420. /* 表体单元格 */
  421. table.workflow tbody td {
  422. border-right: 1px solid #e4e4e7;
  423. border-bottom: 1px solid #e4e4e7;
  424. padding: 10px 12px;
  425. vertical-align: top;
  426. background: #fff;
  427. word-break: break-word;
  428. font-size: 13px;
  429. line-height: 1.55;
  430. }
  431. table.workflow tbody td:last-child { border-right: none; }
  432. /* 步骤之间的清晰分割:每组的第一行加粗顶边(着色) */
  433. table.workflow tbody tr.step-row td {
  434. border-top: 3px solid var(--c);
  435. }
  436. table.workflow tbody tr.step-row-cont td {
  437. border-top: 1px dashed #e4e4e7;
  438. }
  439. table.workflow tbody tr:first-child.step-row td { border-top: none; }
  440. /* 步骤号单元格:彩色背景小徽章 */
  441. td.col-step-id {
  442. text-align: center;
  443. font-weight: 700;
  444. font-size: 18px;
  445. width: 56px;
  446. color: #fff;
  447. background: var(--c) !important;
  448. border-left: none;
  449. vertical-align: middle;
  450. }
  451. td.col-step-name {
  452. color: #18181b;
  453. min-width: 130px;
  454. max-width: 180px;
  455. }
  456. /* ========= 左侧 sticky 列:步骤id + 步骤名称 ========= */
  457. /* tbody:sticky 左侧固定 */
  458. table.workflow tbody td.col-step-id {
  459. position: sticky;
  460. left: 0;
  461. z-index: 2;
  462. }
  463. table.workflow tbody td.col-step-name {
  464. position: sticky;
  465. left: 56px; /* 与 col-step-id 的 width 对齐 */
  466. z-index: 2;
  467. background: #fff; /* 滚动时遮住底下内容 */
  468. /* 右侧投影,提示这是 sticky 列边界 */
  469. box-shadow: 4px 0 6px -4px rgba(0,0,0,0.10);
  470. }
  471. /* thead:sticky-left + sticky-top 角落(z-index 高于普通 sticky-top) */
  472. table.workflow thead .head-l1 th:nth-child(1) {
  473. left: 0;
  474. z-index: 4;
  475. }
  476. table.workflow thead .head-l1 th:nth-child(2) {
  477. left: 56px;
  478. z-index: 4;
  479. box-shadow: 4px 0 6px -4px rgba(0,0,0,0.18);
  480. }
  481. td.col-step-name .step-name-text {
  482. font-weight: 600;
  483. font-size: 13.5px;
  484. line-height: 1.4;
  485. }
  486. td.col-step-actions {
  487. min-width: 100px;
  488. max-width: 150px;
  489. vertical-align: top;
  490. font-size: 12px;
  491. }
  492. td.col-step-roles {
  493. min-width: 100px;
  494. max-width: 150px;
  495. vertical-align: top;
  496. font-size: 12px;
  497. }
  498. td.col-step-purposes {
  499. min-width: 110px;
  500. max-width: 170px;
  501. vertical-align: top;
  502. font-size: 12px;
  503. }
  504. td.col-step-source {
  505. color: #52525b;
  506. font-size: 12px;
  507. line-height: 1.55;
  508. min-width: 180px;
  509. max-width: 260px;
  510. background: #fafafa; /* 视觉上从主区分开(已移到最后一列) */
  511. }
  512. /* 输入列分组淡背景 */
  513. td.col-in-modality, td.col-in-category, td.col-in-src, td.col-in-value { background: #fafbfd; }
  514. td.col-in-modality {
  515. width: 60px;
  516. text-align: center;
  517. vertical-align: middle;
  518. padding: 6px 4px;
  519. }
  520. td.col-in-category {
  521. width: 110px;
  522. text-align: center;
  523. vertical-align: middle;
  524. padding: 6px 8px;
  525. font-size: 11.5px;
  526. color: #475569;
  527. }
  528. td.col-in-src {
  529. width: 130px;
  530. white-space: nowrap;
  531. }
  532. td.col-in-value {
  533. color: #18181b;
  534. min-width: 220px;
  535. }
  536. /* 工具列:tool_name(粗、产品名)+ tool_method(细、mono、参数) */
  537. td.col-tool {
  538. background: #f5f5fa;
  539. min-width: 160px;
  540. max-width: 220px;
  541. word-break: break-word;
  542. font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
  543. }
  544. td.col-tool .tool-name {
  545. font-size: 13px;
  546. font-weight: 600;
  547. color: #312e81;
  548. line-height: 1.35;
  549. }
  550. td.col-tool .tool-method {
  551. font-family: "SF Mono", "Menlo", "Consolas", monospace;
  552. font-size: 11px;
  553. color: #6366f1;
  554. line-height: 1.4;
  555. margin-top: 3px;
  556. padding-top: 3px;
  557. border-top: 1px dashed #ddd6fe;
  558. word-break: break-all;
  559. }
  560. td.col-tool .tool-name.empty .empty-val { font-weight: 400; }
  561. td.col-tool .tool-legacy {
  562. font-family: "SF Mono", "Menlo", "Consolas", monospace;
  563. font-size: 12px;
  564. color: #3730a3;
  565. word-break: break-all;
  566. }
  567. /* 输出列分组淡背景 */
  568. td.col-out-idx, td.col-out-modality, td.col-out-category, td.col-out-value { background: #fefaf5; }
  569. td.col-out-idx {
  570. width: 56px;
  571. text-align: center;
  572. vertical-align: middle;
  573. background: var(--bg) !important;
  574. }
  575. td.col-out-modality {
  576. width: 60px;
  577. text-align: center;
  578. vertical-align: middle;
  579. padding: 6px 4px;
  580. }
  581. td.col-out-category {
  582. width: 110px;
  583. text-align: center;
  584. vertical-align: middle;
  585. padding: 6px 8px;
  586. font-size: 11.5px;
  587. color: #475569;
  588. }
  589. td.col-out-value {
  590. color: #18181b;
  591. min-width: 220px;
  592. }
  593. /* ========== 数组字段渲染(actions / roles / purposes) ========== */
  594. .tag-list {
  595. display: flex; flex-wrap: wrap; gap: 4px;
  596. }
  597. .tag {
  598. display: inline-block;
  599. padding: 1px 7px;
  600. border-radius: 4px;
  601. font-size: 11px;
  602. font-weight: 500;
  603. line-height: 1.5;
  604. white-space: nowrap;
  605. }
  606. .tag-action {
  607. background: #e0e7ff;
  608. color: #3730a3;
  609. border: 1px solid #c7d2fe;
  610. }
  611. .tag-role {
  612. background: #ecfeff;
  613. color: #155e75;
  614. border: 1px solid #a5f3fc;
  615. }
  616. .tag-purpose {
  617. background: #f5f3ff;
  618. color: #6d28d9;
  619. border: 1px solid #ddd6fe;
  620. }
  621. /* 类型 pill(category 单元格用) */
  622. .category-pill {
  623. display: inline-block;
  624. padding: 2px 8px;
  625. border-radius: 4px;
  626. background: #f1f5f9;
  627. border: 1px solid #cbd5e1;
  628. color: #334155;
  629. font-size: 11px;
  630. font-weight: 500;
  631. line-height: 1.5;
  632. white-space: nowrap;
  633. max-width: 100%;
  634. overflow: hidden;
  635. text-overflow: ellipsis;
  636. }
  637. /* 类型图标(输入类型 / 输出类型 共用) */
  638. .type-icon {
  639. display: inline-flex;
  640. align-items: center;
  641. justify-content: center;
  642. width: 30px; height: 30px;
  643. border-radius: 7px;
  644. color: var(--c);
  645. background: var(--bg);
  646. border: 1px solid var(--bd);
  647. vertical-align: middle;
  648. }
  649. .type-icon svg { width: 17px; height: 17px; display: block; }
  650. .type-fallback {
  651. display: inline-block;
  652. font-size: 11px;
  653. color: #475569;
  654. padding: 2px 8px;
  655. border-radius: 4px;
  656. background: #f1f5f9;
  657. border: 1px solid #cbd5e1;
  658. font-weight: 500;
  659. max-width: 60px;
  660. overflow: hidden;
  661. text-overflow: ellipsis;
  662. white-space: nowrap;
  663. }
  664. .sr-only {
  665. position: absolute; width: 1px; height: 1px;
  666. padding: 0; margin: -1px; overflow: hidden;
  667. clip: rect(0,0,0,0); white-space: nowrap; border: 0;
  668. }
  669. /* 空单元格 */
  670. .empty-cell { background: #fafafa !important; }
  671. .empty-val { color: #d4d4d8; font-style: italic; font-size: 12px; }
  672. /* ========== Chip ========== */
  673. .chip {
  674. display: inline-flex; align-items: center; gap: 5px;
  675. border-radius: 5px;
  676. font-size: 11.5px; font-weight: 500;
  677. text-decoration: none;
  678. vertical-align: middle;
  679. line-height: 1.4;
  680. padding: 2px 6px 2px 8px;
  681. }
  682. .chip-tag { font-weight: 600; }
  683. .chip-idx {
  684. display: inline-flex; align-items: center; justify-content: center;
  685. font-size: 13px; line-height: 1;
  686. }
  687. .chip-type {
  688. display: inline-flex; align-items: center; justify-content: center;
  689. width: 16px; height: 16px;
  690. border-radius: 3px;
  691. background: rgba(255,255,255,0.6);
  692. margin-left: 1px;
  693. }
  694. .chip-type svg { width: 11px; height: 11px; display: block; }
  695. .ref-chip {
  696. background: var(--bg); color: var(--c);
  697. border: 1px solid var(--bd);
  698. cursor: pointer;
  699. transition: transform 0.1s, box-shadow 0.1s, outline 0.1s;
  700. outline: 2px solid transparent;
  701. }
  702. .ref-chip .chip-type { color: var(--c); }
  703. .ref-chip:hover {
  704. transform: translateY(-1px);
  705. box-shadow: 0 2px 6px rgba(0,0,0,0.10);
  706. }
  707. .ref-chip:hover .chip-tag { text-decoration: underline; }
  708. /* 兄弟联动高亮:同 step 内引用同一 source step 的 chip */
  709. .ref-chip.hl-sibling {
  710. outline: 2px solid var(--c);
  711. outline-offset: 1px;
  712. }
  713. .init-chip {
  714. background: #f4f4f5; color: #52525b;
  715. border: 1px solid #d4d4d8;
  716. }
  717. .orphan-chip {
  718. background: #fef2f2; color: #b91c1c;
  719. border: 1px solid #fecaca;
  720. }
  721. /* 输入区左侧"来源色条":把同 source step 的连续多行视觉拢起来 */
  722. td.col-in-type.has-src-stripe {
  723. position: relative;
  724. }
  725. td.col-in-type.has-src-stripe::before {
  726. content: "";
  727. position: absolute;
  728. top: 0; bottom: 0; left: 0;
  729. width: 3px;
  730. background: var(--src-c, transparent);
  731. opacity: 0.85;
  732. }
  733. /* 输出# 单元格里的彩色圈号 */
  734. .out-idx-badge {
  735. display: inline-flex; align-items: center; justify-content: center;
  736. font-size: 18px; line-height: 1;
  737. color: var(--c); font-weight: 700;
  738. }
  739. /* ========== 高亮 / 闪烁 ========== */
  740. td.hl-target {
  741. outline: 2px solid var(--c);
  742. outline-offset: -2px;
  743. background: var(--bg) !important;
  744. }
  745. @keyframes cell-flash {
  746. 0%, 30% {
  747. outline: 3px solid var(--c);
  748. outline-offset: -3px;
  749. background: var(--bg);
  750. }
  751. 100% { outline: 2px solid transparent; }
  752. }
  753. td.hl-flash {
  754. animation: cell-flash 1.6s ease-out;
  755. }
  756. /* 摘要 */
  757. .summary-card p { color: #3f3f46; line-height: 1.75; font-size: 13.5px; }
  758. </style>
  759. </head>
  760. <body>
  761. <div class="app-layout">
  762. <aside class="sidebar">
  763. <div class="sidebar-head">
  764. <div class="sidebar-title">工序列表</div>
  765. <div class="sidebar-count">1 条</div>
  766. <button type="button" class="sidebar-toggle" aria-label="收起/展开工序列表"
  767. title="收起/展开工序列表">
  768. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
  769. stroke-linecap="round" stroke-linejoin="round">
  770. <polyline points="15 18 9 12 15 6"></polyline>
  771. </svg>
  772. </button>
  773. </div>
  774. <div class="sidebar-list">
  775. <a class="sidebar-item active" href="#panel-6ee74f95d4aa2c810d32d6cf0f833806" data-wid="6ee74f95d4aa2c810d32d6cf0f833806" title="33万播放!豆包+Seedance 2.0做AI短剧,保姆级教程来了 · 6 步 · in_progress"><span class="cover"><img src="https://res.cybertogether.net/crawler/image/fb6599951a28.jpg" alt="封面" loading="lazy" referrerpolicy="no-referrer" /></span><span class="meta"><span class="item-title">33万播放!豆包+Seedance 2.0做AI短剧,保姆级教程来了</span><span class="item-meta"><span class="item-status status-in_progress">in_progress</span><span class="item-steps">6 步</span></span></span></a>
  776. </div>
  777. </aside>
  778. <main class="detail">
  779. <section class="workflow-panel" data-wid="6ee74f95d4aa2c810d32d6cf0f833806" id="panel-6ee74f95d4aa2c810d32d6cf0f833806">
  780. <header class="page-header">
  781. <h1>33万播放!豆包+Seedance 2.0做AI短剧,保姆级教程来了</h1>
  782. <div class="meta">
  783. <span class="mono dim">6ee74f95d4aa2c810d32d6cf0f833806</span>
  784. <span class="status status-in_progress">in_progress</span>
  785. <span class="dim">共 6 步</span>
  786. </div>
  787. </header>
  788. <section class="card source-card">
  789. <h2>原始内容</h2>
  790. <details class="source-body">
  791. <summary>正文(2779 字)— 点击展开</summary>
  792. <pre class="body-text">Seedance 2.0 出来后,在 AI 短剧、AI 漫剧的玩法,带来了很多新玩法。
  793. 很多二创的短视频火了,但是涉及到版权,后面限制了 IP 和真人图片视了。
  794. 那么还能不能玩?
  795. 行者一直是用 AI 生成的 虚拟人物在做视频的,虽然也会遇到审核问题,但是链条还是通的。
  796. 前段时间做了两个短剧小视频,数据还可以。
  797. 真假千金视频号 21 万播放(抖音11万),退婚那条 12 万播放,完播率都在 40-50%。
  798. 评论区有不少看官表示15秒看不过瘾,在催更,也有不少人希望行者出一期教程。
  799. 今天这篇文章,就是把我这套豆包+Seedance 2.0 做 AI 短剧的完整流程。
  800. Seedance2.0真人照片生成限制
  801. 首先分享下 seedance2.0 绕开真人照片生成视频限制的方法:
  802. 1.把你的真人照片,用AI重新生成一遍,可以修改衣服,换背景,AI的图片比较好过审
  803. 2.使用APP的创建分身,适用真人出境
  804. 3.把你的图片转成手绘电影分镜插画风格,也可以做成三视图,AI更好识别特征
  805. 4.群友Chiphen 分享的技巧,用校色A B C来代替
  806. 豆包+Seedance 2.0
  807. 豆包在节前上线了Seedance 2.0(Fast模型)。
  808. 选豆包的一个原因是,Seedance 2.0是很强,但是缺点是只能生成15秒视频,如果从完整的短剧角度来说。
  809. 还需要剧本、分集、人物设定。
  810. 豆包刚好派上用场了。
  811. 它能帮你从生成完整的短剧剧本、分集大纲、人物设定,甚至连每一集的台词都给你写好。
  812. 关键是——
  813. 豆包 AI 是免费使用的,
  814. 每天有免费生成额度。
  815. 对于想入局 AI 短剧但预算有限的朋友来说,这就是最佳组合。
  816. 第一步:找爆款题材
  817. 互联网已经多次印证了,曝过的还会曝。
  818. 之前行者也自己调试了几个剧本,结果肝了一晚上的视频,完全没人看。
  819. 所以逻辑是,去找已经曝过的题材,然后让AI来优化。
  820. 我曝的两条 10W+的视频,一条是霸总题材,一条是真假千金题材。
  821. 都是抖音、快手上已经验证过的爆款类型。
  822. 第二步:用豆包生成完整剧本
  823. 行者拿真假千金这个视频来示范。
  824. 我们使用豆包中,根据这个题材,生成一个完整的剧本。
  825. 提炼出这个剧情:豪门恩怨,真假千金梗,极致反转,撕绿茶爽文,高定礼服,美艳御姐风。
  826. 再豆包AI中,输入我们的剧本创作思路:
  827. 剧本类型:都市爱情
  828. 总集数:15 集
  829. 目标受众:通用(可以选男频或女频)
  830. 点击生成,豆包很快就生成了剧本大纲以及每一集的剧情。
  831. 接着再让豆包生成每一集的分镜说明、景别、镜头、人物动作和台词。
  832. 剧本质量直接决定了后面视频的效果。
  833. 如果豆包生成的剧本有些地方不满意,可以让它重新生成某一集,或者手动修改调整。
  834. 确认没问题后,把剧本下载下来,准备进入下一步。
  835. 第三步:设计核心人物形象
  836. 剧本有了,接下来最重要的是
  837. 人物形象设计
  838. 短剧能不能吸引人,人物颜值和气质占了一半。
  839. 我还是用豆包生成人物设定:
  840. 人物设定有了,接下来就是
  841. 生成人物形象图
  842. 苏晚提示词:
  843. 一张极具时尚感的高清摄影作品。画面中心是一位气质高雅的年轻亚洲女性,留着精致的深色复古卷发,面带温柔自信的微笑。她身着一件洁白的丝绸改良式晚礼服,设计融合了旗袍元素,领口微低,裙身点缀着精美的金色花卉刺绣和精细的蕾丝边。她优雅地提起宽大且带有细密百褶的裙摆,展现出修长的线条和精致的白色刺绣高跟鞋。背景是深红棕色的活动背景墙,上面印有明显的白色品牌标志。脚下是鲜艳的红地毯。光影明亮且柔和,具有专业的人像摄影质感,整体色彩以纯净的白、沉稳的棕和热情的红为主,展现出奢华、高贵且现代的时尚气息。
  844. 晏辰提
  845. 示词:
  846. 一张精致的高端时尚人像摄影,镜头中央是一位英俊的年轻亚洲男性,留着蓬松且略显凌乱的黑发,眼神清冷而深邃。他穿着一套极具质感的黑色三件套西装,包括缎面领口的黑色西服、同色马甲和挺括的白色衬衫,系着黑色领带,胸袋中露出一截精致的花纹口袋巾。背景是黄昏时分的都市街景,深蓝色的天幕下,街道上的汽车尾灯和路灯形成梦幻的虚化圆斑(bokeh)。整体色调偏向冷色系,光线柔和地勾勒出男子的面部轮廓,营造出一种优雅、忧郁且富有电影感的氛围。
  847. 这一步可以用你习惯的工具:豆包、Seedream 4.5、MJ V7、Nano Banana Pro 都行。
  848. 我会给每个核心角色生成 2-3 张不同角度、不同表情的参考图,后面做视频时直接用。
  849. 第四步:生成分镜视频
  850. 剧本和角色都完成了,接下来就开始生成视频了。
  851. Seedance 2.0 的人物参考功能和分镜控制能力,能高度还原人物和场景的一致性。
  852. 操作步骤:
  853. 打开豆包 APP(网页版只能上传一张参考图,APP 可以上传多张)
  854. 选择 Seedance 2.0 功能
  855. 上传人物参考图
  856. 输入这一集的剧情提示词(从豆包生成的剧本里复制)
  857. 点击生成,等待几分钟。
  858. 假千金装可怜、真千金霸气打脸、最后女王加冕,整个剧情流畅自然,口型完美对上,音效也恰到好处。
  859. 当然,生成过程中也会遇到生成失败的情况,看下提示词有没有违规。
  860. 第五步:剪辑合成
  861. 单个 15 秒的视频确实有点短,看得不够过瘾。可以把多个15秒的视频拼接起来。
  862. 把视频导入到剪映中,调整速度,剪掉一些镜头,添加字幕。
  863. 我踩过的坑和经验总结
  864. 行者在关注AI 短剧一段时间了,我也踩过不少坑。这里分享几个关键经验:
  865. 1.剧本一定要有“钩子”
  866. 每一集结尾必须留悬念,让观众想看下一集。比如:“就在这时,一个神秘男人出现了……”“她不知道的是,更大的阴谋正在等着她……”
  867. 2.人物形象要稳定
  868. 同一个角色,在不同视频里,脸型、发型、穿搭风格要保持一致。
  869. 3.完播率比播放量更重要
  870. 我那两条 10W+的视频,完播率都在 40-50%。平台会根据完播率推荐你的视频。所以宁可做短一点,也要保证观众看完。
  871. 4.多测试,多迭代
  872. 不要指望一次就爆。我做了十几条视频,才摸索出这套玩法。多测试不同题材、不同风格,看数据反馈,不断优化。
  873. 5.合规很重要
  874. 不要用真人 IP 二创,不要用明星脸,不要涉及敏感话题。用 AI 生成的虚拟人物,相对安全很多。
  875. 最后说两句
  876. AI 短剧这条赛道,现在还处于红利期。
  877. 制作门槛确实槛高,想爆也不容易。
  878. Seedance 2.0出来后,在内容创作方面,是提效很多,流程跑通了,一天做几集小短片完全没问题。
  879. 豆包+Seedance 2.0 这套组合,我已经验证过了,好用还免费。
  880. 当然,工具只是辅助,核心还是
  881. 内容
  882. 你得懂什么题材受欢迎,什么剧情能抓住观众,什么节奏能提高完播率。
  883. 这些都需要你去实战、去测试、去总结。
  884. 我把我的经验都写在这篇文章里了。接下来,就看你的了。
  885. #豆包
  886.   
  887. #AI视频
  888.   
  889. #
  890. Seedance
  891.  
  892. #AI短剧
  893.  
  894. #AI教程</pre>
  895. </details>
  896. <div class="image-strip">
  897. <button type="button" class="thumb" data-idx="0" aria-label="查看图1(大图)"><img src="https://res.cybertogether.net/crawler/image/fb6599951a28.jpg" alt="图1" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图1</span></button>
  898. <button type="button" class="thumb" data-idx="1" aria-label="查看图2(大图)"><img src="https://res.cybertogether.net/crawler/image/342672a2f5e9.jpg" alt="图2" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图2</span></button>
  899. <button type="button" class="thumb" data-idx="2" aria-label="查看图3(大图)"><img src="https://res.cybertogether.net/crawler/image/cb4a01876352.jpg" alt="图3" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图3</span></button>
  900. <button type="button" class="thumb" data-idx="3" aria-label="查看图4(大图)"><img src="https://res.cybertogether.net/crawler/image/a7759682adca.jpg" alt="图4" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图4</span></button>
  901. <button type="button" class="thumb" data-idx="4" aria-label="查看图5(大图)"><img src="https://res.cybertogether.net/crawler/image/1f3a4c926956.jpg" alt="图5" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图5</span></button>
  902. <button type="button" class="thumb" data-idx="5" aria-label="查看图6(大图)"><img src="https://res.cybertogether.net/crawler/image/303c46445415.jpg" alt="图6" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图6</span></button>
  903. <button type="button" class="thumb" data-idx="6" aria-label="查看图7(大图)"><img src="https://res.cybertogether.net/crawler/image/ad26d612237c.jpg" alt="图7" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图7</span></button>
  904. <button type="button" class="thumb" data-idx="7" aria-label="查看图8(大图)"><img src="https://res.cybertogether.net/crawler/image/09f95b406eec.jpg" alt="图8" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图8</span></button>
  905. <button type="button" class="thumb" data-idx="8" aria-label="查看图9(大图)"><img src="https://res.cybertogether.net/crawler/image/fbbe2fd07e40.jpg" alt="图9" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图9</span></button>
  906. <button type="button" class="thumb" data-idx="9" aria-label="查看图10(大图)"><img src="https://res.cybertogether.net/crawler/image/c5c1fae9591a.jpg" alt="图10" loading="lazy" referrerpolicy="no-referrer" /><span class="thumb-label">图10</span></button>
  907. </div>
  908. </section>
  909. <section class="card workflow-card">
  910. <div class="card-head"><h2>完整工序序列</h2></div>
  911. <div class="table-wrap">
  912. <table class="workflow">
  913. <thead>
  914. <tr class="head-l1">
  915. <th rowspan="2">步骤id</th>
  916. <th rowspan="2">步骤名称</th>
  917. <th rowspan="2">动作</th>
  918. <th rowspan="2">作用</th>
  919. <th rowspan="2">目的</th>
  920. <th colspan="4" class="col-grp-in">步骤输入</th>
  921. <th rowspan="2">工具</th>
  922. <th colspan="4" class="col-grp-out">步骤输出</th>
  923. <th rowspan="2">步骤来源</th>
  924. </tr>
  925. <tr class="head-l2">
  926. <th class="col-sub-in">输入模态</th>
  927. <th class="col-sub-in">输入类型</th>
  928. <th class="col-sub-in">输入来源</th>
  929. <th class="col-sub-in">输入value</th>
  930. <th class="col-sub-out">#</th>
  931. <th class="col-sub-out">输出模态</th>
  932. <th class="col-sub-out">输出类型</th>
  933. <th class="col-sub-out">输出value</th>
  934. </tr>
  935. </thead>
  936. <tbody>
  937. <tr class="step-row" style="--c: #4f46e5; --bg: #eef2ff;"><td class="col-step-id" rowspan="1" style="--c: #4f46e5; --bg: #eef2ff">1</td><td class="col-step-name" rowspan="1"><div class="step-name-text">分析爆款题材并生成剧本大纲</div></td><td class="col-step-actions" rowspan="1"><div class="tag-list"><span class="tag tag-action">分析</span><span class="tag tag-action">生成</span></div></td><td class="col-step-roles" rowspan="1"><div class="tag-list"><span class="tag tag-role">参考收集</span><span class="tag tag-role">输入准备转大纲生成阶段</span></div></td><td class="col-step-purposes" rowspan="1"><div class="tag-list"><span class="tag tag-purpose">完整性</span></div></td><td class="col-in-modality has-src-stripe" style="--src-c: #9ca3af" data-src-key="init"><span class="type-icon" style="--c: #475569; --bg: #f1f5f9; --bd: #cbd5e1;" title="文本" role="img" aria-label="文本"><span class="sr-only">文本</span><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M4 5v3.2h6V19h4V8.2h6V5z"/></svg></span></td><td class="col-in-category"><span class="category-pill">脚本创作思路</span></td><td class="col-in-src"><span class="chip init-chip" data-source-init="1" title="初始输入 #1"><span class="chip-tag">初始</span><span class="chip-idx">①</span></span></td><td class="col-in-value">剧本类型:都市爱情;总集数:15 集;核心故事:真假千金梗等内容。</td><td class="col-tool" rowspan="1"><div class="tool-name">豆包</div><div class="tool-method">帮我写作</div></td><td class="col-out-idx" id="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-1-out-1" style="--c: #4f46e5; --bg: #eef2ff; --bd: #c7d2fe;"><span class="out-idx-badge">①</span></td><td class="col-out-modality"><span class="type-icon" style="--c: #475569; --bg: #f1f5f9; --bd: #cbd5e1;" title="文本" role="img" aria-label="文本"><span class="sr-only">文本</span><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M4 5v3.2h6V19h4V8.2h6V5z"/></svg></span></td><td class="col-out-category"><span class="category-pill">剧本大纲</span></td><td class="col-out-value">包含 15 集节奏的剧本大纲。</td><td class="col-step-source" rowspan="1">图5 / 正文“第一步”、“第二步”内容</td></tr>
  938. <tr class="step-row" style="--c: #0d9488; --bg: #f0fdfa;"><td class="col-step-id" rowspan="1" style="--c: #0d9488; --bg: #f0fdfa">2</td><td class="col-step-name" rowspan="1"><div class="step-name-text">细化生成分集脚本与分镜说明</div></td><td class="col-step-actions" rowspan="1"><div class="tag-list"><span class="tag tag-action">生成</span></div></td><td class="col-step-roles" rowspan="1"><div class="tag-list"><span class="tag tag-role">分镜脚本生成阶段</span></div></td><td class="col-step-purposes" rowspan="1"><div class="tag-list"><span class="tag tag-purpose">详细度</span></div></td><td class="col-in-modality has-src-stripe" style="--src-c: #4f46e5" data-src-key="1"><span class="type-icon" style="--c: #475569; --bg: #f1f5f9; --bd: #cbd5e1;" title="文本" role="img" aria-label="文本"><span class="sr-only">文本</span><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M4 5v3.2h6V19h4V8.2h6V5z"/></svg></span></td><td class="col-in-category"><span class="category-pill">剧本大纲</span></td><td class="col-in-src"><a class="chip ref-chip" href="#wf-6ee74f95d4aa2c810d32d6cf0f833806-step-1-out-1" data-target="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-1-out-1" data-source-step="1" style="--c: #4f46e5; --bg: #eef2ff; --bd: #c7d2fe;" title="步骤1「分析爆款题材并生成剧本大纲」· 第 ① 输出(未知类型)
  939. 包含 15 集节奏的剧本大纲。"><span class="chip-tag">步骤1</span></a></td><td class="col-in-value">15 集剧本大纲。</td><td class="col-tool" rowspan="1"><div class="tool-name">豆包</div><div class="tool-method">帮我写作</div></td><td class="col-out-idx" id="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-2-out-1" style="--c: #0d9488; --bg: #f0fdfa; --bd: #99f6e4;"><span class="out-idx-badge">①</span></td><td class="col-out-modality"><span class="type-icon" style="--c: #475569; --bg: #f1f5f9; --bd: #cbd5e1;" title="文本" role="img" aria-label="文本"><span class="sr-only">文本</span><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M4 5v3.2h6V19h4V8.2h6V5z"/></svg></span></td><td class="col-out-category"><span class="category-pill">分镜脚本</span></td><td class="col-out-value">每一集的分镜说明、景别、镜头、人物动作和台词。</td><td class="col-step-source" rowspan="1">图6 / 正文“第二步”结尾内容</td></tr>
  940. <tr class="step-row" style="--c: #e11d48; --bg: #fff1f2;"><td class="col-step-id" rowspan="1" style="--c: #e11d48; --bg: #fff1f2">3</td><td class="col-step-name" rowspan="1"><div class="step-name-text">基于剧本设定核心人物背景特征</div></td><td class="col-step-actions" rowspan="1"><div class="tag-list"><span class="tag tag-action">提取</span><span class="tag tag-action">生成</span></div></td><td class="col-step-roles" rowspan="1"><div class="tag-list"><span class="tag tag-role">人物设定阶段</span></div></td><td class="col-step-purposes" rowspan="1"><div class="tag-list"><span class="tag tag-purpose">角色一致性</span></div></td><td class="col-in-modality has-src-stripe" style="--src-c: #0d9488" data-src-key="2"><span class="type-icon" style="--c: #475569; --bg: #f1f5f9; --bd: #cbd5e1;" title="文本" role="img" aria-label="文本"><span class="sr-only">文本</span><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M4 5v3.2h6V19h4V8.2h6V5z"/></svg></span></td><td class="col-in-category"><span class="category-pill">分镜脚本</span></td><td class="col-in-src"><a class="chip ref-chip" href="#wf-6ee74f95d4aa2c810d32d6cf0f833806-step-2-out-1" data-target="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-2-out-1" data-source-step="2" style="--c: #0d9488; --bg: #f0fdfa; --bd: #99f6e4;" title="步骤2「细化生成分集脚本与分镜说明」· 第 ① 输出(未知类型)
  941. 每一集的分镜说明、景别、镜头、人物动作和台词。"><span class="chip-tag">步骤2</span></a></td><td class="col-in-value">包含人物动作和台词的脚本。</td><td class="col-tool" rowspan="1"><div class="tool-name">豆包</div><div class="tool-method">帮我写作</div></td><td class="col-out-idx" id="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-3-out-1" style="--c: #e11d48; --bg: #fff1f2; --bd: #fecdd3;"><span class="out-idx-badge">①</span></td><td class="col-out-modality"><span class="type-icon" style="--c: #475569; --bg: #f1f5f9; --bd: #cbd5e1;" title="文本" role="img" aria-label="文本"><span class="sr-only">文本</span><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M4 5v3.2h6V19h4V8.2h6V5z"/></svg></span></td><td class="col-out-category"><span class="category-pill">人物描述</span></td><td class="col-out-value">苏晚、顾晏辰等核心人物的身份、人设、核心特征。</td><td class="col-step-source" rowspan="1">图7 / 正文“第三步”开头内容</td></tr>
  942. <tr class="step-row" style="--c: #7c3aed; --bg: #f5f3ff;"><td class="col-step-id" rowspan="1" style="--c: #7c3aed; --bg: #f5f3ff">4</td><td class="col-step-name" rowspan="1"><div class="step-name-text">根据设定生成核心角色参考图集</div></td><td class="col-step-actions" rowspan="1"><div class="tag-list"><span class="tag tag-action">生成业务图集</span></div></td><td class="col-step-roles" rowspan="1"><div class="tag-list"><span class="tag tag-role">参考图集生成阶段</span></div></td><td class="col-step-purposes" rowspan="1"><div class="tag-list"><span class="tag tag-purpose">角色一致性</span></div></td><td class="col-in-modality has-src-stripe" style="--src-c: #9ca3af" data-src-key="init"><span class="type-icon" style="--c: #475569; --bg: #f1f5f9; --bd: #cbd5e1;" title="文本" role="img" aria-label="文本"><span class="sr-only">文本</span><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M4 5v3.2h6V19h4V8.2h6V5z"/></svg></span></td><td class="col-in-category"><span class="category-pill">prompt</span></td><td class="col-in-src"><span class="chip init-chip" data-source-init="2" title="初始输入 #2"><span class="chip-tag">初始</span><span class="chip-idx">②</span></span></td><td class="col-in-value">苏晚、顾晏辰等角色的详细高清人像提示词。</td><td class="col-tool" rowspan="1"><div class="tool-name">豆包</div><div class="tool-method">图像生成</div></td><td class="col-out-idx" id="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-4-out-1" style="--c: #7c3aed; --bg: #f5f3ff; --bd: #ddd6fe;"><span class="out-idx-badge">①</span></td><td class="col-out-modality"><span class="type-icon" style="--c: #047857; --bg: #ecfdf5; --bd: #a7f3d0;" title="图片" role="img" aria-label="图片"><span class="sr-only">图片</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="8.5" cy="9.5" r="1.5"/><path d="M21 16l-5-5-9 9"/></svg></span></td><td class="col-out-category"><span class="category-pill">人物参考图</span></td><td class="col-out-value">包含苏晚、顾晏辰、林薇薇等角色的多角度表情参考图集。</td><td class="col-step-source" rowspan="1">图8 / 正文“第三步”角色提示词内容</td></tr>
  943. <tr class="step-row" style="--c: #0284c7; --bg: #f0f9ff;"><td class="col-step-id" rowspan="2" style="--c: #0284c7; --bg: #f0f9ff">5</td><td class="col-step-name" rowspan="2"><div class="step-name-text">输入脚本与参考图生成分镜视频</div></td><td class="col-step-actions" rowspan="2"><div class="tag-list"><span class="tag tag-action">合成视频</span></div></td><td class="col-step-roles" rowspan="2"><div class="tag-list"><span class="tag tag-role">分镜视频生成阶段</span></div></td><td class="col-step-purposes" rowspan="2"><div class="tag-list"><span class="tag tag-purpose">角色一致性</span><span class="tag tag-purpose">细节还原度</span></div></td><td class="col-in-modality has-src-stripe" style="--src-c: #7c3aed" data-src-key="4"><span class="type-icon" style="--c: #047857; --bg: #ecfdf5; --bd: #a7f3d0;" title="图片" role="img" aria-label="图片"><span class="sr-only">图片</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="8.5" cy="9.5" r="1.5"/><path d="M21 16l-5-5-9 9"/></svg></span></td><td class="col-in-category"><span class="category-pill">人物参考图</span></td><td class="col-in-src"><a class="chip ref-chip" href="#wf-6ee74f95d4aa2c810d32d6cf0f833806-step-4-out-1" data-target="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-4-out-1" data-source-step="4" style="--c: #7c3aed; --bg: #f5f3ff; --bd: #ddd6fe;" title="步骤4「根据设定生成核心角色参考图集」· 第 ① 输出(未知类型)
  944. 包含苏晚、顾晏辰、林薇薇等角色的多角度表情参考图集。"><span class="chip-tag">步骤4</span></a></td><td class="col-in-value">已生成的核心角色形象参考图。</td><td class="col-tool" rowspan="2"><div class="tool-name">Seedance (豆包内)</div><div class="tool-method">2.0 Fast 模型</div></td><td class="col-out-idx" id="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-5-out-1" style="--c: #0284c7; --bg: #f0f9ff; --bd: #bae6fd;"><span class="out-idx-badge">①</span></td><td class="col-out-modality"><span class="type-icon" style="--c: #be123c; --bg: #fff1f2; --bd: #fecdd3;" title="视频" role="img" aria-label="视频"><span class="sr-only">视频</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="2"/><polygon points="10,9 10,15 16,12" fill="currentColor" stroke="none"/></svg></span></td><td class="col-out-category"><span class="category-pill">视频片段</span></td><td class="col-out-value">符合剧情设定的单集 15 秒短剧视频片段。</td><td class="col-step-source" rowspan="2">图9 / 正文“第四步”内容</td></tr>
  945. <tr class="step-row-cont" style="--c: #0284c7; --bg: #f0f9ff;"><td class="col-in-modality has-src-stripe" style="--src-c: #0d9488" data-src-key="2"><span class="type-icon" style="--c: #475569; --bg: #f1f5f9; --bd: #cbd5e1;" title="文本" role="img" aria-label="文本"><span class="sr-only">文本</span><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M4 5v3.2h6V19h4V8.2h6V5z"/></svg></span></td><td class="col-in-category"><span class="category-pill">分镜脚本</span></td><td class="col-in-src"><a class="chip ref-chip" href="#wf-6ee74f95d4aa2c810d32d6cf0f833806-step-2-out-1" data-target="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-2-out-1" data-source-step="2" style="--c: #0d9488; --bg: #f0fdfa; --bd: #99f6e4;" title="步骤2「细化生成分集脚本与分镜说明」· 第 ① 输出(未知类型)
  946. 每一集的分镜说明、景别、镜头、人物动作和台词。"><span class="chip-tag">步骤2</span></a></td><td class="col-in-value">对应分集的剧情、动作及对白口型指导提示词。</td><td class="col-out-idx empty-cell"></td><td class="col-out-modality empty-cell"></td><td class="col-out-category empty-cell"></td><td class="col-out-value empty-cell"></td></tr>
  947. <tr class="step-row" style="--c: #ca8a04; --bg: #fefce8;"><td class="col-step-id" rowspan="1" style="--c: #ca8a04; --bg: #fefce8">6</td><td class="col-step-name" rowspan="1"><div class="step-name-text">通过拼接与特效合成短剧成品视频</div></td><td class="col-step-actions" rowspan="1"><div class="tag-list"><span class="tag tag-action">拼接剪辑</span></div></td><td class="col-step-roles" rowspan="1"><div class="tag-list"><span class="tag tag-role">成品输出阶段</span></div></td><td class="col-step-purposes" rowspan="1"><div class="tag-list"><span class="tag tag-purpose">叙事完整</span></div></td><td class="col-in-modality has-src-stripe" style="--src-c: #0284c7" data-src-key="5"><span class="type-icon" style="--c: #be123c; --bg: #fff1f2; --bd: #fecdd3;" title="视频" role="img" aria-label="视频"><span class="sr-only">视频</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="2"/><polygon points="10,9 10,15 16,12" fill="currentColor" stroke="none"/></svg></span></td><td class="col-in-category"><span class="category-pill">视频片段</span></td><td class="col-in-src"><a class="chip ref-chip" href="#wf-6ee74f95d4aa2c810d32d6cf0f833806-step-5-out-1" data-target="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-5-out-1" data-source-step="5" style="--c: #0284c7; --bg: #f0f9ff; --bd: #bae6fd;" title="步骤5「输入脚本与参考图生成分镜视频」· 第 ① 输出(未知类型)
  948. 符合剧情设定的单集 15 秒短剧视频片段。"><span class="chip-tag">步骤5</span></a></td><td class="col-in-value">多段单集短剧视频片段。</td><td class="col-tool" rowspan="1"><div class="tool-name">剪映</div><div class="tool-method">剪辑工具</div></td><td class="col-out-idx" id="wf-6ee74f95d4aa2c810d32d6cf0f833806-step-6-out-1" style="--c: #ca8a04; --bg: #fefce8; --bd: #fde68a;"><span class="out-idx-badge">①</span></td><td class="col-out-modality"><span class="type-icon" style="--c: #be123c; --bg: #fff1f2; --bd: #fecdd3;" title="视频" role="img" aria-label="视频"><span class="sr-only">视频</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="2"/><polygon points="10,9 10,15 16,12" fill="currentColor" stroke="none"/></svg></span></td><td class="col-out-category"><span class="category-pill">视频成品</span></td><td class="col-out-value">包含字幕、音效、调速处理后的完整短剧视频。</td><td class="col-step-source" rowspan="1">图10 / 正文“第五步”内容</td></tr>
  949. </tbody>
  950. </table>
  951. </div>
  952. </section>
  953. <section class="card summary-card">
  954. <h2>工序摘要</h2>
  955. <p><span class="empty-val">—</span></p>
  956. </section>
  957. </section>
  958. </main>
  959. </div>
  960. <!-- Lightbox 大图查看器(全局唯一) -->
  961. <div id="lightbox" class="lightbox" hidden role="dialog" aria-modal="true" aria-label="图片查看">
  962. <button type="button" class="lb-btn lb-close" aria-label="关闭(Esc)">×</button>
  963. <button type="button" class="lb-btn lb-prev" aria-label="上一张(←)">‹</button>
  964. <button type="button" class="lb-btn lb-next" aria-label="下一张(→)">›</button>
  965. <div class="lb-stage">
  966. <img id="lb-img" alt="" referrerpolicy="no-referrer" />
  967. </div>
  968. <div class="lb-footer">
  969. <span id="lb-counter"></span>
  970. <span class="lb-hint">← → 切换 · Esc 关闭 · 移动端可左右滑动</span>
  971. </div>
  972. </div>
  973. <script type="application/json" class="image-data" data-wid="6ee74f95d4aa2c810d32d6cf0f833806">["https://res.cybertogether.net/crawler/image/fb6599951a28.jpg", "https://res.cybertogether.net/crawler/image/342672a2f5e9.jpg", "https://res.cybertogether.net/crawler/image/cb4a01876352.jpg", "https://res.cybertogether.net/crawler/image/a7759682adca.jpg", "https://res.cybertogether.net/crawler/image/1f3a4c926956.jpg", "https://res.cybertogether.net/crawler/image/303c46445415.jpg", "https://res.cybertogether.net/crawler/image/ad26d612237c.jpg", "https://res.cybertogether.net/crawler/image/09f95b406eec.jpg", "https://res.cybertogether.net/crawler/image/fbbe2fd07e40.jpg", "https://res.cybertogether.net/crawler/image/c5c1fae9591a.jpg"]</script>
  974. <script>
  975. (function () {
  976. /* ============================== Sidebar 折叠 ============================== */
  977. const layout = document.querySelector('.app-layout');
  978. const toggleBtn = document.querySelector('.sidebar-toggle');
  979. const COLLAPSE_KEY = 'decode_sidebar_collapsed';
  980. // 恢复上次的折叠状态
  981. try {
  982. if (localStorage.getItem(COLLAPSE_KEY) === '1' && layout) {
  983. layout.classList.add('sidebar-collapsed');
  984. }
  985. } catch (_) {}
  986. if (toggleBtn && layout) {
  987. toggleBtn.addEventListener('click', (e) => {
  988. e.preventDefault();
  989. e.stopPropagation();
  990. layout.classList.toggle('sidebar-collapsed');
  991. try {
  992. localStorage.setItem(
  993. COLLAPSE_KEY,
  994. layout.classList.contains('sidebar-collapsed') ? '1' : '0'
  995. );
  996. } catch (_) {}
  997. });
  998. }
  999. /* ============================== Sidebar / Panel 切换 ============================== */
  1000. const panels = Array.from(document.querySelectorAll('.workflow-panel'));
  1001. const items = Array.from(document.querySelectorAll('.sidebar-item'));
  1002. // 收集每个 panel 的图片数据(每个 panel 一份独立的 IMAGES)
  1003. const imagesByWid = {};
  1004. document.querySelectorAll('script.image-data').forEach((s) => {
  1005. try { imagesByWid[s.dataset.wid] = JSON.parse(s.textContent || '[]'); }
  1006. catch (e) { imagesByWid[s.dataset.wid] = []; }
  1007. });
  1008. let IMAGES = [];
  1009. function activate(wid) {
  1010. if (!wid) return;
  1011. panels.forEach((p) => {
  1012. if (p.dataset.wid === wid) p.removeAttribute('hidden');
  1013. else p.setAttribute('hidden', '');
  1014. });
  1015. items.forEach((it) => {
  1016. if (it.dataset.wid === wid) it.classList.add('active');
  1017. else it.classList.remove('active');
  1018. });
  1019. IMAGES = imagesByWid[wid] || [];
  1020. if (lightbox && !lightbox.hasAttribute('hidden')) lbCloseFn();
  1021. }
  1022. items.forEach((it) => {
  1023. it.addEventListener('click', (e) => {
  1024. e.preventDefault();
  1025. const wid = it.dataset.wid;
  1026. activate(wid);
  1027. // 更新 hash 不触发跳转
  1028. try { history.replaceState(null, '', '#panel-' + wid); } catch (_) {}
  1029. window.scrollTo({ top: 0 });
  1030. });
  1031. });
  1032. // 初始 active:URL hash 优先;否则用第一个 panel
  1033. (function initActive() {
  1034. let wid = null;
  1035. const m = /^#panel-(.+)$/.exec(window.location.hash || '');
  1036. if (m && Object.prototype.hasOwnProperty.call(imagesByWid, m[1])) wid = m[1];
  1037. if (!wid && panels.length) wid = panels[0].dataset.wid;
  1038. if (wid) activate(wid);
  1039. })();
  1040. /* ============================== Lightbox ============================== */
  1041. const lightbox = document.getElementById('lightbox');
  1042. const lbImg = document.getElementById('lb-img');
  1043. const lbCounter = document.getElementById('lb-counter');
  1044. const lbPrev = lightbox && lightbox.querySelector('.lb-prev');
  1045. const lbNext = lightbox && lightbox.querySelector('.lb-next');
  1046. const lbClose = lightbox && lightbox.querySelector('.lb-close');
  1047. let lbIndex = 0;
  1048. function lbOpen(idx) {
  1049. if (!lightbox || !IMAGES.length) return;
  1050. lbIndex = ((idx % IMAGES.length) + IMAGES.length) % IMAGES.length;
  1051. lbRender();
  1052. lightbox.removeAttribute('hidden');
  1053. document.body.style.overflow = 'hidden';
  1054. }
  1055. function lbCloseFn() {
  1056. if (!lightbox) return;
  1057. lightbox.setAttribute('hidden', '');
  1058. document.body.style.overflow = '';
  1059. lbImg.src = '';
  1060. }
  1061. function lbGo(delta) {
  1062. if (!IMAGES.length) return;
  1063. lbIndex = ((lbIndex + delta) % IMAGES.length + IMAGES.length) % IMAGES.length;
  1064. lbRender();
  1065. }
  1066. function lbRender() {
  1067. lbImg.src = IMAGES[lbIndex];
  1068. lbImg.alt = '图' + (lbIndex + 1);
  1069. lbCounter.textContent = (lbIndex + 1) + ' / ' + IMAGES.length;
  1070. [-1, 1].forEach((d) => {
  1071. const j = ((lbIndex + d) % IMAGES.length + IMAGES.length) % IMAGES.length;
  1072. const pre = new Image();
  1073. pre.referrerPolicy = 'no-referrer';
  1074. pre.src = IMAGES[j];
  1075. });
  1076. }
  1077. // 事件委托:sidebar 切换后新的 panel 里 thumb 也能响应
  1078. document.addEventListener('click', (e) => {
  1079. const t = e.target.closest('.image-strip .thumb');
  1080. if (!t) return;
  1081. const i = parseInt(t.dataset.idx, 10);
  1082. if (!Number.isNaN(i)) lbOpen(i);
  1083. });
  1084. if (lbPrev) lbPrev.addEventListener('click', (e) => { e.stopPropagation(); lbGo(-1); });
  1085. if (lbNext) lbNext.addEventListener('click', (e) => { e.stopPropagation(); lbGo(1); });
  1086. if (lbClose) lbClose.addEventListener('click', (e) => { e.stopPropagation(); lbCloseFn(); });
  1087. if (lightbox) {
  1088. lightbox.addEventListener('click', (e) => {
  1089. if (e.target === lightbox || e.target.classList.contains('lb-stage')) lbCloseFn();
  1090. });
  1091. }
  1092. // 键盘:Esc 关闭,← → 切换
  1093. document.addEventListener('keydown', (e) => {
  1094. if (!lightbox || lightbox.hasAttribute('hidden')) return;
  1095. if (e.key === 'Escape') { e.preventDefault(); lbCloseFn(); }
  1096. else if (e.key === 'ArrowLeft') { e.preventDefault(); lbGo(-1); }
  1097. else if (e.key === 'ArrowRight') { e.preventDefault(); lbGo(1); }
  1098. });
  1099. // 触屏左右滑动
  1100. let tStartX = 0, tStartY = 0, tTracking = false;
  1101. if (lightbox) {
  1102. lightbox.addEventListener('touchstart', (e) => {
  1103. if (e.touches.length !== 1) { tTracking = false; return; }
  1104. tStartX = e.touches[0].clientX;
  1105. tStartY = e.touches[0].clientY;
  1106. tTracking = true;
  1107. }, { passive: true });
  1108. lightbox.addEventListener('touchend', (e) => {
  1109. if (!tTracking || e.changedTouches.length !== 1) return;
  1110. const dx = e.changedTouches[0].clientX - tStartX;
  1111. const dy = e.changedTouches[0].clientY - tStartY;
  1112. if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy)) {
  1113. lbGo(dx < 0 ? 1 : -1);
  1114. }
  1115. tTracking = false;
  1116. }, { passive: true });
  1117. }
  1118. /* ============================== Chip 交互 ============================== */
  1119. const flashClass = 'hl-flash';
  1120. const targetClass = 'hl-target';
  1121. const siblingClass = 'hl-sibling';
  1122. function flash(el) {
  1123. el.classList.remove(flashClass);
  1124. void el.offsetWidth;
  1125. el.classList.add(flashClass);
  1126. }
  1127. /**
  1128. * 给定一个 chip,找出"当前 step 内"引用同一 source step 的兄弟 chip。
  1129. *
  1130. * 「当前 step」的定义:chip 所属那个 step-row + 紧随其后所有 step-row-cont
  1131. * 行(直到下一个 step-row)。我们用 DOM 遍历找出这组 tr。
  1132. */
  1133. function collectSiblingsInStep(chip) {
  1134. const srcStep = chip.dataset.sourceStep;
  1135. if (!srcStep) return [];
  1136. const tr = chip.closest('tr');
  1137. if (!tr) return [];
  1138. // 向上找到本 step 的 step-row(自己或前面的兄弟里第一个 .step-row)
  1139. let head = tr;
  1140. while (head && !head.classList.contains('step-row')) {
  1141. head = head.previousElementSibling;
  1142. }
  1143. if (!head) return [];
  1144. // 从 step-row 起,收集这组里所有 chip
  1145. const group = [head];
  1146. let n = head.nextElementSibling;
  1147. while (n && n.classList.contains('step-row-cont')) {
  1148. group.push(n);
  1149. n = n.nextElementSibling;
  1150. }
  1151. const sibs = [];
  1152. group.forEach((row) => {
  1153. row.querySelectorAll(`.ref-chip[data-source-step="${srcStep}"]`).forEach((c) => {
  1154. if (c !== chip) sibs.push(c);
  1155. });
  1156. });
  1157. return sibs;
  1158. }
  1159. document.querySelectorAll('.ref-chip[data-target]').forEach((chip) => {
  1160. const id = chip.dataset.target;
  1161. if (!id) return;
  1162. chip.addEventListener('mouseenter', () => {
  1163. const t = document.getElementById(id);
  1164. if (t) t.classList.add(targetClass);
  1165. // 兄弟联动:当前 step 内引用同一 source step 的所有 chip
  1166. collectSiblingsInStep(chip).forEach((s) => s.classList.add(siblingClass));
  1167. });
  1168. chip.addEventListener('mouseleave', () => {
  1169. const t = document.getElementById(id);
  1170. if (t) t.classList.remove(targetClass);
  1171. collectSiblingsInStep(chip).forEach((s) => s.classList.remove(siblingClass));
  1172. });
  1173. chip.addEventListener('click', (e) => {
  1174. e.preventDefault();
  1175. const t = document.getElementById(id);
  1176. if (!t) return;
  1177. t.scrollIntoView({ behavior: 'smooth', block: 'center' });
  1178. flash(t);
  1179. });
  1180. });
  1181. // 深链接:若 hash 形如 #wf-{wid}-step-N-out-M,则切到对应 panel 并滚到那个 cell
  1182. (function handleDeepHash() {
  1183. const hash = window.location.hash || '';
  1184. const m = /^#wf-(.+)-step-\d+-out-\d+$/.exec(hash);
  1185. if (!m) return;
  1186. const candidateWid = m[1];
  1187. if (!Object.prototype.hasOwnProperty.call(imagesByWid, candidateWid)) return;
  1188. activate(candidateWid);
  1189. setTimeout(() => {
  1190. const t = document.getElementById(hash.slice(1));
  1191. if (t) {
  1192. t.scrollIntoView({ behavior: 'smooth', block: 'center' });
  1193. flash(t);
  1194. }
  1195. }, 100);
  1196. })();
  1197. })();
  1198. </script>
  1199. </body>
  1200. </html>