@@ -1,190 +0,0 @@
< template >
< view class = "tj-icon-wrap" :class = "[`tj-icon-wrap--${size}`]" >
< image
v-if = "!useFallback"
class = "tj-icon"
: class = "[`tj-icon--${name}`, `tj-icon--${size}`]"
:src = "iconSrc"
mode = "aspectFit"
@error ="onImageError"
/ >
<!-- < text
v-else
class = "tj-icon-fallback"
:class = "[`tj-icon-fallback--${size}`]"
: style = "{ color }"
> { { fallbackGlyph } } < / text > -- >
< / view >
< / template >
< script setup >
import { computed , ref } from 'vue'
import { svgToDataUrl } from '../utils/svgDataUrl.js'
const props = defineProps ( {
name : { type : String , required : true } ,
size : { type : String , default : 'md' } ,
color : { type : String , default : '#204E2B' }
} )
const useFallback = ref ( false )
/** Lucide 风格描边路径 */
const ICON _PATHS = {
view : '<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>' ,
ticket : '<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>' ,
calendar : '<rect width="18" height="18" x="3" y="4" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/>' ,
flame : '<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/>' ,
'check-circle' : '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path d="m9 11 3 3L22 4"/>' ,
glucose : '<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"/>' ,
heart : '<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/>' ,
activity : '<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>' ,
users : '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>' ,
user : '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>' ,
person : '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>' ,
send : '<path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>' ,
'alert-triangle' : '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>' ,
minus : '<path d="M5 12h14"/>' ,
droplet : '<path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.5-2.5-5.5C15 7 12 2 12 2S9 7 7.5 9.5 5 13 5 15a7 7 0 0 0 7 7z"/>' ,
sparkles : '<path d="m12 3-1.9 5.8L4 12l5.8 1.9L12 21l1.9-5.8L20 12l-5.8-1.9L12 3Z"/><path d="M5 3v4M19 17v4M3 5h4M17 19h4"/>' ,
trophy : '<path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6M18 9h1.5a2.5 2.5 0 0 0 0-5H18M4 22h16M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20 7 22M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20 17 22M18 2H6v7a6 6 0 0 0 12 0V2Z"/>' ,
share : '<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8M16 6l-4-4-4 4M12 2v13"/>' ,
plus : '<path d="M5 12h14M12 5v14"/>' ,
refresh : '<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8M3 3v5h5M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16M21 21v-5h-5"/>' ,
volume : '<path d="M11 5 6 9H2v6h4l5 4V5zM15.54 8.46a5 5 0 0 1 0 7.07M19.07 4.93a10 10 0 0 1 0 14.14"/>' ,
pause : '<rect width="4" height="16" x="14" y="4" rx="1"/><rect width="4" height="16" x="6" y="4" rx="1"/>' ,
play : '<polygon points="6 3 20 12 6 21 6 3"/>' ,
info : '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/>' ,
sun : '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41-1.41M17.66 6.34l1.41-1.41M6.34 4.93l1.41 1.41"/>' ,
moon : '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>' ,
sunset : '<path d="M12 10V2M18.364 5.636l-2.12 2.12M5.636 18.364l2.12-2.12M22 18h-3M5 18H2M18.364 18.364l-2.12-2.12M5.636 5.636l2.12 2.12M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"/>' ,
mic : '<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/>' ,
camera : '<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/>' ,
bulb : '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1.3.5 2.6 1.5 3.5.8.8 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/>' ,
leaf : '<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10Z"/><path d="M2 21c0-3 1.85-5.36 5.08-6"/>' ,
utensils : '<path d="M3 2v7c0 1.1.9 2 2 2a2 2 0 0 0 2-2V2"/><path d="M7 2v20"/><path d="M21 15V2a5 5 0 0 0-3 4.5v6a2 2 0 0 0 2 2h1Z"/><path d="M18 15v7"/>' ,
egg : '<path d="M12 22c4.97 0 8-3.27 8-7.31C20 9.65 16.42 2 12 2S4 9.65 4 14.69C4 18.73 7.03 22 12 22Z"/>' ,
home : '<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/>' ,
'plus-circle' : '<circle cx="12" cy="12" r="10"/><path d="M8 12h8M12 8v8"/>' ,
'chevron-right' : '<path d="m9 18 6-6-6-6"/>' ,
'chevron-left' : '<path d="m15 18-6-6 6-6"/>' ,
check : '<path d="M20 6 9 17l-5-5"/>' ,
settings : '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>' ,
syringe : '<path d="m18 2 4 4"/><path d="m17 7 3-3"/><path d="M19 9 8.7 19.3c-1 1-2.5 1-3.4 0l-.6-.6c-1-1-1-2.5 0-3.4L15 5"/><path d="m9 11 4 4"/><path d="m5 19-3 3"/><path d="m14 4 6 6"/>' ,
zap : '<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>'
}
/** 图片加载失败时的 emoji 回退 */
const ICON _FALLBACK = {
view : '👁' ,
ticket : '🎫' ,
calendar : '📅' ,
flame : '🔥' ,
'check-circle' : '✓' ,
glucose : '💧' ,
heart : '❤' ,
activity : '🏃' ,
users : '👥' ,
user : '👤' ,
person : '👤' ,
send : '➤' ,
'alert-triangle' : '⚠' ,
minus : '—' ,
droplet : '💧' ,
sparkles : '✨' ,
trophy : '🏆' ,
share : '↗' ,
plus : '+ ' ,
refresh : '↻' ,
volume : '🔊' ,
pause : '⏸' ,
play : '▶' ,
info : '!' ,
sun : '☀' ,
moon : '🌙' ,
sunset : '☀' ,
mic : '🎤' ,
camera : '📷' ,
bulb : '💡' ,
leaf : '🥬' ,
utensils : '🍴' ,
egg : '🍳' ,
home : '🏠' ,
'plus-circle' : '⊕' ,
'chevron-right' : '› ' ,
'chevron-left' : '‹ ' ,
check : '✓' ,
settings : '⚙' ,
syringe : '💉' ,
zap : '⚡'
}
const strokeColor = computed ( ( ) => {
const c = String ( props . color || '#204E2B' ) . trim ( )
return /^#[0-9A-Fa-f]{3,8}$/ . test ( c ) ? c : '#204E2B'
} )
const iconSrc = computed ( ( ) => {
const path = ICON _PATHS [ props . name ] || ICON _PATHS . view
const svg = ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke=" ${ strokeColor . value } " stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> ${ path } </svg> `
return svgToDataUrl ( svg )
} )
const fallbackGlyph = computed ( ( ) => ICON _FALLBACK [ props . name ] || ICON _FALLBACK . view )
function onImageError ( ) {
useFallback . value = true
}
< / script >
< style lang = "scss" scoped >
. tj - icon - wrap {
display : inline - flex ;
align - items : center ;
justify - content : center ;
flex - shrink : 0 ;
}
. tj - icon - wrap -- sm {
width : 32 rpx ;
height : 32 rpx ;
}
. tj - icon - wrap -- md {
width : 40 rpx ;
height : 40 rpx ;
}
. tj - icon - wrap -- lg {
width : 48 rpx ;
height : 48 rpx ;
}
. tj - icon - wrap -- xl {
width : 56 rpx ;
height : 56 rpx ;
}
. tj - icon {
display : block ;
width : 100 % ;
height : 100 % ;
}
/* Lucide send 路径重心偏右上,微调使圆形按钮内视觉居中 */
. tj - icon -- send {
transform : translate ( - 10 % , 10 % ) ;
}
. tj - icon - fallback {
display : block ;
line - height : 1 ;
font - weight : 700 ;
text - align : center ;
}
. tj - icon - fallback -- sm {
font - size : 24 rpx ;
}
. tj - icon - fallback -- md {
font - size : 30 rpx ;
}
. tj - icon - fallback -- lg {
font - size : 36 rpx ;
}
. tj - icon - fallback -- xl {
font - size : 42 rpx ;
}
< / style >