index.html 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  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="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></button>
  39. <div class="nav-section-label">设置</div>
  40. <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>
  41. <div class="sidebar-bottom">
  42. <span>v1.0.0</span>
  43. <img src="/assets/powered-by.png" alt="Hyreal IDE" class="sidebar-powered">
  44. </div>
  45. </aside>
  46. <div class="main-wrap">
  47. <!-- ==================== 门店采集 ==================== -->
  48. <div id="viewCrawl" class="view active">
  49. <main class="content">
  50. <div class="page-header">
  51. <div>
  52. <h1>门店采集</h1>
  53. <div class="sub">从淘宝闪购后台按店铺批量采集菜品图,用于后续视觉美化</div>
  54. </div>
  55. </div>
  56. <div class="crawl-grid">
  57. <div class="left-col">
  58. <div class="panel panel-narrow panel-designed">
  59. <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>
  60. <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>
  61. <div class="panel-body">
  62. <div class="field">
  63. <label>抓取内容</label>
  64. <select disabled><option>菜品图片 + 名称</option></select>
  65. </div>
  66. <button class="collapse-trigger" id="advToggle" aria-expanded="false">
  67. <span>高级设置</span><span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 6l6 6-6 6"/></svg></span>
  68. </button>
  69. <div id="advBody" class="collapse">
  70. <div style="padding-top:12px;display:flex;flex-direction:column;gap:12px;">
  71. <label style="display:flex;align-items:center;gap:6px;font-size:13px;cursor:pointer;">
  72. <input type="checkbox" id="forceFullCrawl">
  73. <span>强制全量抓取(覆盖已有图片)</span>
  74. </label>
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. <button class="btn btn-primary btn-block" id="startBtn">
  80. <span id="startBtnIcon"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg></span>
  81. <span id="startBtnText">开始抓取</span>
  82. </button>
  83. <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>
  84. <div class="panel panel-fixed panel-progress">
  85. <div class="panel-title">
  86. <span>进度列表</span>
  87. <span class="actions"><span class="badge" id="crawlStatusBadge"></span></span>
  88. </div>
  89. <div id="progressList"></div>
  90. </div>
  91. </div>
  92. <div class="right-col">
  93. <div class="panel panel-fixed panel-shops">
  94. <div class="panel-title">
  95. <span>店铺清单</span>
  96. <span class="actions">
  97. <span class="badge badge-info" id="shopCount">0 家</span>
  98. <button class="btn btn-ghost btn-small" id="btnToggleAdd">+ 添加店铺</button>
  99. </span>
  100. </div>
  101. <div style="overflow-x:auto;">
  102. <table class="data-table">
  103. <thead>
  104. <tr>
  105. <th style="width:36px;"><input type="checkbox" id="selectAllCb"></th>
  106. <th style="min-width:220px;">店铺</th>
  107. <th style="width:100px;">shopId</th>
  108. <th style="width:100px;">状态</th>
  109. <th style="width:220px;">文件夹</th>
  110. </tr>
  111. </thead>
  112. <tbody id="shopTableBody"></tbody>
  113. </table>
  114. <div id="shopTableEmpty" class="empty-hint">
  115. <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>
  116. <p>尚无店铺<br><small>点击右上角「+ 添加店铺」手动录入</small></p>
  117. </div>
  118. </div>
  119. </div>
  120. </div>
  121. </div>
  122. </main>
  123. </div>
  124. <!-- ==================== 视觉生成 ==================== -->
  125. <div id="viewGen" class="view">
  126. <main class="content">
  127. <div class="page-header">
  128. <div>
  129. <h1>视觉生成</h1>
  130. <div class="sub">基于已采集的菜品图片,批量生成 LOGO / 海报 / 招牌 / 贴纸 + 菜品图</div>
  131. </div>
  132. <div class="toolbar">
  133. <button class="btn btn-ghost btn-small" id="btnRefreshGen">
  134. <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>
  135. 刷新
  136. </button>
  137. </div>
  138. </div>
  139. <div class="gen-overview">
  140. <div class="stats-grid" id="statsBar">
  141. <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>
  142. <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>
  143. <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>
  144. <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>
  145. </div>
  146. <div class="gen-controls">
  147. <div class="field">
  148. <label>扫描范围</label>
  149. <div class="scan-dropdown" id="scanDropdown">
  150. <button type="button" class="scan-dropdown-trigger" id="scanDropdownTrigger" aria-expanded="false" aria-haspopup="listbox">
  151. <span id="scanDropdownText">全量扫描</span>
  152. <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>
  153. </button>
  154. <div class="scan-dropdown-menu" id="scanDropdownMenu" role="listbox" aria-multiselectable="true" hidden></div>
  155. </div>
  156. </div>
  157. <button class="btn btn-primary" id="scanBtn">
  158. <span id="scanIcon"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg></span>
  159. <span id="scanText">扫描店铺</span>
  160. </button>
  161. </div>
  162. </div>
  163. <div class="shop-cards" id="shopList"></div>
  164. </main>
  165. </div>
  166. <!-- ==================== 历史创作 ==================== -->
  167. <div id="viewTasks" class="view">
  168. <main class="content">
  169. <div class="page-header">
  170. <div>
  171. <h1>任务中心</h1>
  172. <div class="sub">跟踪生成任务状态,服务重启后可恢复进行中的生成任务</div>
  173. </div>
  174. <div class="toolbar">
  175. <button class="btn btn-ghost btn-small" id="btnRefreshTasks">
  176. <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>
  177. 刷新
  178. </button>
  179. </div>
  180. </div>
  181. <div class="tasks-overview panel panel-designed">
  182. <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>
  183. <div class="tasks-stats">
  184. <div class="task-stat"><span>全部</span><strong id="taskCountAll">0</strong></div>
  185. <div class="task-stat running"><span>进行中</span><strong id="taskCountRunning">0</strong></div>
  186. <div class="task-stat queued"><span>排队</span><strong id="taskCountQueued">0</strong></div>
  187. <div class="task-stat success"><span>成功</span><strong id="taskCountSuccess">0</strong></div>
  188. <div class="task-stat failed"><span>失败</span><strong id="taskCountFailed">0</strong></div>
  189. </div>
  190. <div class="tasks-filters" id="taskFilters">
  191. <button class="history-filter active" data-filter="all">全部</button>
  192. <button class="history-filter" data-filter="running">进行中</button>
  193. <button class="history-filter" data-filter="queued">排队中</button>
  194. <button class="history-filter" data-filter="success">已完成</button>
  195. <button class="history-filter" data-filter="failed">失败</button>
  196. </div>
  197. </div>
  198. <div class="history-summary" id="taskSummary">正在加载任务…</div>
  199. <div class="task-list" id="taskList"></div>
  200. </main>
  201. </div>
  202. <!-- ==================== 历史创作 ==================== -->
  203. <div id="viewHistory" class="view">
  204. <main class="content">
  205. <div class="page-header">
  206. <div>
  207. <h1>历史创作</h1>
  208. <div class="sub">以图片墙回顾已生成的品牌物料与菜品作品</div>
  209. </div>
  210. <div class="toolbar">
  211. <button class="btn btn-ghost btn-small" id="btnRefreshHistory">
  212. <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>
  213. 刷新
  214. </button>
  215. </div>
  216. </div>
  217. <div class="history-toolbar panel">
  218. <div class="history-search">
  219. <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>
  220. <input type="text" id="historySearch" placeholder="搜索门店或作品名称">
  221. </div>
  222. <div class="history-filters" id="historyFilters">
  223. <button class="history-filter active" data-filter="all">全部</button>
  224. <button class="history-filter" data-filter="logo">LOGO</button>
  225. <button class="history-filter" data-filter="banner">海报</button>
  226. <button class="history-filter" data-filter="signage">招牌</button>
  227. <button class="history-filter" data-filter="sticker">贴纸</button>
  228. <button class="history-filter" data-filter="dish">菜品</button>
  229. </div>
  230. </div>
  231. <div class="history-summary" id="historySummary">加载中…</div>
  232. <div class="history-gallery" id="historyGallery"></div>
  233. </main>
  234. </div>
  235. <!-- ==================== 数据采集 ==================== -->
  236. <div id="viewReports" class="view">
  237. <main class="content">
  238. <div class="page-header">
  239. <div>
  240. <h1>数据采集</h1>
  241. <div class="sub">同步商家客户端经营数据到本地数据湖,多维报表与导出</div>
  242. </div>
  243. <div class="toolbar">
  244. <span class="conn-help-wrap">
  245. <span class="badge badge-info" id="reportConnBadge">检测连接…</span>
  246. <button type="button" class="help-btn" id="btnClientHelp" title="客户端连接排查" aria-expanded="false">?</button>
  247. <div class="help-popover" id="clientHelpPopover" hidden>
  248. <div class="help-popover-title">客户端连接排查</div>
  249. <ol class="help-popover-steps">
  250. <li><b>验证调试端口:</b>客户端带参启动后,浏览器打开 <code>http://127.0.0.1:9669/json/version</code> —— 看到一段 JSON(含 "Browser")就是通的;打不开就是客户端没带参。</li>
  251. <li><b>macOS 带参启动:</b>⌘Q 完全退出客户端(活动监视器确认进程没了)→ 终端执行:<code>open -a "淘宝闪购商家版" --args --remote-debugging-port=9669</code></li>
  252. <li><b>Windows 带参启动:</b>任务栏右键完全退出(任务管理器确认)→ 右键快捷方式 → 属性 → 「目标」末尾加 <code>--remote-debugging-port=9669</code> → 以后都用这个快捷方式启动。</li>
  253. <li>回到本页点「刷新」,徽章变「客户端已连接」后同步即可。若同步弹窗报 <code>Failed to fetch</code>,先确认服务在跑(启动窗口未关闭)。</li>
  254. </ol>
  255. <div class="help-popover-note">注意:客户端每次重启都必须带参启动,否则端口不会开启。</div>
  256. </div>
  257. </span>
  258. <button class="btn btn-ghost btn-small" id="btnReportRefresh">
  259. <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>
  260. 刷新
  261. </button>
  262. <button class="btn btn-primary btn-small" id="btnOpenSync">
  263. <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>
  264. 同步数据
  265. </button>
  266. </div>
  267. </div>
  268. <div class="entity-tabs" id="entityTabs"></div>
  269. <div class="report-toolbar panel">
  270. <div class="field report-field-sm">
  271. <label>日期范围</label>
  272. <select id="reportRangePreset">
  273. <option value="7">近 7 天</option>
  274. <option value="30" selected>近 30 天</option>
  275. <option value="90">近 90 天</option>
  276. <option value="custom">自定义</option>
  277. </select>
  278. </div>
  279. <div class="field report-field-md" id="reportCustomRange" hidden>
  280. <label>起止日期</label>
  281. <div class="report-date-range">
  282. <input type="date" id="reportFromDate">
  283. <span>–</span>
  284. <input type="date" id="reportToDate">
  285. </div>
  286. </div>
  287. <div class="field report-field-lg">
  288. <label>门店</label>
  289. <div class="scan-dropdown" id="reportShopDropdown">
  290. <button type="button" class="scan-dropdown-trigger" id="reportShopTrigger" aria-expanded="false" aria-haspopup="listbox">
  291. <span id="reportShopText">全部门店</span>
  292. <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>
  293. </button>
  294. <div class="scan-dropdown-menu" id="reportShopMenu" role="listbox" aria-multiselectable="true" hidden></div>
  295. </div>
  296. </div>
  297. <div class="field">
  298. <label>视图</label>
  299. <div class="seg-control" id="reportViewModes" role="tablist">
  300. <button type="button" class="active" data-mode="detail">明细</button>
  301. <button type="button" data-mode="shop">按门店</button>
  302. <button type="button" data-mode="date">按日期</button>
  303. </div>
  304. </div>
  305. <div class="field report-field-grow">
  306. <label>搜索门店</label>
  307. <input type="text" id="reportSearch" placeholder="输入门店名称过滤">
  308. </div>
  309. <div class="report-toolbar-actions">
  310. <button class="btn btn-ghost btn-small" id="btnColumnCfg">列设置</button>
  311. <button class="btn btn-ghost btn-small" id="btnExportCsv">导出 CSV</button>
  312. </div>
  313. </div>
  314. <div class="stats-grid" id="reportKpis"></div>
  315. <div class="panel panel-fixed report-table-panel">
  316. <div class="panel-title">
  317. <span>报表数据</span>
  318. <span class="actions"><span class="badge badge-info" id="reportRowCount">0 行</span></span>
  319. </div>
  320. <div class="report-table-wrap" id="reportTableWrap">
  321. <table class="data-table report-table" id="reportTable">
  322. <thead id="reportTableHead"></thead>
  323. <tbody id="reportTableBody"></tbody>
  324. </table>
  325. <div id="reportEmpty" class="empty-hint" hidden>
  326. <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>
  327. <p id="reportEmptyText">暂无数据<br><small>请确认商家客户端已登录,然后点击右上角「同步数据」</small></p>
  328. </div>
  329. </div>
  330. </div>
  331. </main>
  332. </div>
  333. <!-- ==================== 趋势图表 ==================== -->
  334. <div id="viewAnalysis" class="view">
  335. <main class="content">
  336. <div class="page-header">
  337. <div>
  338. <h1>趋势图表</h1>
  339. <div class="sub">主指标趋势 · 门店对比 · 转化漏斗,支持环比;有疑问点右下角问数助手</div>
  340. </div>
  341. <div class="toolbar">
  342. <button class="btn btn-ghost btn-small" id="btnExportHtml">
  343. <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>
  344. 导出周报
  345. </button>
  346. </div>
  347. </div>
  348. <div class="entity-tabs" id="analysisEntityTabs"></div>
  349. <div class="report-toolbar panel">
  350. <div class="field report-field-sm">
  351. <label>日期范围</label>
  352. <select id="analysisRangePreset">
  353. <option value="7">近 7 天</option>
  354. <option value="30" selected>近 30 天</option>
  355. <option value="90">近 90 天</option>
  356. </select>
  357. </div>
  358. <div class="field report-field-lg">
  359. <label>门店</label>
  360. <div class="scan-dropdown" id="anaShopDropdown">
  361. <button type="button" class="scan-dropdown-trigger" id="anaShopTrigger" aria-expanded="false" aria-haspopup="listbox">
  362. <span id="anaShopText">全部门店</span>
  363. <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>
  364. </button>
  365. <div class="scan-dropdown-menu" id="anaShopMenu" role="listbox" aria-multiselectable="true" hidden></div>
  366. </div>
  367. </div>
  368. <div class="field report-field-sm">
  369. <label>主指标</label>
  370. <select id="chartMetric" class="chart-metric-select"></select>
  371. </div>
  372. <label class="ana-compare-toggle" title="主趋势图中叠加上一周期的虚线对照">
  373. <input type="checkbox" id="anaComparePrev" checked>
  374. <span>对比上期</span>
  375. </label>
  376. </div>
  377. <div class="delta-strip" id="analysisDeltas"></div>
  378. <div class="ana-grid">
  379. <div class="panel ana-cell ana-span2">
  380. <div class="panel-title"><span id="anaTrendTitle">主指标趋势</span></div>
  381. <div id="chartTrend" class="chart chart-lg"></div>
  382. </div>
  383. <div class="panel ana-cell">
  384. <div class="panel-title"><span>门店排行 Top 10</span></div>
  385. <div id="chartRank" class="chart chart-lg"></div>
  386. </div>
  387. <div class="panel ana-cell">
  388. <div class="panel-title"><span>转化漏斗(曝光 → 进店 → 下单)</span></div>
  389. <div id="chartFunnel" class="chart chart-lg"></div>
  390. </div>
  391. <div class="panel ana-cell ana-span2">
  392. <div class="panel-title"><span id="anaCompareTitle">门店对比 · Top 5</span></div>
  393. <div id="chartCompare" class="chart chart-lg"></div>
  394. </div>
  395. </div>
  396. </main>
  397. </div>
  398. <!-- ==================== 系统设置 ==================== -->
  399. <div id="viewSettings" class="view">
  400. <main class="content">
  401. <div class="page-header">
  402. <div>
  403. <h1>系统设置</h1>
  404. <div class="sub">API 密钥与采集配置,保存后立即生效</div>
  405. </div>
  406. </div>
  407. <div class="settings-workspace">
  408. <div class="settings-forms">
  409. <div class="panel panel-narrow">
  410. <div class="stage-marker">
  411. <span class="num">1</span><span>API 设置</span>
  412. <div class="seg-control settings-api-tabs" id="settingsApiTabs" role="tablist">
  413. <button type="button" class="active" data-tab="image">图像模型</button>
  414. <button type="button" data-tab="chat">对话模型</button>
  415. </div>
  416. </div>
  417. <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>
  418. <div class="panel-body">
  419. <div id="settingsTabImage" class="settings-tab-body" role="tabpanel">
  420. <div class="field">
  421. <div class="field-label-row">
  422. <label>API Key<span class="req">*</span></label>
  423. <span class="label-links">
  424. <a class="settings-link settings-link-compact" href="https://o-hubs.com/api/console/keys" target="_blank" rel="noopener noreferrer">
  425. <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>
  426. <span>获取 API Key</span>
  427. </a>
  428. <a class="settings-link settings-link-compact" href="https://o-hubs.com/home" target="_blank" rel="noopener noreferrer">
  429. <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>
  430. <span>充值</span>
  431. </a>
  432. </span>
  433. </div>
  434. <input type="text" id="settingsApiKey" placeholder="sk-...">
  435. </div>
  436. <div class="field">
  437. <label>API 地址</label>
  438. <input type="text" id="settingsApiBase" value="https://api.lk888.ai" disabled>
  439. </div>
  440. <div class="field">
  441. <label>默认模型</label>
  442. <select id="settingsModel"></select>
  443. </div>
  444. <div class="settings-links">
  445. <button type="button" class="settings-link settings-link-btn" id="btnTestImage">
  446. <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>
  447. <span>测试 API 与模型</span>
  448. </button>
  449. <span id="imageTestStatus" class="badge"></span>
  450. </div>
  451. </div>
  452. <div id="settingsTabChat" class="settings-tab-body" role="tabpanel" hidden>
  453. <div class="field">
  454. <label>API Key</label>
  455. <input type="text" id="settingsChatApiKey" placeholder="留空复用生图 API Key;否则填对话模型专用 Key">
  456. <div class="help">留空则复用「图像模型」的 API Key</div>
  457. </div>
  458. <div class="field">
  459. <label>API 地址</label>
  460. <input type="text" id="settingsChatApiBase" disabled placeholder="默认与生图 API 地址一致">
  461. <div class="help">默认使用生图的 API 地址,无需修改</div>
  462. </div>
  463. <div class="field">
  464. <label>对话模型</label>
  465. <select id="settingsChatModel">
  466. <option value="deepseek-v4.1-flash">deepseek-v4.1-flash</option>
  467. <option value="glm-5.3-flash">glm-5.3-flash</option>
  468. </select>
  469. </div>
  470. <div class="settings-links">
  471. <button type="button" class="settings-link settings-link-btn" id="btnTestChat">
  472. <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>
  473. <span>测试对话模型</span>
  474. </button>
  475. <span id="chatTestStatus" class="badge"></span>
  476. </div>
  477. </div>
  478. </div>
  479. </div>
  480. <div class="panel panel-narrow">
  481. <div class="stage-marker"><span class="num">2</span><span>采集配置</span></div>
  482. <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>
  483. <div class="panel-body">
  484. <div class="field">
  485. <label>输出根目录</label>
  486. <input type="text" id="settingsRootDir" placeholder="/path/to/workspace">
  487. </div>
  488. <div class="field-row">
  489. <div class="field">
  490. <label>每页条数</label>
  491. <input type="number" id="pageSizeInput" min="1" value="50">
  492. </div>
  493. <div class="field">
  494. <label>间隔 (ms)</label>
  495. <input type="number" id="delayInput" min="0" value="300">
  496. <div class="help">建议 ≥ 200</div>
  497. </div>
  498. </div>
  499. <div class="field">
  500. <label>Cookie(淘宝闪购)</label>
  501. <textarea id="settingsCookie" rows="3" placeholder="登录 melody.shop.ele.me 后从 DevTools 复制"></textarea>
  502. <div class="help">Cookie 保存后不再回显;会话过期时粘贴新值即可更新</div>
  503. </div>
  504. <div style="display:flex;align-items:center;gap:8px;">
  505. <button class="btn btn-primary btn-small" id="btnSaveSettings">保存设置</button>
  506. <span id="settingsStatus" class="badge"></span>
  507. </div>
  508. </div>
  509. </div>
  510. </div>
  511. <div class="panel panel-fixed panel-log settings-log">
  512. <div class="panel-title">
  513. <span>实时日志</span>
  514. <span class="actions">
  515. <button class="btn btn-ghost btn-small" id="btnClearLog">清空日志</button>
  516. </span>
  517. </div>
  518. <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>
  519. <div class="log-box" id="logPanel"></div>
  520. </div>
  521. </div>
  522. </main>
  523. </div>
  524. </div>
  525. </div>
  526. <div class="toast-wrap" id="toast-wrap"></div>
  527. <!-- 智能问数 · 机器人(仅经营分析相关页面显示) -->
  528. <div class="chatbot-fab" id="chatbotFab" title="智能问数" hidden>
  529. <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"/><circle cx="9" cy="13" r="1.1" fill="currentColor" stroke="none"/><circle cx="15" cy="13" r="1.1" fill="currentColor" stroke="none"/><path d="M9.5 16.2h5"/></svg>
  530. </div>
  531. <div class="chatbot-panel" id="chatbotPanel" hidden>
  532. <div class="chatbot-head">
  533. <span class="chatbot-title">
  534. <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"/><circle cx="9" cy="13" r="1.1" fill="currentColor" stroke="none"/><circle cx="15" cy="13" r="1.1" fill="currentColor" stroke="none"/><path d="M9.5 16.2h5"/></svg>
  535. 智能问数
  536. </span>
  537. <span class="chatbot-sub">支持上下文追问</span>
  538. <button type="button" class="chatbot-icon-btn" id="btnChatClear" title="清空对话">
  539. <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 6h18"/><path d="M8 6V4h8v2"/><path d="M6 6l1 14h10l1-14"/></svg>
  540. </button>
  541. <button type="button" class="chatbot-icon-btn" id="btnChatClose" title="收起">
  542. <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 6l12 12M18 6L6 18"/></svg>
  543. </button>
  544. </div>
  545. <div class="chatbot-body" id="chatbotBody">
  546. <div class="chatbot-msg bot">
  547. <div class="chatbot-bubble">你好,我是经营数据助手。可以直接问,也支持追问 —— 比如先问「上月营业额Top5门店」,再问「那二店的单量呢」。</div>
  548. </div>
  549. <div class="chatbot-chips" id="chatbotChips">
  550. <button type="button" data-q="上月营业额Top5门店">上月营业额Top5门店</button>
  551. <button type="button" data-q="最近7天进店转化率趋势">最近7天进店转化率趋势</button>
  552. <button type="button" data-q="昨天营业额和订单总量">昨天营业额和订单总量</button>
  553. <button type="button" data-q="哪几家门店曝光高但下单少">哪几家门店曝光高但下单少</button>
  554. </div>
  555. </div>
  556. <div class="chatbot-input-row">
  557. <input type="text" id="chatbotInput" placeholder="输入问题,回车发送…" autocomplete="off">
  558. <button type="button" class="btn btn-primary btn-small" id="btnChatSend" title="发送">
  559. <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M3 11l18-8-8 18-2.5-7.5z"/></svg>
  560. </button>
  561. </div>
  562. </div>
  563. <!-- 添加店铺弹窗 -->
  564. <div class="modal-overlay" id="modalAddShop" hidden>
  565. <div class="modal">
  566. <div class="modal-header">
  567. <div class="modal-title">
  568. <span class="modal-status-icon">
  569. <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>
  570. </span>
  571. <span>添加店铺</span>
  572. </div>
  573. <button class="modal-close" id="btnCloseModal">&times;</button>
  574. </div>
  575. <div class="modal-body">
  576. <div class="field">
  577. <label>店铺清单</label>
  578. <textarea id="manualInput" rows="5" placeholder="每行一个:shopId 店铺名&#10;507068342 鑫龙福麻辣烫(康诺美食城店)"></textarea>
  579. <div class="help">每行一条,shopId 和店铺名用空格或逗号分隔</div>
  580. </div>
  581. </div>
  582. <div class="modal-footer">
  583. <button class="btn btn-ghost btn-small" id="btnCancelAdd">取消</button>
  584. <button class="btn btn-primary btn-small" id="useManualBtn">确认添加</button>
  585. </div>
  586. </div>
  587. </div>
  588. <!-- 列设置弹窗 -->
  589. <div class="modal-overlay" id="modalReportColumns" hidden>
  590. <div class="modal">
  591. <div class="modal-header">
  592. <div class="modal-title">
  593. <span class="modal-status-icon">
  594. <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>
  595. </span>
  596. <span>列设置</span>
  597. </div>
  598. <button class="modal-close" id="btnCloseColumnCfg">&times;</button>
  599. </div>
  600. <div class="modal-body">
  601. <div class="help" style="margin-bottom:8px;">勾选需要在报表中展示的字段(日期与门店名称固定展示)</div>
  602. <div class="colcfg-list" id="columnCfgList"></div>
  603. </div>
  604. <div class="modal-footer">
  605. <button class="btn btn-ghost btn-small" id="btnResetColumns">恢复默认</button>
  606. <button class="btn btn-primary btn-small" id="btnApplyColumns">应用</button>
  607. </div>
  608. </div>
  609. </div>
  610. <!-- 数据同步弹窗 -->
  611. <div class="modal-overlay" id="modalReportSync" hidden>
  612. <div class="modal modal-wide">
  613. <div class="modal-header">
  614. <div class="modal-title">
  615. <span class="modal-status-icon">
  616. <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>
  617. </span>
  618. <span>同步数据到本地</span>
  619. </div>
  620. <button class="modal-close" id="btnCloseSync">&times;</button>
  621. </div>
  622. <div class="modal-body">
  623. <div class="badge" id="syncEntityBadge">未识别主体</div>
  624. <div class="field">
  625. <label>同步范围</label>
  626. <select id="syncScope">
  627. <option value="all" selected>全部(公司 + 连锁 + 门店明细)</option>
  628. <option value="shop">仅门店明细</option>
  629. <option value="company">仅公司 / 连锁汇总</option>
  630. <option value="custom">指定门店</option>
  631. </select>
  632. </div>
  633. <div class="field" id="syncShopPickWrap" hidden>
  634. <label>选择门店</label>
  635. <div class="scan-dropdown" id="syncShopDropdown">
  636. <button type="button" class="scan-dropdown-trigger" id="syncShopTrigger" aria-expanded="false" aria-haspopup="listbox">
  637. <span id="syncShopText">选择门店</span>
  638. <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>
  639. </button>
  640. <div class="scan-dropdown-menu" id="syncShopMenu" role="listbox" aria-multiselectable="true" hidden></div>
  641. </div>
  642. </div>
  643. <div class="field">
  644. <label>日期范围</label>
  645. <select id="syncRange">
  646. <option value="7">近 7 天</option>
  647. <option value="30" selected>近 30 天</option>
  648. <option value="90">近 90 天</option>
  649. </select>
  650. <div class="help">经营数据后台保留约 180 天;当天数据未结算,同步区间右端取昨日</div>
  651. </div>
  652. <div id="syncProgressWrap" hidden>
  653. <div class="sync-progress-row">
  654. <div class="sync-progress-bar"><span id="syncProgressBar"></span></div>
  655. <span class="badge badge-info" id="syncProgressText">0 / 0</span>
  656. </div>
  657. <div class="sync-stat-row">
  658. <span class="badge badge-ok" id="syncStatAdded">新增 0</span>
  659. <span class="badge badge-info" id="syncStatUpdated">更新 0</span>
  660. <span class="badge badge-fail" id="syncStatFailed">失败 0</span>
  661. <button class="btn btn-ghost btn-small" id="btnSyncCancel" hidden>取消</button>
  662. </div>
  663. <div class="sync-steps" id="syncSteps"></div>
  664. </div>
  665. <div class="help" id="syncHint">同步会把所选区间逐日、逐店的经营数据写入本地数据湖(data/reports/),可反复同步自动去重合并。</div>
  666. </div>
  667. <div class="modal-footer">
  668. <button class="btn btn-ghost btn-small" id="btnCancelSync">关闭</button>
  669. <button class="btn btn-primary btn-small" id="btnStartSync">开始同步</button>
  670. </div>
  671. </div>
  672. </div>
  673. <script src="/crawl.js"></script>
  674. <script src="/gen.js"></script>
  675. <script src="/tasks.js"></script>
  676. <script src="/history.js"></script>
  677. <script src="/vendor/echarts.min.js"></script>
  678. <script src="/reports.js"></script>
  679. <script src="/app.js"></script>
  680. </body>
  681. </html>