(type, items)
| 796 | } |
| 797 | |
| 798 | function renderLocalCacheList(type, items) { |
| 799 | const body = type === 'image' ? ui.localImageBody : ui.localVideoBody; |
| 800 | if (!body) return; |
| 801 | if (!items || items.length === 0) { |
| 802 | body.innerHTML = `<tr><td colspan="5">暂无文件</td></tr>`; |
| 803 | syncLocalSelectAllState(type); |
| 804 | return; |
| 805 | } |
| 806 | const selected = selectedLocal[type]; |
| 807 | body.innerHTML = items.map(item => { |
| 808 | const timeText = formatTime(item.mtime_ms); |
| 809 | const preview = item.preview_url ? `<img src="${item.preview_url}" alt="" class="cache-preview">` : ''; |
| 810 | const checked = selected.has(item.name) ? 'checked' : ''; |
| 811 | const rowClass = selected.has(item.name) ? 'row-selected' : ''; |
| 812 | return ` |
| 813 | <tr class="${rowClass}"> |
| 814 | <td class="text-center"> |
| 815 | <input type="checkbox" class="checkbox" data-name="${item.name}" ${checked} onchange="toggleLocalSelect('${type}', '${item.name}', this)"> |
| 816 | </td> |
| 817 | <td class="text-left"> |
| 818 | <div class="flex items-center gap-2"> |
| 819 | ${preview} |
| 820 | <span class="font-mono text-xs text-gray-500">${item.name}</span> |
| 821 | </div> |
| 822 | </td> |
| 823 | <td class="text-left">${formatSize(item.size_bytes)}</td> |
| 824 | <td class="text-left text-xs text-gray-500">${timeText}</td> |
| 825 | <td class="text-center"> |
| 826 | <div class="cache-list-actions"> |
| 827 | <button class="cache-icon-button" onclick="viewLocalFile('${type}', '${item.name}')" title="查看"> |
| 828 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
| 829 | <path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"></path> |
| 830 | <circle cx="12" cy="12" r="3"></circle> |
| 831 | </svg> |
| 832 | </button> |
| 833 | <button class="cache-icon-button" onclick="deleteLocalFile('${type}', '${item.name}')" title="删除"> |
| 834 | <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
| 835 | <polyline points="3 6 5 6 21 6"></polyline> |
| 836 | <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path> |
| 837 | </svg> |
| 838 | </button> |
| 839 | </div> |
| 840 | </td> |
| 841 | </tr> |
| 842 | `; |
| 843 | }).join(''); |
| 844 | syncLocalSelectAllState(type); |
| 845 | updateSelectedCount(); |
| 846 | } |
| 847 | |
| 848 | function viewLocalFile(type, name) { |
| 849 | const safeName = encodeURIComponent(name); |
no test coverage detected