replace-hex-to-token.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. #!/usr/bin/env node
  2. /**
  3. * 批量替换硬编码色值为 SCSS 令牌(CSS style 块内,零视觉风险)
  4. * 用法:
  5. * node scripts/replace-hex-to-token.js --batch gray # 仅灰阶
  6. * node scripts/replace-hex-to-token.js --batch brand # 仅品牌/状态色
  7. * node scripts/replace-hex-to-token.js # 全部
  8. */
  9. const fs = require('fs')
  10. const path = require('path')
  11. const ROOT = path.resolve(__dirname, '..')
  12. const EXCLUDE_DIRS = new Set(['node_modules', 'dist', 'unpackage', '.git', 'docs'])
  13. const ALL_RULES = [
  14. // ---- 灰阶(neutral)----
  15. ['#FFFFFF', '$app-surface'],
  16. ['#ffffff', '$app-surface'],
  17. ['#FFF', '$app-surface'],
  18. ['#fff', '$app-surface'],
  19. ['#333333', '$facade-text-strong'],
  20. ['#333', '$facade-text-strong'],
  21. ['#666666', '$facade-text-body'],
  22. ['#666', '$facade-text-body'],
  23. ['#999999', '$facade-text-muted'],
  24. ['#999', '$facade-text-muted'],
  25. ['#F5F5F5', '$facade-fill-2'],
  26. ['#f5f5f5', '$facade-fill-2'],
  27. ['#F0F0F0', '$facade-fill'],
  28. ['#f0f0f0', '$facade-fill'],
  29. ['#FAFAFA', '$facade-fill-ghost'],
  30. ['#fafafa', '$facade-fill-ghost'],
  31. ['#F9FAFB', '$facade-fill-gray-50'],
  32. ['#f9fafb', '$facade-fill-gray-50'],
  33. ['#BBBBBB', '$facade-text-ghost-light'],
  34. ['#bbbbbb', '$facade-text-ghost-light'],
  35. ['#BBB', '$facade-text-ghost-light'],
  36. ['#bbb', '$facade-text-ghost-light'],
  37. ['#CCCCCC', '$facade-text-ghost'],
  38. ['#cccccc', '$facade-text-ghost'],
  39. ['#CCC', '$facade-text-ghost'],
  40. ['#ccc', '$facade-text-ghost'],
  41. ['#DDDDDD', '$facade-border-soft'],
  42. ['#dddddd', '$facade-border-soft'],
  43. ['#DDD', '$facade-border-soft'],
  44. ['#ddd', '$facade-border-soft'],
  45. ['#EEEEEE', '$facade-hairline-2'],
  46. ['#eeeeee', '$facade-hairline-2'],
  47. ['#EEE', '$facade-hairline-2'],
  48. ['#eee', '$facade-hairline-2'],
  49. // ---- 品牌/状态色(brand/status)----
  50. ['#F97316', '$brand-primary'],
  51. ['#f97316', '$brand-primary'],
  52. ['#FF8C42', '$dim-body'],
  53. ['#ff8c42', '$dim-body'],
  54. ['#FFF7ED', '$uni-primary-pale'],
  55. ['#fff7ed', '$uni-primary-pale'],
  56. ['#FB923C', '$brand-primary-light'],
  57. ['#fb923c', '$brand-primary-light'],
  58. ['#10B981', '$status-success'],
  59. ['#10b981', '$status-success'],
  60. ['#EF4444', '$status-danger'],
  61. ['#ef4444', '$status-danger'],
  62. ['#6366F1', '$dim-wisdom'],
  63. ['#6366f1', '$dim-wisdom'],
  64. ['#C62828', '$status-danger-strong-2'],
  65. ['#c62828', '$status-danger-strong-2'],
  66. // ---- 第二轮(高频色,2026-10-20 批量收割)----
  67. ['#94A3B8', '$app-text-subtle'],
  68. ['#94a3b8', '$app-text-subtle'],
  69. ['#1E293B', '$uni-text-color'],
  70. ['#1e293b', '$uni-text-color'],
  71. ['#64748B', '$uni-text-color-secondary'],
  72. ['#64748b', '$uni-text-color-secondary'],
  73. ['#E2E8F0', '$uni-border-color-light'],
  74. ['#e2e8f0', '$uni-border-color-light'],
  75. ['#F5F7FA', '$app-bg'],
  76. ['#f5f7fa', '$app-bg'],
  77. ['#667EEA', '$accent-indigo'],
  78. ['#667eea', '$accent-indigo'],
  79. ['#9CA3AF', '$facade-text-gray-400'],
  80. ['#9ca3af', '$facade-text-gray-400'],
  81. ['#FFF3E0', '$fortune-1-tint'],
  82. ['#fff3e0', '$fortune-1-tint'],
  83. ['#FED7AA', '$brand-primary-light-2'],
  84. ['#fed7aa', '$brand-primary-light-2'],
  85. ['#F1F5F9', '$facade-fill-slate'],
  86. ['#f1f5f9', '$facade-fill-slate'],
  87. ['#4CAF50', '$mat-green'],
  88. ['#4caf50', '$mat-green'],
  89. ['#E8F5E9', '$mat-green-tint'],
  90. ['#e8f5e9', '$mat-green-tint'],
  91. ['#F59E0B', '$dim-wealth'],
  92. ['#f59e0b', '$dim-wealth'],
  93. ['#F8F8F8', '$facade-fill-page'],
  94. ['#f8f8f8', '$facade-fill-page'],
  95. ['#FEF3C7', '$status-warning-soft'],
  96. ['#fef3c7', '$status-warning-soft'],
  97. ['#E65100', '$mat-orange-strong'],
  98. ['#e65100', '$mat-orange-strong'],
  99. ['#2E7D32', '$mat-green-strong'],
  100. ['#2e7d32', '$mat-green-strong'],
  101. ['#888888', '$facade-text-faint'],
  102. ['#888', '$facade-text-faint'],
  103. ['#F3F4F6', '$facade-fill-gray-100'],
  104. ['#f3f4f6', '$facade-fill-gray-100'],
  105. ['#E3F2FD', '$mat-blue-tint'],
  106. ['#e3f2fd', '$mat-blue-tint'],
  107. ['#EA580C', '$brand-primary-dark'],
  108. ['#ea580c', '$brand-primary-dark'],
  109. ['#E5E7EB', '$facade-gray-200'],
  110. ['#e5e7eb', '$facade-gray-200'],
  111. ['#E0E0E0', '$facade-hairline'],
  112. ['#e0e0e0', '$facade-hairline'],
  113. ['#1F2937', '$facade-text-gray-800'],
  114. ['#1f2937', '$facade-text-gray-800'],
  115. ['#6B7280', '$facade-text-slate'],
  116. ['#6b7280', '$facade-text-slate'],
  117. ['#F5FAFE', '$facade-fill-blue-ghost'],
  118. ['#f5fafe', '$facade-fill-blue-ghost'],
  119. ['#CBD5E1', '$uni-border-color'],
  120. ['#cbd5e1', '$uni-border-color'],
  121. ['#374151', '$facade-text-gray-700'],
  122. ['#374151', '$facade-text-gray-700'],
  123. ['#764BA2', '$accent-indigo-deep'],
  124. ['#764ba2', '$accent-indigo-deep'],
  125. ['#F8FAFC', '$facade-fill-slate-50'],
  126. ['#f8fafc', '$facade-fill-slate-50'],
  127. ['#FFD700', '$accent-gold'],
  128. ['#ffd700', '$accent-gold'],
  129. ['#1565C0', '$mat-blue-strong'],
  130. ['#1565c0', '$mat-blue-strong'],
  131. ['#DC2626', '$status-danger-deep'],
  132. ['#dc2626', '$status-danger-deep'],
  133. ['#475569', '$facade-text-slate-deep'],
  134. ['#475569', '$facade-text-slate-deep'],
  135. ['#8B5CF6', '$facade-violet'],
  136. ['#8b5cf6', '$facade-violet'],
  137. ['#334155', '$facade-text-slate-700'],
  138. ['#334155', '$facade-text-slate-700'],
  139. ['#FFB366', '$dim-body-light'],
  140. ['#ffb366', '$dim-body-light'],
  141. ['#FFEBEE', '$fortune-0-tint'],
  142. ['#ffebee', '$fortune-0-tint'],
  143. ['#34D399', '$status-success-light-3'],
  144. ['#34d399', '$status-success-light-3'],
  145. ['#1A1A2E', '$app-ink-night'],
  146. ['#1a1a2e', '$app-ink-night'],
  147. ['#E8E8E8', '$facade-fill-4'],
  148. ['#e8e8e8', '$facade-fill-4'],
  149. ['#16A34A', '$status-success-strong-2'],
  150. ['#16a34a', '$status-success-strong-2'],
  151. ['#FEE2E2', '$status-danger-soft'],
  152. ['#fee2e2', '$status-danger-soft'],
  153. ['#C2410C', '$brand-primary-deep'],
  154. ['#c2410c', '$brand-primary-deep'],
  155. ['#FFF7E6', '$brand-primary-tint-2'],
  156. ['#fff7e6', '$brand-primary-tint-2'],
  157. ['#22C55E', '$status-success-2'],
  158. ['#22c55e', '$status-success-2'],
  159. ['#3B82F6', '$status-info'],
  160. ['#3b82f6', '$status-info'],
  161. ['#2196F3', '$mat-blue-base'],
  162. ['#2196f3', '$mat-blue-base'],
  163. ['#D97706', '$status-warning-deep'],
  164. ['#d97706', '$status-warning-deep'],
  165. ['#0EA5E9', '$uni-info'],
  166. ['#0ea5e9', '$uni-info'],
  167. ['#DCFCE7', '$status-success-soft-2'],
  168. ['#dcfce7', '$status-success-soft-2'],
  169. ['#555555', '$facade-text-deep'],
  170. ['#555', '$facade-text-deep'],
  171. ['#818CF8', '$dim-wisdom-light'],
  172. ['#818cf8', '$dim-wisdom-light'],
  173. ['#FF9800', '$mat-orange-base'],
  174. ['#ff9800', '$mat-orange-base'],
  175. ['#F5F9FC', '$uni-bg-color'],
  176. ['#f5f9fc', '$uni-bg-color'],
  177. ['#F0FDF4', '$status-success-tint-2'],
  178. ['#f0fdf4', '$status-success-tint-2'],
  179. ['#D1FAE5', '$status-success-soft'],
  180. ['#d1fae5', '$status-success-soft'],
  181. ['#DBEAFE', '$status-info-soft'],
  182. ['#dbeafe', '$status-info-soft'],
  183. ['#92400E', '$status-warning-strong'],
  184. ['#92400e', '$status-warning-strong'],
  185. // ---- 第四轮(flat 设计主题,2026-10-20 收编为 $flat-* 全局令牌)----
  186. ['#4ECDC4', '$flat-teal'],
  187. ['#4ecdc4', '$flat-teal'],
  188. ['#44B09E', '$flat-teal-deep'],
  189. ['#44b09e', '$flat-teal-deep'],
  190. ['#A8E063', '$flat-green'],
  191. ['#a8e063', '$flat-green'],
  192. ['#56AB2F', '$flat-green-deep'],
  193. ['#56ab2f', '$flat-green-deep'],
  194. ['#FFF9F0', '$flat-bg'],
  195. ['#fff9f0', '$flat-bg'],
  196. ['#FFF5EB', '$flat-card'],
  197. ['#fff5eb', '$flat-card'],
  198. ['#FFF0E6', '$flat-warm'],
  199. ['#fff0e6', '$flat-warm'],
  200. ['#F5F0EB', '$flat-line'],
  201. ['#f5f0eb', '$flat-line'],
  202. ['#2D3436', '$flat-ink'],
  203. ['#2d3436', '$flat-ink'],
  204. ['#B2BEC3', '$flat-muted'],
  205. ['#b2bec3', '$flat-muted'],
  206. ['#636E72', '$flat-muted-2'],
  207. ['#636e72', '$flat-muted-2'],
  208. ['#FFE0E0', '$flat-danger-line'],
  209. ['#ffe0e0', '$flat-danger-line'],
  210. ['#E17055', '$flat-orange-text'],
  211. ['#e17055', '$flat-orange-text'],
  212. ['#FFB088', '$flat-orange-soft'],
  213. ['#ffb088', '$flat-orange-soft'],
  214. ['#FFD93D', '$flat-bar'],
  215. ['#ffd93d', '$flat-bar'],
  216. // ---- 第五轮(现有令牌补登记,2026-10-20)----
  217. ['#059669', '$status-success-deep'],
  218. ['#059669', '$status-success-deep'],
  219. ['#FF6B35', '$mind-accent'],
  220. ['#ff6b35', '$mind-accent'],
  221. ['#D1D5DB', '$app-border-strong'],
  222. ['#d1d5db', '$app-border-strong'],
  223. ['#FFEDD5', '$facade-brand-tint-2'],
  224. ['#ffedd5', '$facade-brand-tint-2'],
  225. ['#B45309', '$status-warning-strong-2'],
  226. ['#b45309', '$status-warning-strong-2'],
  227. ['#16213E', '$app-ink-night-2'],
  228. ['#16213e', '$app-ink-night-2'],
  229. ['#ECFDF5', '$status-success-tint'],
  230. ['#ecfdf5', '$status-success-tint'],
  231. ['#FFFBEB', '$dim-wealth-tint'],
  232. ['#fffbeb', '$dim-wealth-tint'],
  233. ['#9A3412', '$mat-orange-deep'],
  234. ['#9a3412', '$mat-orange-deep'],
  235. ['#FDE68A', '$status-warning-light'],
  236. ['#fde68a', '$status-warning-light'],
  237. ['#F0F4FF', '$dim-wisdom-tint-3'],
  238. ['#f0f4ff', '$dim-wisdom-tint-3'],
  239. ['#2563EB', '$status-info-deep'],
  240. ['#2563eb', '$status-info-deep'],
  241. ['#FFF8E1', '$mat-amber-tint'],
  242. ['#fff8e1', '$mat-amber-tint'],
  243. ['#0284C7', '$status-info-deep-2'],
  244. ['#0284c7', '$status-info-deep-2'],
  245. ['#1890FF', '$status-info-3'],
  246. ['#1890ff', '$status-info-3'],
  247. ['#EEF2FF', '$dim-wisdom-tint-2'],
  248. ['#eef2ff', '$dim-wisdom-tint-2'],
  249. ['#F9F9F9', '$facade-fill-ghost-2'],
  250. ['#f9f9f9', '$facade-fill-ghost-2'],
  251. ['#EC4899', '$accent-pink'],
  252. ['#ec4899', '$accent-pink'],
  253. // ---- 第六轮(新增全局令牌,2026-10-20)----
  254. ['#FFF0E0', '$warm-peach'],
  255. ['#fff0e0', '$warm-peach'],
  256. ['#8A94A6', '$text-steel'],
  257. ['#8a94a6', '$text-steel'],
  258. ['#2A2A4A', '$game-ink'],
  259. ['#2a2a4a', '$game-ink'],
  260. ['#1F2D3D', '$ink-steel'],
  261. ['#1f2d3d', '$ink-steel'],
  262. ['#EDE9FE', '$violet-tint-100'],
  263. ['#ede9fe', '$violet-tint-100'],
  264. ['#8D6E63', '$brown-warm'],
  265. ['#8d6e63', '$brown-warm'],
  266. ['#F56C6C', '$el-danger'],
  267. ['#f56c6c', '$el-danger'],
  268. ['#F44336', '$mat-red'],
  269. ['#f44336', '$mat-red'],
  270. ['#52C41A', '$status-success-3'],
  271. ['#52c41a', '$status-success-3'],
  272. ['#1A1A1A', '$text-charcoal'],
  273. ['#1a1a1a', '$text-charcoal'],
  274. ['#AAA', '$text-ghost-soft'],
  275. ['#aaa', '$text-ghost-soft'],
  276. // ---- 第七轮(2026-10-21)----
  277. ['#FEF2F2', '$status-danger-tint-2'],
  278. ['#fef2f2', '$status-danger-tint-2'],
  279. ['#FFA500', '$accent-gold-deep'],
  280. ['#ffa500', '$accent-gold-deep'],
  281. ['#38BDF8', '$status-info-light'],
  282. ['#38bdf8', '$status-info-light'],
  283. ['#FF4D4F', '$status-danger-2'],
  284. ['#ff4d4f', '$status-danger-2'],
  285. ['#E6F7FF', '$status-info-tint-3'],
  286. ['#e6f7ff', '$status-info-tint-3'],
  287. ['#FF6B9D', '$dim-mind'],
  288. ['#ff6b9d', '$dim-mind'],
  289. ['#1E3A5F', '$accent-navy'],
  290. ['#1e3a5f', '$accent-navy'],
  291. ['#1976D2', '$mat-blue'],
  292. ['#1976d2', '$mat-blue'],
  293. ['#FBBF24', '$status-warning'],
  294. ['#fbbf24', '$status-warning'],
  295. ['#FFF1E6', '$tint-warm-soft'],
  296. ['#fff1e6', '$tint-warm-soft'],
  297. ['#BAE6FD', '$tint-sky-line'],
  298. ['#bae6fd', '$tint-sky-line'],
  299. ['#F7F7F7', '$facade-fill-cool'],
  300. ['#f7f7f7', '$facade-fill-cool'],
  301. ['#F6FFED', '$status-success-tint-3'],
  302. ['#f6ffed', '$status-success-tint-3'],
  303. ['#FECACA', '$danger-line'],
  304. ['#fecaca', '$danger-line'],
  305. ['#BBDEFB', '$mat-blue-light'],
  306. ['#bbdefb', '$mat-blue-light'],
  307. ['#E0E7FF', '$accent-indigo-tint'],
  308. ['#e0e7ff', '$accent-indigo-tint'],
  309. ['#F57F17', '$mat-amber-strong'],
  310. ['#f57f17', '$mat-amber-strong'],
  311. ['#F0F9FF', '$tint-sky-soft'],
  312. ['#f0f9ff', '$tint-sky-soft'],
  313. ['#A5B4FC', '$indigo-light'],
  314. ['#a5b4fc', '$indigo-light'],
  315. ['#F57C00', '$mat-orange-700'],
  316. ['#f57c00', '$mat-orange-700'],
  317. ['#7C2D12', '$mat-orange-900'],
  318. ['#7c2d12', '$mat-orange-900'],
  319. ['#FFE66D', '$accent-yellow-light'],
  320. ['#ffe66d', '$accent-yellow-light'],
  321. ['#E64340', '$wechat-red'],
  322. ['#e64340', '$wechat-red'],
  323. ['#8A6D3B', '$brown-olive'],
  324. ['#8a6d3b', '$brown-olive'],
  325. ['#67C23A', '$el-success'],
  326. ['#67c23a', '$el-success'],
  327. ['#E5E5E5', '$facade-line'],
  328. ['#e5e5e5', '$facade-line'],
  329. ['#E0F2FE', '$tint-sky-tint'],
  330. ['#e0f2fe', '$tint-sky-tint'],
  331. ['#047857', '$status-success-deep-2'],
  332. ['#047857', '$status-success-deep-2'],
  333. ['#FF6B6B', '$accent-coral'],
  334. ['#ff6b6b', '$accent-coral'],
  335. ['#FCD34D', '$accent-amber-light'],
  336. ['#fcd34d', '$accent-amber-light'],
  337. ['#42A5F5', '$mat-blue-400'],
  338. ['#42a5f5', '$mat-blue-400'],
  339. ['#166534', '$mat-green-800'],
  340. ['#166534', '$mat-green-800'],
  341. ['#FDBA74', '$mat-orange-300'],
  342. ['#fdba74', '$mat-orange-300'],
  343. ['#FFE0B2', '$mat-orange-100'],
  344. ['#ffe0b2', '$mat-orange-100'],
  345. ['#C8E6C9', '$mat-green-100'],
  346. ['#c8e6c9', '$mat-green-100'],
  347. ['#ECEFF1', '$mat-bluegray-50'],
  348. ['#eceff1', '$mat-bluegray-50'],
  349. ['#78909C', '$mat-bluegray-400'],
  350. ['#78909c', '$mat-bluegray-400'],
  351. ['#6D28D9', '$violet-700'],
  352. ['#6d28d9', '$violet-700'],
  353. ['#D6EAF8', '$tint-sky-blue'],
  354. ['#d6eaf8', '$tint-sky-blue'],
  355. ['#FF4444', '$danger-bright'],
  356. ['#ff4444', '$danger-bright'],
  357. // ---- 第八轮(uni.scss 已有令牌补登记,2026-10-21)----
  358. ['#43A047', '$mat-green-base-2'],
  359. ['#43a047', '$mat-green-base-2'],
  360. ['#EDF2F7', '$uni-bg-color-grey'],
  361. ['#edf2f7', '$uni-bg-color-grey'],
  362. ['#7C3AED', '$facade-violet-deep'],
  363. ['#7c3aed', '$facade-violet-deep'],
  364. ['#FA8C16', '$brand-primary-2'],
  365. ['#fa8c16', '$brand-primary-2'],
  366. ['#065F46', '$status-success-strong'],
  367. ['#065f46', '$status-success-strong'],
  368. ['#BBF7D0', '$tint-green-line'],
  369. ['#bbf7d0', '$tint-green-line'],
  370. ['#FFF3E6', '$warm-cream'],
  371. ['#fff3e6', '$warm-cream'],
  372. ['#F3E5F5', '$fortune-3-tint'],
  373. ['#f3e5f5', '$fortune-3-tint'],
  374. ['#7B1FA2', '$fortune-3-ink'],
  375. ['#7b1fa2', '$fortune-3-ink'],
  376. ['#B8860B', '$hint-gold-ink'],
  377. ['#b8860b', '$hint-gold-ink'],
  378. ['#F9F9FF', '$dim-wisdom-tint-5'],
  379. ['#f9f9ff', '$dim-wisdom-tint-5'],
  380. ['#66BB6A', '$mat-green-light-2'],
  381. ['#66bb6a', '$mat-green-light-2'],
  382. ['#A5D6A7', '$mat-green-soft'],
  383. ['#a5d6a7', '$mat-green-soft'],
  384. ['#FFE4CC', '$tint-warm-edge'],
  385. ['#ffe4cc', '$tint-warm-edge'],
  386. ['#F87171', '$status-danger-light'],
  387. ['#f87171', '$status-danger-light'],
  388. ['#A78BFA', '$facade-violet-light'],
  389. ['#a78bfa', '$facade-violet-light'],
  390. ['#FFF8F0', '$dim-body-tint'],
  391. ['#fff8f0', '$dim-body-tint'],
  392. ['#3730A3', '$dim-wisdom-ink'],
  393. ['#3730a3', '$dim-wisdom-ink'],
  394. ['#F5F6FA', '$app-bg-2'],
  395. ['#f5f6fa', '$app-bg-2'],
  396. ['#F5F3FF', '$facade-violet-tint-2'],
  397. ['#f5f3ff', '$facade-violet-tint-2'],
  398. ['#F0F0FF', '$dim-wisdom-tint'],
  399. ['#f0f0ff', '$dim-wisdom-tint'],
  400. ['#4338CA', '$accent-indigo-ink'],
  401. ['#4338ca', '$accent-indigo-ink'],
  402. ['#1D4ED8', '$status-info-deep-3'],
  403. ['#1d4ed8', '$status-info-deep-3'],
  404. ['#FFF5F5', '$status-danger-tint'],
  405. ['#fff5f5', '$status-danger-tint'],
  406. ['#EFF6FF', '$status-info-tint'],
  407. ['#eff6ff', '$status-info-tint'],
  408. ['#F08A7A', '$uni-warm'],
  409. ['#f08a7a', '$uni-warm'],
  410. ['#FF8FB1', '$dim-mind-light-2'],
  411. ['#ff8fb1', '$dim-mind-light-2'],
  412. ['#A7F3D0', '$status-success-light'],
  413. ['#a7f3d0', '$status-success-light'],
  414. ['#444', '$facade-text-deep-2'],
  415. ['#444', '$facade-text-deep-2'],
  416. ['#4F46E5', '$dim-wisdom-deep'],
  417. ['#4f46e5', '$dim-wisdom-deep'],
  418. ['#F7F8FA', '$app-surface-subtle'],
  419. ['#f7f8fa', '$app-surface-subtle'],
  420. ['#A855F7', '$facade-violet-light-2'],
  421. ['#a855f7', '$facade-violet-light-2'],
  422. ['#81C784', '$mat-green-light'],
  423. ['#81c784', '$mat-green-light'],
  424. ['#BFDBFE', '$tint-blue-line'],
  425. ['#bfdbfe', '$tint-blue-line'],
  426. ['#FFF1F0', '$status-danger-tint-3'],
  427. ['#fff1f0', '$status-danger-tint-3'],
  428. ['#C7D2FE', '$tint-indigo-line'],
  429. ['#c7d2fe', '$tint-indigo-line'],
  430. ['#FCE7F3', '$accent-pink-tint'],
  431. ['#fce7f3', '$accent-pink-tint'],
  432. ['#FEF9E7', '$hint-cream-bg'],
  433. ['#fef9e7', '$hint-cream-bg'],
  434. ['#F0E6C0', '$hint-cream-border'],
  435. ['#f0e6c0', '$hint-cream-border'],
  436. ['#F3E8FF', '$tint-purple-soft'],
  437. ['#f3e8ff', '$tint-purple-soft'],
  438. // ---- 第九轮(跨页重复色提升,2026-10-21)----
  439. ['#F2F4F7', '$facade-fill-slate-25'],
  440. ['#f2f4f7', '$facade-fill-slate-25'],
  441. ['#F7F9FC', '$facade-fill-slate-50-2'],
  442. ['#f7f9fc', '$facade-fill-slate-50-2'],
  443. ['#5A6478', '$facade-text-slate-600'],
  444. ['#5a6478', '$facade-text-slate-600'],
  445. ['#4B5563', '$facade-text-gray-600'],
  446. ['#4b5563', '$facade-text-gray-600'],
  447. ['#1A2A4A', '$facade-ink-navy'],
  448. ['#1a2a4a', '$facade-ink-navy'],
  449. ['#FCA5A5', '$status-danger-light-2'],
  450. ['#fca5a5', '$status-danger-light-2'],
  451. ['#E53E3E', '$status-danger-strong-3'],
  452. ['#e53e3e', '$status-danger-strong-3'],
  453. ['#FDE8E8', '$status-danger-tint-4'],
  454. ['#fde8e8', '$status-danger-tint-4'],
  455. ['#FFF0F0', '$status-danger-tint-5'],
  456. ['#fff0f0', '$status-danger-tint-5'],
  457. ['#FFC107', '$status-warning-amber'],
  458. ['#ffc107', '$status-warning-amber'],
  459. ['#E6A23C', '$status-warning-4'],
  460. ['#e6a23c', '$status-warning-4'],
  461. ['#F0B429', '$status-warning-gold'],
  462. ['#f0b429', '$status-warning-gold'],
  463. ['#388E3C', '$mat-green-700'],
  464. ['#388e3c', '$mat-green-700'],
  465. ['#FF5722', '$mat-deep-orange'],
  466. ['#ff5722', '$mat-deep-orange'],
  467. ['#93C5FD', '$tint-blue-300'],
  468. ['#93c5fd', '$tint-blue-300'],
  469. ['#C4B5FD', '$accent-violet-300'],
  470. ['#c4b5fd', '$accent-violet-300'],
  471. ['#EAF4FC', '$tint-sky-soft-2'],
  472. ['#eaf4fc', '$tint-sky-soft-2'],
  473. ['#F0F7FF', '$tint-sky-ghost'],
  474. ['#f0f7ff', '$tint-sky-ghost'],
  475. ['#F0F9EB', '$tint-green-ghost'],
  476. ['#f0f9eb', '$tint-green-ghost'],
  477. ['#FCE4EC', '$accent-pink-tint-2'],
  478. ['#fce4ec', '$accent-pink-tint-2'],
  479. ['#07C160', '$wechat-green'],
  480. ['#07c160', '$wechat-green'],
  481. ].sort((a, b) => b[0].length - a[0].length)
  482. const GRAY_RULES = ALL_RULES.filter(r => { const i = ALL_RULES.indexOf(r); return i < 30 })
  483. const BRAND_RULES = ALL_RULES.filter(r => { const i = ALL_RULES.indexOf(r); return i >= 30 })
  484. const SECOND_RULES = ALL_RULES.filter(r => { const i = ALL_RULES.indexOf(r); return i >= 30 })
  485. const batch = process.argv.find(a => a.startsWith('--batch='))?.split('=')[1]
  486. const rules = batch === 'brand' ? BRAND_RULES : batch === 'gray' ? GRAY_RULES : ALL_RULES
  487. const label = batch === 'brand' ? '品牌/状态色' : batch === 'gray' ? '灰阶' : '全部'
  488. console.log(`\n批次: ${label} 规则数: ${rules.length}\n`)
  489. function walk(dir, out = []) {
  490. for (const name of fs.readdirSync(dir)) {
  491. if (EXCLUDE_DIRS.has(name)) continue
  492. const p = path.join(dir, name)
  493. const st = fs.statSync(p)
  494. if (st.isDirectory()) walk(p, out)
  495. else if (name.endsWith('.vue')) out.push(p)
  496. }
  497. return out
  498. }
  499. function countReplacements(text, rules) {
  500. let n = 0
  501. for (const [hex] of rules) {
  502. const escaped = hex.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  503. n += (text.match(new RegExp(escaped + '(?![0-9a-fA-F])', 'g')) || []).length
  504. }
  505. return n
  506. }
  507. function applyRules(text, rules) {
  508. let result = text
  509. for (const [hex, token] of rules) {
  510. const escaped = hex.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  511. result = result.replace(new RegExp(escaped + '(?![0-9a-fA-F])', 'g'), token)
  512. }
  513. return result
  514. }
  515. function processFile(filePath) {
  516. const src = fs.readFileSync(filePath, 'utf8')
  517. // 按 <style ...> 块拆分,只处理有目标 hex 的块
  518. const STYLE_RE = /(<style)([^>]*)>([\s\S]*?)(<\/style>)/gi
  519. let result = src
  520. let changed = false
  521. let totalRepl = 0
  522. let blocksModified = 0
  523. const matches = [...src.matchAll(STYLE_RE)]
  524. for (const match of matches) {
  525. const fullMatch = match[0]
  526. const tagOpen = match[1] // "<style"
  527. const attrs = match[2] // e.g. ' scoped lang="scss"'
  528. const body = match[3] // CSS content
  529. const tagClose = match[4] // "</style>"
  530. const replCount = countReplacements(body, rules)
  531. if (replCount === 0) continue
  532. const newBody = applyRules(body, rules)
  533. totalRepl += replCount
  534. // 若 <style> 没有 lang="scss",补充上(SCSS 是 CSS 超集,安全)
  535. let newAttrs = attrs
  536. if (!/lang\s*=\s*["']scss["']/i.test(attrs)) {
  537. newAttrs = attrs.trim() ? attrs.trim() + ' lang="scss"' : ' lang="scss"'
  538. }
  539. const newBlock = `${tagOpen}${newAttrs}>${newBody}${tagClose}`
  540. if (newBlock !== fullMatch) {
  541. result = result.split(fullMatch).join(newBlock)
  542. changed = true
  543. blocksModified++
  544. }
  545. }
  546. if (changed) {
  547. fs.writeFileSync(filePath, result, 'utf8')
  548. return { changed: true, replacements: totalRepl, blocks: blocksModified }
  549. }
  550. return { changed: false, replacements: 0, blocks: 0 }
  551. }
  552. function main() {
  553. const files = walk(ROOT)
  554. let totalChanged = 0
  555. let totalReplacements = 0
  556. let totalBlocks = 0
  557. const log = []
  558. for (const file of files) {
  559. const rel = path.relative(ROOT, file)
  560. const result = processFile(file)
  561. if (result.changed) {
  562. totalChanged++
  563. totalReplacements += result.replacements
  564. totalBlocks += result.blocks
  565. log.push(`${result.replacements.toString().padStart(4)} ${rel}`)
  566. }
  567. }
  568. console.log(`\n=== 替换完成 (${label}) ===`)
  569. console.log(`文件变更: ${totalChanged}`)
  570. console.log(`style 块修改: ${totalBlocks}`)
  571. console.log(`替换次数: ${totalReplacements}`)
  572. console.log('')
  573. log.sort((a, b) => parseInt(b.split(' ')[0]) - parseInt(a.split(' ')[0]))
  574. for (const line of log.slice(0, 50)) console.log(line)
  575. if (log.length > 50) console.log(`... 共 ${log.length} 个文件`)
  576. }
  577. main()