当前位置: 首页 > news >正文

11.15 脚本网页 剪切板管家

   1. 博主QQ输入法,剪切板有点不好用。

  2. 于是在个人APP里,设计了一个网页,当然你直接也可以运行。

功能1 ,可以排序。

功能2 ,可以导出一天的剪切板。

其他,后续待开发

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>剪贴板管家</title>

    <style>

        * {

            margin: 0;

            padding: 0;

            box-sizing: border-box;

        }

 

        :root {

            --primary-color: #5e72e4;

            --secondary-color: #f5365c;

            --success-color: #2dce89;

            --warning-color: #fb6340;

            --info-color: #11cdef;

            --dark: #32325d;

            --light: #f8f9fe;

            --border-radius: 6px;

            --transition: all 0.3s ease;

        }

 

        body {

            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;

            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

            min-height: 100vh;

            padding: 8px;

            transform-origin: center center;

            transition: transform 0.3s ease;

        }

 

        .container {

            max-width: 1400px;

            margin: 0 auto;

            background: rgba(255, 255, 255, 0.95);

            border-radius: 8px;

            padding: 10px;

            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);

        }

 

        /* 精简顶部 */

        .header {

            display: flex;

            justify-content: space-between;

            align-items: center;

            margin-bottom: 10px;

            padding-bottom: 8px;

            border-bottom: 1px solid #e9ecef;

            flex-wrap: wrap;

            gap: 8px;

        }

 

        .title {

            font-size: 18px;

            color: var(--dark);

            font-weight: 600;

        }

 

        .header-actions {

            display: flex;

            gap: 6px;

            flex-wrap: wrap;

        }

 

        .btn {

            padding: 6px 10px;

            border: none;

            border-radius: 4px;

            font-size: 11px;

            font-weight: 500;

            cursor: pointer;

            transition: var(--transition);

            display: inline-flex;

            align-items: center;

            gap: 3px;

            color: white;

            white-space: nowrap;

        }

 

        .btn:hover {

            transform: translateY(-1px);

            box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);

        }

 

        .btn-primary { background: var(--primary-color); }

        .btn-success { background: var(--success-color); }

        .btn-warning { background: var(--warning-color); }

        .btn-info { background: var(--info-color); }

 

        /* 修复响应式布局 - 强制手机端3列 */

        .cards-grid {

            display: grid;

            grid-template-columns: repeat(3, 1fr);

            gap: 8px;

            margin-bottom: 20px;

        }

 

        /* 大屏幕 - 4列 */

        @media (min-width: 1200px) {

            .cards-grid {

                grid-template-columns: repeat(4, 1fr);

                gap: 10px;

            }

        }

 

        /* 中屏幕 - 3列 */

        @media (min-width: 768px) and (max-width: 1199px) {

            .cards-grid {

                grid-template-columns: repeat(3, 1fr);

                gap: 9px;

            }

        }

 

        /* 小屏幕 - 强制3列 */

        @media (max-width: 767px) {

            .cards-grid {

                grid-template-columns: repeat(3, 1fr);

                gap: 6px;

            }

            body {

                padding: 5px;

            }

            .container {

                padding: 8px;

            }

            .header {

                flex-direction: column;

                align-items: stretch;

                gap: 8px;

            }

            .header-actions {

                justify-content: center;

            }

            .btn {

                padding: 5px 8px;

                font-size: 10px;

            }

        }

 

        /* 超小屏幕调整间距 */

        @media (max-width: 400px) {

            .cards-grid {

                gap: 4px;

            }

        }

 

        /* 卡片样式 - 更紧凑 */

        .card {

            background: white;

            border-radius: var(--border-radius);

            padding: 8px;

            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);

            transition: var(--transition);

            position: relative;

            cursor: move;

            user-select: none;

        }

 

        .card:hover {

            transform: translateY(-2px);

            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.12);

        }

 

        .card.dragging {

            opacity: 0.5;

            transform: rotate(5deg);

            z-index: 1000;

        }

 

        .card.drag-over {

            border: 2px dashed var(--primary-color);

            background: rgba(94, 114, 228, 0.1);

        }

 

        .card.selected-for-swap {

            border: 2px solid var(--info-color);

            background: rgba(17, 205, 239, 0.1);

        }

 

        .card-header {

            display: flex;

            justify-content: space-between;

            align-items: center;

            margin-bottom: 6px;

        }

 

        .card-title {

            font-weight: 600;

            color: var(--dark);

            font-size: 12px;

            display: flex;

            align-items: center;

            gap: 4px;

            flex: 1;

            min-width: 0;

        }

 

        .card-number {

            background: var(--primary-color);

            color: white;

            width: 16px;

            height: 16px;

            border-radius: 50%;

            display: flex;

            align-items: center;

            justify-content: center;

            font-size: 9px;

            font-weight: bold;

            flex-shrink: 0;

        }

 

        .card-name {

            overflow: hidden;

            text-overflow: ellipsis;

            white-space: nowrap;

        }

 

        .card-actions {

            display: flex;

            gap: 2px;

        }

 

        .icon-btn {

            width: 20px;

            height: 20px;

            border: none;

            background: transparent;

            border-radius: 3px;

            cursor: pointer;

            display: flex;

            align-items: center;

            justify-content: center;

            transition: var(--transition);

            color: var(--dark);

            font-size: 10px;

        }

 

        .icon-btn:hover {

            background: var(--primary-color);

            color: white;

        }

 

        .card-content {

            background: var(--light);

            padding: 6px;

            border-radius: 4px;

            min-height: 45px;

            max-height: 90px;

            overflow-y: auto;

            font-size: 10px;

            line-height: 1.4;

            color: #495057;

            word-break: break-all;

            white-space: pre-wrap;

        }

 

        .card-footer {

            margin-top: 6px;

            display: flex;

            justify-content: space-between;

            align-items: center;

            font-size: 9px;

            color: #868e96;

        }

 

        .add-card {

            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);

            border: 2px dashed #cbd5e0;

            display: flex;

            flex-direction: column;

            align-items: center;

            justify-content: center;

            cursor: pointer;

            transition: var(--transition);

            min-height: 100px;

        }

 

        .add-card:hover {

            background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);

            border-color: var(--primary-color);

        }

 

        .add-icon {

            font-size: 24px;

            color: #adb5bd;

            margin-bottom: 6px;

        }

 

        /* 删除模式样式 */

        .delete-mode .card {

            cursor: pointer;

        }

 

        .delete-mode .card::after {

            content: '✓';

            position: absolute;

            top: 3px;

            right: 3px;

            width: 16px;

            height: 16px;

            background: var(--success-color);

            color: white;

            border-radius: 50%;

            display: none;

            align-items: center;

            justify-content: center;

            font-size: 10px;

            z-index: 10;

        }

 

        .delete-mode .card.selected::after {

            display: flex;

        }

 

        .delete-mode .card.selected {

            border: 2px solid var(--success-color);

            background: rgba(45, 206, 137, 0.1);

        }

 

        .delete-mode .card:hover {

            border-color: var(--success-color);

        }

 

        .delete-actions {

            position: fixed;

            bottom: 80px;

            left: 50%;

            transform: translateX(-50%);

            background: white;

            padding: 8px 15px;

            border-radius: 6px;

            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);

            display: none;

            gap: 8px;

            z-index: 1000;

        }

 

        .delete-actions.active {

            display: flex;

        }

 

        /* 交换模式提示 */

        .swap-hint {

            position: fixed;

            top: 20px;

            left: 50%;

            transform: translateX(-50%);

            background: var(--info-color);

            color: white;

            padding: 8px 15px;

            border-radius: 6px;

            font-size: 12px;

            display: none;

            z-index: 1000;

        }

 

        .swap-hint.active {

            display: block;

        }

 

        .modal {

            display: none;

            position: fixed;

            top: 0;

            left: 0;

            width: 100%;

            height: 100%;

            background: rgba(0, 0, 0, 0.5);

            z-index: 1000;

            animation: fadeIn 0.3s ease;

        }

 

        .modal.active {

            display: flex;

            align-items: center;

            justify-content: center;

            padding: 20px;

        }

 

        @keyframes fadeIn {

            from { opacity: 0; }

            to { opacity: 1; }

        }

 

        .modal-content {

            background: white;

            border-radius: 8px;

            padding: 15px;

            max-width: 450px;

            width: 100%;

            max-height: 90vh;

            overflow-y: auto;

            animation: slideUp 0.3s ease;

        }

 

        @keyframes slideUp {

            from {

                transform: translateY(20px);

                opacity: 0;

            }

            to {

                transform: translateY(0);

                opacity: 1;

            }

        }

 

        .modal-header {

            margin-bottom: 12px;

        }

 

        .modal-title {

            font-size: 16px;

            color: var(--dark);

            font-weight: 600;

        }

 

        .form-group {

            margin-bottom: 12px;

        }

 

        .form-label {

            display: block;

            margin-bottom: 5px;

            color: var(--dark);

            font-weight: 500;

            font-size: 12px;

        }

 

        .form-input, .form-textarea {

            width: 100%;

            padding: 8px;

            border: 2px solid #e9ecef;

            border-radius: 4px;

            font-size: 12px;

            transition: var(--transition);

        }

 

        .form-input:focus, .form-textarea:focus {

            outline: none;

            border-color: var(--primary-color);

            box-shadow: 0 0 0 3px rgba(94, 114, 228, 0.1);

        }

 

        .form-textarea {

            min-height: 80px;

            resize: vertical;

            font-family: inherit;

        }

 

        .modal-footer {

            display: flex;

            justify-content: flex-end;

            gap: 8px;

            margin-top: 12px;

        }

 

        .zoom-controls {

            position: fixed;

            bottom: 20px;

            right: 20px;

            background: white;

            border-radius: 6px;

            padding: 6px;

            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);

            display: flex;

            gap: 6px;

            z-index: 100;

        }

 

        .zoom-btn {

            width: 28px;

            height: 28px;

            border: none;

            background: var(--primary-color);

            color: white;

            border-radius: 4px;

            cursor: pointer;

            font-size: 14px;

            transition: var(--transition);

        }

 

        .zoom-btn:hover {

            background: #4c63d2;

        }

 

        .zoom-level {

            display: flex;

            align-items: center;

            padding: 0 6px;

            font-size: 11px;

            font-weight: 600;

            color: var(--dark);

            min-width: 35px;

            justify-content: center;

        }

 

        .toast {

            position: fixed;

            top: 20px;

            right: 20px;

            background: white;

            padding: 10px 12px;

            border-radius: 4px;

            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);

            display: flex;

            align-items: center;

            gap: 6px;

            z-index: 2000;

            animation: slideInRight 0.3s ease;

            max-width: 250px;

            font-size: 11px;

        }

 

        @keyframes slideInRight {

            from {

                transform: translateX(100%);

                opacity: 0;

            }

            to {

                transform: translateX(0);

                opacity: 1;

            }

        }

 

        .toast.success { border-left: 3px solid var(--success-color); }

        .toast.error { border-left: 3px solid var(--secondary-color); }

        .toast.info { border-left: 3px solid var(--info-color); }

 

        .import-options {

            margin-bottom: 12px;

        }

 

        .radio-group {

            display: flex;

            gap: 12px;

            margin-top: 6px;

        }

 

        .radio-label {

            display: flex;

            align-items: center;

            gap: 4px;

            font-size: 12px;

            cursor: pointer;

        }

 

        .empty-state {

            text-align: center;

            padding: 30px 15px;

            color: #868e96;

            grid-column: 1 / -1;

        }

 

        .empty-icon {

            font-size: 36px;

            margin-bottom: 10px;

            opacity: 0.5;

        }

    </style>

</head>

<body>

    <div class="container">

        <header class="header">

            <h1 class="title">📋 剪贴板管家</h1>

            <div class="header-actions">

                <button class="btn btn-primary" onclick="copyAll()">

                    <span>📄</span> 粘贴全部

                </button>

                <button class="btn btn-success" onclick="exportData()">

                    <span>💾</span> 导出

                </button>

                <button class="btn btn-info" onclick="importData()">

                    <span>📂</span> 导入

                </button>

                <button class="btn btn-warning" id="deleteBtn" onclick="toggleDeleteMode()">

                    <span>🗑️</span> 删除

                </button>

                <button class="btn btn-info" id="swapBtn" onclick="toggleSwapMode()">

                    <span>🔄</span> 交换

                </button>

            </div>

        </header>

 

        <main>

            <div class="cards-grid" id="cardsGrid">

                <!-- 卡片将动态生成 -->

            </div>

        </main>

    </div>

 

    <!-- 交换模式提示 -->

    <div class="swap-hint" id="swapHint">

        点击两个卡片交换位置

    </div>

 

    <!-- 删除操作栏 -->

    <div class="delete-actions" id="deleteActions">

        <span>已选择 <span id="selectedCount">0</span> 项</span>

        <button class="btn btn-warning" onclick="deleteSelected()">删除选中</button>

        <button class="btn" style="background: #6c757d;" onclick="exitDeleteMode()">取消</button>

    </div>

 

    <!-- 添加/编辑卡片模态框 -->

    <div class="modal" id="cardModal">

        <div class="modal-content">

            <div class="modal-header">

                <h2 class="modal-title" id="modalTitle">添加新内容</h2>

            </div>

            <div class="form-group">

                <label class="form-label">简写名称 *</label>

                <input type="text" class="form-input" id="cardName" placeholder="例如:地址、邮箱、代码片段...">

            </div>

            <div class="form-group">

                <label class="form-label">内容 *</label>

                <textarea class="form-textarea" id="cardContent" placeholder="粘贴或输入你的内容..."></textarea>

            </div>

            <div class="modal-footer">

                <button class="btn btn-primary" onclick="saveCard()">保存</button>

                <button class="btn" style="background: #6c757d;" onclick="closeModal()">取消</button>

            </div>

        </div>

    </div>

 

    <!-- 导入模态框 -->

    <div class="modal" id="importModal">

        <div class="modal-content">

            <div class="modal-header">

                <h2 class="modal-title">导入数据</h2>

            </div>

            <div class="import-options">

                <label class="form-label">导入方式</label>

                <div class="radio-group">

                    <label class="radio-label">

                        <input type="radio" name="importMode" value="replace" checked>

                        覆盖现有数据

                    </label>

                    <label class="radio-label">

                        <input type="radio" name="importMode" value="append">

                        追加到现有数据

                    </label>

                </div>

            </div>

            <div class="form-group">

                <label class="form-label">粘贴导入数据</label>

                <textarea class="form-textarea" id="importData" placeholder="格式示例:

1. 【名称1】

内容1

 

2. 【名称2】

内容2"></textarea>

            </div>

            <div class="modal-footer">

                <button class="btn btn-success" onclick="doImport()">导入</button>

                <button class="btn" style="background: #6c757d;" onclick="closeImportModal()">取消</button>

            </div>

        </div>

    </div>

 

    <!-- 缩放控制 -->

    <div class="zoom-controls">

        <button class="zoom-btn" onclick="zoomOut()">-</button>

        <div class="zoom-level" id="zoomLevel">100%</div>

        <button class="zoom-btn" onclick="zoomIn()">+</button>

        <button class="zoom-btn" onclick="resetZoom()">⟲</button>

    </div>

 

    <script>

        let cards = [];

        let currentEditIndex = -1;

        let currentZoom = 100;

        let deleteMode = false;

        let swapMode = false;

        let selectedCards = new Set();

        let swapFirstIndex = -1;

        let draggedIndex = -1;

 

        // 初始化

        document.addEventListener('DOMContentLoaded', function() {

            loadFromLocalStorage();

            renderCards();

            initDragAndDrop();

        });

 

        // 从本地存储加载数据

        function loadFromLocalStorage() {

            const saved = localStorage.getItem('clipboardCards');

            if (saved) {

                try {

                    cards = JSON.parse(saved);

                } catch (e) {

                    cards = [];

                }

            }

        }

 

        // 保存到本地存储

        function saveToLocalStorage() {

            localStorage.setItem('clipboardCards', JSON.stringify(cards));

        }

 

        // 渲染卡片

        function renderCards() {

            const grid = document.getElementById('cardsGrid');

            

            if (cards.length === 0) {

                grid.innerHTML = `

                    <div class="empty-state">

                        <div class="empty-icon">📋</div>

                        <div>暂无内容,点击下方按钮添加</div>

                    </div>

                    <div class="add-card" onclick="openModal()">

                        <div class="add-icon">➕</div>

                        <div style="color: #868e96; font-size: 12px;">添加新内容</div>

                    </div>

                `;

                return;

            }

 

            grid.innerHTML = cards.map((card, index) => `

                <div class="card ${deleteMode ? 'clickable' : ''} ${swapMode ? 'swapable' : ''}" 

                     data-index="${index}" 

                     draggable="${!deleteMode && !swapMode}"

                     onclick="${deleteMode ? 'toggleCardSelection(' + index + ')' : (swapMode ? 'handleSwapClick(' + index + ')' : '')}">

                    <div class="card-header">

                        <div class="card-title">

                            <span class="card-number">${index + 1}</span>

                            <span class="card-name">${card.name || '未命名'}</span>

                        </div>

                        ${!deleteMode && !swapMode ? `

                        <div class="card-actions">

                            <button class="icon-btn" onclick="event.stopPropagation(); copyCard(${index})" title="复制">📋</button>

                            <button class="icon-btn" onclick="event.stopPropagation(); editCard(${index})" title="编辑">✏️</button>

                        </div>

                        ` : ''}

                    </div>

                    <div class="card-content">${escapeHtml(card.content || '')}</div>

                    <div class="card-footer">

                        <span>${formatTime(card.timestamp)}</span>

                        <span>${(card.content || '').length} 字符</span>

                    </div>

                </div>

            `).join('') + `

                <div class="add-card" onclick="openModal()">

                    <div class="add-icon">➕</div>

                    <div style="color: #868e96; font-size: 12px;">添加新内容</div>

                </div>

            `;

 

            // 更新选中状态

            if (deleteMode) {

                selectedCards.forEach(index => {

                    const card = grid.querySelector(`[data-index="${index}"]`);

                    if (card) card.classList.add('selected');

                });

                updateSelectedCount();

            }

        }

 

        // 初始化拖拽功能

        function initDragAndDrop() {

            const grid = document.getElementById('cardsGrid');

            

            grid.addEventListener('dragstart', function(e) {

                if (e.target.classList.contains('card') && !e.target.classList.contains('add-card')) {

                    draggedIndex = parseInt(e.target.dataset.index);

                    e.target.classList.add('dragging');

                    e.dataTransfer.effectAllowed = 'move';

                }

            });

            

            grid.addEventListener('dragend', function(e) {

                if (e.target.classList.contains('card')) {

                    e.target.classList.remove('dragging');

                }

            });

            

            grid.addEventListener('dragover', function(e) {

                e.preventDefault();

                e.dataTransfer.dropEffect = 'move';

                

                const card = e.target.closest('.card:not(.add-card)');

                if (card && !card.classList.contains('dragging')) {

                    card.classList.add('drag-over');

                }

            });

            

            grid.addEventListener('dragleave', function(e) {

                const card = e.target.closest('.card:not(.add-card)');

                if (card) {

                    card.classList.remove('drag-over');

                }

            });

            

            grid.addEventListener('drop', function(e) {

                e.preventDefault();

                

                const card = e.target.closest('.card:not(.add-card)');

                if (card) {

                    card.classList.remove('drag-over');

                    const dropIndex = parseInt(card.dataset.index);

                    

                    if (draggedIndex !== -1 && draggedIndex !== dropIndex) {

                        // 交换卡片

                        const temp = cards[draggedIndex];

                        cards[draggedIndex] = cards[dropIndex];

                        cards[dropIndex] = temp;

                        

                        saveToLocalStorage();

                        renderCards();

                        showToast('卡片位置已交换', 'success');

                    }

                }

                

                draggedIndex = -1;

            });

        }

 

        // 切换删除模式

        function toggleDeleteMode() {

            deleteMode = !deleteMode;

            swapMode = false;

            const container = document.querySelector('.container');

            const deleteActions = document.getElementById('deleteActions');

            const deleteBtn = document.getElementById('deleteBtn');

            const swapBtn = document.getElementById('swapBtn');

            const swapHint = document.getElementById('swapHint');

            

            if (deleteMode) {

                container.classList.add('delete-mode');

                deleteActions.classList.add('active');

                deleteBtn.innerHTML = '<span>❌</span> 退出删除';

                swapBtn.innerHTML = '<span>🔄</span> 交换';

                selectedCards.clear();

            } else {

                container.classList.remove('delete-mode');

                deleteActions.classList.remove('active');

                deleteBtn.innerHTML = '<span>🗑️</span> 删除';

                selectedCards.clear();

            }

            

            swapHint.classList.remove('active');

            renderCards();

        }

 

        // 切换交换模式

        function toggleSwapMode() {

            swapMode = !swapMode;

            deleteMode = false;

            const container = document.querySelector('.container');

            const deleteBtn = document.getElementById('deleteBtn');

            const swapBtn = document.getElementById('swapBtn');

            const deleteActions = document.getElementById('deleteActions');

            const swapHint = document.getElementById('swapHint');

            

            if (swapMode) {

                container.classList.remove('delete-mode');

                deleteActions.classList.remove('active');

                deleteBtn.innerHTML = '<span>🗑️</span> 删除';

                swapBtn.innerHTML = '<span>❌</span> 退出交换';

                swapHint.classList.add('active');

                swapFirstIndex = -1;

            } else {

                swapBtn.innerHTML = '<span>🔄</span> 交换';

                swapHint.classList.remove('active');

                // 清除选中状态

                document.querySelectorAll('.card.selected-for-swap').forEach(card => {

                    card.classList.remove('selected-for-swap');

                });

            }

            

            renderCards();

        }

 

        // 处理交换点击

        function handleSwapClick(index) {

            if (swapFirstIndex === -1) {

                // 第一次点击

                swapFirstIndex = index;

                const card = document.querySelector(`[data-index="${index}"]`);

                if (card) card.classList.add('selected-for-swap');

            } else {

                // 第二次点击

                if (swapFirstIndex !== index) {

                    // 交换卡片

                    const temp = cards[swapFirstIndex];

                    cards[swapFirstIndex] = cards[index];

                    cards[index] = temp;

                    

                    saveToLocalStorage();

                    showToast('卡片位置已交换', 'success');

                }

                

                // 清除选中状态

                document.querySelectorAll('.card.selected-for-swap').forEach(card => {

                    card.classList.remove('selected-for-swap');

                });

                

                swapFirstIndex = -1;

                renderCards();

            }

        }

 

        // 退出删除模式

        function exitDeleteMode() {

            deleteMode = false;

            const container = document.querySelector('.container');

            const deleteActions = document.getElementById('deleteActions');

            const deleteBtn = document.getElementById('deleteBtn');

            

            container.classList.remove('delete-mode');

            deleteActions.classList.remove('active');

            deleteBtn.innerHTML = '<span>🗑️</span> 删除';

            selectedCards.clear();

            renderCards();

        }

 

        // 切换卡片选中状态

        function toggleCardSelection(index) {

            if (selectedCards.has(index)) {

                selectedCards.delete(index);

            } else {

                selectedCards.add(index);

            }

            

            const card = document.querySelector(`[data-index="${index}"]`);

            if (card) {

                card.classList.toggle('selected');

            }

            

            updateSelectedCount();

        }

 

        // 更新选中数量

        function updateSelectedCount() {

            document.getElementById('selectedCount').textContent = selectedCards.size;

        }

 

        // 删除选中的卡片

        function deleteSelected() {

            if (selectedCards.size === 0) {

                showToast('请选择要删除的内容', 'info');

                return;

            }

            

            if (confirm(`确定要删除选中的 ${selectedCards.size} 条内容吗?`)) {

                // 按索引从大到小排序,避免删除时索引错位

                const indicesToDelete = Array.from(selectedCards).sort((a, b) => b - a);

                indicesToDelete.forEach(index => {

                    cards.splice(index, 1);

                });

                

                saveToLocalStorage();

                exitDeleteMode();

                showToast(`已删除 ${indicesToDelete.length} 条内容`, 'success');

            }

        }

 

        // HTML转义

        function escapeHtml(text) {

            const map = {

                '&': '&amp;',

                '<': '&lt;',

                '>': '&gt;',

                '"': '&quot;',

                "'": '&#039;'

            };

            return text.replace(/[&<>"']/g, m => map[m]);

        }

 

        // 格式化时间

        function formatTime(timestamp) {

            if (!timestamp) return '';

            const date = new Date(timestamp);

            const now = new Date();

            const diff = now - date;

            

            if (diff < 60000) return '刚刚';

            if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前';

            if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前';

            

            return date.toLocaleDateString('zh-CN');

        }

 

        // 打开添加模态框

        function openModal() {

            currentEditIndex = -1;

            document.getElementById('modalTitle').textContent = '添加新内容';

            document.getElementById('cardName').value = '';

            document.getElementById('cardContent').value = '';

            document.getElementById('cardModal').classList.add('active');

            

            // 尝试从剪贴板获取内容

            if (navigator.clipboard && navigator.clipboard.readText) {

                navigator.clipboard.readText().then(text => {

                    if (text && text.trim()) {

                        document.getElementById('cardContent').value = text;

                    }

                }).catch(() => {

                    // 忽略错误

                });

            }

        }

 

        // 编辑卡片

        function editCard(index) {

            currentEditIndex = index;

            const card = cards[index];

            document.getElementById('modalTitle').textContent = '编辑内容';

            document.getElementById('cardName').value = card.name || '';

            document.getElementById('cardContent').value = card.content || '';

            document.getElementById('cardModal').classList.add('active');

        }

 

        // 关闭模态框

        function closeModal() {

            document.getElementById('cardModal').classList.remove('active');

        }

 

        // 保存卡片

        function saveCard() {

            const name = document.getElementById('cardName').value.trim();

            const content = document.getElementById('cardContent').value.trim();

            

            if (!name || !content) {

                showToast('请填写名称和内容', 'error');

                return;

            }

            

            const card = {

                name: name,

                content: content,

                timestamp: new Date().toISOString()

            };

            

            if (currentEditIndex >= 0) {

                cards[currentEditIndex] = card;

                showToast('内容已更新', 'success');

            } else {

                cards.push(card);

                showToast('内容已添加', 'success');

            }

            

            saveToLocalStorage();

            renderCards();

            closeModal();

        }

 

        // 复制单个卡片

        function copyCard(index) {

            const card = cards[index];

            if (navigator.clipboard && navigator.clipboard.writeText) {

                navigator.clipboard.writeText(card.content).then(() => {

                    showToast(`已复制: ${card.name}`, 'success');

                }).catch(() => {

                    showToast('复制失败', 'error');

                });

            } else {

                // 降级方案

                const textarea = document.createElement('textarea');

                textarea.value = card.content;

                document.body.appendChild(textarea);

                textarea.select();

                try {

                    document.execCommand('copy');

                    showToast(`已复制: ${card.name}`, 'success');

                } catch (err) {

                    showToast('复制失败', 'error');

                }

                document.body.removeChild(textarea);

            }

        }

 

        // 复制全部

        function copyAll() {

            if (cards.length === 0) {

                showToast('没有可复制的内容', 'info');

                return;

            }

            

            let allText = cards.map((card, index) => {

                return `${index + 1}. 【${card.name}】\n${card.content}`;

            }).join('\n\n---\n\n');

            

            if (navigator.clipboard && navigator.clipboard.writeText) {

                navigator.clipboard.writeText(allText).then(() => {

                    showToast(`已复制全部 ${cards.length} 条内容`, 'success');

                }).catch(() => {

                    showToast('复制失败', 'error');

                });

            } else {

                // 降级方案

                const textarea = document.createElement('textarea');

                textarea.value = allText;

                document.body.appendChild(textarea);

                textarea.select();

                try {

                    document.execCommand('copy');

                    showToast(`已复制全部 ${cards.length} 条内容`, 'success');

                } catch (err) {

                    showToast('复制失败', 'error');

                }

                document.body.removeChild(textarea);

            }

        }

 

        // 导出数据

        function exportData() {

            if (cards.length === 0) {

                showToast('没有可导出的内容', 'info');

                return;

            }

            

            let exportText = cards.map((card, index) => {

                return `${index + 1}. 【${card.name}】\n${card.content}`;

            }).join('\n\n---\n\n');

            

            if (navigator.clipboard && navigator.clipboard.writeText) {

                navigator.clipboard.writeText(exportText).then(() => {

                    showToast('数据已复制到剪贴板,请保存', 'success');

                }).catch(() => {

                    showToast('导出失败', 'error');

                });

            } else {

                // 降级方案

                const textarea = document.createElement('textarea');

                textarea.value = exportText;

                document.body.appendChild(textarea);

                textarea.select();

                try {

                    document.execCommand('copy');

                    showToast('数据已复制到剪贴板,请保存', 'success');

                } catch (err) {

                    showToast('导出失败', 'error');

                }

                document.body.removeChild(textarea);

            }

        }

 

        // 打开导入模态框

        function importData() {

            document.getElementById('importData').value = '';

            document.querySelector('input[name="importMode"][value="replace"]').checked = true;

            document.getElementById('importModal').classList.add('active');

        }

 

        // 关闭导入模态框

        function closeImportModal() {

            document.getElementById('importModal').classList.remove('active');

        }

 

        // 执行导入

        function doImport() {

            const importText = document.getElementById('importData').value.trim();

            if (!importText) {

                showToast('请输入要导入的数据', 'error');

                return;

            }

            

            const importMode = document.querySelector('input[name="importMode"]:checked').value;

            

            try {

                const sections = importText.split(/\n\s*---\s*\n/);

                const newCards = [];

                

                sections.forEach(section => {

                    const lines = section.trim().split('\n');

                    if (lines.length > 0) {

                        let name = '';

                        let content = '';

                        

                        let match = lines[0].match(/^\d+\.\s*【(.+?)】/);

                        if (match) {

                            name = match[1];

                            content = lines.slice(1).join('\n').trim();

                        } else {

                            match = lines[0].match(/^【(.+?)】$/);

                            if (match) {

                                name = match[1];

                                content = lines.slice(1).join('\n').trim();

                            } else {

                                if (lines.length >= 2) {

                                    name = lines[0].trim();

                                    content = lines.slice(1).join('\n').trim();

                                } else if (lines.length === 1) {

                                    const line = lines[0];

                                    name = line.length > 20 ? line.substring(0, 20) + '...' : line;

                                    content = line;

                                }

                            }

                        }

                        

                        if (name && content) {

                            newCards.push({

                                name: name,

                                content: content,

                                timestamp: new Date().toISOString()

                            });

                        }

                    }

                });

                

                if (newCards.length > 0) {

                    const message = importMode === 'replace' 

                        ? `将导入 ${newCards.length} 条内容并覆盖现有数据,确定吗?`

                        : `将追加 ${newCards.length} 条内容到现有数据,确定吗?`;

                    

                    if (confirm(message)) {

                        if (importMode === 'replace') {

                            cards = newCards;

                        } else {

                            cards = cards.concat(newCards);

                        }

                        saveToLocalStorage();

                        renderCards();

                        closeImportModal();

                        showToast(`成功导入 ${newCards.length} 条内容`, 'success');

                    }

                } else {

                    showToast('未找到有效的内容,请检查格式', 'error');

                }

            } catch (error) {

                console.error('Import error:', error);

                showToast('导入失败,请检查数据格式', 'error');

            }

        }

 

        // 清空所有

        function clearAll() {

            if (cards.length === 0) {

                showToast('没有可清空的内容', 'info');

                return;

            }

            

            if (confirm(`确定要清空所有 ${cards.length} 条内容吗?此操作不可恢复!`)) {

                cards = [];

                saveToLocalStorage();

                renderCards();

                showToast('所有内容已清空', 'info');

            }

        }

 

        // 缩放功能

        function zoomIn() {

            if (currentZoom < 150) {

                currentZoom += 10;

                applyZoom();

            }

        }

 

        function zoomOut() {

            if (currentZoom > 50) {

                currentZoom -= 10;

                applyZoom();

            }

        }

 

        function resetZoom() {

            currentZoom = 100;

            applyZoom();

        }

 

        function applyZoom() {

            document.body.style.transform = `scale(${currentZoom / 100})`;

            document.getElementById('zoomLevel').textContent = `${currentZoom}%`;

        }

 

        // Toast 提示

        function showToast(message, type = 'info') {

            const toast = document.createElement('div');

            toast.className = `toast ${type}`;

            

            const icon = {

                success: '✅',

                error: '❌',

                info: 'ℹ️'

            }[type] || 'ℹ️';

            

            toast.innerHTML = `

                <span style="font-size: 14px;">${icon}</span>

                <span>${message}</span>

            `;

            

            document.body.appendChild(toast);

            

            setTimeout(() => {

                toast.style.animation = 'slideInRight 0.3s ease reverse';

                setTimeout(() => {

                    if (document.body.contains(toast)) {

                        document.body.removeChild(toast);

                    }

                }, 300);

            }, 3000);

        }

 

        // 键盘快捷键

        document.addEventListener('keydown', function(e) {

            // Ctrl/Cmd + N: 新建

            if ((e.ctrlKey || e.metaKey) && e.key === 'n') {

                e.preventDefault();

                openModal();

            }

            // Ctrl/Cmd + E: 导出

            if ((e.ctrlKey || e.metaKey) && e.key === 'e') {

                e.preventDefault();

                exportData();

            }

            // Ctrl/Cmd + I: 导入

            if ((e.ctrlKey || e.metaKey) && e.key === 'i') {

                e.preventDefault();

                importData();

            }

            // Ctrl/Cmd + A: 复制全部

            if ((e.ctrlKey || e.metaKey) && e.key === 'a' && !e.shiftKey) {

                e.preventDefault();

                copyAll();

            }

            // ESC: 关闭模态框或退出模式

            if (e.key === 'Escape') {

                if (deleteMode) {

                    exitDeleteMode();

                } else if (swapMode) {

                    toggleSwapMode();

                } else {

                    closeModal();

                    closeImportModal();

                }

            }

        });

 

        // 点击模态框外部关闭

        document.getElementById('cardModal').addEventListener('click', function(e) {

            if (e.target === this) {

                closeModal();

            }

        });

 

        document.getElementById('importModal').addEventListener('click', function(e) {

            if (e.target === this) {

                closeImportModal();

            }

        });

    </script>

</body>

</html>

 

 

http://www.dtcms.com/a/613595.html

相关文章:

  • 基于python代码自动生成关于建筑安全检测的报告
  • 【Chrono库】Chrono Traits 模块解析(traits.rs)
  • Go语言使用的编译器 | 探索Go编程语言的工具链和编译过程
  • Logback,SLF4J的经典后继日志实现!
  • 搭建个人知识库
  • leetcode寻找第k大的值
  • 瑞安外贸网站制作php做网站都需要学什么软件
  • 企业级 Spring Boot + WebSocket + Redis 分布式消息推送方案
  • 线性代数 · SVD | 从线性代数到数据科学的“盛大”应用(scr:bzv)
  • 专门做推广的网站吗做当地门户网站多少钱
  • 【Java Web学习 | 第12篇】JavaScript(6)DOM
  • VVIC item_search 接口对接全攻略:从入门到精通
  • 四川住建厅官方网站的网址北京专业建设
  • 网站开发实训课程的总结手机网游
  • 《道德经》第五十八章
  • 【面试经验】梅赛德斯奔驰X-Seed AI Systems - Autonomous Driving Agent Efficiency
  • MATLAB基于CNN和DE-NSGAIII的齿盘切削参数优化
  • Node.js+Vue的学习笔记
  • 哪些网站设计的好嘉兴互联网公司
  • GM-3568JHF丨ARM+FPGA异构开发板系列教程:基础入门 1- 开发环境搭建
  • 从括号匹配到字符串解码:递归思想的巧妙应用
  • 第7章 Node框架实战篇 - Express 中间件与RESTful API 接口规范
  • 编译器用什么语言开发 | 深入探讨编译器开发的语言选择及其影响
  • 实战内网PTH上线域控
  • 基于YOLOv5-AUX的棕熊目标检测与识别系统实现
  • 东北网站建设国网典型设计最新版
  • 白酒网站设计广告设计与制作教程
  • 建设文明网站包括个人网站学生作业
  • 面对AI的思考,如何区分什么能力是人最根本的能力?
  • 当“能者”不再“多劳”:于倦怠深处,寻一方从容