convert-tianpan-px.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. #!/usr/bin/env node
  2. /**
  3. * tianpan 家族 px→rpx 转换(×2,375px 设计基准)
  4. * 转换范围:<style> 块内 CSS 声明(font-size, padding, margin, width, height, border-radius,
  5. * box-shadow offset, border-width, gap 等)
  6. * 不转换:canvas 元素尺寸(<canvas width=... height=...>),canvas 绘图 JS(ctx.font="14px")
  7. */
  8. const fs = require('fs')
  9. const path = require('path')
  10. const ROOT = path.resolve(__dirname, '..')
  11. const TIANPAN_DIR = path.join(ROOT, 'pages', 'tianpan')
  12. const FILES = [
  13. 'index.vue',
  14. 'relation-detail.vue',
  15. 'member-detail.vue',
  16. 'traditional.vue',
  17. 'daily-fortune.vue',
  18. 'annual-energy.vue',
  19. 'related-items.vue',
  20. ]
  21. // 匹配:属性值中的 px(在 <style> 块内,排除 canvas 元素和 ctx.font 等 JS)
  22. // 策略:用正则替换 <style> 块内的 \d+px → \d*2rpx
  23. const PX_RE = /(\d+)\s*px/g
  24. function convertPxToRpx(text) {
  25. // 替换 px 为单位转换(注意:保留 canvas 宽高属性、保留 JS 内的 px)
  26. // 通过只匹配 <style>...</style> 内容来隔离
  27. const styleStart = text.search(/<style/)
  28. if (styleStart === -1) return text
  29. let result = ''
  30. let pos = 0
  31. const styleRegex = /(<style)([^>]*)>([\s\S]*?)(<\/style>)/gi
  32. let m
  33. let matchCount = 0
  34. while ((m = styleRegex.exec(text)) !== null) {
  35. // 跳过 canvas 内部(canvas 标签不在 style 块里)
  36. const body = m[3]
  37. const converted = body.replace(PX_RE, (_match, num) => {
  38. matchCount++
  39. const px = parseInt(num, 10)
  40. return `${px * 2}rpx`
  41. })
  42. if (converted !== body) {
  43. result += text.slice(pos, m.index) + m[1] + m[2] + '>' + converted + '</style>'
  44. pos = styleRegex.lastIndex
  45. }
  46. }
  47. result += text.slice(pos)
  48. return { newSrc: result, count: matchCount }
  49. }
  50. function main() {
  51. let totalConverted = 0
  52. const log = []
  53. for (const file of FILES) {
  54. const filePath = path.join(TIANPAN_DIR, file)
  55. if (!fs.existsSync(filePath)) {
  56. console.warn(`跳过不存在文件: ${file}`)
  57. continue
  58. }
  59. const src = fs.readFileSync(filePath, 'utf8')
  60. const { newSrc, count } = convertPxToRpx(src)
  61. if (count > 0) {
  62. fs.writeFileSync(filePath, newSrc, 'utf8')
  63. totalConverted += count
  64. log.push(`${count.toString().padStart(3)} ${file}`)
  65. }
  66. }
  67. console.log(`\n=== tianpan px→rpx 完成 ===`)
  68. console.log(`转换次数: ${totalConverted}`)
  69. console.log('')
  70. log.sort((a, b) => parseInt(b.split(' ')[0]) - parseInt(a.split(' ')[0]))
  71. for (const line of log) console.log(line)
  72. }
  73. main()