SOURCE

console 命令行工具 X clear

                    
>
console
<!DOCTYPE html>
<html>
<head>
    <title>管理系统</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/xlsx.full.min.js"></script>
    <style>
        :root {
            --card-width: 180px;
            --card-height: 300px;
        }

        body {
            margin: 0;
            font-family: Arial;
            display: grid;
            grid-template-columns: 200px 1fr;
            min-height: 100vh;
        }

        .nav {
            background: #f5f5f5;
            padding: 20px;
            border-right: 1px solid #ddd;
            position: relative;
        }

        .category-list {
            list-style: none;
            padding: 0;
            margin-top: 20px;
        }

        .category-item {
            padding: 10px;
            cursor: pointer;
            border-radius: 4px;
            margin-bottom: 5px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .category-item.active {
            background: #2196F3;
            color: white;
        }

        .main {
            padding: 20px;
            display: grid;
            grid-template-columns: repeat(auto-fill, var(--card-width));
            gap: 20px;
            justify-content: center;
            align-content: start;
        }

        .card {
            width: var(--card-width);
            height: var(--card-height);
            border: 1px solid #ddd;
            border-radius: 8px;
            overflow: hidden;
            position: relative;
            transition: 0.3s;
            background: white;
        }

        .upload-card {
            width: var(--card-width);
            height: var(--card-height);
            border: 2px dashed #ccc;
            border-radius: 8px;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            transition: 0.3s;
        }

        .card {
            width: var(--card-width);
            height: var(--card-height);
            border: 1px solid #ddd;
            border-radius: 8px;
            overflow: hidden;
            position: relative;
            transition: 0.3s;
            background: white;
            display: flex;
            flex-direction: column;
        }

        .card-body {
            padding: 10px;
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
        }

        .card-title {
            font-weight: bold;
            font-size: 16px;
            width: 100%;
            height: 70px;
            overflow: hidden;
            text-overflow: ellipsis;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
        }

        .card-actions {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            padding: 10px 5px;
            display: flex;
            justify-content: space-around;
            background: rgba(255,255,255,0.95);
            border-top: 1px solid #eee;
            height: 10px; /* 固定高度 */
            align-items: center;
        }

        .card-actions button {
            width: 28px;
            height: 28px;
            flex-shrink: 0;
        }

        .card-actions button i {
            font-size: 18px;
        }

        .card:hover .card-actions {
            opacity: 1;
        }

        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100vw;
            height: 100vh;
            background: rgba(0,0,0,0.5);
            justify-content: center;
            align-items: center;
            z-index: 1000;
        }

        .modal-content {
            background: white;
            padding: 20px;
            border-radius: 8px;
            width: 90%;
            max-width: 500px;
        }

        .modal-input-group {
            margin: 10px 0;
        }

        .modal-input-group input {
            width: 100%;
            padding: 8px;
            margin: 5px 0;
            border: 1px solid #ddd;
            border-radius: 4px;
        }

        #cropper-container {
            width: 100%;
            height: 400px;
            margin: 10px 0;
        }

        button {
            padding: 0;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            background: transparent;
            margin: 0;
            width: 30px;
            height: 30px;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        button i {
            font-size: 16px;
            color: #666;
        }

        button:hover i {
            color: #2196F3;
        }
        
        /* 新增拖拽样式 */
       .card.dragging {
            opacity: 0.5;
            transform: rotate(3deg);
            transition: none;
        }
        .card.ghost {
            background: #f5f5f5;
            border: 2px dashed #2196F3;
        }

        .confirm-dialog {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.2);
            z-index: 2000;
        }
        .confirm-buttons {
            margin-top: 15px;
            display: flex;
            gap: 10px;
            justify-content: flex-end;
        }

        .modal-actions {
            display: flex;
            gap: 15px;
            justify-content: center;
            margin-top: 20px;
        }

        .modal-btn {
            flex: 1;
            max-width: 150px;
            padding: 10px 20px;
            border-radius: 6px;
            font-size: 14px;
            transition: all 0.3s;
            border: none;
            cursor: pointer;
        }

        .primary {
            background: #2196F3;
            color: white;
        }

        .secondary {
            background: #666;
            color: white;
        }

    </style>
    </style>
</head>
<body>
    <div class="nav">
        <button onclick="showAddCategoryModal()" style="width:auto;padding:8px 16px;background:#f88888;color:white">
            + 添加类目
        </button>
        <button onclick="exportCards()" style="margin-top:10px;width:auto;padding:8px 16px;background:#f88888;color:white">
            ⇩ 导出全部
        </button>
        <ul class="category-list" id="categoryList"></ul>
    </div>

    <div class="main" id="mainContent"></div>

    <!-- 裁剪模态框 -->
    <div class="modal" id="cropModal">
        <div class="modal-content">
            <div id="cropper-container"></div>
            <div class="modal-input-group">
                <input type="text" id="cardName" placeholder="卡片名称" required>
            </div>
            <div class="modal-input-group">
                <input type="text" id="cardPath" placeholder="本地路径">
            </div>
            <div class="modal-input-group">
                <input type="url" id="cardUrl" placeholder="网址 (可选)">
            
            </div>
            <div class="modal-actions">
            <button onclick="confirmCrop()" class="modal-btn primary">确认创建</button>
            <button onclick="closeModal()" class="modal-btn secondary">取消</button>
        </div>
    </div>

    <!-- 在HTML部分新增编辑模态框 -->
    <div class="modal" id="editModal">
        <div class="modal-content">
            <div id="edit-cropper-container"></div>
            <div class="modal-input-group">
                <input type="text" id="editCardName" placeholder="卡片名称" required>
            </div>
            <div class="modal-input-group">
                <input type="text" id="editCardPath" placeholder="本地路径">
            </div>
            <div class="modal-input-group">
                <input type="url" id="editCardUrl" placeholder="网址 (可选)">
            </div>
            <button onclick="confirmEdit()" style="width:auto;padding:8px 16px;background:#2196F3;color:white">保存修改</button>
            <button onclick="closeModal()"  style="background: #666;width:auto;padding:8px 16px;color:white">取消</button>
        </div>
    </div>

    <!-- 分类模态框 -->
    <div class="modal" id="categoryModal">
        <div class="modal-content">
            <div class="modal-input-group">
                <input type="text" id="newCategoryName" placeholder="分类名称" required>
            </div>
            <div class="modal-actions">  <!-- 新增按钮容器 -->
            <button onclick="addCategory()" class="modal-btn primary">添加分类</button>
            <button onclick="closeModal()" class="modal-btn secondary">取消</button>
        </div>
    </div>

    <input type="file" id="fileInput" hidden accept="image/*">

    <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.js"></script>
    <script>
        let data = {
            categories: [],
            currentCategoryId: null
        };

        function loadData() {
            const savedData = localStorage.getItem('cardData');
            if (savedData) {
                try {
                    data = JSON.parse(savedData);
                    if (!data.currentCategoryId && data.categories.length > 0) {
                        data.currentCategoryId = data.categories[0].id;
                    }
                } catch (e) {
                    initDefaultData();
                }
            } else {
                initDefaultData();
            }
        }

        function initDefaultData() {
            const allCategory = {
                id: 0,
                name: '全部',
                cards: [],
                isDefault: true
            };
            const defaultCategory = {
                id: Date.now(),
                name: 'S级',
                cards: []
            };
            data.categories = [allCategory, defaultCategory];
            data.currentCategoryId = allCategory.id;
            saveData();
        }

        function saveData() {
            localStorage.setItem('cardData', JSON.stringify(data));
        }

        function renderCategories() {
            const list = document.getElementById('categoryList');
            list.innerHTML = data.categories.map(cat => `
                <li class="category-item ${cat.id === data.currentCategoryId ? 'active' : ''}" 
                    onclick="switchCategory(${cat.id})">
                    <span>${cat.name}</span>
                    ${!cat.isDefault ? `<button onclick="event.stopPropagation();deleteCategory(${cat.id})" 
                        style="padding:2px 5px;font-size:0.8em">×</button>` : ''}
                </li>
            `).join('');
        }

        function switchCategory(id) {
            data.currentCategoryId = id;
            saveData();
            renderCategories();
            renderCards();
        }

        function showAddCategoryModal() {
            document.getElementById('categoryModal').style.display = 'flex';
        }

        function addCategory() {
            const nameInput = document.getElementById('newCategoryName');
            const newCategory = {
                id: Date.now(),
                name: nameInput.value,
                cards: []
            };
            data.categories.push(newCategory);
            saveData();
            nameInput.value = '';
            renderCategories();
            closeModal();
        }

        function deleteCategory(id) {
            if (!confirm(`确定要删除该分类吗?\n(分类内的卡片不会被删除)`)) return;
            
            data.categories = data.categories.filter(cat => cat.id !== id);
            if (data.currentCategoryId === id) {
                data.currentCategoryId = data.categories[0]?.id || null;
            }
            saveData();
            renderCategories();
            renderCards();
        }

        function renderCards() {
            const main = document.getElementById('mainContent');
            const currentCategory = data.categories.find(c => c.id === data.currentCategoryId);
            
            main.innerHTML = `
                ${currentCategory.isDefault ? '' : `
                <div class="upload-card" onclick="document.getElementById('fileInput').click()">
                    + 上传新卡片
                </div>`}
                ${currentCategory.cards.map(card => `
                    <div class="card" data-id="${card.id}">
                        <img src="${card.image}" class="card-image">
                        <div class="card-body">
                            <div class="card-title">${card.name}</div>
                        </div>
                        <div class="card-actions">
                            ${card.path ? `<button onclick="openFolder('${escapePath(card.path)}')">
                                <i class="fas fa-folder-open"></i>
                            </button>` : ''}
                            ${card.url ? `<button onclick="window.open('${card.url}', '_blank')">
                                <i class="fas fa-external-link-alt"></i>
                            </button>` : ''}
                            <!-- 分离编辑按钮 -->
                            <button onclick="openEditModal(${card.id})">
                                <i class="fas fa-edit"></i>
                            </button>
                            <button onclick="deleteCard(${card.id})">
                                <i class="fas fa-trash"></i>
                            </button>
                        </div>
                    </div>
                `).join('')}
                `;

            // 每次渲染都重新初始化拖拽
            initSortable();
        }
        // 新增拖拽排序功能
        let sortable = null;
        let sortableInstances = [];
        function initSortable() {
            // 清除旧实例
            sortableInstances.forEach(instance => instance.destroy());
            sortableInstances = [];

            const container = document.getElementById('mainContent');
            const currentCategoryId = data.currentCategoryId;
            
            const sortable = new Sortable(container, {
                animation: 150,
                handle: '.card',
                ghostClass: 'ghost',
                chosenClass: 'dragging',
                delay: 300,
                forceFallback: true,
                onEnd: function(evt) {
                    const currentCategory = data.categories.find(c => c.id === currentCategoryId);
                    const [removed] = currentCategory.cards.splice(evt.oldIndex, 1);
                    currentCategory.cards.splice(evt.newIndex, 0, removed);
                    saveData();
                }
            });
            
            sortableInstances.push(sortable);
        }

        function escapePath(path) {
      // 将路径中的反斜杠转换为双反斜杠
            const escaped = path.replace(/\\/g, '\\\\');
            // 使用JSON.stringify确保字符串正确编码
            return JSON.stringify(escaped).replace(/^"|"$/g, '');
        }
        
        function openFolder(path) {
            // 解码双反斜杠为单反斜杠
            const decodedPath = path.replace(/\\\\/g, '\\');
            try {
                navigator.clipboard.writeText(decodedPath).then(() => {
                    alert(`✅ 正确路径已复制:\n${decodedPath}`);
                }).catch(() => {
                    copyViaFallback(decodedPath);
                });
            } catch (e) {
                copyViaFallback(decodedPath);
            }

            function copyViaFallback(path) {
                const textarea = document.createElement('textarea');
                textarea.value = path;
                textarea.style.position = 'fixed';
                document.body.appendChild(textarea);
                textarea.select();
                document.execCommand('copy');
                document.body.removeChild(textarea);
                alert(`✅ 路径已复制:\n${path}`);
            }
        }

        document.getElementById('fileInput').addEventListener('change', function(e) {
            const file = e.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = function(e) {
                    initCropper(e.target.result);
                }
                reader.readAsDataURL(file);
            }
        });

        let cropper = null;
        function initCropper(imageSrc) {
            document.getElementById('cropModal').style.display = 'flex';
            const container = document.getElementById('cropper-container');
            container.innerHTML = `<img src="${imageSrc}" style="max-width: 100%">`;
            
            cropper = new Cropper(container.querySelector('img'), {
                aspectRatio: 3/4,
                viewMode: 1,
                guides: false
            });
        }

        function confirmCrop() {
            const canvas = cropper.getCroppedCanvas({
                width: 180,
                height: 240
            });
            
            const newCard = {
                id: Date.now(),
                image: canvas.toDataURL(),
                name: document.getElementById('cardName').value,
                path: document.getElementById('cardPath').value,
                url: document.getElementById('cardUrl').value
            };

            // 添加到当前分类和全部分类
            const currentCategory = data.categories.find(c => c.id === data.currentCategoryId);
            const allCategory = data.categories.find(c => c.isDefault);
            
            if (currentCategory) currentCategory.cards.push(newCard);
            if (allCategory) allCategory.cards.push(newCard);

            saveData();
            renderCards();
            closeModal();
            
            // 清空创建表单
            document.getElementById('cardName').value = '';
            document.getElementById('cardPath').value = '';
            document.getElementById('cardUrl').value = '';
        }

        // 辅助方法:获取所有卡片
        function getAllCards() {
            return data.categories.flatMap(c => c.cards);
        }

        function deleteCard(cardId) {
            if (!confirm('确定要永久删除该卡片吗?')) return;
            
            const currentCategory = data.categories.find(c => c.id === data.currentCategoryId);
            currentCategory.cards = currentCategory.cards.filter(c => c.id !== cardId);
            
            // 同步删除其他分类
            data.categories.forEach(cat => {
                if (!cat.isDefault) {
                    cat.cards = cat.cards.filter(c => c.id !== cardId);
                }
            });
            
            saveData();
            renderCards();
        }

        // 在每次渲染模态框后调用
        function showCropModal() {
            document.getElementById('cropModal').style.display = 'flex';
            initModalEvents(); // 重要!重新绑定事件
        }

        function editCard(cardId) {
            editingCardId = cardId;
            const card = getAllCards().find(c => c.id === cardId);
            
            document.getElementById('cropModal').style.display = 'flex';
            document.getElementById('cardName').value = card.name;
            document.getElementById('cardPath').value = card.path || '';
            document.getElementById('cardUrl').value = card.url || '';
            
            const container = document.getElementById('cropper-container');
            container.innerHTML = `<img src="${card.image}" style="max-width: 100%">`;
            cropper = new Cropper(container.querySelector('img'), {
                aspectRatio: 3/4,
                viewMode: 1
            });
        }

        function closeModal() {
            document.querySelectorAll('.modal').forEach(m => m.style.display = 'none');
            if (cropper) {
                cropper.destroy();
                cropper = null;
            }
            if (editCropper) {
                editCropper.destroy();
                editCropper = null;
            }
            editingCardId = null;
        }

        // 独立编辑功能
        let editCropper = null;
        let editingCardId = null;

        function openEditModal(cardId) {
            editingCardId = cardId;
            const card = data.categories.flatMap(c => c.cards).find(c => c.id === cardId);
            
            document.getElementById('editModal').style.display = 'flex';
            document.getElementById('editCardName').value = card.name;
            document.getElementById('editCardPath').value = card.path || '';
            document.getElementById('editCardUrl').value = card.url || '';
            
            const container = document.getElementById('edit-cropper-container');
            container.innerHTML = `<img src="${card.image}" style="max-width: 100%">`;
            editCropper = new Cropper(container.querySelector('img'), {
                aspectRatio: 3/4,
                viewMode: 1
            });
        }

        function confirmEdit() {
            const canvas = editCropper.getCroppedCanvas({
                width: 180,
                height: 240
            });
            
            const updatedData = {
                id: editingCardId,
                image: canvas.toDataURL(),
                name: document.getElementById('editCardName').value,
                path: document.getElementById('editCardPath').value,
                url: document.getElementById('editCardUrl').value
            };

            // 更新所有分类中的卡片
            data.categories.forEach(category => {
                const index = category.cards.findIndex(c => c.id === editingCardId);
                if (index !== -1) {
                    category.cards[index] = updatedData;
                }
            });

            saveData();
            renderCards();
            closeModal();
        }

        loadData();
        renderCategories();
        renderCards();
    </script>
</body>
</html>