xzx-icon-vue2 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,578 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>XZX Icon Vue2 - 图标预览</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ color: #333;
19
+ }
20
+
21
+ .container {
22
+ max-width: 1200px;
23
+ margin: 0 auto;
24
+ padding: 20px;
25
+ }
26
+
27
+ .header {
28
+ text-align: center;
29
+ margin-bottom: 40px;
30
+ color: white;
31
+ }
32
+
33
+ .header h1 {
34
+ font-size: 3rem;
35
+ margin-bottom: 10px;
36
+ text-shadow: 0 2px 4px rgba(0,0,0,0.3);
37
+ }
38
+
39
+ .header p {
40
+ font-size: 1.2rem;
41
+ opacity: 0.9;
42
+ }
43
+
44
+ .stats {
45
+ background: rgba(255,255,255,0.2);
46
+ -webkit-backdrop-filter: blur(10px);
47
+ backdrop-filter: blur(10px);
48
+ border-radius: 12px;
49
+ padding: 20px;
50
+ margin-bottom: 30px;
51
+ color: white;
52
+ }
53
+
54
+ .stats-grid {
55
+ display: grid;
56
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
57
+ gap: 20px;
58
+ text-align: center;
59
+ }
60
+
61
+ .stat-item h3 {
62
+ font-size: 2rem;
63
+ margin-bottom: 5px;
64
+ }
65
+
66
+ .controls {
67
+ background: rgba(255,255,255,0.95);
68
+ -webkit-backdrop-filter: blur(10px);
69
+ backdrop-filter: blur(10px);
70
+ border-radius: 12px;
71
+ padding: 20px;
72
+ margin-bottom: 30px;
73
+ box-shadow: 0 8px 32px rgba(0,0,0,0.1);
74
+ }
75
+
76
+ .search-box {
77
+ position: relative;
78
+ margin-bottom: 20px;
79
+ }
80
+
81
+ .search-box input {
82
+ width: 100%;
83
+ padding: 15px 50px 15px 20px;
84
+ border: 2px solid #e1e5e9;
85
+ border-radius: 8px;
86
+ font-size: 16px;
87
+ transition: all 0.3s ease;
88
+ }
89
+
90
+ .search-box input:focus {
91
+ outline: none;
92
+ border-color: #667eea;
93
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
94
+ }
95
+
96
+ .search-icon {
97
+ position: absolute;
98
+ right: 15px;
99
+ top: 50%;
100
+ transform: translateY(-50%);
101
+ color: #666;
102
+ }
103
+
104
+ .filters {
105
+ display: flex;
106
+ gap: 10px;
107
+ flex-wrap: wrap;
108
+ align-items: center;
109
+ }
110
+
111
+ .filter-tag {
112
+ padding: 8px 16px;
113
+ background: #f5f5f5;
114
+ border: 2px solid transparent;
115
+ border-radius: 20px;
116
+ cursor: pointer;
117
+ transition: all 0.3s ease;
118
+ font-size: 14px;
119
+ }
120
+
121
+ .filter-tag:hover {
122
+ background: #e1e5e9;
123
+ }
124
+
125
+ .filter-tag.active {
126
+ background: #667eea;
127
+ color: white;
128
+ }
129
+
130
+ .icons-grid {
131
+ background: white;
132
+ border-radius: 12px;
133
+ padding: 30px;
134
+ box-shadow: 0 8px 32px rgba(0,0,0,0.1);
135
+ display: grid;
136
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
137
+ gap: 20px;
138
+ }
139
+
140
+ .icon-item {
141
+ text-align: center;
142
+ padding: 20px;
143
+ border: 2px solid #f5f5f5;
144
+ border-radius: 8px;
145
+ cursor: pointer;
146
+ transition: all 0.3s ease;
147
+ position: relative;
148
+ }
149
+
150
+ .icon-item:hover {
151
+ border-color: #667eea;
152
+ transform: translateY(-2px);
153
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
154
+ }
155
+
156
+ .icon-item.copied {
157
+ border-color: #52c41a;
158
+ background: #f6ffed;
159
+ }
160
+
161
+ .icon-display {
162
+ font-size: 32px;
163
+ margin-bottom: 10px;
164
+ color: #333;
165
+ height: 40px;
166
+ display: flex;
167
+ align-items: center;
168
+ justify-content: center;
169
+ }
170
+
171
+ .icon-name {
172
+ font-size: 12px;
173
+ color: #666;
174
+ word-break: break-all;
175
+ line-height: 1.3;
176
+ }
177
+
178
+ .copy-tooltip {
179
+ position: absolute;
180
+ top: -30px;
181
+ left: 50%;
182
+ transform: translateX(-50%);
183
+ background: #333;
184
+ color: white;
185
+ padding: 5px 10px;
186
+ border-radius: 4px;
187
+ font-size: 12px;
188
+ opacity: 0;
189
+ transition: opacity 0.3s ease;
190
+ pointer-events: none;
191
+ white-space: nowrap;
192
+ }
193
+
194
+ .copy-tooltip.show {
195
+ opacity: 1;
196
+ }
197
+
198
+ .no-results {
199
+ text-align: center;
200
+ padding: 60px 20px;
201
+ color: #999;
202
+ }
203
+
204
+ .no-results .icon {
205
+ font-size: 64px;
206
+ margin-bottom: 20px;
207
+ opacity: 0.5;
208
+ }
209
+
210
+ .loading {
211
+ text-align: center;
212
+ padding: 60px 20px;
213
+ color: #666;
214
+ }
215
+
216
+ .loading .spinner {
217
+ display: inline-block;
218
+ width: 40px;
219
+ height: 40px;
220
+ border: 4px solid #f3f3f3;
221
+ border-top: 4px solid #667eea;
222
+ border-radius: 50%;
223
+ animation: spin 1s linear infinite;
224
+ margin-bottom: 20px;
225
+ }
226
+
227
+ @keyframes spin {
228
+ 0% { transform: rotate(0deg); }
229
+ 100% { transform: rotate(360deg); }
230
+ }
231
+
232
+ .footer {
233
+ text-align: center;
234
+ margin-top: 40px;
235
+ color: rgba(255,255,255,0.8);
236
+ }
237
+
238
+ .footer a {
239
+ color: rgba(255,255,255,0.9);
240
+ text-decoration: none;
241
+ }
242
+
243
+ .footer a:hover {
244
+ text-decoration: underline;
245
+ }
246
+
247
+ @media (max-width: 768px) {
248
+ .header h1 {
249
+ font-size: 2rem;
250
+ }
251
+
252
+ .icons-grid {
253
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
254
+ gap: 15px;
255
+ padding: 20px;
256
+ }
257
+
258
+ .icon-item {
259
+ padding: 15px;
260
+ }
261
+
262
+ .icon-display {
263
+ font-size: 24px;
264
+ height: 30px;
265
+ }
266
+ }
267
+ </style>
268
+ </head>
269
+ <body>
270
+ <div class="container">
271
+ <header class="header">
272
+ <h1>🎨 XZX Icon Vue2</h1>
273
+ <p>精美的 Vue2 图标组件库</p>
274
+ </header>
275
+
276
+ <div class="stats">
277
+ <div class="stats-grid">
278
+ <div class="stat-item">
279
+ <h3 id="total-count">-</h3>
280
+ <p>总图标数</p>
281
+ </div>
282
+ <div class="stat-item">
283
+ <h3 id="filtered-count">-</h3>
284
+ <p>当前显示</p>
285
+ </div>
286
+ <div class="stat-item">
287
+ <h3>Vue2</h3>
288
+ <p>兼容版本</p>
289
+ </div>
290
+ </div>
291
+ </div>
292
+
293
+ <div class="controls">
294
+ <div class="search-box">
295
+ <input
296
+ type="text"
297
+ id="search-input"
298
+ placeholder="🔍 搜索图标名称... (按 / 快速聚焦,ESC 清空)"
299
+ autocomplete="off"
300
+ >
301
+ <div class="search-icon">⌕</div>
302
+ </div>
303
+
304
+ <div class="filters">
305
+ <span style="color: #666; margin-right: 10px;">快速筛选:</span>
306
+ <div class="filter-tag active" data-filter="all">全部</div>
307
+ <div class="filter-tag" data-filter="filled">填充类</div>
308
+ <div class="filter-tag" data-filter="one">单色类</div>
309
+ <div class="filter-tag" data-filter="arrow">箭头类</div>
310
+ <div class="filter-tag" data-filter="file">文件类</div>
311
+ <div class="filter-tag" data-filter="edit">编辑类</div>
312
+ <div class="filter-tag" data-filter="close">关闭类</div>
313
+ </div>
314
+ </div>
315
+
316
+ <div id="icons-container">
317
+ <div class="loading">
318
+ <div class="spinner"></div>
319
+ <p>正在加载图标...</p>
320
+ </div>
321
+ </div>
322
+
323
+ <footer class="footer">
324
+ <p>
325
+ 基于 <a href="https://www.npmjs.com/package/@xzx-design/icons-svg" target="_blank" rel="noopener">@xzx-design/icons-svg</a> 构建
326
+ | 点击图标复制 Vue 代码
327
+ </p>
328
+ </footer>
329
+ </div>
330
+
331
+ <!-- Vue2 -->
332
+ <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
333
+ <!-- 本地图标库 -->
334
+ <script src="/lib/index.umd.js"></script>
335
+
336
+ <script>
337
+ // 全局变量存储Vue实例
338
+ let vueApp = null
339
+
340
+ // 复制图标代码功能
341
+ function copyIconCode(iconName, element) {
342
+ const code = `<xzx-icon name="${iconName}" size="24"></xzx-icon>`
343
+
344
+ // 创建临时元素复制到剪贴板
345
+ const textarea = document.createElement('textarea')
346
+ textarea.value = code
347
+ document.body.appendChild(textarea)
348
+ textarea.select()
349
+ document.execCommand('copy')
350
+ document.body.removeChild(textarea)
351
+
352
+ // 显示复制成功提示
353
+ element.classList.add('copied')
354
+ const tooltip = element.querySelector('.copy-tooltip')
355
+ tooltip.textContent = '已复制!'
356
+ tooltip.classList.add('show')
357
+
358
+ setTimeout(() => {
359
+ element.classList.remove('copied')
360
+ tooltip.classList.remove('show')
361
+ tooltip.textContent = '点击复制代码'
362
+ }, 1500)
363
+
364
+ // 更新统计
365
+ if (vueApp) {
366
+ vueApp.copyCount++
367
+ }
368
+ }
369
+
370
+ // 页面加载完成后初始化Vue应用
371
+ document.addEventListener('DOMContentLoaded', () => {
372
+ console.log('🚀 预览页面开始初始化...')
373
+
374
+ // 简化的组件注册逻辑
375
+ if (window.XzxIconVue2) {
376
+ // 尝试不同的注册方式(按优先级)
377
+ if (window.XzxIconVue2.default && typeof window.XzxIconVue2.default.install === 'function') {
378
+ Vue.use(window.XzxIconVue2.default)
379
+ console.log('✅ 使用 default.install 注册组件')
380
+ } else if (typeof window.XzxIconVue2.install === 'function') {
381
+ Vue.use(window.XzxIconVue2)
382
+ console.log('✅ 使用 install 注册组件')
383
+ } else if (window.XzxIconVue2.XzxIcon) {
384
+ Vue.component('XzxIcon', window.XzxIconVue2.XzxIcon)
385
+ console.log('✅ 手动注册组件')
386
+ } else if (window.XzxIconVue2.default && window.XzxIconVue2.default.XzxIcon) {
387
+ Vue.component('XzxIcon', window.XzxIconVue2.default.XzxIcon)
388
+ console.log('✅ 通过 default 手动注册组件')
389
+ }
390
+ } else {
391
+ console.error('❌ XzxIconVue2 全局变量未找到')
392
+ }
393
+
394
+ // Vue 应用
395
+ vueApp = new Vue({
396
+ el: '.container',
397
+ data: {
398
+ allIcons: [],
399
+ filteredIcons: [],
400
+ searchQuery: '',
401
+ activeFilter: 'all',
402
+ loading: true,
403
+ copyCount: 0
404
+ },
405
+ async mounted() {
406
+ console.log('🎯 Vue应用已挂载')
407
+ await this.loadIcons()
408
+ this.setupEventListeners()
409
+
410
+ // 添加渲染检查
411
+ this.$nextTick(() => {
412
+ setTimeout(() => {
413
+ const iconElements = document.querySelectorAll('.icon-display xzx-icon')
414
+ const svgElements = document.querySelectorAll('.icon-display svg')
415
+ console.log(`🔍 图标元素: ${iconElements.length}, SVG元素: ${svgElements.length}`)
416
+
417
+ if (iconElements.length > 0 && svgElements.length === 0) {
418
+ console.error('❌ 图标组件未正确渲染为SVG')
419
+ } else if (svgElements.length > 0) {
420
+ console.log('✅ 图标渲染成功')
421
+ }
422
+ }, 200)
423
+ })
424
+ },
425
+ methods: {
426
+ async loadIcons() {
427
+ try {
428
+ const response = await fetch('/api/icons')
429
+ const data = await response.json()
430
+ this.allIcons = data.icons || []
431
+ this.filteredIcons = [...this.allIcons]
432
+
433
+ document.getElementById('total-count').textContent = this.allIcons.length
434
+ document.getElementById('filtered-count').textContent = this.filteredIcons.length
435
+
436
+ this.renderIcons()
437
+ this.loading = false
438
+ } catch (error) {
439
+ console.error('加载图标失败:', error)
440
+ this.showError()
441
+ }
442
+ },
443
+
444
+ setupEventListeners() {
445
+ // 搜索功能
446
+ const searchInput = document.getElementById('search-input')
447
+ searchInput.addEventListener('input', (e) => {
448
+ this.searchQuery = e.target.value.toLowerCase()
449
+ this.filterIcons()
450
+ })
451
+
452
+ // 筛选标签
453
+ document.querySelectorAll('.filter-tag').forEach(tag => {
454
+ tag.addEventListener('click', (e) => {
455
+ document.querySelectorAll('.filter-tag').forEach(t => t.classList.remove('active'))
456
+ e.target.classList.add('active')
457
+ this.activeFilter = e.target.dataset.filter
458
+ this.filterIcons()
459
+ })
460
+ })
461
+
462
+ // 键盘快捷键
463
+ document.addEventListener('keydown', (e) => {
464
+ if (e.key === '/' && !e.ctrlKey && !e.metaKey) {
465
+ e.preventDefault()
466
+ searchInput.focus()
467
+ }
468
+ if (e.key === 'Escape') {
469
+ searchInput.blur()
470
+ searchInput.value = ''
471
+ this.searchQuery = ''
472
+ this.filterIcons()
473
+ }
474
+ })
475
+ },
476
+
477
+ filterIcons() {
478
+ let filtered = [...this.allIcons]
479
+
480
+ // 文本搜索
481
+ if (this.searchQuery) {
482
+ filtered = filtered.filter(icon =>
483
+ icon.toLowerCase().includes(this.searchQuery)
484
+ )
485
+ }
486
+
487
+ // 分类筛选
488
+ if (this.activeFilter !== 'all') {
489
+ filtered = filtered.filter(icon => {
490
+ switch (this.activeFilter) {
491
+ case 'filled':
492
+ return icon.includes('filled')
493
+ case 'one':
494
+ return icon.includes('one')
495
+ case 'arrow':
496
+ return ['left', 'right', 'up', 'down', 'arrow'].some(word => icon.includes(word))
497
+ case 'file':
498
+ return icon.includes('file')
499
+ case 'edit':
500
+ return ['edit', 'write', 'pen', 'pencil'].some(word => icon.includes(word))
501
+ case 'close':
502
+ return ['close', 'delete', 'remove', 'minus'].some(word => icon.includes(word))
503
+ default:
504
+ return true
505
+ }
506
+ })
507
+ }
508
+
509
+ this.filteredIcons = filtered
510
+ document.getElementById('filtered-count').textContent = filtered.length
511
+ this.renderIcons()
512
+ },
513
+
514
+ renderIcons() {
515
+ const container = document.getElementById('icons-container')
516
+
517
+ if (this.filteredIcons.length === 0) {
518
+ container.innerHTML = `
519
+ <div class="no-results">
520
+ <div class="icon">🔍</div>
521
+ <h3>未找到匹配的图标</h3>
522
+ <p>尝试使用其他关键词搜索,或按 Esc 重置搜索</p>
523
+ </div>
524
+ `
525
+ return
526
+ }
527
+
528
+ const iconsHTML = this.filteredIcons.map(iconName => `
529
+ <div class="icon-item" data-icon="${iconName}" onclick="copyIconCode('${iconName}', this)">
530
+ <div class="icon-display">
531
+ <xzx-icon name="${iconName}" size="32"></xzx-icon>
532
+ </div>
533
+ <div class="icon-name">${iconName}</div>
534
+ <div class="copy-tooltip">点击复制代码</div>
535
+ </div>
536
+ `).join('')
537
+
538
+ container.innerHTML = `<div class="icons-grid">${iconsHTML}</div>`
539
+
540
+ // 等待DOM更新后重新编译Vue组件
541
+ this.$nextTick(() => {
542
+ // 获取新插入的图标元素并手动编译
543
+ const newIconElements = container.querySelectorAll('xzx-icon')
544
+ console.log(`🔄 正在编译 ${newIconElements.length} 个图标组件`)
545
+
546
+ // 使用Vue的编译方法重新编译新插入的元素
547
+ if (newIconElements.length > 0) {
548
+ newIconElements.forEach(el => {
549
+ if (!el.__vue__) {
550
+ // 手动创建Vue组件实例
551
+ new Vue({
552
+ el: el,
553
+ components: {
554
+ 'xzx-icon': Vue.options.components.XzxIcon
555
+ }
556
+ })
557
+ }
558
+ })
559
+ }
560
+ })
561
+ },
562
+
563
+ showError() {
564
+ document.getElementById('icons-container').innerHTML = `
565
+ <div class="no-results">
566
+ <div class="icon">❌</div>
567
+ <h3>加载失败</h3>
568
+ <p>请检查网络连接或稍后重试</p>
569
+ </div>
570
+ `
571
+ this.loading = false
572
+ }
573
+ }
574
+ })
575
+ })
576
+ </script>
577
+ </body>
578
+ </html>