|
@@ -1,83 +1,505 @@
|
|
<!DOCTYPE html>
|
|
<!DOCTYPE html>
|
|
-<html lang="en">
|
|
|
|
|
|
+<html lang="zh-CN">
|
|
<head>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta charset="UTF-8">
|
|
- <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
|
|
|
|
- <title>可爱登录页面</title>
|
|
|
|
|
|
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
+ <title>简易链接管理器</title>
|
|
|
|
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.2/papaparse.min.js"></script>
|
|
<style>
|
|
<style>
|
|
- .wrap {
|
|
|
|
|
|
+ body {
|
|
|
|
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
|
|
+ line-height: 1.6;
|
|
|
|
+ margin: 0;
|
|
|
|
+ padding: 20px;
|
|
|
|
+ color: #333;
|
|
|
|
+ background-color: #f9f9f9;
|
|
|
|
+ }
|
|
|
|
+ .container {
|
|
|
|
+ max-width: 1200px;
|
|
|
|
+ margin: 0 auto;
|
|
|
|
+ }
|
|
|
|
+ .header {
|
|
display: flex;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|
|
- /*justify-content: center;*/
|
|
|
|
- /*align-items: center;*/
|
|
|
|
- /*height: 100vh;*/
|
|
|
|
|
|
+ align-items: center;
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+ }
|
|
|
|
+ h1 {
|
|
|
|
+ color: #2c3e50;
|
|
|
|
+ margin: 0;
|
|
|
|
+ }
|
|
|
|
+ .toolbar {
|
|
|
|
+ display: flex;
|
|
|
|
+ gap: 10px;
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
+ }
|
|
|
|
+ .tag-list {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
+ gap: 8px;
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+ }
|
|
|
|
+ .tag {
|
|
|
|
+ background-color: #e0f2fe;
|
|
|
|
+ color: #0369a1;
|
|
|
|
+ padding: 5px 10px;
|
|
|
|
+ border-radius: 15px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ transition: all 0.2s;
|
|
|
|
+ user-select: none;
|
|
|
|
+ }
|
|
|
|
+ .tag.active {
|
|
|
|
+ background-color: #0284c7;
|
|
|
|
+ color: white;
|
|
|
|
+ }
|
|
|
|
+ .tag:hover {
|
|
|
|
+ opacity: 0.8;
|
|
|
|
+ }
|
|
|
|
+ input, button, select {
|
|
|
|
+ padding: 8px 12px;
|
|
|
|
+ border: 1px solid #ddd;
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ }
|
|
|
|
+ input:focus, select:focus {
|
|
|
|
+ outline: none;
|
|
|
|
+ border-color: #0284c7;
|
|
|
|
+ }
|
|
|
|
+ button {
|
|
|
|
+ background-color: #0284c7;
|
|
|
|
+ color: white;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ border: none;
|
|
|
|
+ }
|
|
|
|
+ button:hover {
|
|
|
|
+ background-color: #0369a1;
|
|
|
|
+ }
|
|
|
|
+ table {
|
|
|
|
+ width: 100%;
|
|
|
|
+ border-collapse: collapse;
|
|
|
|
+ margin-top: 20px;
|
|
|
|
+ background-color: white;
|
|
|
|
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
|
|
+ border-radius: 8px;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ }
|
|
|
|
+ th, td {
|
|
|
|
+ padding: 12px 15px;
|
|
|
|
+ text-align: left;
|
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
|
+ }
|
|
|
|
+ th {
|
|
|
|
+ background-color: #f1f5f9;
|
|
|
|
+ font-weight: 600;
|
|
|
|
+ color: #334155;
|
|
|
|
+ }
|
|
|
|
+ tr:hover {
|
|
|
|
+ background-color: #f1f5f9;
|
|
|
|
+ }
|
|
|
|
+ .preview-img {
|
|
|
|
+ max-width: 100px;
|
|
|
|
+ max-height: 100px;
|
|
|
|
+ object-fit: contain;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ }
|
|
|
|
+ .modal {
|
|
|
|
+ display: none;
|
|
|
|
+ position: fixed;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ background-color: rgba(0,0,0,0.8);
|
|
|
|
+ z-index: 1000;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ align-items: center;
|
|
}
|
|
}
|
|
- .item{
|
|
|
|
- width: 30vw;
|
|
|
|
|
|
+ .modal-content {
|
|
|
|
+ max-width: 90%;
|
|
|
|
+ max-height: 90%;
|
|
|
|
+ }
|
|
|
|
+ .modal-content img {
|
|
|
|
+ max-width: 100%;
|
|
|
|
+ max-height: 90vh;
|
|
|
|
+ object-fit: contain;
|
|
|
|
+ }
|
|
|
|
+ .close {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 20px;
|
|
|
|
+ right: 30px;
|
|
|
|
+ color: white;
|
|
|
|
+ font-size: 30px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ }
|
|
|
|
+ .export-import {
|
|
|
|
+ margin-top: 20px;
|
|
|
|
+ display: flex;
|
|
|
|
+ gap: 10px;
|
|
|
|
+ }
|
|
|
|
+ #importInput {
|
|
|
|
+ display: none;
|
|
}
|
|
}
|
|
</style>
|
|
</style>
|
|
</head>
|
|
</head>
|
|
<body>
|
|
<body>
|
|
-<div class="wrap">
|
|
|
|
- <div class="item">123</div>
|
|
|
|
- <div class="item">123</div>
|
|
|
|
- <div class="item">123</div>
|
|
|
|
|
|
+<div class="container">
|
|
|
|
+ <div class="header">
|
|
|
|
+ <h1>简易链接管理器</h1>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="toolbar">
|
|
|
|
+ <input type="text" id="searchInput" placeholder="搜索...">
|
|
|
|
+ <select id="sortSelect">
|
|
|
|
+ <option value="dateDesc">日期 (新→旧)</option>
|
|
|
|
+ <option value="dateAsc">日期 (旧→新)</option>
|
|
|
|
+ <option value="titleAsc">标题 (A→Z)</option>
|
|
|
|
+ <option value="titleDesc">标题 (Z→A)</option>
|
|
|
|
+ </select>
|
|
|
|
+ <button id="addNewBtn">添加新链接</button>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="tag-list" id="tagList"></div>
|
|
|
|
+
|
|
|
|
+ <table id="dataTable">
|
|
|
|
+ <thead>
|
|
|
|
+ <tr>
|
|
|
|
+ <th>标题</th>
|
|
|
|
+ <th>链接</th>
|
|
|
|
+ <th>图片</th>
|
|
|
|
+ <th>标签</th>
|
|
|
|
+ <th>添加日期</th>
|
|
|
|
+ <th>操作</th>
|
|
|
|
+ </tr>
|
|
|
|
+ </thead>
|
|
|
|
+ <tbody id="tableBody"></tbody>
|
|
|
|
+ </table>
|
|
|
|
+
|
|
|
|
+ <div class="export-import">
|
|
|
|
+ <button id="exportBtn">导出数据</button>
|
|
|
|
+ <button id="importBtn">导入数据</button>
|
|
|
|
+ <input type="file" id="importInput" accept=".json">
|
|
|
|
+ </div>
|
|
|
|
+</div>
|
|
|
|
+
|
|
|
|
+<div id="imageModal" class="modal">
|
|
|
|
+ <span class="close">×</span>
|
|
|
|
+ <div class="modal-content">
|
|
|
|
+ <img id="modalImage" src="">
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
-<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.5.16/vue.min.js"></script>
|
|
|
|
-<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
|
|
|
|
-<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.min.js"></script>
|
|
|
|
-<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
|
|
|
|
-</body>
|
|
|
|
<script>
|
|
<script>
|
|
- // curl 'https://www.suoyoutongxue.cn/biz/video_lesson' \
|
|
|
|
- // -H 'Referer: http://localhost:8080/' \
|
|
|
|
- // -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36' \
|
|
|
|
- // -H 'Token: 4fefa7f2f623b3ab-e2dbfaadc863221a941069830427056a276b8882fed736822ec83102eeefa660aaa055c1003cf4ab0da572' \
|
|
|
|
- // -H 'content-type: application/json;charset=UTF-8' \
|
|
|
|
- // --data-raw '{"id":"970","token":"4fefa7f2f623b3ab-e2dbfaadc863221a941069830427056a276b8882fed736822ec83102eeefa660aaa055c1003cf4ab0da572"}'
|
|
|
|
- // const data = {
|
|
|
|
- // id: "970",
|
|
|
|
- // token: "4fefa7f2f623b3ab-e2dbfaadc863221a941069830427056a276b8882fed736822ec83102eeefa660aaa055c1003cf4ab0da572"
|
|
|
|
- // };
|
|
|
|
- //
|
|
|
|
- // const config = {
|
|
|
|
- // headers: {
|
|
|
|
- // 'Referer': 'http://localhost:8080/',
|
|
|
|
- // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
|
|
|
|
- // 'Token': '4fefa7f2f623b3ab-e2dbfaadc863221a941069830427056a276b8882fed736822ec83102eeefa660aaa055c1003cf4ab0da572',
|
|
|
|
- // 'content-type': 'application/json;charset=UTF-8'
|
|
|
|
- // }
|
|
|
|
- // };
|
|
|
|
- //
|
|
|
|
- // axios.post('https://www.suoyoutongxue.cn/biz/video_lesson', data, config)
|
|
|
|
- // .then(response => {
|
|
|
|
- // console.log(response.data);
|
|
|
|
- // })
|
|
|
|
- // .catch(error => {
|
|
|
|
- // console.error(error);
|
|
|
|
- // });
|
|
|
|
- var myHeaders = new Headers();
|
|
|
|
- myHeaders.append("Referer", "http://localhost:8080/");
|
|
|
|
- myHeaders.append("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36");
|
|
|
|
- myHeaders.append("Token", "4fefa7f2f623b3ab-e2dbfaadc863221a941069830427056a276b8882fed736822ec83102eeefa660aaa055c1003cf4ab0da572");
|
|
|
|
- myHeaders.append("content-type", "application/json;charset=UTF-8");
|
|
|
|
- myHeaders.append("Accept", "*/*");
|
|
|
|
- myHeaders.append("Host", "www.suoyoutongxue.cn");
|
|
|
|
- myHeaders.append("Connection", "keep-alive");
|
|
|
|
-
|
|
|
|
- var raw = "{\"id\":\"970\",\"token\":\"4fefa7f2f623b3ab-e2dbfaadc863221a941069830427056a276b8882fed736822ec83102eeefa660aaa055c1003cf4ab0da572\"}";
|
|
|
|
-
|
|
|
|
- var requestOptions = {
|
|
|
|
- method: 'POST',
|
|
|
|
- headers: myHeaders,
|
|
|
|
- body: raw,
|
|
|
|
- redirect: 'follow'
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- fetch("https://www.suoyoutongxue.cn/biz/video_lesson", requestOptions)
|
|
|
|
- .then(response => response.text())
|
|
|
|
- .then(result => console.log(result))
|
|
|
|
- .catch(error => console.log('error', error));
|
|
|
|
|
|
+ // 初始数据存储
|
|
|
|
+ let data = JSON.parse(localStorage.getItem('linkData')) || [];
|
|
|
|
+ let allTags = new Set();
|
|
|
|
+ let activeTagFilters = new Set();
|
|
|
|
+
|
|
|
|
+ // 初始化应用
|
|
|
|
+ function init() {
|
|
|
|
+ renderTags();
|
|
|
|
+ renderTable();
|
|
|
|
+ setupEventListeners();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 从数据中提取所有唯一标签
|
|
|
|
+ function extractAllTags() {
|
|
|
|
+ allTags = new Set();
|
|
|
|
+ data.forEach(item => {
|
|
|
|
+ if (item.tags && Array.isArray(item.tags)) {
|
|
|
|
+ item.tags.forEach(tag => allTags.add(tag));
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return Array.from(allTags).sort();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 渲染标签筛选器
|
|
|
|
+ function renderTags() {
|
|
|
|
+ const tags = extractAllTags();
|
|
|
|
+ const tagList = document.getElementById('tagList');
|
|
|
|
+ tagList.innerHTML = '';
|
|
|
|
+
|
|
|
|
+ tags.forEach(tag => {
|
|
|
|
+ const tagElement = document.createElement('span');
|
|
|
|
+ tagElement.className = `tag ${activeTagFilters.has(tag) ? 'active' : ''}`;
|
|
|
|
+ tagElement.textContent = tag;
|
|
|
|
+ tagElement.dataset.tag = tag;
|
|
|
|
+ tagList.appendChild(tagElement);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 渲染数据表格
|
|
|
|
+ function renderTable() {
|
|
|
|
+ const tableBody = document.getElementById('tableBody');
|
|
|
|
+ tableBody.innerHTML = '';
|
|
|
|
+
|
|
|
|
+ const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
|
|
|
+ const sortMethod = document.getElementById('sortSelect').value;
|
|
|
|
+
|
|
|
|
+ // 筛选数据
|
|
|
|
+ let filteredData = data.filter(item => {
|
|
|
|
+ // 标签筛选
|
|
|
|
+ if (activeTagFilters.size > 0) {
|
|
|
|
+ if (!item.tags || !Array.isArray(item.tags)) return false;
|
|
|
|
+ const hasAllTags = Array.from(activeTagFilters).every(tag =>
|
|
|
|
+ item.tags.includes(tag)
|
|
|
|
+ );
|
|
|
|
+ if (!hasAllTags) return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 搜索筛选
|
|
|
|
+ if (searchTerm) {
|
|
|
|
+ const searchFields = [
|
|
|
|
+ item.title,
|
|
|
|
+ item.url,
|
|
|
|
+ (item.tags || []).join(' ')
|
|
|
|
+ ].join(' ').toLowerCase();
|
|
|
|
+
|
|
|
|
+ return searchFields.includes(searchTerm);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 排序数据
|
|
|
|
+ filteredData.sort((a, b) => {
|
|
|
|
+ switch (sortMethod) {
|
|
|
|
+ case 'dateDesc':
|
|
|
|
+ return new Date(b.date) - new Date(a.date);
|
|
|
|
+ case 'dateAsc':
|
|
|
|
+ return new Date(a.date) - new Date(b.date);
|
|
|
|
+ case 'titleAsc':
|
|
|
|
+ return a.title.localeCompare(b.title);
|
|
|
|
+ case 'titleDesc':
|
|
|
|
+ return b.title.localeCompare(a.title);
|
|
|
|
+ default:
|
|
|
|
+ return new Date(b.date) - new Date(a.date);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 渲染数据行
|
|
|
|
+ filteredData.forEach((item, index) => {
|
|
|
|
+ const row = document.createElement('tr');
|
|
|
|
+
|
|
|
|
+ // 标题列
|
|
|
|
+ const titleCell = document.createElement('td');
|
|
|
|
+ titleCell.textContent = item.title;
|
|
|
|
+ row.appendChild(titleCell);
|
|
|
|
+
|
|
|
|
+ // 链接列
|
|
|
|
+ const urlCell = document.createElement('td');
|
|
|
|
+ const urlLink = document.createElement('a');
|
|
|
|
+ urlLink.href = item.url;
|
|
|
|
+ urlLink.textContent = item.url.length > 30 ? item.url.substring(0, 30) + '...' : item.url;
|
|
|
|
+ urlLink.target = '_blank';
|
|
|
|
+ urlCell.appendChild(urlLink);
|
|
|
|
+ row.appendChild(urlCell);
|
|
|
|
+
|
|
|
|
+ // 图片列
|
|
|
|
+ const imageCell = document.createElement('td');
|
|
|
|
+ if (item.imageUrl) {
|
|
|
|
+ const img = document.createElement('img');
|
|
|
|
+ img.src = item.imageUrl;
|
|
|
|
+ img.alt = item.title;
|
|
|
|
+ img.className = 'preview-img';
|
|
|
|
+ img.dataset.fullImage = item.imageUrl;
|
|
|
|
+ imageCell.appendChild(img);
|
|
|
|
+ }
|
|
|
|
+ row.appendChild(imageCell);
|
|
|
|
+
|
|
|
|
+ // 标签列
|
|
|
|
+ const tagsCell = document.createElement('td');
|
|
|
|
+ if (item.tags && Array.isArray(item.tags)) {
|
|
|
|
+ item.tags.forEach(tag => {
|
|
|
|
+ const tagSpan = document.createElement('span');
|
|
|
|
+ tagSpan.className = 'tag';
|
|
|
|
+ tagSpan.textContent = tag;
|
|
|
|
+ tagsCell.appendChild(tagSpan);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ row.appendChild(tagsCell);
|
|
|
|
+
|
|
|
|
+ // 日期列
|
|
|
|
+ const dateCell = document.createElement('td');
|
|
|
|
+ dateCell.textContent = new Date(item.date).toLocaleDateString();
|
|
|
|
+ row.appendChild(dateCell);
|
|
|
|
+
|
|
|
|
+ // 操作列
|
|
|
|
+ const actionsCell = document.createElement('td');
|
|
|
|
+ const editBtn = document.createElement('button');
|
|
|
|
+ editBtn.textContent = '编辑';
|
|
|
|
+ editBtn.dataset.index = index;
|
|
|
|
+ editBtn.style.marginRight = '5px';
|
|
|
|
+
|
|
|
|
+ const deleteBtn = document.createElement('button');
|
|
|
|
+ deleteBtn.textContent = '删除';
|
|
|
|
+ deleteBtn.dataset.index = index;
|
|
|
|
+ deleteBtn.style.backgroundColor = '#ef4444';
|
|
|
|
+
|
|
|
|
+ actionsCell.appendChild(editBtn);
|
|
|
|
+ actionsCell.appendChild(deleteBtn);
|
|
|
|
+ row.appendChild(actionsCell);
|
|
|
|
+
|
|
|
|
+ tableBody.appendChild(row);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 设置事件监听器
|
|
|
|
+ function setupEventListeners() {
|
|
|
|
+ // 搜索框变化时更新表格
|
|
|
|
+ document.getElementById('searchInput').addEventListener('input', renderTable);
|
|
|
|
+
|
|
|
|
+ // 排序选择变化时更新表格
|
|
|
|
+ document.getElementById('sortSelect').addEventListener('change', renderTable);
|
|
|
|
+
|
|
|
|
+ // 标签点击事件
|
|
|
|
+ document.getElementById('tagList').addEventListener('click', event => {
|
|
|
|
+ if (event.target.classList.contains('tag')) {
|
|
|
|
+ const tag = event.target.dataset.tag;
|
|
|
|
+ if (activeTagFilters.has(tag)) {
|
|
|
|
+ activeTagFilters.delete(tag);
|
|
|
|
+ } else {
|
|
|
|
+ activeTagFilters.add(tag);
|
|
|
|
+ }
|
|
|
|
+ renderTags();
|
|
|
|
+ renderTable();
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 添加新链接按钮
|
|
|
|
+ document.getElementById('addNewBtn').addEventListener('click', () => {
|
|
|
|
+ const title = prompt('输入标题:');
|
|
|
|
+ if (!title) return;
|
|
|
|
+
|
|
|
|
+ const url = prompt('输入URL:');
|
|
|
|
+ if (!url) return;
|
|
|
|
+
|
|
|
|
+ const imageUrl = prompt('输入图片URL (可选):');
|
|
|
|
+
|
|
|
|
+ const tagsInput = prompt('输入标签 (用逗号分隔):');
|
|
|
|
+ const tags = tagsInput ? tagsInput.split(',').map(t => t.trim()).filter(t => t) : [];
|
|
|
|
+
|
|
|
|
+ const newItem = {
|
|
|
|
+ title,
|
|
|
|
+ url,
|
|
|
|
+ imageUrl,
|
|
|
|
+ tags,
|
|
|
|
+ date: new Date().toISOString()
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ data.push(newItem);
|
|
|
|
+ saveData();
|
|
|
|
+ renderTags();
|
|
|
|
+ renderTable();
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 表格行操作按钮
|
|
|
|
+ document.getElementById('tableBody').addEventListener('click', event => {
|
|
|
|
+ if (event.target.tagName === 'BUTTON') {
|
|
|
|
+ const index = parseInt(event.target.dataset.index);
|
|
|
|
+ const item = data[index];
|
|
|
|
+
|
|
|
|
+ if (event.target.textContent === '编辑') {
|
|
|
|
+ // 编辑操作
|
|
|
|
+ const title = prompt('编辑标题:', item.title);
|
|
|
|
+ if (!title) return;
|
|
|
|
+
|
|
|
|
+ const url = prompt('编辑URL:', item.url);
|
|
|
|
+ if (!url) return;
|
|
|
|
+
|
|
|
|
+ const imageUrl = prompt('编辑图片URL (可选):', item.imageUrl || '');
|
|
|
|
+
|
|
|
|
+ const tagsInput = prompt('编辑标签 (用逗号分隔):', (item.tags || []).join(', '));
|
|
|
|
+ const tags = tagsInput ? tagsInput.split(',').map(t => t.trim()).filter(t => t) : [];
|
|
|
|
+
|
|
|
|
+ data[index] = {
|
|
|
|
+ ...item,
|
|
|
|
+ title,
|
|
|
|
+ url,
|
|
|
|
+ imageUrl,
|
|
|
|
+ tags
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ saveData();
|
|
|
|
+ renderTags();
|
|
|
|
+ renderTable();
|
|
|
|
+ } else if (event.target.textContent === '删除') {
|
|
|
|
+ // 删除操作
|
|
|
|
+ if (confirm(`确定要删除 "${item.title}" 吗?`)) {
|
|
|
|
+ data.splice(index, 1);
|
|
|
|
+ saveData();
|
|
|
|
+ renderTags();
|
|
|
|
+ renderTable();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else if (event.target.classList.contains('preview-img')) {
|
|
|
|
+ // 图片预览
|
|
|
|
+ const modal = document.getElementById('imageModal');
|
|
|
|
+ const modalImg = document.getElementById('modalImage');
|
|
|
|
+ modal.style.display = 'flex';
|
|
|
|
+ modalImg.src = event.target.dataset.fullImage;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 图片模态框关闭按钮
|
|
|
|
+ document.querySelector('.close').addEventListener('click', () => {
|
|
|
|
+ document.getElementById('imageModal').style.display = 'none';
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 导出数据
|
|
|
|
+ document.getElementById('exportBtn').addEventListener('click', () => {
|
|
|
|
+ const dataStr = JSON.stringify(data, null, 2);
|
|
|
|
+ const blob = new Blob([dataStr], {type: 'application/json'});
|
|
|
|
+ const url = URL.createObjectURL(blob);
|
|
|
|
+ const a = document.createElement('a');
|
|
|
|
+ a.href = url;
|
|
|
|
+ a.download = `bookmark-data-${new Date().toISOString().slice(0,10)}.json`;
|
|
|
|
+ document.body.appendChild(a);
|
|
|
|
+ a.click();
|
|
|
|
+ document.body.removeChild(a);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 导入数据点击
|
|
|
|
+ document.getElementById('importBtn').addEventListener('click', () => {
|
|
|
|
+ document.getElementById('importInput').click();
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 导入数据处理
|
|
|
|
+ document.getElementById('importInput').addEventListener('change', event => {
|
|
|
|
+ const file = event.target.files[0];
|
|
|
|
+ if (!file) return;
|
|
|
|
+
|
|
|
|
+ const reader = new FileReader();
|
|
|
|
+ reader.onload = function(e) {
|
|
|
|
+ try {
|
|
|
|
+ const importedData = JSON.parse(e.target.result);
|
|
|
|
+ if (Array.isArray(importedData)) {
|
|
|
|
+ if (confirm(`确定要导入 ${importedData.length} 条数据吗? 这将覆盖当前数据。`)) {
|
|
|
|
+ data = importedData;
|
|
|
|
+ saveData();
|
|
|
|
+ renderTags();
|
|
|
|
+ renderTable();
|
|
|
|
+ alert('数据导入成功!');
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ alert('导入的文件格式不正确。请提供有效的JSON数组。');
|
|
|
|
+ }
|
|
|
|
+ } catch (err) {
|
|
|
|
+ alert('导入失败: ' + err.message);
|
|
|
|
+ }
|
|
|
|
+ event.target.value = '';
|
|
|
|
+ };
|
|
|
|
+ reader.readAsText(file);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 保存数据到localStorage
|
|
|
|
+ function saveData() {
|
|
|
|
+ localStorage.setItem('linkData', JSON.stringify(data));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 初始化应用
|
|
|
|
+ init();
|
|
</script>
|
|
</script>
|
|
|
|
+</body>
|
|
</html>
|
|
</html>
|