专栏文章

云剪贴板取缔计划

算法·理论参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minls2jv
此快照首次捕获于
2025/12/02 04:29
3 个月前
此快照最后确认于
2025/12/02 04:29
3 个月前
查看原文
洛谷的云剪贴板实在好用,我很多时候登洛谷就是为了用云剪贴板。所以,我决定自己写一个
CPP
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>快捷工具</title>
  <!-- 引入Tailwind CSS -->
  <script src="https://cdn.tailwindcss.com"></script>
  <!-- 引入Font Awesome图标 -->
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  
  <!-- 配置Tailwind -->
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#165DFF',
            secondary: '#36CFC9',
            dark: '#1D2129',
            light: '#F2F3F5'
          },
          fontFamily: {
            inter: ['Inter', 'system-ui', 'sans-serif'],
          },
        },
      }
    }
  </script>
  
  <style type="text/tailwindcss">
    @layer utilities {
      .content-auto {
        content-visibility: auto;
      }
      .card-hover {
        @apply transition-all duration-300 hover:shadow-lg hover:-translate-y-1;
      }
      .btn-effect {
        @apply transform transition-all duration-200 active:scale-95;
      }
    }
  </style>
</head>
<body class="font-inter bg-gray-50 min-h-screen">
  <!-- 顶部导航 -->
  <header class="bg-white shadow-sm sticky top-0 z-10">
    <div class="container mx-auto px-4 py-4 flex justify-between items-center">
      <h1 class="text-[clamp(1.5rem,3vw,2rem)] font-bold text-primary flex items-center">
        <i class="fa fa-wrench mr-2"></i>快捷工具
      </h1>
      <div class="flex space-x-2">
        <button id="clearAll" class="bg-red-500 text-white px-4 py-2 rounded-lg btn-effect flex items-center">
          <i class="fa fa-trash mr-1"></i>清空
        </button>
      </div>
    </div>
  </header>

  <main class="container mx-auto px-4 py-6">
    <!-- 标签页切换 -->
    <div class="flex border-b mb-6">
      <button id="tabSites" class="px-6 py-3 font-medium text-primary border-b-2 border-primary">
        <i class="fa fa-link mr-1"></i>网站快捷方式
      </button>
      <button id="tabNotes" class="px-6 py-3 font-medium text-gray-500 hover:text-primary transition-colors">
        <i class="fa fa-sticky-note mr-1"></i>文本存储
      </button>
    </div>

    <!-- 网站快捷方式区域 -->
    <section id="sitesSection" class="space-y-6">
      <!-- 添加网站表单 -->
      <div class="bg-white p-4 rounded-xl shadow-sm">
        <h2 class="text-lg font-semibold mb-4">添加新网站</h2>
        <form id="addSiteForm" class="grid grid-cols-1 md:grid-cols-3 gap-4">
          <div>
            <label for="siteName" class="block text-sm font-medium text-gray-700 mb-1">网站名称</label>
            <input type="text" id="siteName" placeholder="例如:洛谷" 
                  class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-all">
          </div>
          <div>
            <label for="siteUrl" class="block text-sm font-medium text-gray-700 mb-1">网站URL</label>
            <input type="url" id="siteUrl" placeholder="例如:https://luogu.com" 
                  class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-all">
          </div>
          <div class="flex items-end">
            <button type="submit" class="w-full bg-primary text-white px-4 py-2 rounded-lg btn-effect flex items-center justify-center">
              <i class="fa fa-plus mr-1"></i>添加
            </button>
          </div>
        </form>
      </div>

      <!-- 网站列表 -->
      <div>
        <h2 class="text-lg font-semibold mb-4">已添加网站</h2>
        <div id="sitesList" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
          <!-- 网站卡片将通过JS动态生成 -->
        </div>
      </div>
    </section>

    <!-- 文本存储区域 -->
    <section id="notesSection" class="space-y-6 hidden">
      <!-- 添加笔记表单 -->
      <div class="bg-white p-4 rounded-xl shadow-sm">
        <h2 class="text-lg font-semibold mb-4">添加新笔记</h2>
        <form id="addNoteForm" class="space-y-4">
          <div>
            <label for="noteTitle" class="block text-sm font-medium text-gray-700 mb-1">笔记标题</label>
            <input type="text" id="noteTitle" placeholder="例如:常用代码片段" 
                  class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-all">
          </div>
          <div>
            <label for="noteContent" class="block text-sm font-medium text-gray-700 mb-1">笔记内容</label>
            <textarea id="noteContent" rows="4" placeholder="输入要保存的文字或代码..." 
                      class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-all font-mono text-sm"></textarea>
          </div>
          <div class="flex justify-end">
            <button type="submit" class="bg-primary text-white px-6 py-2 rounded-lg btn-effect flex items-center">
              <i class="fa fa-save mr-1"></i>保存
            </button>
          </div>
        </form>
      </div>

      <!-- 笔记列表 -->
      <div>
        <h2 class="text-lg font-semibold mb-4">已保存笔记</h2>
        <div id="notesList" class="space-y-4">
          <!-- 笔记卡片将通过JS动态生成 -->
        </div>
      </div>
    </section>
  </main>

  <!-- 通知提示 -->
  <div id="notification" class="fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center">
    <i id="notificationIcon" class="mr-2"></i>
    <span id="notificationText"></span>
  </div>

  <script>
    // 数据存储键名
    const SITES_KEY = 'quickSites';
    const NOTES_KEY = 'quickNotes';

    // DOM元素
    const tabSites = document.getElementById('tabSites');
    const tabNotes = document.getElementById('tabNotes');
    const sitesSection = document.getElementById('sitesSection');
    const notesSection = document.getElementById('notesSection');
    const addSiteForm = document.getElementById('addSiteForm');
    const addNoteForm = document.getElementById('addNoteForm');
    const sitesList = document.getElementById('sitesList');
    const notesList = document.getElementById('notesList');
    const clearAllBtn = document.getElementById('clearAll');
    const notification = document.getElementById('notification');
    const notificationIcon = document.getElementById('notificationIcon');
    const notificationText = document.getElementById('notificationText');

    // 初始化
    document.addEventListener('DOMContentLoaded', () => {
      loadSites();
      loadNotes();
      setupEventListeners();
    });

    // 设置事件监听器
    function setupEventListeners() {
      // 标签页切换
      tabSites.addEventListener('click', () => {
        switchToSitesTab();
      });
      
      tabNotes.addEventListener('click', () => {
        switchToNotesTab();
      });
      
      // 添加网站表单提交
      addSiteForm.addEventListener('submit', (e) => {
        e.preventDefault();
        addNewSite();
      });
      
      // 添加笔记表单提交
      addNoteForm.addEventListener('submit', (e) => {
        e.preventDefault();
        addNewNote();
      });
      
      // 清空所有数据
      clearAllBtn.addEventListener('click', () => {
        if (confirm('确定要清空所有数据吗?此操作不可恢复!')) {
          localStorage.removeItem(SITES_KEY);
          localStorage.removeItem(NOTES_KEY);
          sitesList.innerHTML = '';
          notesList.innerHTML = '';
          showNotification('所有数据已清空', 'fa-check-circle', 'bg-green-500');
        }
      });
    }

    // 切换到网站标签页
    function switchToSitesTab() {
      tabSites.classList.add('text-primary', 'border-b-2', 'border-primary');
      tabSites.classList.remove('text-gray-500');
      tabNotes.classList.remove('text-primary', 'border-b-2', 'border-primary');
      tabNotes.classList.add('text-gray-500');
      sitesSection.classList.remove('hidden');
      notesSection.classList.add('hidden');
    }

    // 切换到笔记标签页
    function switchToNotesTab() {
      tabNotes.classList.add('text-primary', 'border-b-2', 'border-primary');
      tabNotes.classList.remove('text-gray-500');
      tabSites.classList.remove('text-primary', 'border-b-2', 'border-primary');
      tabSites.classList.add('text-gray-500');
      notesSection.classList.remove('hidden');
      sitesSection.classList.add('hidden');
    }

    // 加载网站列表
    function loadSites() {
      const sites = JSON.parse(localStorage.getItem(SITES_KEY) || '[]');
      sitesList.innerHTML = '';
      
      if (sites.length === 0) {
        sitesList.innerHTML = `
          <div class="col-span-full text-center py-10 text-gray-500">
            <i class="fa fa-link text-4xl mb-2 opacity-30"></i>
            <p>还没有添加网站,点击上方表单添加</p>
          </div>
        `;
        return;
      }
      
      sites.forEach((site, index) => {
        const siteCard = document.createElement('div');
        siteCard.className = 'bg-white rounded-xl shadow-sm p-4 card-hover';
        siteCard.innerHTML = `
          <div class="flex justify-between items-start mb-3">
            <h3 class="font-semibold text-lg">${site.name}</h3>
            <div class="flex space-x-1">
              <button class="delete-site text-gray-400 hover:text-red-500 p-1" data-index="${index}">
                <i class="fa fa-trash"></i>
              </button>
            </div>
          </div>
          <p class="text-sm text-gray-500 mb-3 truncate">${site.url}</p>
          <a href="${site.url}" target="_blank" class="block w-full text-center bg-primary/10 text-primary px-4 py-2 rounded-lg hover:bg-primary/20 transition-colors btn-effect">
            访问网站
          </a>
        `;
        sitesList.appendChild(siteCard);
      });
      
      // 添加删除事件
      document.querySelectorAll('.delete-site').forEach(btn => {
        btn.addEventListener('click', (e) => {
          const index = parseInt(e.currentTarget.dataset.index);
          deleteSite(index);
        });
      });
    }

    // 添加新网站
    function addNewSite() {
      const nameInput = document.getElementById('siteName');
      const urlInput = document.getElementById('siteUrl');
      
      const name = nameInput.value.trim();
      let url = urlInput.value.trim();
      
      if (!name || !url) {
        showNotification('请填写网站名称和URL', 'fa-exclamation-circle', 'bg-yellow-500');
        return;
      }
      
      // 确保URL以http://或https://开头
      if (!url.startsWith('http://') && !url.startsWith('https://')) {
        url = 'https://' + url;
      }
      
      const sites = JSON.parse(localStorage.getItem(SITES_KEY) || '[]');
      sites.push({ name, url });
      localStorage.setItem(SITES_KEY, JSON.stringify(sites));
      
      // 清空输入框
      nameInput.value = '';
      urlInput.value = '';
      
      // 重新加载列表
      loadSites();
      showNotification('网站添加成功', 'fa-check-circle', 'bg-green-500');
    }

    // 删除网站
    function deleteSite(index) {
      const sites = JSON.parse(localStorage.getItem(SITES_KEY) || '[]');
      sites.splice(index, 1);
      localStorage.setItem(SITES_KEY, JSON.stringify(sites));
      loadSites();
      showNotification('网站已删除', 'fa-info-circle', 'bg-blue-500');
    }

    // 加载笔记列表
    function loadNotes() {
      const notes = JSON.parse(localStorage.getItem(NOTES_KEY) || '[]');
      notesList.innerHTML = '';
      
      if (notes.length === 0) {
        notesList.innerHTML = `
          <div class="text-center py-10 text-gray-500 bg-white rounded-xl p-4">
            <i class="fa fa-sticky-note text-4xl mb-2 opacity-30"></i>
            <p>还没有添加笔记,点击上方表单添加</p>
          </div>
        `;
        return;
      }
      
      notes.forEach((note, index) => {
        const noteCard = document.createElement('div');
        noteCard.className = 'bg-white rounded-xl shadow-sm p-4 card-hover';
        noteCard.innerHTML = `
          <div class="flex justify-between items-start mb-3">
            <h3 class="font-semibold text-lg">${note.title}</h3>
            <div class="flex space-x-1">
              <button class="copy-note text-gray-400 hover:text-green-500 p-1" data-index="${index}">
                <i class="fa fa-copy"></i>
              </button>
              <button class="delete-note text-gray-400 hover:text-red-500 p-1" data-index="${index}">
                <i class="fa fa-trash"></i>
              </button>
            </div>
          </div>
          <div class="bg-gray-50 p-3 rounded-lg text-sm font-mono overflow-x-auto max-h-40 mb-3">
            <pre>${escapeHtml(note.content)}</pre>
          </div>
          <button class="w-full text-center bg-primary/10 text-primary px-4 py-2 rounded-lg hover:bg-primary/20 transition-colors copy-note btn-effect" data-index="${index}">
            <i class="fa fa-copy mr-1"></i>复制内容
          </button>
        `;
        notesList.appendChild(noteCard);
      });
      
      // 添加复制和删除事件
      document.querySelectorAll('.copy-note').forEach(btn => {
        btn.addEventListener('click', (e) => {
          const index = parseInt(e.currentTarget.dataset.index);
          copyNote(index);
        });
      });
      
      document.querySelectorAll('.delete-note').forEach(btn => {
        btn.addEventListener('click', (e) => {
          const index = parseInt(e.currentTarget.dataset.index);
          deleteNote(index);
        });
      });
    }

    // 添加新笔记
    function addNewNote() {
      const titleInput = document.getElementById('noteTitle');
      const contentInput = document.getElementById('noteContent');
      
      const title = titleInput.value.trim();
      const content = contentInput.value.trim();
      
      if (!title || !content) {
        showNotification('请填写笔记标题和内容', 'fa-exclamation-circle', 'bg-yellow-500');
        return;
      }
      
      const notes = JSON.parse(localStorage.getItem(NOTES_KEY) || '[]');
      notes.push({ title, content });
      localStorage.setItem(NOTES_KEY, JSON.stringify(notes));
      
      // 清空输入框
      titleInput.value = '';
      contentInput.value = '';
      
      // 重新加载列表
      loadNotes();
      showNotification('笔记保存成功', 'fa-check-circle', 'bg-green-500');
    }

    // 复制笔记内容
    function copyNote(index) {
      const notes = JSON.parse(localStorage.getItem(NOTES_KEY) || '[]');
      const content = notes[index].content;
      
      navigator.clipboard.writeText(content).then(() => {
        showNotification('内容已复制到剪贴板', 'fa-check-circle', 'bg-green-500');
      }).catch(err => {
        showNotification('复制失败,请手动复制', 'fa-exclamation-circle', 'bg-red-500');
        console.error('复制失败:', err);
      });
    }

    // 删除笔记
    function deleteNote(index) {
      const notes = JSON.parse(localStorage.getItem(NOTES_KEY) || '[]');
      notes.splice(index, 1);
      localStorage.setItem(NOTES_KEY, JSON.stringify(notes));
      loadNotes();
      showNotification('笔记已删除', 'fa-info-circle', 'bg-blue-500');
    }

    // 显示通知
    function showNotification(text, icon, bgColor) {
      notificationText.textContent = text;
      notificationIcon.className = `fa ${icon}`;
      notification.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transform translate-y-0 opacity-100 transition-all duration-300 flex items-center ${bgColor} text-white`;
      
      setTimeout(() => {
        notification.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center ${bgColor} text-white`;
      }, 3000);
    }

    // HTML转义,防止XSS
    function escapeHtml(unsafe) {
      return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
    }
  </script>
</body>
</html>
现在这个版本还有不少问题,这是我做的初稿,我会尝试改进

评论

0 条评论,欢迎与作者交流。

正在加载评论...