feat: 全部图标替换为 Lucide 高质量 PNG (29个)
This commit is contained in:
119
scripts/generate-icons.js
Normal file
119
scripts/generate-icons.js
Normal 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)
|
||||
Reference in New Issue
Block a user