starmusic-pc
1<script setup>
2/**
3 * Icon — 内联 SVG 图标
4 *
5 * 相比移动端「预生成多套主题色 PNG/SVG 文件」的方案,桌面端改为单份内联矢量:
6 * 颜色用 currentColor 直接继承父级,主题切换零成本,任意尺寸都保持锐利。
7 * 图标库基于统一的 24×24 网格(stroke 2 / round),风格与移动端保持一致。
8 */
9import { computed } from 'vue'
10
11const props = defineProps({
12 /** 图标名,见下方 ICONS */
13 name: { type: String, required: true },
14 /** 尺寸(px) */
15 size: { type: [Number, String], default: 20 },
16 /** 线宽 */
17 width: { type: [Number, String], default: 1.8 },
18 /** 持续旋转(加载中/唱片) */
19 spin: Boolean,
20})
21
22const ICONS = {
23 // ---- 播放控制 ----
24 play: { mode: 'fill', d: ['M8 5.5v13a1 1 0 0 0 1.53.85l10.5-6.5a1 1 0 0 0 0-1.7L9.53 4.65A1 1 0 0 0 8 5.5z'] },
25 pause: {
26 mode: 'fill',
27 d: [
28 'M7 4.5a1 1 0 0 0-1 1v13a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-13a1 1 0 0 0-1-1H7z',
29 'M15 4.5a1 1 0 0 0-1 1v13a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-13a1 1 0 0 0-1-1h-2z',
30 ],
31 },
32 prev: {
33 mode: 'fill',
34 d: [
35 'M6 5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h1.5a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1H6z',
36 'M19.5 5.3a1 1 0 0 0-1.06.05l-9 6.7a1 1 0 0 0 0 1.6l9 6.7A1 1 0 0 0 20.5 19V6a1 1 0 0 0-1-.7z',
37 ],
38 },
39 next: {
40 mode: 'fill',
41 d: [
42 'M16.5 5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1H18a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-1.5z',
43 'M4.5 5.3a1 1 0 0 0-.5.87v12.66a1 1 0 0 0 1.56.83l9-6.7a1 1 0 0 0 0-1.6l-9-6.7A1 1 0 0 0 4.5 5.3z',
44 ],
45 },
46 'play-all': { mode: 'fill', d: ['M8 5.5v13a1 1 0 0 0 1.53.85l10.5-6.5a1 1 0 0 0 0-1.7L9.53 4.65A1 1 0 0 0 8 5.5z'] },
47 shuffle: {
48 d: [
49 'M3.5 6.8h3.1c1.6 0 3.1.8 4 2.1l3.7 6c.9 1.4 2.4 2.2 4 2.2h2.2',
50 'M17.7 3.6l2.9 2.9-2.9 2.9',
51 'M17.7 13.9l2.9 2.9-2.9 2.9',
52 'M3.5 17.1h3.1c1 0 1.9-.3 2.7-.9',
53 'M13.4 8.1c.9-1 2.2-1.6 3.6-1.6h3.6',
54 ],
55 },
56 repeat: {
57 d: [
58 'M17 3.6l2.9 2.9L17 9.4',
59 'M4 13.2V9.9a3 3 0 0 1 3-3h12.9',
60 'M7 20.4l-2.9-2.9L7 14.6',
61 'M20 10.8v3.3a3 3 0 0 1-3 3H4.1',
62 ],
63 },
64 'repeat-one': {
65 d: [
66 'M17 3.6l2.9 2.9L17 9.4',
67 'M4 13.2V9.9a3 3 0 0 1 3-3h12.9',
68 'M7 20.4l-2.9-2.9L7 14.6',
69 'M20 10.8v3.3a3 3 0 0 1-3 3H4.1',
70 'M11.6 10.6l1.3-.9v5.6',
71 ],
72 },
73 music: {
74 d: [
75 'M9.5 18.5V6.2a1 1 0 0 1 .77-.97l8-1.8a1 1 0 0 1 1.23.97v11.6',
76 'M6.5 18.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0z',
77 'M14.5 16.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0z',
78 ],
79 },
80
81 // ---- 导航 ----
82 search: { d: ['M10.8 4a6.8 6.8 0 1 0 0 13.6 6.8 6.8 0 0 0 0-13.6z', 'M15.9 15.9L21 21'] },
83 queue: {
84 d: ['M3.5 7h11', 'M3.5 12h11', 'M3.5 17h7', 'M19.8 7.4v8.4', 'M19.8 0', 'M17.6 15.9a2.3 2.3 0 1 0 2.2 2.3'],
85 },
86 'list-music': {
87 d: ['M3.5 6.5h11.5', 'M3.5 12h11.5', 'M3.5 17.5h7', 'M19.5 10.5v8.4', 'M19.5 18.9a2.4 2.4 0 1 1-2.4 2.4'],
88 },
89 settings: {
90 d: [
91 'M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z',
92 ],
93 c: [[12, 12, 3]],
94 },
95 user: { d: ['M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'], c: [[12, 7, 4]] },
96 info: { d: ['M12 16.2v-5', 'M12 8.2h.01'], c: [[12, 12, 9]] },
97 star: {
98 d: ['M12 3.6l2.63 5.33 5.87.86-4.25 4.14 1 5.85L12 17.02l-5.25 2.76 1-5.85L3.5 9.79l5.87-.86z'],
99 },
100 sparkle: {
101 mode: 'fill',
102 d: ['M12 3.2l1.5 4.9 4.9 1.5-4.9 1.5-1.5 4.9-1.5-4.9L5.6 9.6l4.9-1.5z', 'M18.6 15.2l.7 2.1 2.1.7-2.1.7-.7 2.1-.7-2.1-2.1-.7 2.1-.7z'],
103 },
104 home: { d: ['M3.5 10.2L12 3.5l8.5 6.7V19a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 19z'] },
105
106 // ---- 通用操作 ----
107 close: { d: ['M6 6l12 12', 'M18 6L6 18'] },
108 check: { d: ['M20 6L9 17l-5-5'] },
109 plus: { d: ['M12 5v14', 'M5 12h14'] },
110 minus: { d: ['M5 12h14'] },
111 'chevron-left': { d: ['M15 5l-7 7 7 7'] },
112 'chevron-right': { d: ['M9 5l7 7-7 7'] },
113 'chevron-down': { d: ['M5 9l7 7 7-7'] },
114 'chevron-up': { d: ['M5 15l7-7 7 7'] },
115 arrow: { d: ['M9 5l7 7-7 7'] },
116 more: { c: [[12, 5.5, 1.6], [12, 12, 1.6], [12, 18.5, 1.6]] },
117 trash: {
118 d: [
119 'M4 7h16',
120 'M9.5 7V5.2A1.2 1.2 0 0 1 10.7 4h2.6a1.2 1.2 0 0 1 1.2 1.2V7',
121 'M6.6 7l.9 11.4a2 2 0 0 0 2 1.9h5a2 2 0 0 0 2-1.9L17.4 7',
122 'M10.2 11v5.6',
123 'M13.8 11v5.6',
124 ],
125 },
126 edit: { d: ['M4 20h4l10.4-10.4a2.1 2.1 0 0 0-3-3L5 17v3z', 'M13.6 6.6l3 3'] },
127 folder: { d: ['M3.5 7.4a2 2 0 0 1 2-2h3.1l2 2.5h7.9a2 2 0 0 1 2 2v7.7a2 2 0 0 1-2 2H5.5a2 2 0 0 1-2-2z'] },
128 image: {
129 d: ['M4 5.5h16v13H4z', 'M4 15l4.6-4.6 3.4 3.4 3-3L20 15'],
130 c: [[9, 9.6, 1.2]],
131 },
132 download: { d: ['M12 4v10', 'M8 10.4l4 4 4-4', 'M4.5 19h15'] },
133 upload: { d: ['M12 19.5V9.5', 'M8 13.4l4-4 4 4', 'M4.5 19.5h15'] },
134 copy: {
135 d: [
136 'M9 9.5A1.5 1.5 0 0 1 10.5 8h8A1.5 1.5 0 0 1 20 9.5v8a1.5 1.5 0 0 1-1.5 1.5h-8A1.5 1.5 0 0 1 9 17.5z',
137 'M15 8V5.5A1.5 1.5 0 0 0 13.5 4h-8A1.5 1.5 0 0 0 4 5.5v8A1.5 1.5 0 0 0 5.5 15H9',
138 ],
139 },
140 refresh: { d: ['M20 12a8 8 0 1 1-2.4-5.7', 'M20.4 4v4.4h-4.4'] },
141 link: {
142 d: [
143 'M10.6 13.4a4 4 0 0 0 5.65 0l2.83-2.83a4 4 0 0 0-5.66-5.66l-1.1 1.1',
144 'M13.4 10.6a4 4 0 0 0-5.65 0L4.9 13.4a4 4 0 0 0 5.66 5.66l1.1-1.1',
145 ],
146 },
147 external: {
148 d: ['M14 4.5h5.5V10', 'M19.5 4.5L11 13', 'M18 14.2v4.8a1.5 1.5 0 0 1-1.5 1.5H5a1.5 1.5 0 0 1-1.5-1.5V8A1.5 1.5 0 0 1 5 6.5h4.8'],
149 },
150 filter: { d: ['M4 6.5h16', 'M7 12h10', 'M10 17.5h4'] },
151 sort: { d: ['M7 4.5v15', 'M4 16.5l3 3 3-3', 'M17 19.5v-15', 'M14 7.5l3-3 3 3'] },
152 palette: {
153 d: [
154 'M12 3.5a8.5 8.5 0 0 0 0 17c1.4 0 2.1-.9 2.1-1.9 0-1.4-1.4-1.6-1.4-2.6 0-.8.7-1.4 1.6-1.4h1.6a4.7 4.7 0 0 0 4.7-4.7c0-3.5-3.8-6.4-8.6-6.4z',
155 ],
156 c: [[8.2, 12.2, 1.15], [10.6, 8.4, 1.15], [14.4, 8.4, 1.15]],
157 },
158 volume: { d: ['M4 9.5h3l4-3.2v11.4l-4-3.2H4z', 'M15 9.2a4.2 4.2 0 0 1 0 5.6', 'M17.6 6.5a7.9 7.9 0 0 1 0 11'] },
159 'volume-1': { d: ['M4 9.5h3l4-3.2v11.4l-4-3.2H4z', 'M15 9.2a4.2 4.2 0 0 1 0 5.6'] },
160 'volume-mute': { d: ['M4 9.5h3l4-3.2v11.4l-4-3.2H4z', 'M16 10l4 4', 'M20 10l-4 4'] },
161 heart: {
162 d: [
163 'M12 20.5s-7.5-4.9-9.7-9.2C0.9 8.5 2.6 4.8 6 4.8c2.1 0 3.6 1.2 4.3 2.6l1.7 2.6 1.7-2.6c0.7-1.4 2.2-2.6 4.3-2.6 3.4 0 5.1 3.7 3.7 6.5-2.2 4.3-9.7 9.2-9.7 9.2z',
164 ],
165 },
166 'heart-fill': {
167 mode: 'fill',
168 d: [
169 'M12 20.5s-7.5-4.9-9.7-9.2C0.9 8.5 2.6 4.8 6 4.8c2.1 0 3.6 1.2 4.3 2.6l1.7 2.6 1.7-2.6c0.7-1.4 2.2-2.6 4.3-2.6 3.4 0 5.1 3.7 3.7 6.5-2.2 4.3-9.7 9.2-9.7 9.2z',
170 ],
171 },
172 trend: { d: ['M3 16.5l5.5-5.5 3.5 3.5 8-8', 'M15.5 6.5H20V11'] },
173 cloud: { d: ['M7.6 18.5h9.1a3.8 3.8 0 0 0 .5-7.6A5.5 5.5 0 0 0 6.7 9.5 4.3 4.3 0 0 0 7.6 18.5z'] },
174 database: {
175 d: [
176 'M12 4.4c4 0 7 1 7 2.2S16 8.8 12 8.8 5 7.8 5 6.6s3-2.2 7-2.2z',
177 'M5 6.6v10.8c0 1.2 3 2.2 7 2.2s7-1 7-2.2V6.6',
178 'M5 12c0 1.2 3 2.2 7 2.2s7-1 7-2.2',
179 ],
180 },
181 clock: { d: ['M12 7.6V12l2.9 1.9'], c: [[12, 12, 8]] },
182 log: { d: ['M4 5.5h16v13H4z', 'M8 10l2.2 2L8 14', 'M13.2 14.4h3.2'] },
183 shield: { d: ['M12 3.6l7 2.5v5.4c0 4-3 7.1-7 8.6-4-1.5-7-4.6-7-8.6V6.1z'] },
184 eye: { d: ['M2.6 12S6 6.6 12 6.6 21.4 12 21.4 12 18 17.4 12 17.4 2.6 12 2.6 12z'], c: [[12, 12, 2.6]] },
185 eye2: { d: ['M9.9 5a10 10 0 0 1 2.1-.2c6 0 9.4 5.4 9.4 5.4a17 17 0 0 1-2.6 3.3', 'M6.6 6.9A17 17 0 0 0 2.6 10.2S6 15.6 12 15.6a9 9 0 0 0 3.5-.7', 'M3 3l18 18', 'M10.3 10.5a2.6 2.6 0 0 0 3.4 3.4'] },
186 pin: { d: ['M12 16.6V21', 'M8.4 3.6h7.2l-1.3 6.3 3.4 3.3H6.3l3.4-3.3z'] },
187 miniplayer: { d: ['M4 5.5h16v13H4z', 'M4 14.5h16', 'M7.5 17h3'] },
188 'minimize-win': { d: ['M5.5 12h13'] },
189 'maximize-win': { d: ['M5.5 5.5h13v13h-13z'] },
190 'restore-win': { d: ['M8.5 8.5V6.5A1 1 0 0 1 9.5 5.5h9A1 1 0 0 1 19.5 6.5v9a1 1 0 0 1-1 1h-2', 'M5.5 9.5A1 1 0 0 1 6.5 8.5h9a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1z'] },
191 fullscreen: { d: ['M4 9V5.5A1.5 1.5 0 0 1 5.5 4H9', 'M15 4h3.5A1.5 1.5 0 0 1 20 5.5V9', 'M20 15v3.5a1.5 1.5 0 0 1-1.5 1.5H15', 'M9 20H5.5A1.5 1.5 0 0 1 4 18.5V15'] },
192 music2: { d: ['M12 8.6V17', 'M12 8.6a3 3 0 1 1 3-3'], c: [[9, 17, 3]] },
193 star4: { mode: 'fill', d: ['M12 2.8l2.3 6.2 6.6.4-5.1 4.2 1.7 6.4-5.5-3.6-5.5 3.6 1.7-6.4L3.1 9.4l6.6-.4z'] },
194 rocket: { d: ['M12 3.5c3.4 0 6 2.6 6 6 0 4.6-3.4 7.6-6 10-2.6-2.4-6-5.4-6-10 0-3.4 2.6-6 6-6z', 'M9.5 13.4l-2.4 1 1-2.6', 'M14.5 13.4l2.4 1-1-2.6'], c: [[12, 9.6, 2.2]] },
195 gear: { d: ['M12 15.2a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4z'] },
196 dot: { mode: 'fill', c: [[12, 12, 3.2]] },
197 'dot-sm': { mode: 'fill', c: [[12, 12, 2]] },
198}
199
200const icon = computed(() => ICONS[props.name] || ICONS.music)
201const isFill = computed(() => icon.value.mode === 'fill')
202</script>
203
204<template>
205 <svg
206 class="i"
207 :class="{ 'is-spin': spin }"
208 :width="size"
209 :height="size"
210 viewBox="0 0 24 24"
211 :fill="isFill ? 'currentColor' : 'none'"
212 :stroke="isFill ? 'none' : 'currentColor'"
213 :stroke-width="isFill ? 0 : width"
214 stroke-linecap="round"
215 stroke-linejoin="round"
216 aria-hidden="true"
217 focusable="false"
218 >
219 <circle v-for="(c, i) in icon.c || []" :key="`c${i}`" :cx="c[0]" :cy="c[1]" :r="c[2]" />
220 <path v-for="(d, i) in icon.d || []" :key="`p${i}`" :d="d" />
221 </svg>
222</template>
223
224<style scoped>
225.i {
226 display: block;
227 flex: none;
228 overflow: visible;
229}
230
231.is-spin {
232 animation: kSpin 1s linear infinite;
233}
234</style>