build-demos.mjs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. import fs from 'fs'
  2. import path from 'path'
  3. import { fileURLToPath } from 'url'
  4. const __dirname = path.dirname(fileURLToPath(import.meta.url))
  5. const themes = [
  6. {
  7. id: '01',
  8. file: '01-dark-sidebar.html',
  9. title: '01 · 深色侧栏藏青',
  10. desc: '侧栏 #0f172a · 点缀天蓝 · Logo 绿最跳眼',
  11. note: '风格差异大:深色导航 + 白内容区,很多 LIS/HIS 系统采用;Logo 绿色会非常醒目。',
  12. primary: '#38bdf8',
  13. dark: '#0ea5e9',
  14. bg: 'rgba(56, 189, 248, 0.14)',
  15. border: '#e2e8f0',
  16. swatches: ['#0f172a', '#38bdf8', '#f8fafc', '#e2e8f0'],
  17. darkSidebar: true,
  18. recommended: true
  19. },
  20. {
  21. id: '02',
  22. file: '02-warm-stone.html',
  23. title: '02 · 暖石米灰',
  24. desc: '暖灰主色 #57534e · 米白底 · 不冷不艳',
  25. note: '偏纸质/档案感,长时间看不疲劳;适合财务、合规、大量表格录入。',
  26. primary: '#57534e',
  27. dark: '#44403c',
  28. bg: '#f5f5f4',
  29. border: '#e7e5e4',
  30. pageBg: '#fafaf9',
  31. tableHead: '#f5f5f4',
  32. text: '#292524',
  33. textMuted: '#78716c',
  34. swatches: ['#57534e', '#44403c', '#fafaf9', '#e7e5e4'],
  35. warm: true
  36. },
  37. {
  38. id: '03',
  39. file: '03-medical-blue.html',
  40. title: '03 · 经典医院蓝',
  41. desc: '主色 #0077b6 · 传统医疗信息系统气质',
  42. note: '比 Tailwind 蓝更「医院感」;稳重、熟悉,中老年用户接受度高。',
  43. primary: '#0077b6',
  44. dark: '#023e8a',
  45. bg: '#e8f4fa',
  46. border: '#bde0fe',
  47. swatches: ['#0077b6', '#023e8a', '#e8f4fa', '#bde0fe']
  48. },
  49. {
  50. id: '04',
  51. file: '04-zinc-minimal.html',
  52. title: '04 · 锌灰极简',
  53. desc: '几乎无彩色 · 主按钮 #18181b · Logo 是唯一亮点',
  54. note: '极简 B 端:界面本身不抢戏,状态 Tag 保留语义色;适合数据密集页面。',
  55. primary: '#52525b',
  56. dark: '#3f3f46',
  57. bg: '#f4f4f5',
  58. border: '#e4e4e7',
  59. cta: '#18181b',
  60. ctaDark: '#27272a',
  61. swatches: ['#52525b', '#18181b', '#fafafa', '#e4e4e7']
  62. },
  63. {
  64. id: '05',
  65. file: '05-soft-violet.html',
  66. title: '05 · 薄雾紫',
  67. desc: '主色 #7c6fad · 舒缓、偏医疗康养',
  68. note: '低饱和紫,比靛蓝更柔;适合客服、客户档案类模块,主列表页需实测。',
  69. primary: '#7c6fad',
  70. dark: '#6b5b95',
  71. bg: '#f3f0f8',
  72. border: '#ddd6fe',
  73. swatches: ['#7c6fad', '#6b5b95', '#f3f0f8', '#ddd6fe']
  74. },
  75. {
  76. id: '06',
  77. file: '06-navy-authority.html',
  78. title: '06 · 墨蓝权威',
  79. desc: '主色 #1e3a5f · 传统企业门户',
  80. note: '深藏青,权威合规感强;与绿色 Logo 对比清晰,适合审批流、协议类页面。',
  81. primary: '#1e3a5f',
  82. dark: '#152a45',
  83. bg: '#eef2f6',
  84. border: '#c5d0dc',
  85. swatches: ['#1e3a5f', '#152a45', '#eef2f6', '#c5d0dc']
  86. }
  87. ]
  88. function themeJump(currentId) {
  89. return themes
  90. .map((t) => {
  91. const cls = t.id === currentId ? 'is-current' : ''
  92. return `<a class="${cls}" href="${t.file}">${t.id}</a>`
  93. })
  94. .join('')
  95. }
  96. function swatchesHtml(theme) {
  97. const labels = theme.cta
  98. ? ['导航', '按钮', '浅底', '边']
  99. : theme.darkSidebar
  100. ? ['侧栏', '点缀', '内容', '边']
  101. : ['主色', '深', '浅底', '边']
  102. return theme.swatches
  103. .map((c, i) => `<span class="swatch"><i style="background:${c}"></i>${labels[i]}</span>`)
  104. .join('')
  105. }
  106. function rootVars(theme) {
  107. const lines = [
  108. ` --primary: ${theme.primary};`,
  109. ` --primary-dark: ${theme.dark};`,
  110. ` --primary-bg: ${theme.bg};`,
  111. ` --text: ${theme.text || '#1e293b'};`,
  112. ` --text-muted: ${theme.textMuted || '#64748b'};`,
  113. ` --border: ${theme.border};`,
  114. ` --bg-page: ${theme.pageBg || '#fefefe'};`,
  115. ` --surface: #ffffff;`,
  116. ` --table-head: ${theme.tableHead || '#f8fafc'};`,
  117. ` --row-alt: ${theme.warm ? '#f5f5f4' : '#fafafa'};`,
  118. ` --row-hover: ${theme.warm ? '#ececea' : '#f1f5f9'};`,
  119. ` --shadow: 0 1px 3px rgba(0, 0, 0, 0.05);`,
  120. ` --radius: 10px;`
  121. ]
  122. if (theme.darkSidebar) {
  123. lines.push(` --sidebar-bg: #0f172a;`, ` --sidebar-border: #1e293b;`)
  124. }
  125. if (theme.cta) {
  126. lines.push(` --cta: ${theme.cta};`, ` --cta-dark: ${theme.ctaDark};`)
  127. }
  128. return lines.join('\n')
  129. }
  130. function renderPage(theme) {
  131. const badge = theme.recommended ? '<span class="demo-badge">推荐</span>' : ''
  132. const bodyClass = theme.warm ? ' class="layout-warm"' : ''
  133. const sidebarClass = theme.darkSidebar ? 'sidebar sidebar--dark' : 'sidebar'
  134. return `<!doctype html>
  135. <html lang="zh-CN">
  136. <head>
  137. <meta charset="UTF-8" />
  138. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  139. <title>${theme.title} · 管理端界面 Demo B</title>
  140. <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
  141. <style>
  142. :root {
  143. ${rootVars(theme)}
  144. }
  145. </style>
  146. <link rel="stylesheet" href="_demo-styles.css" />
  147. </head>
  148. <body${bodyClass}>
  149. <div class="demo-banner">
  150. <div>
  151. <h1>${badge}${theme.title}</h1>
  152. <p>${theme.desc}</p>
  153. </div>
  154. <div class="demo-banner__actions">
  155. <div class="swatch-row">${swatchesHtml(theme)}</div>
  156. <div class="theme-jump">${themeJump(theme.id)}</div>
  157. <a class="link-back" href="index.html">选型入口</a>
  158. </div>
  159. </div>
  160. <div class="app-layout">
  161. <aside class="${sidebarClass}">
  162. <div class="brand">
  163. <img class="brand-logo" src="../../web-admin/public/logo.png" alt="中科兴皖" width="36" height="36" />
  164. <div class="brand-text">中科兴皖<small>一体化门户</small></div>
  165. </div>
  166. <div class="nav">
  167. <div class="nav-group">
  168. <div class="nav-group-title">市场运营</div>
  169. <a href="#">工作台</a>
  170. <a href="#" class="active">采集协议单</a>
  171. <a href="#">样本接收</a>
  172. <a href="#">缴费确认</a>
  173. </div>
  174. <div class="nav-group">
  175. <div class="nav-group-title">质量管理</div>
  176. <a href="#">质检工单</a>
  177. <a href="#">环境检测</a>
  178. </div>
  179. <div class="nav-group">
  180. <div class="nav-group-title">系统</div>
  181. <a href="#">用户管理</a>
  182. <a href="#">角色权限</a>
  183. </div>
  184. </div>
  185. </aside>
  186. <div class="main-col">
  187. <header class="topbar">
  188. <div class="crumb">市场运营 / <b>采集协议单</b></div>
  189. <div class="topbar-right">
  190. <span>管理员</span>
  191. <div class="avatar">管</div>
  192. </div>
  193. </header>
  194. <div class="work-tabs">
  195. <div class="work-tab">工作台</div>
  196. <div class="work-tab is-active">采集协议单</div>
  197. <div class="work-tab">样本接收</div>
  198. </div>
  199. <div class="content">
  200. <section class="panel">
  201. <div class="panel-top">
  202. <h2 class="title-bar-heading">采集协议单</h2>
  203. </div>
  204. <div class="entity-switch">
  205. <div class="entity-card is-active">
  206. <div class="entity-card__icon">📄</div>
  207. <div>
  208. <div class="entity-card__title">协议单管理</div>
  209. <div class="entity-card__desc">协议审核与状态维护</div>
  210. </div>
  211. </div>
  212. <div class="entity-card">
  213. <div class="entity-card__icon">💰</div>
  214. <div>
  215. <div class="entity-card__title">缴费信息</div>
  216. <div class="entity-card__desc">缴费计划与到账</div>
  217. </div>
  218. </div>
  219. <div class="entity-card">
  220. <div class="entity-card__icon">🏅</div>
  221. <div>
  222. <div class="entity-card__title">储存证管理</div>
  223. <div class="entity-card__desc">储存证开具管理</div>
  224. </div>
  225. </div>
  226. <div class="entity-card">
  227. <div class="entity-card__icon">📅</div>
  228. <div>
  229. <div class="entity-card__title">满月待办</div>
  230. <div class="entity-card__desc">满月事项跟进</div>
  231. </div>
  232. </div>
  233. </div>
  234. <div class="filter-block">
  235. <div class="filter-grid">
  236. <div class="field">
  237. <label>关键词</label>
  238. <input class="input" type="text" placeholder="协议编号 / 客户姓名 / 手机号" />
  239. </div>
  240. <div class="field">
  241. <label>协议状态</label>
  242. <select class="select"><option>全部</option><option>生效中</option><option>待生效</option></select>
  243. </div>
  244. <div class="field">
  245. <label>预产期起</label>
  246. <input class="input" type="date" />
  247. </div>
  248. <div class="field">
  249. <label>预产期止</label>
  250. <input class="input" type="date" />
  251. </div>
  252. </div>
  253. <div class="filter-actions">
  254. <button type="button" class="btn btn-primary">查询</button>
  255. <button type="button" class="btn">重置</button>
  256. </div>
  257. </div>
  258. <div class="list-toolbar">
  259. <span class="sel-num">已选 <b>2</b> 项</span>
  260. <button type="button" class="btn btn-primary">新增协议</button>
  261. <button type="button" class="btn">批量审核</button>
  262. <button type="button" class="btn">导出</button>
  263. </div>
  264. <div class="list-body">
  265. <table>
  266. <thead>
  267. <tr>
  268. <th><input type="checkbox" checked /></th>
  269. <th>序号</th>
  270. <th>状态</th>
  271. <th>协议编号</th>
  272. <th>客户姓名</th>
  273. <th>手机号</th>
  274. <th>预产期</th>
  275. <th>缴费状态</th>
  276. <th>创建时间</th>
  277. <th>操作</th>
  278. </tr>
  279. </thead>
  280. <tbody>
  281. <tr>
  282. <td><input type="checkbox" checked /></td>
  283. <td>1</td>
  284. <td><span class="tag tag-primary">生效中</span></td>
  285. <td>XY-2026-0042</td>
  286. <td>张女士</td>
  287. <td>138****6621</td>
  288. <td>2026-08-12</td>
  289. <td><span class="tag tag-success">已缴费</span></td>
  290. <td>2026-03-01 10:22</td>
  291. <td>
  292. <button class="btn-op primary">查看</button>
  293. <button class="btn-op">编辑</button>
  294. <button class="btn-op">审核</button>
  295. </td>
  296. </tr>
  297. <tr>
  298. <td><input type="checkbox" checked /></td>
  299. <td>2</td>
  300. <td><span class="tag tag-info">待生效</span></td>
  301. <td>XY-2026-0038</td>
  302. <td>李先生</td>
  303. <td>139****8890</td>
  304. <td>2026-07-20</td>
  305. <td><span class="tag tag-warning">缴费确认中</span></td>
  306. <td>2026-02-28 15:06</td>
  307. <td>
  308. <button class="btn-op primary">查看</button>
  309. <button class="btn-op">编辑</button>
  310. <button class="btn-op">生效</button>
  311. </td>
  312. </tr>
  313. <tr>
  314. <td><input type="checkbox" /></td>
  315. <td>3</td>
  316. <td><span class="tag tag-warning">已过期</span></td>
  317. <td>XY-2025-1201</td>
  318. <td>王女士</td>
  319. <td>136****3345</td>
  320. <td>2025-12-08</td>
  321. <td><span class="tag tag-success">已缴费</span></td>
  322. <td>2026-02-28 09:18</td>
  323. <td>
  324. <button class="btn-op primary">查看</button>
  325. <button class="btn-op">续签</button>
  326. </td>
  327. </tr>
  328. <tr>
  329. <td><input type="checkbox" /></td>
  330. <td>4</td>
  331. <td><span class="tag tag-danger">已作废</span></td>
  332. <td>XY-2025-0899</td>
  333. <td>赵先生</td>
  334. <td>137****2210</td>
  335. <td>2025-10-15</td>
  336. <td><span class="tag tag-info">待缴费</span></td>
  337. <td>2025-09-20 14:40</td>
  338. <td>
  339. <button class="btn-op primary">查看</button>
  340. </td>
  341. </tr>
  342. </tbody>
  343. </table>
  344. </div>
  345. <div class="pager">
  346. <span>共 128 条</span>
  347. <button class="pager-btn">‹</button>
  348. <button class="pager-btn is-active">1</button>
  349. <button class="pager-btn">2</button>
  350. <button class="pager-btn">3</button>
  351. <button class="pager-btn">›</button>
  352. <span>10 条/页</span>
  353. </div>
  354. </section>
  355. <div class="demo-bottom">
  356. <div>
  357. <div class="stats-row">
  358. <div class="stat-card">
  359. <div class="label">本月新增协议</div>
  360. <div class="value">42</div>
  361. </div>
  362. <div class="stat-card">
  363. <div class="label">生效中</div>
  364. <div class="value">86</div>
  365. </div>
  366. <div class="stat-card warn">
  367. <div class="label">待缴费</div>
  368. <div class="value">12</div>
  369. </div>
  370. </div>
  371. <p class="theme-note">${theme.note}</p>
  372. </div>
  373. <div class="dialog-preview">
  374. <div class="dialog-head">查看协议 · XY-2026-0042</div>
  375. <div class="dialog-body">
  376. <div class="form-grid-2">
  377. <div class="field">
  378. <label>协议状态</label>
  379. <div><span class="tag tag-primary">生效中</span></div>
  380. </div>
  381. <div class="field">
  382. <label>缴费状态</label>
  383. <div><span class="tag tag-success">已缴费</span></div>
  384. </div>
  385. <div class="field">
  386. <label>客户姓名</label>
  387. <input class="input" value="张女士" readonly />
  388. </div>
  389. <div class="field">
  390. <label>手机号</label>
  391. <input class="input" value="138****6621" readonly />
  392. </div>
  393. <div class="field" style="grid-column: 1 / -1">
  394. <label>备注</label>
  395. <input class="input" value="—" readonly />
  396. </div>
  397. </div>
  398. </div>
  399. <div class="dialog-foot">
  400. <button class="btn">关闭</button>
  401. <button class="btn btn-primary">确定</button>
  402. </div>
  403. </div>
  404. </div>
  405. </div>
  406. </div>
  407. </div>
  408. </body>
  409. </html>`
  410. }
  411. for (const theme of themes) {
  412. fs.writeFileSync(path.join(__dirname, theme.file), renderPage(theme))
  413. console.log('written', theme.file)
  414. }