feat: 全部图标替换为 Lucide 高质量 PNG (29个)

This commit is contained in:
wangxiaogang
2026-06-06 15:25:15 +08:00
parent 1e975cd828
commit bc6eca1e9b
30 changed files with 119 additions and 0 deletions

119
scripts/generate-icons.js Normal file
View File

@@ -0,0 +1,119 @@
/**
* 从 Lucide SVG 图标生成高质量 PNG
* 用法: node scripts/generate-icons.js
*/
const sharp = require('sharp')
const fs = require('fs')
const path = require('path')
const ICONS_DIR = path.join(__dirname, '../client/src/static/icons')
const LUCIDE_DIR = path.join(__dirname, '../node_modules/lucide-static/icons')
// 颜色映射hex -> RGB
const COLORS = {
'#2D1B1B': { r: 45, g: 27, b: 27 }, // 暖深棕
'#8B7E7E': { r: 139, g: 126, b: 126 }, // 灰色
'#BFB3B3': { r: 191, g: 179, b: 179 }, // 浅灰
'#E0D6D0': { r: 224, g: 214, b: 208 }, // 极浅灰
'#FF8C69': { r: 255, g: 140, b: 105 }, // 珊瑚橙
'#FF6B6B': { r: 255, g: 107, b: 107 }, // 红色
'#7BC67E': { r: 123, g: 198, b: 126 }, // 绿色
'#FFFFFF': { r: 255, g: 255, b: 255 }, // 白色
}
// 图标配置:{ lucideName, variants: { colorHex: outputName } }
const ICONS = [
// 导航
{ lucide: 'arrow-left', variants: { '#2D1B1B': 'arrow-left-dark', '#8B7E7E': 'arrow-left-gray' } },
{ lucide: 'arrow-right', variants: { '#8B7E7E': 'arrow-right-gray', '#E0D6D0': 'arrow-right-pale' } },
{ lucide: 'chevron-right', variants: { '#BFB3B3': 'chevron-right-light' } },
// 操作
{ lucide: 'plus', variants: { '#FFFFFF': 'plus-white' } },
{ lucide: 'check', variants: { '#FFFFFF': 'check-white' } },
{ lucide: 'edit', variants: { '#8B7E7E': 'edit-gray', '#BFB3B3': 'edit-light' } },
{ lucide: 'trash-2', variants: { '#FF8C69': 'trash-orange', '#FF6B6B': 'trash-red', '#FFFFFF': 'trash-white' } },
{ lucide: 'refresh-cw', variants: { '#8B7E7E': 'refresh-gray', '#FF8C69': 'refresh-orange' } },
// 功能
{ lucide: 'calendar', variants: { '#8B7E7E': 'calendar-gray' } },
{ lucide: 'bell', variants: { '#8B7E7E': 'bell-gray' } },
{ lucide: 'user', variants: { '#8B7E7E': 'user-gray', '#FFFFFF': 'user-white' } },
{ lucide: 'alert-circle', variants: { '#BFB3B3': 'alert-circle-light' } },
// 趋势
{ lucide: 'trending-down', variants: { '#FF6B6B': 'trend-down-red' } },
{ lucide: 'trending-up', variants: { '#7BC67E': 'trend-up-green' } },
]
// TabBar 图标(需要 normal + active 两套)
const TABBAR_ICONS = [
{ lucide: 'home', normal: 'home', active: 'home-active', normalColor: '#BFB3B3', activeColor: '#FF8C69' },
{ lucide: 'bar-chart-2', normal: 'chart', active: 'chart-active', normalColor: '#BFB3B3', activeColor: '#FF8C69' },
{ lucide: 'receipt', normal: 'bill', active: 'bill-active', normalColor: '#BFB3B3', activeColor: '#FF8C69' },
{ lucide: 'user', normal: 'user', active: 'user-active', normalColor: '#BFB3B3', activeColor: '#FF8C69' },
]
const SIZE = 96 // 输出 PNG 尺寸px
async function generateIcon(svgName, outputPath, color) {
const svgPath = path.join(LUCIDE_DIR, `${svgName}.svg`)
if (!fs.existsSync(svgPath)) {
console.warn(`⚠ SVG not found: ${svgName}.svg`)
return false
}
let svgContent = fs.readFileSync(svgPath, 'utf-8')
// 替换 SVG 中的 stroke 颜色
const { r, g, b } = COLORS[color]
svgContent = svgContent.replace(/stroke="[^"]*"/g, `stroke="rgb(${r},${g},${b})"`)
// 也处理可能的 fill
svgContent = svgContent.replace(/fill="none"/g, 'fill="none"')
// 设置输出尺寸
svgContent = svgContent.replace(/width="[^"]*"/, `width="${SIZE}"`)
svgContent = svgContent.replace(/height="[^"]*"/, `height="${SIZE}"`)
await sharp(Buffer.from(svgContent))
.resize(SIZE, SIZE)
.png()
.toFile(outputPath)
return true
}
async function main() {
console.log('🎨 开始生成图标...\n')
let total = 0
let success = 0
// 生成普通图标
for (const icon of ICONS) {
for (const [color, outputName] of Object.entries(icon.variants)) {
const outputPath = path.join(ICONS_DIR, `${outputName}.png`)
total++
if (await generateIcon(icon.lucide, outputPath, color)) {
console.log(`${outputName}.png`)
success++
}
}
}
// 生成 TabBar 图标
for (const icon of TABBAR_ICONS) {
for (const [outputName, color] of [[icon.normal, icon.normalColor], [icon.active, icon.activeColor]]) {
const outputPath = path.join(ICONS_DIR, `${outputName}.png`)
total++
if (await generateIcon(icon.lucide, outputPath, color)) {
console.log(`${outputName}.png (tabbar)`)
success++
}
}
}
console.log(`\n✅ 完成: ${success}/${total} 个图标已生成`)
}
main().catch(console.error)