index.html 73 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241
  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.0">
  6. <title>炘和数智智慧平台</title>
  7. <link rel="icon" href="/assets/logo.png">
  8. <link rel="stylesheet" href="/style.css">
  9. </head>
  10. <body>
  11. <!-- Splash Screen -->
  12. <div class="splash" id="splash">
  13. <img src="/assets/logo.png" alt="炘和数智智慧平台" class="splash-logo">
  14. <div class="splash-name">炘和数智智慧平台</div>
  15. <div class="splash-sub">让餐饮经营可视化</div>
  16. <div class="splash-bar"><span></span></div>
  17. <div class="splash-powered">
  18. <span>powered by</span>
  19. <img src="/assets/powered-by.png" alt="Hyreal IDE">
  20. </div>
  21. </div>
  22. <div class="app-frame">
  23. <aside class="sidebar">
  24. <div class="sidebar-brand">
  25. <div class="sidebar-logo"><img src="/assets/logo.png" alt="门"></div>
  26. <div class="sidebar-titles">
  27. <div class="t1">炘和数智智慧平台</div>
  28. <div class="t2">让餐饮经营可视化</div>
  29. </div>
  30. </div>
  31. <div class="nav-section-label">门店装修</div>
  32. <button class="nav-item active" data-view="crawl"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16M4 12h10M4 18h16"/></svg></span><span>门店采集</span></button>
  33. <button class="nav-item" data-view="gen"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l1.8 4.9L19 9.7l-4.3 3 1.2 5.3-3.9-3-3.9 3 1.2-5.3-4.3-3 5.2-1.8z"/></svg></span><span>视觉生成</span></button>
  34. <button class="nav-item" data-view="tasks"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h10M4 12h16M4 17h7"/><circle cx="18" cy="7" r="2"/><circle cx="15" cy="17" r="2"/></svg></span><span>任务中心</span></button>
  35. <button class="nav-item" data-view="history"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><circle cx="9" cy="10" r="1.8"/><path d="M21 16l-5-5-6 6"/></svg></span><span>历史创作</span></button>
  36. <div class="nav-section-label">经营分析</div>
  37. <button class="nav-item" data-view="reports"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12"/><path d="M7 10l5 5 5-5"/><path d="M4 21h16"/></svg></span><span>数据采集</span></button>
  38. <button class="nav-item" data-view="schools"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="10" r="3"/><path d="M12 21s7-6.2 7-11a7 7 0 1 0-14 0c0 4.8 7 11 7 11Z"/></svg></span><span>校区地图</span></button>
  39. <button class="nav-item" data-view="cockpit"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 13a9 9 0 0 1 18 0"/><path d="M12 13l4-4"/><circle cx="12" cy="13" r="1.6"/><path d="M3 17h18"/></svg></span><span>经营驾驶舱</span></button>
  40. <button class="nav-item" data-view="report"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3h7l4 4v14H7z"/><path d="M14 3v4h4"/><path d="M10 12h6M10 16h6"/></svg></span><span>经营报告</span></button>
  41. <button class="nav-item" data-view="ops"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20h16"/><path d="M7 20V9M12 20V4M17 20v-7"/></svg></span><span>店铺运营</span></button>
  42. <button class="nav-item" data-view="lake"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><ellipse cx="12" cy="5" rx="7" ry="2.5"/><path d="M5 5v6c0 1.4 3.1 2.5 7 2.5s7-1.1 7-2.5V5"/><path d="M5 11v6c0 1.4 3.1 2.5 7 2.5s7-1.1 7-2.5v-6"/></svg></span><span>数据中台</span></button>
  43. <button class="nav-item" data-view="analysis"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 17l5-6 4 3 5-8 4 5"/><path d="M3 21h18"/></svg></span><span>趋势图表</span><span class="nav-beta">Beta</span></button>
  44. <button class="nav-item" data-view="ask"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="8" width="16" height="11" rx="3"/><path d="M12 8V5"/><circle cx="12" cy="3.6" r="1.4"/><path d="M9.5 13.5h.01M14.5 13.5h.01"/><path d="M9.5 16.2h5"/></svg></span><span>智能问数</span><span class="nav-beta">Beta</span></button>
  45. <div class="nav-section-label">设置</div>
  46. <button class="nav-item" data-view="settings"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.87l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06A1.7 1.7 0 0 0 15 19.4a1.7 1.7 0 0 0-1 1.55V21a2 2 0 1 1-4 0v-.05A1.7 1.7 0 0 0 9 19.4a1.7 1.7 0 0 0-1.87.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-1.55-1H3a2 2 0 1 1 0-4h.05A1.7 1.7 0 0 0 4.6 9a1.7 1.7 0 0 0-.34-1.87l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.7 1.7 0 0 0 9 4.6h.09A1.7 1.7 0 0 0 10 3.05V3a2 2 0 1 1 4 0v.05A1.7 1.7 0 0 0 15 4.6a1.7 1.7 0 0 0 1.87-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.7 1.7 0 0 0 19.4 9v.09a1.7 1.7 0 0 0 1.55.91H21a2 2 0 1 1 0 4h-.05a1.7 1.7 0 0 0-1.55 1z"/></svg></span><span>系统设置</span></button>
  47. <button class="nav-item" data-view="logs"><span class="ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16v11H8l-4 4Z"/><path d="M8 9h8M8 12h5"/></svg></span><span>系统日志</span></button>
  48. <div class="sidebar-bottom">
  49. <span id="sidebarVersion">淘宝闪购 · v1.0.0</span>
  50. <img src="/assets/powered-by.png" alt="Hyreal IDE" class="sidebar-powered">
  51. </div>
  52. </aside>
  53. <div class="main-wrap">
  54. <!-- ==================== 门店采集 ==================== -->
  55. <div id="viewCrawl" class="view active">
  56. <main class="content">
  57. <div class="page-header">
  58. <div>
  59. <h1>门店采集</h1>
  60. <div class="sub">从淘宝闪购后台按店铺批量采集菜品图,用于后续视觉美化</div>
  61. </div>
  62. </div>
  63. <div class="crawl-grid">
  64. <div class="left-col">
  65. <div class="panel panel-narrow panel-designed">
  66. <div class="stage-marker"><span class="stage-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M4 17h16"/><circle cx="9" cy="7" r="2.2"/><circle cx="15" cy="17" r="2.2"/></svg></span><span>抓取设置</span></div>
  67. <span class="panel-watermark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z"/><path d="M12 8v4l3 2"/></svg></span>
  68. <div class="panel-body">
  69. <div class="field">
  70. <label>采集通道</label>
  71. <select id="crawlChannel">
  72. <option value="auto">自动 · 优先客户端,不可用回落 Cookie</option>
  73. <option value="client">仅客户端(淘宝闪购)</option>
  74. <option value="cookie">仅 Cookie</option>
  75. </select>
  76. <div class="panel-hint" id="crawlChannelHint">正在检测通道状态…</div>
  77. </div>
  78. <label style="display:flex;align-items:center;gap:6px;font-size:13px;cursor:pointer;">
  79. <input type="checkbox" id="forceFullCrawl">
  80. <span>强制全量抓取(覆盖已有图片)</span>
  81. </label>
  82. </div>
  83. </div>
  84. <button class="btn btn-primary btn-block" id="startBtn">
  85. <span id="startBtnIcon"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg></span>
  86. <span id="startBtnText">开始抓取</span>
  87. </button>
  88. <button class="btn btn-danger btn-block" id="stopBtn" disabled style="display:none;"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2"/></svg><span>停止</span></button>
  89. <div class="panel panel-fixed panel-progress">
  90. <div class="panel-title">
  91. <span>进度列表</span>
  92. <span class="actions"><span class="badge" id="crawlStatusBadge"></span></span>
  93. </div>
  94. <div id="progressList"></div>
  95. </div>
  96. </div>
  97. <div class="right-col">
  98. <div class="panel panel-fixed panel-shops">
  99. <div class="panel-title">
  100. <span>店铺清单</span>
  101. <span class="actions">
  102. <span class="badge badge-info" id="shopCount">0 家</span>
  103. <button class="btn btn-ghost btn-small" id="btnToggleAdd">+ 添加店铺</button>
  104. </span>
  105. </div>
  106. <div style="overflow-x:auto;">
  107. <table class="data-table">
  108. <thead>
  109. <tr>
  110. <th style="width:36px;"><input type="checkbox" id="selectAllCb"></th>
  111. <th style="min-width:220px;">店铺</th>
  112. <th style="width:100px;">shopId</th>
  113. <th style="width:100px;">状态</th>
  114. <th style="width:220px;">文件夹</th>
  115. </tr>
  116. </thead>
  117. <tbody id="shopTableBody"></tbody>
  118. </table>
  119. <div id="shopTableEmpty" class="empty-hint">
  120. <div class="empty-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5" y="4" width="14" height="17" rx="2"/><path d="M9 4a3 3 0 0 1 6 0"/><path d="M9 10h6M9 14h6M9 18h4"/></svg></div>
  121. <p>尚无店铺<br><small>点击右上角「+ 添加店铺」手动录入</small></p>
  122. </div>
  123. </div>
  124. </div>
  125. </div>
  126. </div>
  127. </main>
  128. </div>
  129. <!-- ==================== 视觉生成 ==================== -->
  130. <div id="viewGen" class="view">
  131. <main class="content">
  132. <div class="page-header">
  133. <div>
  134. <h1>视觉生成</h1>
  135. <div class="sub">基于已采集的菜品图片,批量生成 LOGO / 海报 / 招牌 / 贴纸 + 菜品图</div>
  136. </div>
  137. <div class="toolbar">
  138. <button class="btn btn-ghost btn-small" id="btnRefreshGen">
  139. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 12a8 8 0 1 1-2.3-5.6"/><path d="M20 4v5h-5"/></svg>
  140. 刷新
  141. </button>
  142. </div>
  143. </div>
  144. <div class="gen-overview">
  145. <div class="stats-grid" id="statsBar">
  146. <div class="metric accent"><span class="metric-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20V9l8-5 8 5v11"/><path d="M9 20v-6h6v6"/></svg></span><span class="metric-body"><span class="metric-label">店铺</span><span class="metric-value" id="statShops">—</span><span class="stat-delta" id="statShopsDelta"></span></span></div>
  147. <div class="metric ok"><span class="metric-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span><span class="metric-body"><span class="metric-label">已生成</span><span class="metric-value" id="statDone">—</span><span class="stat-delta" id="statDoneDelta"></span></span></div>
  148. <div class="metric warn"><span class="metric-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3h10M7 21h10"/><path d="M8 3c0 8 8 8 8 18M16 3c0 8-8 8-8 18"/></svg></span><span class="metric-body"><span class="metric-label">待处理</span><span class="metric-value" id="statPending">—</span><span class="stat-delta" id="statPendingDelta"></span></span></div>
  149. <div class="metric fail"><span class="metric-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v6M12 16.5v.5"/></svg></span><span class="metric-body"><span class="metric-label">失败</span><span class="metric-value" id="statFail">—</span><span class="stat-delta" id="statFailDelta"></span></span></div>
  150. </div>
  151. <div class="gen-controls">
  152. <div class="field">
  153. <label>扫描范围</label>
  154. <div class="scan-dropdown" id="scanDropdown">
  155. <button type="button" class="scan-dropdown-trigger" id="scanDropdownTrigger" aria-expanded="false" aria-haspopup="listbox">
  156. <span id="scanDropdownText">全量扫描</span>
  157. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
  158. </button>
  159. <div class="scan-dropdown-menu" id="scanDropdownMenu" role="listbox" aria-multiselectable="true" hidden></div>
  160. </div>
  161. </div>
  162. <button class="btn btn-primary" id="scanBtn">
  163. <span id="scanIcon"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg></span>
  164. <span id="scanText">扫描店铺</span>
  165. </button>
  166. </div>
  167. </div>
  168. <div class="shop-cards" id="shopList"></div>
  169. </main>
  170. </div>
  171. <!-- ==================== 历史创作 ==================== -->
  172. <div id="viewTasks" class="view">
  173. <main class="content">
  174. <div class="page-header">
  175. <div>
  176. <h1>任务中心</h1>
  177. <div class="sub">跟踪生成任务状态,服务重启后可恢复进行中的生成任务</div>
  178. </div>
  179. <div class="toolbar">
  180. <button class="btn btn-ghost btn-small" id="btnRefreshTasks">
  181. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 12a8 8 0 1 1-2.3-5.6"/><path d="M20 4v5h-5"/></svg>
  182. 刷新
  183. </button>
  184. </div>
  185. </div>
  186. <div class="tasks-overview panel panel-designed">
  187. <span class="panel-watermark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h10M4 12h16M4 17h7"/><circle cx="18" cy="7" r="2"/><circle cx="15" cy="17" r="2"/></svg></span>
  188. <div class="tasks-stats">
  189. <div class="task-stat"><span>全部</span><strong id="taskCountAll">0</strong></div>
  190. <div class="task-stat running"><span>进行中</span><strong id="taskCountRunning">0</strong></div>
  191. <div class="task-stat queued"><span>排队</span><strong id="taskCountQueued">0</strong></div>
  192. <div class="task-stat success"><span>成功</span><strong id="taskCountSuccess">0</strong></div>
  193. <div class="task-stat failed"><span>失败</span><strong id="taskCountFailed">0</strong></div>
  194. </div>
  195. <div class="tasks-filters" id="taskFilters">
  196. <button class="history-filter active" data-filter="all">全部</button>
  197. <button class="history-filter" data-filter="running">进行中</button>
  198. <button class="history-filter" data-filter="queued">排队中</button>
  199. <button class="history-filter" data-filter="success">已完成</button>
  200. <button class="history-filter" data-filter="failed">失败</button>
  201. </div>
  202. </div>
  203. <div class="history-summary" id="taskSummary">正在加载任务…</div>
  204. <div class="task-list" id="taskList"></div>
  205. </main>
  206. </div>
  207. <!-- ==================== 历史创作 ==================== -->
  208. <div id="viewHistory" class="view">
  209. <main class="content">
  210. <div class="page-header">
  211. <div>
  212. <h1>历史创作</h1>
  213. <div class="sub">以图片墙回顾已生成的品牌物料与菜品作品</div>
  214. </div>
  215. <div class="toolbar">
  216. <button class="btn btn-ghost btn-small" id="btnRefreshHistory">
  217. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 12a8 8 0 1 1-2.3-5.6"/><path d="M20 4v5h-5"/></svg>
  218. 刷新
  219. </button>
  220. </div>
  221. </div>
  222. <div class="history-toolbar panel">
  223. <div class="history-search">
  224. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="6.5"/><path d="M20 20l-4.2-4.2"/></svg>
  225. <input type="text" id="historySearch" placeholder="搜索门店或作品名称">
  226. </div>
  227. <div class="history-filters" id="historyFilters">
  228. <button class="history-filter active" data-filter="all">全部</button>
  229. <button class="history-filter" data-filter="logo">LOGO</button>
  230. <button class="history-filter" data-filter="banner">海报</button>
  231. <button class="history-filter" data-filter="signage">招牌</button>
  232. <button class="history-filter" data-filter="sticker">贴纸</button>
  233. <button class="history-filter" data-filter="dish">菜品</button>
  234. </div>
  235. </div>
  236. <div class="history-summary" id="historySummary">加载中…</div>
  237. <div class="history-gallery" id="historyGallery"></div>
  238. </main>
  239. </div>
  240. <!-- ==================== 数据采集 ==================== -->
  241. <div id="viewReports" class="view">
  242. <main class="content">
  243. <div class="page-header">
  244. <div>
  245. <h1>数据采集</h1>
  246. <div class="sub">同步控制台:连接商家客户端、按区间取数入库、推送云端主档案</div>
  247. </div>
  248. <div class="toolbar">
  249. <span class="conn-help-wrap">
  250. <span class="badge badge-info" id="reportConnBadge">检测连接…</span>
  251. <button type="button" class="help-btn" id="btnClientHelp" title="客户端连接排查" aria-expanded="false">?</button>
  252. <div class="help-popover" id="clientHelpPopover" hidden>
  253. <div class="help-popover-title">客户端连接排查</div>
  254. <ol class="help-popover-steps">
  255. <li><b>验证调试端口:</b>客户端带参启动后,浏览器打开 <code>http://127.0.0.1:9669/json/version</code> —— 看到一段 JSON(含 "Browser")就是通的;打不开就是客户端没带参。</li>
  256. <li><b>macOS 带参启动:</b>⌘Q 完全退出客户端(活动监视器确认进程没了)→ 终端执行:<code>open -a "淘宝闪购商家版" --args --remote-debugging-port=9669</code></li>
  257. <li><b>Windows 带参启动:</b>任务栏右键完全退出(任务管理器确认)→ 右键快捷方式 → 属性 → 「目标」末尾加 <code>--remote-debugging-port=9669</code> → 以后都用这个快捷方式启动。</li>
  258. <li>回到本页点击「刷新」,徽章变「客户端已连接」后同步即可。若同步弹窗报 <code>Failed to fetch</code>,先确认服务在跑(启动窗口未关闭)。</li>
  259. </ol>
  260. <div class="help-popover-note">注意:客户端每次重启都必须带参启动,否则端口不会开启。</div>
  261. </div>
  262. </span>
  263. <button class="btn btn-ghost btn-small" id="btnNaposDiagnose" hidden>取数诊断</button>
  264. <button class="btn btn-ghost btn-small" id="btnHarvest" hidden>定向采集</button>
  265. <button class="btn btn-ghost btn-small" id="btnReportRefresh">
  266. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 12a8 8 0 1 1-2.3-5.6"/><path d="M20 4v5h-5"/></svg>
  267. 刷新
  268. </button>
  269. <button class="btn btn-primary btn-small" id="btnOpenSync">
  270. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/></svg>
  271. 同步数据
  272. </button>
  273. </div>
  274. </div>
  275. <div id="entityTabs"></div>
  276. <div class="panel panel-fixed" id="syncHistoryPanel">
  277. <div class="panel-title">
  278. <span>同步历史</span>
  279. <span class="actions">
  280. <span class="badge" id="reportPushBadge">云端未启用</span>
  281. <button class="btn btn-primary btn-small" id="btnReportPush">
  282. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 21V9"/><path d="M7 14l5-5 5 5"/><path d="M4 3h16"/></svg>
  283. 推送云端
  284. </button>
  285. </span>
  286. </div>
  287. <div class="table-wrap">
  288. <table class="data-table" id="syncHistoryTable">
  289. <thead><tr><th>时间</th><th>区间</th><th>范围</th><th>新增</th><th>更新</th><th>失败</th><th>备注</th><th>云端</th></tr></thead>
  290. <tbody id="syncHistoryRows"><tr><td colspan="8" class="empty-cell">加载中…</td></tr></tbody>
  291. </table>
  292. <div id="syncHistoryEmpty" class="empty-hint" hidden>
  293. <div class="empty-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20V11M10 20V4M16 20v-6M2.5 20h19"/></svg></div>
  294. <p id="syncHistoryEmptyText"></p>
  295. </div>
  296. </div>
  297. </div>
  298. </main>
  299. </div>
  300. <!-- ==================== 校区地图 ==================== -->
  301. <div id="viewSchools" class="view">
  302. <main class="content">
  303. <div class="page-header">
  304. <div>
  305. <h1>校区地图</h1>
  306. <div class="sub">已启用的学校点亮在中国地图上,点击可查看关联的运营数据</div>
  307. </div>
  308. <div class="toolbar">
  309. <button class="btn btn-ghost btn-small" id="btnSchoolsReset">回到全国</button>
  310. </div>
  311. </div>
  312. <div class="schools-layout">
  313. <div class="schools-map-card"><div id="schoolsMap" class="schools-map"></div><div id="schoolsLegend" class="schools-legend"></div></div>
  314. <aside class="schools-side">
  315. <div class="schools-side-head"><b>学校</b><span id="schoolsCount"></span></div>
  316. <div id="schoolsList" class="schools-list"></div>
  317. </aside>
  318. </div>
  319. </main>
  320. </div>
  321. <!-- ==================== 经营驾驶舱 ==================== -->
  322. <div id="viewCockpit" class="view">
  323. <main class="content">
  324. <div class="page-header">
  325. <div>
  326. <h1>经营驾驶舱</h1>
  327. <div class="sub">一屏看清经营大盘:规模、效率、转化与结构</div>
  328. </div>
  329. <div class="toolbar">
  330. <button class="btn btn-ghost btn-small" id="btnCockpitRefresh">刷新</button>
  331. </div>
  332. </div>
  333. <div id="cockpitRoot"></div>
  334. </main>
  335. </div>
  336. <!-- ==================== 经营报告 ==================== -->
  337. <div id="viewReport" class="view">
  338. <main class="content">
  339. <div class="page-header">
  340. <div>
  341. <h1>经营报告</h1>
  342. <div class="sub">按统一口径生成的分析报告,图表可交互,可下载 PDF</div>
  343. </div>
  344. </div>
  345. <div id="reportRoot"></div>
  346. </main>
  347. </div>
  348. <!-- ==================== 店铺运营 ==================== -->
  349. <div id="viewOps" class="view">
  350. <main class="content">
  351. <div class="page-header">
  352. <div>
  353. <h1>店铺运营</h1>
  354. <div class="sub">上传商家后台导出的人工统计表,按学校 / 店铺 / 日期查看运营指标</div>
  355. </div>
  356. <div class="toolbar">
  357. <div id="opsHeaderTools"></div>
  358. <button class="btn btn-ghost btn-small" id="btnOpsRecords">导入记录</button>
  359. <button class="btn btn-primary btn-small" id="btnOpsUpload">
  360. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 16V4"/><path d="M7 9l5-5 5 5"/><path d="M4 20h16"/></svg>
  361. 上传文件
  362. </button>
  363. </div>
  364. </div>
  365. <div id="opsBar"></div>
  366. <div id="opsBody"></div>
  367. </main>
  368. </div>
  369. <!-- ==================== 数据中台 ==================== -->
  370. <div id="viewLake" class="view">
  371. <main class="content">
  372. <div class="page-header">
  373. <div>
  374. <h1>数据中台</h1>
  375. <div class="sub">本地数据湖全景与明细数据(含全部原始字段),支持云端拉取补全</div>
  376. </div>
  377. <div class="toolbar">
  378. <button class="btn btn-ghost btn-small" id="btnLakeRefresh">
  379. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 12a8 8 0 1 1-2.3-5.6"/><path d="M20 4v5h-5"/></svg>
  380. 刷新
  381. </button>
  382. <button class="btn btn-primary btn-small" id="btnLakePull" title="以钉钉 AI 表格(云端主档案)为准覆盖本地湖;未注册的账号自动创建主体">
  383. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12"/><path d="M7 10l5 5 5-5"/><path d="M4 21h16"/></svg>
  384. 云端拉取
  385. </button>
  386. <span class="badge" id="lakeSyncBadge"></span>
  387. </div>
  388. </div>
  389. <div class="seg-control lake-tabs" id="lakeTabs" role="tablist">
  390. <button type="button" class="active" data-tab="overview">湖全景</button>
  391. <button type="button" data-tab="detail">明细数据</button>
  392. </div>
  393. <div id="lakeTabOverview">
  394. <div class="stats-grid" id="lakeKpis">
  395. <div class="metric"><span class="metric-body"><span class="metric-label">数据主体</span><span class="metric-value" id="lakeEntityCount">–</span><span class="metric-sub">已注册的商家账号</span></span></div>
  396. <div class="metric"><span class="metric-body"><span class="metric-label">湖内总行数</span><span class="metric-value" id="lakeRowCount">–</span><span class="metric-sub">按 日期×维度×门店 幂等去重</span></span></div>
  397. <div class="metric"><span class="metric-body"><span class="metric-label">日期覆盖</span><span class="metric-value" id="lakeCoverage">–</span><span class="metric-sub">最早 ~ 最晚(后台约 180 天)</span></span></div>
  398. <div class="metric"><span class="metric-body"><span class="metric-label">最近同步</span><span class="metric-value" id="lakeLastSync">–</span><span class="metric-sub">取数写入本地湖的时间</span></span></div>
  399. </div>
  400. <div class="panel panel-fixed report-table-panel">
  401. <div class="panel-title">
  402. <span>主体明细</span>
  403. <span class="actions">
  404. <span class="badge badge-info" id="lakeRowsBadge">0 主体</span>
  405. <span class="conn-help-wrap">
  406. <button type="button" class="help-btn" id="btnLakeHelp" title="多机数据与云端同步规则" aria-expanded="false">?</button>
  407. <div class="help-popover" id="lakeHelpPopover" hidden>
  408. <div class="help-popover-title">多机与云端同步规则</div>
  409. <ol class="help-popover-steps">
  410. <li><b>不产生重复:</b>每一行的身份是「日期×账号×维度×门店」,多台电脑写同一行只会覆盖,不会重复。</li>
  411. <li><b>数据新者胜:</b>推送时对比本地取数时间与云端修改时间,旧数据不会覆盖云端新数据(跳过并计数)。</li>
  412. <li><b>层间矛盾拦截:</b>门店明细全部全零而连锁/公司汇总有数(取数异常形态)时拒绝推送;新店多导致的大面积全零是正常形态,不受影响。</li>
  413. <li><b>拉取不算待推:</b>云端拉取回来的数据本来就已在云端,不计入本机「待推送」;从未本地同步过的只读机器恒显示与云端一致。</li>
  414. <li><b>拉取以云端为准:</b>其他电脑执行「云端拉取」后,本地同键行被云端版本覆盖。</li>
  415. <li><b>推送前备份:</b>每次推送会把将被覆盖的云端记录自动备份到本机(.genpic/aitable-backup/,保留 20 份),坏推送可回滚;不放心误推的机器可在 系统设置 里开「云端写保护」,只拉不推。</li>
  416. </ol>
  417. </div>
  418. </span>
  419. </span>
  420. </div>
  421. <div class="table-wrap">
  422. <table class="data-table" id="lakeEntityTable">
  423. <thead>
  424. <tr>
  425. <th>账号名</th><th>门店/连锁</th><th>总行数</th><th>公司</th><th>连锁</th><th>门店</th><th>汇总</th><th>覆盖区间</th><th>最近同步</th><th>上次推送云端</th>
  426. </tr>
  427. </thead>
  428. <tbody id="lakeEntityRows">
  429. <tr><td colspan="10" class="empty-cell">加载中…</td></tr>
  430. </tbody>
  431. </table>
  432. <div id="lakeEmpty" class="empty-hint" hidden>
  433. <div class="empty-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20V11M10 20V4M16 20v-6M2.5 20h19"/></svg></div>
  434. <p>本地湖为空<br><small>取数机先「同步数据」推送云端,本机可用右上角「云端拉取」补全</small></p>
  435. </div>
  436. </div>
  437. </div>
  438. </div>
  439. <div id="lakeTabDetail" hidden>
  440. <div id="lakeEntityTabs"></div>
  441. <div class="report-toolbar panel">
  442. <div class="field report-field-sm">
  443. <label>日期范围</label>
  444. <select id="reportRangePreset">
  445. <option value="1">昨日</option>
  446. <option value="7">近 7 天</option>
  447. <option value="30" selected>近 30 天</option>
  448. <option value="90">近 90 天</option>
  449. <option value="custom">自定义</option>
  450. </select>
  451. </div>
  452. <div class="field report-field-md" id="reportCustomRange" hidden>
  453. <label>起止日期</label>
  454. <div class="report-date-range">
  455. <input type="date" id="reportFromDate">
  456. <span>–</span>
  457. <input type="date" id="reportToDate">
  458. </div>
  459. </div>
  460. <div class="field report-field-lg">
  461. <label>门店</label>
  462. <div class="scan-dropdown" id="reportShopDropdown">
  463. <button type="button" class="scan-dropdown-trigger" id="reportShopTrigger" aria-expanded="false" aria-haspopup="listbox">
  464. <span id="reportShopText">全部门店</span>
  465. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
  466. </button>
  467. <div class="scan-dropdown-menu" id="reportShopMenu" role="listbox" aria-multiselectable="true" hidden></div>
  468. </div>
  469. </div>
  470. <div class="field">
  471. <label>视图</label>
  472. <div class="seg-control" id="reportViewModes" role="tablist">
  473. <button type="button" class="active" data-mode="detail">明细</button>
  474. <button type="button" data-mode="shop">按门店</button>
  475. <button type="button" data-mode="date">按日期</button>
  476. </div>
  477. </div>
  478. <div class="field report-field-grow">
  479. <label>搜索门店</label>
  480. <input type="text" id="reportSearch" placeholder="输入门店名称过滤">
  481. </div>
  482. <div class="field">
  483. <label>字段</label>
  484. <div class="seg-control" id="reportFieldModes" role="tablist">
  485. <button type="button" class="active" data-fields="report">报表列</button>
  486. <button type="button" data-fields="all">全部字段</button>
  487. </div>
  488. </div>
  489. <div class="report-toolbar-actions">
  490. <button class="btn btn-ghost btn-small" id="btnColumnCfg">列设置</button>
  491. <button class="btn btn-ghost btn-small" id="btnExportCsv">导出 CSV</button>
  492. </div>
  493. </div>
  494. <div class="panel panel-fixed report-table-panel">
  495. <div class="panel-title">
  496. <span>报表数据</span>
  497. <span class="actions"><span class="badge badge-info" id="reportRowCount">0 行</span></span>
  498. </div>
  499. <div class="report-table-wrap" id="reportTableWrap">
  500. <table class="data-table report-table" id="reportTable">
  501. <thead id="reportTableHead"></thead>
  502. <tbody id="reportTableBody"></tbody>
  503. </table>
  504. <div id="reportEmpty" class="empty-hint" hidden>
  505. <div class="empty-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20V11M10 20V4M16 20v-6M2.5 20h19"/></svg></div>
  506. <p id="reportEmptyText">暂无数据<br><small>取数机先「同步数据」推送云端,本机可用右上角「云端拉取」补全</small></p>
  507. </div>
  508. </div>
  509. <div class="report-pager" id="reportPager"></div>
  510. </div>
  511. </div>
  512. </main>
  513. </div>
  514. <!-- ==================== 趋势图表 ==================== -->
  515. <div id="viewAnalysis" class="view">
  516. <main class="content">
  517. <div class="page-header">
  518. <div>
  519. <h1>趋势图表</h1>
  520. <div class="sub">经营 / 运营双视角内置模板,按模板看趋势与归因;深入分析去「智能问数」页</div>
  521. </div>
  522. <div class="toolbar">
  523. <button class="btn btn-ghost btn-small" id="btnExportHtml">
  524. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12"/><path d="M7 10l5 5 5-5"/><path d="M4 21h16"/></svg>
  525. 导出周报
  526. </button>
  527. </div>
  528. </div>
  529. <div id="analysisEntityTabs"></div>
  530. <div class="ana-tpl-bar" id="anaTplBar"></div>
  531. <div class="ana-note" id="anaTplDesc"></div>
  532. <div class="report-toolbar panel">
  533. <div class="field report-field-sm">
  534. <label>日期范围</label>
  535. <select id="analysisRangePreset">
  536. <option value="1">昨日</option>
  537. <option value="7">近 7 天</option>
  538. <option value="30" selected>近 30 天</option>
  539. <option value="90">近 90 天</option>
  540. </select>
  541. </div>
  542. <div class="field report-field-lg">
  543. <label>门店</label>
  544. <div class="scan-dropdown" id="anaShopDropdown">
  545. <button type="button" class="scan-dropdown-trigger" id="anaShopTrigger" aria-expanded="false" aria-haspopup="listbox">
  546. <span id="anaShopText">全部门店</span>
  547. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
  548. </button>
  549. <div class="scan-dropdown-menu" id="anaShopMenu" role="listbox" aria-multiselectable="true" hidden></div>
  550. </div>
  551. </div>
  552. <div class="field report-field-sm" id="anaMetricField">
  553. <label>主指标</label>
  554. <select id="chartMetric" class="chart-metric-select"></select>
  555. </div>
  556. <label class="ana-compare-toggle" id="anaCompareField" title="主趋势图中叠加上一周期的虚线对照">
  557. <input type="checkbox" id="anaComparePrev" checked>
  558. <span>对比上期</span>
  559. </label>
  560. </div>
  561. <div class="delta-strip" id="analysisDeltas"></div>
  562. <div id="analysisEmpty" class="empty-hint" hidden>
  563. <div class="empty-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20V11M10 20V4M16 20v-6M2.5 20h19"/></svg></div>
  564. <p id="analysisEmptyText"></p>
  565. </div>
  566. <div class="ana-grid" id="anaGrid"></div>
  567. </main>
  568. </div>
  569. <!-- ==================== 智能问数 ==================== -->
  570. <div id="viewAsk" class="view">
  571. <main class="content content-ask">
  572. <div class="ask-shell">
  573. <aside class="ask-side">
  574. <div class="ask-side-head">
  575. <span class="ask-brand">
  576. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="8" width="16" height="11" rx="3"/><path d="M12 8V5"/><circle cx="12" cy="3.6" r="1.4"/><path d="M9.5 13.5h.01M14.5 13.5h.01"/><path d="M9.5 16.2h5"/></svg>
  577. 智能问数
  578. </span>
  579. <button type="button" class="ask-new-btn" id="btnAskNewSession" title="新建会话">+</button>
  580. </div>
  581. <div class="ask-search">
  582. <input type="text" id="askSearch" placeholder="搜索会话" autocomplete="off">
  583. </div>
  584. <div class="ask-side-list" id="askSessions"></div>
  585. </aside>
  586. <section class="ask-stage">
  587. <div class="ask-stage-head">
  588. <span class="ask-stage-title" id="askStageTitle">新对话</span>
  589. <span class="ask-stage-badge" id="askStageBadge"></span>
  590. </div>
  591. <div class="ask-stage-sub">智能体自动取数、做同环比对比与数据体检;结论均来自本地数据湖,过程与数据质量判定全程可见</div>
  592. <div id="askDigest" class="ask-digest" hidden></div>
  593. <div class="ask-stream" id="askStream">
  594. <div class="ask-hero" id="askEmpty">
  595. <div class="ask-hero-title">给问数一个目标</div>
  596. <div class="ask-hero-sub">智能体会自动规划、调用取数与校验工具(湖概览 / 报表查询 / 同环比对比 / 数据体检),逐步给出带真实数字的结论;切换页面后运行仍会继续,可随时回来查看。</div>
  597. <div class="ask-chips" id="askChips">
  598. <button type="button" data-q="最近7天营业额和单量趋势">最近7天营业额和单量趋势</button>
  599. <button type="button" data-q="最近30天比上一个30天营业额变化多少,主要受哪些门店影响">环比变化与影响门店</button>
  600. <button type="button" data-q="做一次数据体检,看看数据质量有没有问题">数据体检</button>
  601. <button type="button" data-q="上月营业额Top5门店">上月营业额Top5门店</button>
  602. </div>
  603. </div>
  604. </div>
  605. <div class="ask-composer">
  606. <textarea id="askInput" rows="1" placeholder="描述你的数据问题,或从「技能」里点名打法;回车发送,Shift + Enter 换行" autocomplete="off"></textarea>
  607. <div class="ask-composer-bar">
  608. <select id="askEntity" class="ask-ent-select" title="数据主体"></select>
  609. <div class="seg-control" id="askMode">
  610. <button type="button" data-mode="auto" class="active">自动</button>
  611. <button type="button" data-mode="agent">深度分析</button>
  612. <button type="button" data-mode="quick">快问</button>
  613. </div>
  614. <select id="askSkill" class="ask-ent-select" title="内置技能(打法模板,可选)">
  615. <option value="">技能:自动</option>
  616. </select>
  617. <span class="ask-composer-spacer"></span>
  618. <button type="button" class="btn btn-ghost btn-small" id="btnAskStop" hidden>停止</button>
  619. <button type="button" class="ask-send-btn" id="btnAskSend" title="发送">
  620. <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M3 11l18-8-8 18-2.5-7.5z"/></svg>
  621. </button>
  622. </div>
  623. </div>
  624. </section>
  625. </div>
  626. </main>
  627. </div>
  628. <!-- ==================== 系统设置 ==================== -->
  629. <div id="viewSettings" class="view">
  630. <main class="content">
  631. <div class="page-header">
  632. <div>
  633. <h1>系统设置</h1>
  634. <div class="sub">API 密钥与采集配置,保存后立即生效</div>
  635. </div>
  636. </div>
  637. <div class="settings-workspace">
  638. <div class="settings-col">
  639. <div class="panel panel-narrow">
  640. <div class="stage-marker">
  641. <span class="num">1</span><span>API 设置</span>
  642. <div class="seg-control settings-api-tabs" id="settingsApiTabs" role="tablist">
  643. <button type="button" class="active" data-tab="image">图像模型</button>
  644. <button type="button" data-tab="chat">对话模型</button>
  645. </div>
  646. </div>
  647. <span class="panel-watermark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="7.5" cy="15.5" r="4.5"/><path d="M11 12L21 2"/><path d="M16 7l3 3"/></svg></span>
  648. <div class="panel-body">
  649. <div id="settingsTabImage" class="settings-tab-body" role="tabpanel">
  650. <div class="field">
  651. <div class="field-label-row">
  652. <label>API Key<span class="req">*</span></label>
  653. <span class="label-links">
  654. <a class="settings-link settings-link-compact" href="https://o-hubs.com/api/console/keys" target="_blank" rel="noopener noreferrer">
  655. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="7.5" cy="15.5" r="4.5"/><path d="M11 12L21 2"/><path d="M16 7l3 3"/></svg>
  656. <span>获取 API Key</span>
  657. </a>
  658. <a class="settings-link settings-link-compact" href="https://o-hubs.com/home" target="_blank" rel="noopener noreferrer">
  659. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="3"/><path d="M3 10h18"/><path d="M7 15h3"/></svg>
  660. <span>充值</span>
  661. </a>
  662. </span>
  663. </div>
  664. <input type="text" id="settingsApiKey" placeholder="sk-...">
  665. </div>
  666. <div class="field">
  667. <label>API 地址</label>
  668. <input type="text" id="settingsApiBase" value="https://api.lk888.ai" disabled>
  669. </div>
  670. <div class="field">
  671. <label>默认模型</label>
  672. <select id="settingsModel"></select>
  673. </div>
  674. <div class="settings-links">
  675. <button type="button" class="settings-link settings-link-btn" id="btnTestImage">
  676. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 12a8 8 0 1 1-2.3-5.6"/><path d="M20 4v5h-5"/></svg>
  677. <span>测试 API 与模型</span>
  678. </button>
  679. <span id="imageTestStatus" class="badge"></span>
  680. </div>
  681. </div>
  682. <div id="settingsTabChat" class="settings-tab-body" role="tabpanel" hidden>
  683. <div class="field">
  684. <label>API Key</label>
  685. <input type="text" id="settingsChatApiKey" placeholder="留空复用上方 API Key">
  686. </div>
  687. <div class="field">
  688. <label>API 地址</label>
  689. <input type="text" id="settingsChatApiBase" disabled placeholder="默认与生图 API 地址一致">
  690. </div>
  691. <div class="field">
  692. <label>对话模型</label>
  693. <select id="settingsChatModel">
  694. <option value="deepseek-v4.1-flash">deepseek-v4.1-flash</option>
  695. <option value="glm-5.3-flash">glm-5.3-flash</option>
  696. </select>
  697. <div class="help">用于快问与问数结论;深度分析缺省也用它</div>
  698. </div>
  699. <div class="field">
  700. <label>深度分析模型(可选)</label>
  701. <select id="settingsChatModelAgent">
  702. <option value="">跟随对话模型</option>
  703. <option value="deepseek-v4.1-flash">deepseek-v4.1-flash</option>
  704. <option value="glm-5.3-flash">glm-5.3-flash</option>
  705. </select>
  706. <div class="help">问数「深度分析」多步规划用的模型,缺省跟随上方对话模型</div>
  707. </div>
  708. <div class="field">
  709. <label>动作 JSON mode</label>
  710. <label style="display:flex;align-items:center;gap:6px;margin:0;">
  711. <input type="checkbox" id="settingsChatJsonMode" />
  712. <span>启用 response_format=json_object(网关支持时更稳,报错请关闭)</span>
  713. </label>
  714. </div>
  715. <div class="settings-links">
  716. <button type="button" class="settings-link settings-link-btn" id="btnTestChat">
  717. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 12a8 8 0 1 1-2.3-5.6"/><path d="M20 4v5h-5"/></svg>
  718. <span>测试对话模型</span>
  719. </button>
  720. <span id="chatTestStatus" class="badge"></span>
  721. </div>
  722. </div>
  723. <div class="panel-save-row">
  724. <button class="btn btn-primary btn-small" id="btnSaveApi">保存设置</button>
  725. <span id="apiSaveStatus" class="badge"></span>
  726. </div>
  727. </div>
  728. </div>
  729. <div class="panel panel-narrow">
  730. <div class="stage-marker"><span class="num">2</span><span>采集配置</span></div>
  731. <span class="panel-watermark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z"/><path d="M3 11h18"/></svg></span>
  732. <div class="panel-body">
  733. <div class="field">
  734. <label>输出根目录</label>
  735. <input type="text" id="settingsRootDir" placeholder="/path/to/workspace">
  736. </div>
  737. <div class="field-row">
  738. <div class="field">
  739. <label>每页条数</label>
  740. <input type="number" id="pageSizeInput" min="1" value="50">
  741. </div>
  742. <div class="field">
  743. <label>间隔 (ms)</label>
  744. <input type="number" id="delayInput" min="0" value="300">
  745. </div>
  746. </div>
  747. <div class="field">
  748. <label>Cookie(淘宝闪购,兜底通道)</label>
  749. <textarea id="settingsCookie" rows="3" placeholder="采集走客户端通道时无需此项;仅 Cookie 通道 / 客户端不可用时使用。登录 melody.shop.ele.me 后从 DevTools 复制粘贴"></textarea>
  750. </div>
  751. <div class="panel-save-row">
  752. <button class="btn btn-primary btn-small" id="btnSaveCrawl">保存设置</button>
  753. <span id="crawlSaveStatus" class="badge"></span>
  754. </div>
  755. </div>
  756. </div>
  757. </div>
  758. <div class="settings-col">
  759. <div class="panel panel-narrow">
  760. <div class="stage-marker"><span class="num">3</span><span>安全设置</span>
  761. <span class="conn-help-wrap">
  762. <button type="button" class="help-btn" id="btnSecurityHelp" title="局域网访问口令说明" aria-expanded="false">?</button>
  763. <div class="help-popover pop-left" id="securityHelpPopover" hidden>
  764. <div class="help-popover-title">局域网访问口令</div>
  765. <ol class="help-popover-steps">
  766. <li><b>作用:</b>设置后,同一局域网的其他电脑访问本服务需要输入此口令;本机 127.0.0.1 不受影响。</li>
  767. <li><b>清除:</b>输入 <code>-</code> 保存即清除;留空保存保持不变。</li>
  768. <li><b>临时开关:</b>也可用环境变量 <code>ACCESS_TOKEN</code>。</li>
  769. </ol>
  770. </div>
  771. </span>
  772. </div>
  773. <span class="panel-watermark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l7 3v5c0 4.4-3 8.4-7 10-4-1.6-7-5.6-7-10V6Z"/><path d="M9.5 12l1.8 1.8L15 10"/></svg></span>
  774. <div class="panel-body">
  775. <div class="field">
  776. <label>局域网访问口令(accessToken)</label>
  777. <input type="text" id="settingsAccessToken" placeholder="未设置" autocomplete="off">
  778. </div>
  779. <div class="panel-save-row">
  780. <button class="btn btn-primary btn-small" id="btnSaveSecurity">保存设置</button>
  781. <span id="securitySaveStatus" class="badge"></span>
  782. </div>
  783. </div>
  784. </div>
  785. <div class="panel panel-narrow">
  786. <div class="stage-marker"><span class="num">4</span><span>钉钉 AI 表格</span>
  787. <span class="conn-help-wrap">
  788. <button type="button" class="help-btn" id="btnAitableHelp" title="钉钉 AI 表格配置说明" aria-expanded="false">?</button>
  789. <div class="help-popover pop-left" id="aitableHelpPopover" hidden>
  790. <div class="help-popover-title">钉钉 AI 表格 · 配置说明</div>
  791. <ol class="help-popover-steps">
  792. <li><b>前置条件:</b>钉钉企业内部应用需开通「AI 表格」API 权限。</li>
  793. <li><b>Base ID:</b>可直接粘贴分享链接(形如 alidocs.dingtalk.com/i/nodes/xxx),保存时自动提取 ID;三张表名留空用默认值。</li>
  794. <li><b>为什么拆三张表:</b>云端按维度拆表(门店明细/连锁汇总/公司汇总),每张表内粒度唯一,钉钉 AI 问数/人工聚合不会跨维度算重。</li>
  795. <li><b>推送与拉取:</b>取数机「同步 → 自动推送」(数据采集页有手动推送按钮),其他电脑在「数据中台」页执行「云端拉取」。本面板只管配置。</li>
  796. </ol>
  797. </div>
  798. </span>
  799. </div>
  800. <span class="panel-watermark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 10h18M9 10v10"/></svg></span>
  801. <div class="panel-body">
  802. <div class="field">
  803. <label style="display:flex;align-items:center;gap:6px;">
  804. <input type="checkbox" id="settingsAitableEnabled">
  805. <span>启用(同步完成后自动推送增量到云端)</span>
  806. </label>
  807. <label style="display:flex;align-items:center;gap:6px;">
  808. <input type="checkbox" id="settingsAitableReadOnly">
  809. <span>云端写保护(本机只拉不推,防误推坏数据;要推送需先回这里关闭)</span>
  810. </label>
  811. </div>
  812. <div class="field">
  813. <label>AppKey / AppSecret(钉钉企业内部应用)</label>
  814. <input type="text" id="settingsAitableAppKey" placeholder="钉钉开放平台应用的 AppKey" autocomplete="off">
  815. <input type="text" id="settingsAitableAppSecret" placeholder="AppSecret 未设置" autocomplete="off" style="margin-top:6px;">
  816. </div>
  817. <div class="field">
  818. <label>Base ID / 三张数据表名称</label>
  819. <input type="text" id="settingsAitableBaseId" placeholder="粘贴 AI 表格分享链接或直接填 Base ID" autocomplete="off">
  820. <div class="sheet-names-grid">
  821. <input type="text" id="settingsAitableSheetDetail" placeholder="门店明细" autocomplete="off">
  822. <input type="text" id="settingsAitableSheetChain" placeholder="连锁汇总" autocomplete="off">
  823. <input type="text" id="settingsAitableSheetCompany" placeholder="公司汇总" autocomplete="off">
  824. </div>
  825. </div>
  826. <div class="settings-links">
  827. <button type="button" class="settings-link settings-link-btn" id="btnTestAitable"><span>测试连通</span></button>
  828. <span id="aitableStatus" class="badge"></span>
  829. </div>
  830. <div class="panel-save-row">
  831. <button class="btn btn-primary btn-small" id="btnSaveAitable">保存设置</button>
  832. <span id="aitableSaveStatus" class="badge"></span>
  833. </div>
  834. </div>
  835. </div>
  836. <div class="panel panel-narrow">
  837. <div class="stage-marker"><span class="num">5</span><span>云端数据库</span></div>
  838. <span class="panel-watermark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v6c0 1.7 3.6 3 8 3s8-1.3 8-3V5"/><path d="M4 11v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6"/></svg></span>
  839. <div class="panel-body">
  840. <div class="field">
  841. <label>存储方式</label>
  842. <select id="settingsStorageDriver">
  843. <option value="sqlite">本地 SQLite(默认)</option>
  844. <option value="postgres">云端 PostgreSQL</option>
  845. </select>
  846. <div class="help">切换后需重启服务生效;已有数据可用 npm run db:export / db:import 迁移</div>
  847. </div>
  848. <div id="settingsPgFields" hidden>
  849. <div class="field">
  850. <label>主机 / 端口</label>
  851. <div style="display:flex;gap:8px;">
  852. <input type="text" id="settingsPgHost" placeholder="111.229.143.134" style="flex:1;">
  853. <input type="text" id="settingsPgPort" placeholder="5432" style="width:88px;">
  854. </div>
  855. </div>
  856. <div class="field">
  857. <label>数据库 / 用户</label>
  858. <input type="text" id="settingsPgDatabase" placeholder="smart_ops" autocomplete="off">
  859. <input type="text" id="settingsPgUser" placeholder="smart_ops" autocomplete="off" style="margin-top:6px;">
  860. </div>
  861. <div class="field">
  862. <label>密码</label>
  863. <input type="password" id="settingsPgPassword" placeholder="数据库密码" autocomplete="off">
  864. </div>
  865. <div class="field">
  866. <label style="display:flex;align-items:center;gap:6px;">
  867. <input type="checkbox" id="settingsPgSsl">
  868. <span>使用 SSL(云数据库要求时勾选)</span>
  869. </label>
  870. </div>
  871. </div>
  872. <div class="settings-links">
  873. <button type="button" class="settings-link settings-link-btn" id="btnTestStorage"><span>测试连接</span></button>
  874. <span id="storageTestStatus" class="badge"></span>
  875. </div>
  876. <div class="panel-save-row">
  877. <button class="btn btn-primary btn-small" id="btnSaveStorage">保存设置</button>
  878. <span id="storageSaveStatus" class="badge"></span>
  879. </div>
  880. </div>
  881. </div>
  882. </div>
  883. </div>
  884. </main>
  885. </div>
  886. <!-- ==================== 系统日志 ==================== --> <!-- ==================== 系统日志 ==================== -->
  887. <div id="viewLogs" class="view">
  888. <main class="content">
  889. <div class="page-header">
  890. <div>
  891. <h1>系统日志</h1>
  892. <div class="sub">任务与系统的实时运行日志(最近 500 条,自动刷新)</div>
  893. </div>
  894. </div>
  895. <div class="panel panel-fixed panel-log settings-log">
  896. <div class="panel-title">
  897. <span>实时日志</span>
  898. <span class="actions">
  899. <button class="btn btn-ghost btn-small" id="btnClearLog">清空日志</button>
  900. </span>
  901. </div>
  902. <span class="panel-watermark log-watermark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16v11H8l-4 4Z"/><path d="M8 9h8M8 12h5"/></svg></span>
  903. <div class="log-box" id="logPanel"></div>
  904. </div>
  905. </main>
  906. </div>
  907. </div>
  908. </div>
  909. <div class="toast-wrap" id="toast-wrap"></div>
  910. <!-- 添加店铺弹窗 -->
  911. <div class="modal-overlay" id="modalAddShop" hidden>
  912. <div class="modal">
  913. <div class="modal-header">
  914. <div class="modal-title">
  915. <span class="modal-status-icon">
  916. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20V9l8-5 8 5v11"/><path d="M9 20v-6h6v6"/></svg>
  917. </span>
  918. <span>添加店铺</span>
  919. </div>
  920. <button class="modal-close" id="btnCloseModal">&times;</button>
  921. </div>
  922. <div class="modal-body">
  923. <div class="field">
  924. <label>店铺清单</label>
  925. <textarea id="manualInput" rows="5" placeholder="每行一个:shopId 店铺名&#10;507068342 鑫龙福麻辣烫(康诺美食城店)"></textarea>
  926. <div class="help">每行一条,shopId 和店铺名用空格或逗号分隔</div>
  927. </div>
  928. </div>
  929. <div class="modal-footer">
  930. <button class="btn btn-ghost btn-small" id="btnCancelAdd">取消</button>
  931. <button class="btn btn-primary btn-small" id="useManualBtn">确认添加</button>
  932. </div>
  933. </div>
  934. </div>
  935. <!-- 列设置弹窗 -->
  936. <div class="modal-overlay" id="modalReportColumns" hidden>
  937. <div class="modal">
  938. <div class="modal-header">
  939. <div class="modal-title">
  940. <span class="modal-status-icon">
  941. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
  942. </span>
  943. <span>列设置</span>
  944. </div>
  945. <button class="modal-close" id="btnCloseColumnCfg">&times;</button>
  946. </div>
  947. <div class="modal-body">
  948. <div class="help" style="margin-bottom:8px;">勾选需要在报表中展示的字段(日期与门店名称固定展示)</div>
  949. <div class="colcfg-list" id="columnCfgList"></div>
  950. </div>
  951. <div class="modal-footer">
  952. <button class="btn btn-ghost btn-small" id="btnResetColumns">恢复默认</button>
  953. <button class="btn btn-primary btn-small" id="btnApplyColumns">应用</button>
  954. </div>
  955. </div>
  956. </div>
  957. <!-- 取数诊断弹窗(隐藏入口:连点侧边栏版本号 5 次显现) -->
  958. <div class="modal-overlay" id="modalNaposDiagnose" hidden>
  959. <div class="modal modal-wide">
  960. <div class="modal-header">
  961. <div class="modal-title">
  962. <span class="modal-status-icon">
  963. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.7 6.3a4.5 4.5 0 0 0-6 6L3 18l3 3 5.7-5.7a4.5 4.5 0 0 0 6-6L14 13l-3-3 3.7-3.7z"/></svg>
  964. </span>
  965. <span>取数诊断</span>
  966. </div>
  967. <button class="modal-close" id="btnCloseDiagnose">&times;</button>
  968. </div>
  969. <div class="modal-body">
  970. <div class="diagnose-summary" id="diagnoseSummary">正在诊断,约需半分钟,请勿关闭…</div>
  971. <details class="diagnose-raw">
  972. <summary>原始报告 JSON(复制或下载发给开发即可)</summary>
  973. <pre id="diagnoseJson"></pre>
  974. </details>
  975. </div>
  976. <div class="modal-footer">
  977. <button class="btn btn-ghost btn-small" id="btnCopyDiagnose">复制报告</button>
  978. <button class="btn btn-ghost btn-small" id="btnDownloadDiagnose">下载报告</button>
  979. <button class="btn btn-primary btn-small" id="btnDismissDiagnose">关闭</button>
  980. </div>
  981. </div>
  982. </div>
  983. <!-- 定向采集弹窗(隐藏入口:与取数诊断一起,连点侧边栏版本号 5 次显现) -->
  984. <div class="modal-overlay" id="modalHarvest" hidden>
  985. <div class="modal modal-wide">
  986. <div class="modal-header">
  987. <div class="modal-title">
  988. <span class="modal-status-icon">
  989. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M5 12a7 7 0 0 1 1.5-4.3M19 12a7 7 0 0 0-1.5-4.3M2.5 12a9.5 9.5 0 0 1 2-5.8M21.5 12a9.5 9.5 0 0 0-2-5.8"/></svg>
  990. </span>
  991. <span>定向采集接口</span>
  992. </div>
  993. <button class="modal-close" id="btnCloseHarvest">&times;</button>
  994. </div>
  995. <div class="modal-body">
  996. <ol class="help-popover-steps" style="position:static;opacity:1;visibility:visible;margin:0 0 10px;">
  997. <li><b>用途:</b>补录接口目录没有覆盖的页面(如「历史订单」「营业时间设置」),供开发侧定位数据源。</li>
  998. <li><b>用法:</b>点「开始采集」后,切到淘宝闪购客户端,<b>正常翻看目标页面约 2 分钟</b>(比如订单页点「历史订单」翻几页),时间到自动结束。</li>
  999. <li><b>安全:</b>只录下你浏览时页面自己发出的请求地址,不重放、不点击、不改任何数据。</li>
  1000. <li><b>完成后:</b>点「复制清单」把结果发回开发侧即可。</li>
  1001. </ol>
  1002. <div class="field-row">
  1003. <div class="field">
  1004. <label>采集时长</label>
  1005. <select id="harvestSeconds">
  1006. <option value="60">1 分钟</option>
  1007. <option value="120" selected>2 分钟</option>
  1008. <option value="300">5 分钟</option>
  1009. </select>
  1010. </div>
  1011. <div class="field" style="flex:0;">
  1012. <label>&nbsp;</label>
  1013. <button class="btn btn-primary btn-small" id="btnHarvestStart">开始采集</button>
  1014. </div>
  1015. </div>
  1016. <div class="badge" id="harvestStatusBadge" style="margin-bottom:8px;">待开始</div>
  1017. <div id="harvestSummary" class="diagnose-summary" hidden></div>
  1018. <details class="diagnose-raw" id="harvestRawWrap" hidden>
  1019. <summary>接口清单 JSON(复制发给开发即可)</summary>
  1020. <pre id="harvestJson"></pre>
  1021. </details>
  1022. </div>
  1023. <div class="modal-footer">
  1024. <button class="btn btn-ghost btn-small" id="btnCopyHarvest" hidden>复制清单</button>
  1025. <button class="btn btn-ghost btn-small" id="btnHarvestStop" hidden>提前结束</button>
  1026. <button class="btn btn-primary btn-small" id="btnDismissHarvest">关闭</button>
  1027. </div>
  1028. </div>
  1029. </div>
  1030. <!-- 数据同步弹窗 -->
  1031. <div class="modal-overlay" id="modalReportSync" hidden>
  1032. <div class="modal modal-wide">
  1033. <div class="modal-header">
  1034. <div class="modal-title">
  1035. <span class="modal-status-icon">
  1036. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12"/><path d="M7 10l5 5 5-5"/><path d="M4 21h16"/></svg>
  1037. </span>
  1038. <span>同步数据到本地</span>
  1039. </div>
  1040. <button class="modal-close" id="btnCloseSync">&times;</button>
  1041. </div>
  1042. <div class="modal-body">
  1043. <div class="badge" id="syncEntityBadge">未识别主体</div>
  1044. <div class="field">
  1045. <label>同步范围</label>
  1046. <select id="syncScope">
  1047. <option value="all" selected>全部(公司 + 连锁 + 门店明细)</option>
  1048. <option value="shop">仅门店明细</option>
  1049. <option value="company">仅公司 / 连锁汇总</option>
  1050. <option value="custom">指定门店</option>
  1051. </select>
  1052. </div>
  1053. <div class="field" id="syncShopPickWrap" hidden>
  1054. <label>选择门店</label>
  1055. <div class="scan-dropdown" id="syncShopDropdown">
  1056. <button type="button" class="scan-dropdown-trigger" id="syncShopTrigger" aria-expanded="false" aria-haspopup="listbox">
  1057. <span id="syncShopText">选择门店</span>
  1058. <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
  1059. </button>
  1060. <div class="scan-dropdown-menu" id="syncShopMenu" role="listbox" aria-multiselectable="true" hidden></div>
  1061. </div>
  1062. </div>
  1063. <div class="field">
  1064. <label>日期范围</label>
  1065. <select id="syncRange">
  1066. <option value="1">昨日</option>
  1067. <option value="7">近 7 天</option>
  1068. <option value="30" selected>近 30 天</option>
  1069. <option value="90">近 90 天</option>
  1070. </select>
  1071. <div class="help">经营数据后台保留约 180 天;当天数据未结算,同步区间右端取昨日</div>
  1072. </div>
  1073. <div class="field">
  1074. <label>每日自动同步</label>
  1075. <div style="display:flex;gap:10px;align-items:center;">
  1076. <label style="display:flex;align-items:center;gap:6px;margin:0;white-space:nowrap;">
  1077. <input type="checkbox" id="autoSyncEnabled" />
  1078. <span>每天自动同步昨日数据</span>
  1079. </label>
  1080. <input type="time" id="autoSyncTime" value="06:00" style="width:110px;" />
  1081. </div>
  1082. <div class="help">需商家客户端以调试端口运行;到点时客户端在线即自动补同步昨日数据,离线则当天在线后补跑。同步记录照常进入同步记录列表。</div>
  1083. </div>
  1084. <div id="syncProgressWrap" hidden>
  1085. <div class="sync-progress-row">
  1086. <div class="sync-progress-bar"><span id="syncProgressBar"></span></div>
  1087. <span class="badge badge-info" id="syncProgressText">0 / 0</span>
  1088. </div>
  1089. <div class="sync-stat-row">
  1090. <span class="badge badge-ok" id="syncStatAdded">新增 0</span>
  1091. <span class="badge badge-info" id="syncStatUpdated">更新 0</span>
  1092. <span class="badge badge-fail" id="syncStatFailed">失败 0</span>
  1093. <button class="btn btn-ghost btn-small" id="btnSyncCancel" hidden>取消</button>
  1094. </div>
  1095. <div class="sync-steps" id="syncSteps"></div>
  1096. </div>
  1097. <div class="help" id="syncHint">同步会把所选区间逐日、逐店的经营数据写入本地数据湖(data/reports/),可反复同步自动去重合并。</div>
  1098. </div>
  1099. <div class="modal-footer">
  1100. <button class="btn btn-ghost btn-small" id="btnCancelSync">关闭</button>
  1101. <button class="btn btn-primary btn-small" id="btnStartSync">开始同步</button>
  1102. </div>
  1103. </div>
  1104. </div>
  1105. <!-- 店铺运营 · 上传弹窗 -->
  1106. <div class="modal-overlay" id="opsUploadModal" hidden>
  1107. <div class="modal ops-upload-modal">
  1108. <div class="modal-header">
  1109. <div class="modal-title"><span>上传店铺运营数据</span></div>
  1110. <button class="modal-close" id="opsUploadClose" type="button" aria-label="关闭">×</button>
  1111. </div>
  1112. <div class="modal-body">
  1113. <div class="ops-drop" id="opsDrop">
  1114. <b>拖拽 .xlsx 文件到这里(可多选)</b>
  1115. <small>或点击选择文件;支持商家后台导出的人工统计表,一次最多 30 个</small>
  1116. </div>
  1117. <input type="file" id="opsFile" accept=".xlsx" multiple hidden>
  1118. <div class="ops-file-list" id="opsFileList"></div>
  1119. <div class="ops-field">
  1120. <label for="opsYear">数据年份</label>
  1121. <input type="number" class="ops-input" id="opsYear" min="2000" max="2100" value="2026">
  1122. <small>表里的日期只有「9.9」这种格式,没有年份,需要你确认</small>
  1123. </div>
  1124. <div class="ops-field">
  1125. <label for="opsSchool">学校</label>
  1126. <select class="ops-input ops-select" id="opsSchool">
  1127. <option value="">自动(读取表格里的「学校名称」列)</option>
  1128. <option value="__custom">手动输入…</option>
  1129. </select>
  1130. <input type="text" class="ops-input" id="opsSchoolCustom" placeholder="输入学校名称" hidden>
  1131. <small>表格里没有「学校名称」列,或想把这批数据归到指定学校时,在这里选或手动填</small>
  1132. </div>
  1133. <label class="ops-check"><input type="checkbox" id="opsForce"> 覆盖已导入的文件(同一文件重新导入时替换旧数据)</label>
  1134. <div class="ops-upload-result" id="opsUploadResult"></div>
  1135. </div>
  1136. <div class="modal-footer">
  1137. <button class="btn btn-ghost btn-small" id="opsUploadCancel" type="button">关闭</button>
  1138. <button class="btn btn-primary btn-small" id="opsUploadStart" type="button">开始导入</button>
  1139. </div>
  1140. </div>
  1141. </div>
  1142. <!-- 店铺运营 · 导入记录 -->
  1143. <div class="modal-overlay" id="opsRecordsModal" hidden>
  1144. <div class="modal ops-records-modal">
  1145. <div class="modal-header">
  1146. <div class="modal-title"><span>导入记录</span></div>
  1147. <button class="modal-close" id="opsRecordsClose" type="button" aria-label="关闭">×</button>
  1148. </div>
  1149. <div class="modal-body" id="opsRecordsList"></div>
  1150. </div>
  1151. </div>
  1152. <script src="/crawl.js"></script>
  1153. <script src="/gen.js"></script>
  1154. <script src="/tasks.js"></script>
  1155. <script src="/history.js"></script>
  1156. <script src="/vendor/echarts.min.js"></script>
  1157. <script src="/analysis-templates.js"></script>
  1158. <script src="/reports.js"></script>
  1159. <script src="/lake.js"></script>
  1160. <script src="/multi-select.js"></script>
  1161. <script src="/ops.js"></script>
  1162. <script src="/cockpit.js"></script>
  1163. <script src="/report.js"></script>
  1164. <script src="/schools.js"></script>
  1165. <script src="/ask.js"></script>
  1166. <script src="/app.js"></script>
  1167. </body>
  1168. </html>