You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1117 lines
44 KiB
1117 lines
44 KiB
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>万物收藏</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600&family=DM+Mono:wght@400;500&family=Outfit:wght@300;400;500;600&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--bg: #0e0f11;
|
|
--bg2: #141518;
|
|
--bg3: #1c1e22;
|
|
--bg4: #242629;
|
|
--border: rgba(255,255,255,0.07);
|
|
--border2: rgba(255,255,255,0.12);
|
|
--text: #e8e6e1;
|
|
--text2: #8a8680;
|
|
--text3: #525050;
|
|
--accent: #c8a97e;
|
|
--accent2: #a07d52;
|
|
--accent-bg: rgba(200,169,126,0.1);
|
|
--teal: #5db8a0;
|
|
--teal-bg: rgba(93,184,160,0.1);
|
|
--blue: #6da4d4;
|
|
--blue-bg: rgba(109,164,212,0.1);
|
|
--red: #d46d6d;
|
|
--red-bg: rgba(212,109,109,0.1);
|
|
--purple: #9d88d4;
|
|
--purple-bg: rgba(157,136,212,0.1);
|
|
--font-display: 'Noto Serif SC', serif;
|
|
--font-body: 'Outfit', sans-serif;
|
|
--font-mono: 'DM Mono', monospace;
|
|
--radius: 10px;
|
|
--radius-lg: 16px;
|
|
--sidebar-w: 220px;
|
|
}
|
|
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
html, body { height: 100%; overflow: hidden; }
|
|
body {
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font-family: var(--font-body);
|
|
font-size: 14px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* ─── Top Bar ─── */
|
|
.topbar {
|
|
height: 48px;
|
|
background: var(--bg2);
|
|
border-bottom: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 16px;
|
|
gap: 12px;
|
|
flex-shrink: 0;
|
|
z-index: 100;
|
|
}
|
|
.logo {
|
|
font-family: var(--font-display);
|
|
font-size: 16px;
|
|
color: var(--accent);
|
|
letter-spacing: 0.04em;
|
|
white-space: nowrap;
|
|
margin-right: 8px;
|
|
}
|
|
.search-wrap {
|
|
flex: 1;
|
|
max-width: 480px;
|
|
position: relative;
|
|
}
|
|
.search-wrap svg { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); opacity: 0.4; }
|
|
.search-wrap input {
|
|
width: 100%;
|
|
background: var(--bg3);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 7px 12px 7px 34px;
|
|
color: var(--text);
|
|
font-family: var(--font-body);
|
|
font-size: 13px;
|
|
outline: none;
|
|
transition: border-color 0.2s;
|
|
}
|
|
.search-wrap input::placeholder { color: var(--text3); }
|
|
.search-wrap input:focus { border-color: var(--border2); }
|
|
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
|
|
.btn-add {
|
|
background: var(--accent);
|
|
color: #1a1208;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 7px 14px;
|
|
font-family: var(--font-body);
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
transition: background 0.2s, transform 0.1s;
|
|
}
|
|
.btn-add:hover { background: #d4b88a; }
|
|
.btn-add:active { transform: scale(0.97); }
|
|
.avatar {
|
|
width: 30px; height: 30px;
|
|
border-radius: 50%;
|
|
background: linear-gradient(135deg, var(--teal), var(--blue));
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 12px; font-weight: 600; color: white; cursor: pointer;
|
|
}
|
|
|
|
/* ─── Layout ─── */
|
|
.app {
|
|
flex: 1;
|
|
display: flex;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* ─── Sidebar ─── */
|
|
.sidebar {
|
|
width: var(--sidebar-w);
|
|
background: var(--bg2);
|
|
border-right: 1px solid var(--border);
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow-y: auto;
|
|
flex-shrink: 0;
|
|
padding: 12px 0;
|
|
}
|
|
.sidebar::-webkit-scrollbar { width: 0; }
|
|
|
|
.sidebar-section { padding: 0 8px; margin-bottom: 20px; }
|
|
.sidebar-label {
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--text3);
|
|
padding: 0 8px;
|
|
margin-bottom: 4px;
|
|
}
|
|
.nav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 7px 8px;
|
|
border-radius: 7px;
|
|
cursor: pointer;
|
|
color: var(--text2);
|
|
font-size: 13px;
|
|
transition: background 0.15s, color 0.15s;
|
|
position: relative;
|
|
}
|
|
.nav-item:hover { background: var(--bg3); color: var(--text); }
|
|
.nav-item.active { background: var(--accent-bg); color: var(--accent); }
|
|
.nav-item svg { flex-shrink: 0; }
|
|
.nav-badge {
|
|
margin-left: auto;
|
|
background: var(--bg4);
|
|
color: var(--text3);
|
|
font-size: 10px;
|
|
padding: 1px 6px;
|
|
border-radius: 10px;
|
|
font-family: var(--font-mono);
|
|
}
|
|
.nav-item.active .nav-badge { background: rgba(200,169,126,0.2); color: var(--accent); }
|
|
|
|
.tag-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
|
|
.cat-indent { padding-left: 20px; }
|
|
|
|
/* ─── Main ─── */
|
|
.main {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
background: var(--bg);
|
|
}
|
|
.main-header {
|
|
padding: 20px 24px 0;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: space-between;
|
|
flex-shrink: 0;
|
|
}
|
|
.main-title {
|
|
font-family: var(--font-display);
|
|
font-size: 20px;
|
|
color: var(--text);
|
|
font-weight: 400;
|
|
}
|
|
.main-sub { font-size: 12px; color: var(--text3); margin-top: 2px; }
|
|
|
|
.view-tabs {
|
|
display: flex;
|
|
gap: 2px;
|
|
background: var(--bg3);
|
|
padding: 3px;
|
|
border-radius: 8px;
|
|
}
|
|
.view-tab {
|
|
padding: 5px 10px;
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
color: var(--text2);
|
|
transition: all 0.15s;
|
|
display: flex; align-items: center; gap: 5px;
|
|
}
|
|
.view-tab.active { background: var(--bg4); color: var(--text); }
|
|
.view-tab:hover:not(.active) { color: var(--text); }
|
|
|
|
.filter-bar {
|
|
padding: 12px 24px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
flex-shrink: 0;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.filter-chip {
|
|
padding: 5px 12px;
|
|
border-radius: 20px;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
border: 1px solid var(--border);
|
|
color: var(--text2);
|
|
background: transparent;
|
|
transition: all 0.15s;
|
|
display: flex; align-items: center; gap: 5px;
|
|
white-space: nowrap;
|
|
}
|
|
.filter-chip:hover { border-color: var(--border2); color: var(--text); }
|
|
.filter-chip.active { background: var(--accent-bg); border-color: rgba(200,169,126,0.3); color: var(--accent); }
|
|
.filter-sep { width: 1px; height: 16px; background: var(--border); margin: 0 4px; }
|
|
|
|
/* ─── Cards Grid ─── */
|
|
.cards-area {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 16px 24px 24px;
|
|
}
|
|
.cards-area::-webkit-scrollbar { width: 4px; }
|
|
.cards-area::-webkit-scrollbar-track { background: transparent; }
|
|
.cards-area::-webkit-scrollbar-thumb { background: var(--bg4); border-radius: 2px; }
|
|
|
|
.cards-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
|
gap: 12px;
|
|
}
|
|
|
|
.card {
|
|
background: var(--bg2);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-lg);
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
transition: border-color 0.2s, transform 0.15s, box-shadow 0.2s;
|
|
animation: fadeUp 0.3s ease both;
|
|
}
|
|
.card:hover {
|
|
border-color: var(--border2);
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
|
|
}
|
|
@keyframes fadeUp {
|
|
from { opacity: 0; transform: translateY(12px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
.card:nth-child(1) { animation-delay: 0.03s; }
|
|
.card:nth-child(2) { animation-delay: 0.06s; }
|
|
.card:nth-child(3) { animation-delay: 0.09s; }
|
|
.card:nth-child(4) { animation-delay: 0.12s; }
|
|
.card:nth-child(5) { animation-delay: 0.15s; }
|
|
.card:nth-child(6) { animation-delay: 0.18s; }
|
|
|
|
.card-thumb {
|
|
height: 120px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.card-thumb-inner {
|
|
width: 100%; height: 100%;
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 32px;
|
|
}
|
|
.card-thumb .type-badge {
|
|
position: absolute; top: 8px; left: 8px;
|
|
font-size: 10px; font-weight: 500; padding: 2px 7px;
|
|
border-radius: 4px; backdrop-filter: blur(8px);
|
|
}
|
|
.card-thumb .fav-btn {
|
|
position: absolute; top: 8px; right: 8px;
|
|
width: 26px; height: 26px; border-radius: 6px;
|
|
background: rgba(0,0,0,0.4); backdrop-filter: blur(8px);
|
|
display: flex; align-items: center; justify-content: center;
|
|
opacity: 0; transition: opacity 0.15s;
|
|
}
|
|
.card:hover .fav-btn { opacity: 1; }
|
|
|
|
.card-body { padding: 12px 14px 14px; }
|
|
.card-title-text {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--text);
|
|
line-height: 1.4;
|
|
margin-bottom: 6px;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
.card-desc {
|
|
font-size: 12px;
|
|
color: var(--text2);
|
|
line-height: 1.5;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
margin-bottom: 10px;
|
|
}
|
|
.card-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
}
|
|
.card-tags { display: flex; gap: 4px; flex-wrap: wrap; }
|
|
.ctag {
|
|
font-size: 10px;
|
|
padding: 2px 7px;
|
|
border-radius: 4px;
|
|
font-weight: 400;
|
|
}
|
|
.card-date { font-size: 11px; color: var(--text3); font-family: var(--font-mono); white-space: nowrap; }
|
|
.card-source {
|
|
display: flex; align-items: center; gap: 4px;
|
|
font-size: 11px; color: var(--text3); margin-bottom: 8px;
|
|
}
|
|
.card-source-dot { width: 12px; height: 12px; border-radius: 3px; background: var(--bg4); display:flex;align-items:center;justify-content:center; font-size:8px; }
|
|
.star-rating { display: flex; gap: 2px; }
|
|
.star { font-size: 11px; }
|
|
|
|
/* ─── Right Panel ─── */
|
|
.detail-panel {
|
|
width: 380px;
|
|
background: var(--bg2);
|
|
border-left: 1px solid var(--border);
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex-shrink: 0;
|
|
overflow: hidden;
|
|
transform: translateX(100%);
|
|
transition: transform 0.25s ease;
|
|
}
|
|
.detail-panel.open { transform: translateX(0); }
|
|
.detail-header {
|
|
padding: 16px 20px;
|
|
border-bottom: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
.detail-header-title { font-size: 13px; font-weight: 500; color: var(--text2); }
|
|
.detail-close {
|
|
width: 26px; height: 26px; border-radius: 6px;
|
|
background: var(--bg3); border: none; cursor: pointer;
|
|
color: var(--text2); display: flex; align-items: center; justify-content: center;
|
|
transition: background 0.15s;
|
|
}
|
|
.detail-close:hover { background: var(--bg4); color: var(--text); }
|
|
.detail-body { flex: 1; overflow-y: auto; padding: 20px; }
|
|
.detail-body::-webkit-scrollbar { width: 0; }
|
|
|
|
.detail-thumb {
|
|
height: 160px;
|
|
border-radius: var(--radius);
|
|
overflow: hidden;
|
|
margin-bottom: 16px;
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 48px;
|
|
}
|
|
.detail-title {
|
|
font-family: var(--font-display);
|
|
font-size: 18px;
|
|
line-height: 1.4;
|
|
color: var(--text);
|
|
margin-bottom: 10px;
|
|
}
|
|
.detail-source {
|
|
display: flex; align-items: center; gap: 6px;
|
|
font-size: 12px; color: var(--text3); margin-bottom: 14px;
|
|
}
|
|
.detail-divider { height: 1px; background: var(--border); margin: 16px 0; }
|
|
.detail-section-label {
|
|
font-size: 10px; font-weight: 500; letter-spacing: 0.08em;
|
|
text-transform: uppercase; color: var(--text3); margin-bottom: 8px;
|
|
}
|
|
.detail-summary {
|
|
font-size: 13px; color: var(--text2); line-height: 1.7;
|
|
background: var(--bg3); border-radius: var(--radius);
|
|
padding: 12px 14px;
|
|
border-left: 2px solid var(--accent);
|
|
margin-bottom: 14px;
|
|
}
|
|
.detail-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
|
|
.detail-tag {
|
|
font-size: 12px; padding: 4px 10px;
|
|
border-radius: 6px; cursor: pointer;
|
|
transition: opacity 0.15s;
|
|
}
|
|
.detail-tag:hover { opacity: 0.8; }
|
|
|
|
.note-area {
|
|
width: 100%;
|
|
background: var(--bg3);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 10px 12px;
|
|
color: var(--text);
|
|
font-family: var(--font-body);
|
|
font-size: 13px;
|
|
line-height: 1.6;
|
|
resize: none;
|
|
outline: none;
|
|
min-height: 80px;
|
|
transition: border-color 0.2s;
|
|
}
|
|
.note-area:focus { border-color: var(--border2); }
|
|
.note-area::placeholder { color: var(--text3); }
|
|
|
|
.detail-actions {
|
|
padding: 12px 20px;
|
|
border-top: 1px solid var(--border);
|
|
display: flex; gap: 8px;
|
|
}
|
|
.btn-action {
|
|
flex: 1;
|
|
padding: 8px 12px;
|
|
border-radius: 8px;
|
|
font-family: var(--font-body);
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
border: 1px solid var(--border);
|
|
background: var(--bg3);
|
|
color: var(--text2);
|
|
transition: all 0.15s;
|
|
display: flex; align-items: center; justify-content: center; gap: 5px;
|
|
}
|
|
.btn-action:hover { border-color: var(--border2); color: var(--text); }
|
|
.btn-action.primary { background: var(--accent-bg); border-color: rgba(200,169,126,0.3); color: var(--accent); }
|
|
.btn-action.primary:hover { background: rgba(200,169,126,0.2); }
|
|
|
|
/* ─── Modal: Add ─── */
|
|
.modal-overlay {
|
|
position: fixed; inset: 0;
|
|
background: rgba(0,0,0,0.6);
|
|
backdrop-filter: blur(4px);
|
|
z-index: 1000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 0; pointer-events: none;
|
|
transition: opacity 0.2s;
|
|
}
|
|
.modal-overlay.open { opacity: 1; pointer-events: all; }
|
|
.modal {
|
|
background: var(--bg2);
|
|
border: 1px solid var(--border2);
|
|
border-radius: var(--radius-lg);
|
|
width: 480px;
|
|
max-width: 90vw;
|
|
padding: 24px;
|
|
transform: translateY(10px) scale(0.98);
|
|
transition: transform 0.2s;
|
|
}
|
|
.modal-overlay.open .modal { transform: translateY(0) scale(1); }
|
|
.modal-title {
|
|
font-family: var(--font-display);
|
|
font-size: 18px;
|
|
color: var(--text);
|
|
margin-bottom: 20px;
|
|
}
|
|
.form-group { margin-bottom: 16px; }
|
|
.form-label { font-size: 12px; color: var(--text2); margin-bottom: 6px; display: block; }
|
|
.form-input {
|
|
width: 100%;
|
|
background: var(--bg3);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 9px 12px;
|
|
color: var(--text);
|
|
font-family: var(--font-body);
|
|
font-size: 13px;
|
|
outline: none;
|
|
transition: border-color 0.2s;
|
|
}
|
|
.form-input:focus { border-color: rgba(200,169,126,0.4); }
|
|
.form-input::placeholder { color: var(--text3); }
|
|
textarea.form-input { resize: vertical; min-height: 80px; line-height: 1.6; }
|
|
.type-selector { display: flex; gap: 6px; flex-wrap: wrap; }
|
|
.type-opt {
|
|
padding: 6px 12px; border-radius: 8px; font-size: 12px;
|
|
border: 1px solid var(--border); cursor: pointer;
|
|
color: var(--text2); transition: all 0.15s;
|
|
}
|
|
.type-opt.selected { border-color: rgba(200,169,126,0.4); background: var(--accent-bg); color: var(--accent); }
|
|
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 20px; }
|
|
.btn-cancel {
|
|
padding: 8px 16px; border-radius: 8px; font-family: var(--font-body);
|
|
font-size: 13px; cursor: pointer; border: 1px solid var(--border);
|
|
background: transparent; color: var(--text2); transition: all 0.15s;
|
|
}
|
|
.btn-cancel:hover { border-color: var(--border2); color: var(--text); }
|
|
.btn-save {
|
|
padding: 8px 20px; border-radius: 8px; font-family: var(--font-body);
|
|
font-size: 13px; font-weight: 500; cursor: pointer; border: none;
|
|
background: var(--accent); color: #1a1208; transition: all 0.15s;
|
|
}
|
|
.btn-save:hover { background: #d4b88a; }
|
|
|
|
/* type colors */
|
|
.bg-web { background: linear-gradient(135deg, #1a2535, #203040); }
|
|
.bg-text { background: linear-gradient(135deg, #1e1a2e, #251e38); }
|
|
.bg-img { background: linear-gradient(135deg, #1a2820, #1e3228); }
|
|
.bg-vid { background: linear-gradient(135deg, #2a1a1a, #381e1e); }
|
|
.bg-doc { background: linear-gradient(135deg, #2a261a, #38321e); }
|
|
|
|
.c-web { color: var(--blue); }
|
|
.c-text { color: var(--purple); }
|
|
.c-img { color: var(--teal); }
|
|
.c-vid { color: var(--red); }
|
|
.c-doc { color: var(--accent); }
|
|
|
|
.badge-web { background: var(--blue-bg); color: var(--blue); }
|
|
.badge-text { background: var(--purple-bg); color: var(--purple); }
|
|
.badge-img { background: var(--teal-bg); color: var(--teal); }
|
|
.badge-vid { background: var(--red-bg); color: var(--red); }
|
|
.badge-doc { background: var(--accent-bg); color: var(--accent); }
|
|
|
|
/* highlight strip */
|
|
.highlight-strip {
|
|
height: 2px;
|
|
border-radius: 0 0 2px 2px;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* empty state */
|
|
.empty-state {
|
|
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
height: 100%; gap: 12px; color: var(--text3); text-align: center;
|
|
}
|
|
.empty-state-icon { font-size: 40px; opacity: 0.5; }
|
|
|
|
/* AI chat strip */
|
|
.ai-strip {
|
|
padding: 10px 24px;
|
|
border-top: 1px solid var(--border);
|
|
display: flex; align-items: center; gap: 10px;
|
|
background: var(--bg2);
|
|
flex-shrink: 0;
|
|
}
|
|
.ai-input {
|
|
flex: 1;
|
|
background: var(--bg3);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 8px 12px;
|
|
color: var(--text);
|
|
font-family: var(--font-body);
|
|
font-size: 13px;
|
|
outline: none;
|
|
transition: border-color 0.2s;
|
|
}
|
|
.ai-input::placeholder { color: var(--text3); }
|
|
.ai-input:focus { border-color: rgba(200,169,126,0.4); }
|
|
.ai-label { font-size: 11px; color: var(--accent); font-weight: 500; white-space: nowrap; display: flex; align-items: center; gap: 4px; }
|
|
.ai-pulse { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: pulse 2s infinite; }
|
|
@keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:0.5;transform:scale(0.8)} }
|
|
.ai-send {
|
|
padding: 8px 14px; border-radius: 8px; border: 1px solid rgba(200,169,126,0.3);
|
|
background: var(--accent-bg); color: var(--accent); font-size: 12px; font-weight: 500;
|
|
cursor: pointer; font-family: var(--font-body); transition: all 0.15s; white-space: nowrap;
|
|
}
|
|
.ai-send:hover { background: rgba(200,169,126,0.2); }
|
|
|
|
/* toast */
|
|
.toast {
|
|
position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%) translateY(60px);
|
|
background: var(--bg4); border: 1px solid var(--border2);
|
|
border-radius: 10px; padding: 10px 18px;
|
|
font-size: 13px; color: var(--text); z-index: 9999;
|
|
transition: transform 0.3s ease;
|
|
display: flex; align-items: center; gap: 8px;
|
|
}
|
|
.toast.show { transform: translateX(-50%) translateY(0); }
|
|
.toast-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--teal); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Top Bar -->
|
|
<div class="topbar">
|
|
<div class="logo">万物收藏</div>
|
|
<div class="search-wrap">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
|
|
<input type="text" placeholder="搜索收藏、标签、内容..." id="searchInput" oninput="handleSearch(this.value)">
|
|
</div>
|
|
<div class="topbar-right">
|
|
<button class="btn-add" onclick="openModal()">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 5v14M5 12h14"/></svg>
|
|
新增收藏
|
|
</button>
|
|
<div class="avatar">陈</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- App Body -->
|
|
<div class="app">
|
|
|
|
<!-- Sidebar -->
|
|
<div class="sidebar">
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-label">视图</div>
|
|
<div class="nav-item active" onclick="setNav(this,'inbox')">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M22 12h-6l-2 3h-4l-2-3H2"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/></svg>
|
|
收件箱
|
|
<span class="nav-badge">3</span>
|
|
</div>
|
|
<div class="nav-item" onclick="setNav(this,'all')">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
|
|
全部收藏
|
|
<span class="nav-badge">24</span>
|
|
</div>
|
|
<div class="nav-item" onclick="setNav(this,'recent')">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
最近收藏
|
|
</div>
|
|
<div class="nav-item" onclick="setNav(this,'starred')">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
|
|
已加星标
|
|
</div>
|
|
<div class="nav-item" onclick="setNav(this,'articles')">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
|
|
我的文章
|
|
<span class="nav-badge">2</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-label">分类</div>
|
|
<div class="nav-item" onclick="setNav(this,'cat-design')">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>
|
|
设计
|
|
<span class="nav-badge">8</span>
|
|
</div>
|
|
<div class="nav-item cat-indent" onclick="setNav(this,'cat-ui')">
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
|
|
UI / 交互
|
|
</div>
|
|
<div class="nav-item cat-indent" onclick="setNav(this,'cat-font')">
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/></svg>
|
|
字体排版
|
|
</div>
|
|
<div class="nav-item" onclick="setNav(this,'cat-ai')">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 2a4 4 0 0 1 4 4v2H8V6a4 4 0 0 1 4-4z"/><rect x="8" y="8" width="8" height="10" rx="2"/><path d="M9 22v-4M15 22v-4"/></svg>
|
|
AI / 技术
|
|
<span class="nav-badge">11</span>
|
|
</div>
|
|
<div class="nav-item" onclick="setNav(this,'cat-read')">
|
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
|
|
阅读 / 思考
|
|
<span class="nav-badge">5</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-label">标签</div>
|
|
<div class="nav-item" onclick="filterByTag('值得深读')">
|
|
<span class="tag-dot" style="background:var(--accent)"></span>
|
|
值得深读
|
|
<span class="nav-badge">6</span>
|
|
</div>
|
|
<div class="nav-item" onclick="filterByTag('灵感')">
|
|
<span class="tag-dot" style="background:var(--purple)"></span>
|
|
灵感
|
|
<span class="nav-badge">9</span>
|
|
</div>
|
|
<div class="nav-item" onclick="filterByTag('参考资料')">
|
|
<span class="tag-dot" style="background:var(--teal)"></span>
|
|
参考资料
|
|
<span class="nav-badge">4</span>
|
|
</div>
|
|
<div class="nav-item" onclick="filterByTag('AI工具')">
|
|
<span class="tag-dot" style="background:var(--blue)"></span>
|
|
AI工具
|
|
<span class="nav-badge">7</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<div class="main">
|
|
<div class="main-header">
|
|
<div>
|
|
<div class="main-title" id="mainTitle">收件箱</div>
|
|
<div class="main-sub" id="mainSub">3 条待整理</div>
|
|
</div>
|
|
<div class="view-tabs">
|
|
<div class="view-tab active" onclick="setView('grid',this)">
|
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>卡片
|
|
</div>
|
|
<div class="view-tab" onclick="setView('list',this)">
|
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>列表
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filter-bar">
|
|
<div class="filter-chip active" onclick="setFilter('all',this)">全部</div>
|
|
<div class="filter-chip" onclick="setFilter('web',this)">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
|
网页
|
|
</div>
|
|
<div class="filter-chip" onclick="setFilter('text',this)">文本</div>
|
|
<div class="filter-chip" onclick="setFilter('img',this)">图片</div>
|
|
<div class="filter-chip" onclick="setFilter('vid',this)">视频</div>
|
|
<div class="filter-chip" onclick="setFilter('doc',this)">文档</div>
|
|
<div class="filter-sep"></div>
|
|
<div class="filter-chip" onclick="setFilter('starred',this)">⭐ 星标</div>
|
|
</div>
|
|
|
|
<div class="cards-area" id="cardsArea">
|
|
<div class="cards-grid" id="cardsGrid"></div>
|
|
</div>
|
|
|
|
<!-- AI Strip -->
|
|
<div class="ai-strip">
|
|
<div class="ai-label"><div class="ai-pulse"></div> AI 问答</div>
|
|
<input class="ai-input" placeholder="问我的收藏:最近有哪些关于设计的内容?" id="aiInput">
|
|
<button class="ai-send" onclick="sendAI()">发送 →</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Detail Panel -->
|
|
<div class="detail-panel" id="detailPanel">
|
|
<div class="detail-header">
|
|
<div class="detail-header-title">收藏详情</div>
|
|
<button class="detail-close" onclick="closeDetail()">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18M6 6l12 12"/></svg>
|
|
</button>
|
|
</div>
|
|
<div class="detail-body" id="detailBody">
|
|
</div>
|
|
<div class="detail-actions">
|
|
<button class="btn-action primary" onclick="writeArticle()">
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
|
写文章
|
|
</button>
|
|
<button class="btn-action" onclick="copyLink()">
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
|
|
复制链接
|
|
</button>
|
|
<button class="btn-action" onclick="deleteItem()">
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>
|
|
删除
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add Modal -->
|
|
<div class="modal-overlay" id="modalOverlay" onclick="handleOverlayClick(event)">
|
|
<div class="modal">
|
|
<div class="modal-title">新增收藏</div>
|
|
<div class="form-group">
|
|
<label class="form-label">内容类型</label>
|
|
<div class="type-selector">
|
|
<div class="type-opt selected" onclick="selectType(this,'web')">🌐 网页</div>
|
|
<div class="type-opt" onclick="selectType(this,'text')">📝 文本</div>
|
|
<div class="type-opt" onclick="selectType(this,'img')">🖼️ 图片</div>
|
|
<div class="type-opt" onclick="selectType(this,'vid')">🎬 视频</div>
|
|
<div class="type-opt" onclick="selectType(this,'doc')">📄 文档</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">链接或内容</label>
|
|
<input class="form-input" type="text" placeholder="https://... 或粘贴文字" id="formUrl">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">标题(可选,留空自动识别)</label>
|
|
<input class="form-input" type="text" placeholder="给这条收藏起个名字" id="formTitle">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">标签(用空格分隔)</label>
|
|
<input class="form-input" type="text" placeholder="灵感 设计 AI工具" id="formTags">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label">备注</label>
|
|
<textarea class="form-input" placeholder="写下你的想法..." id="formNote"></textarea>
|
|
</div>
|
|
<div class="modal-actions">
|
|
<button class="btn-cancel" onclick="closeModal()">取消</button>
|
|
<button class="btn-save" onclick="saveItem()">保存收藏</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast -->
|
|
<div class="toast" id="toast">
|
|
<div class="toast-dot"></div>
|
|
<span id="toastMsg">已保存</span>
|
|
</div>
|
|
|
|
<script>
|
|
const ITEMS = [
|
|
{
|
|
id:1, type:'web', title:'Vercel 设计系统 2024 更新:新组件库与动效规范',
|
|
desc:'Vercel 团队分享了最新的设计系统更新,包括全新的 Radix 组件封装、Motion 动效库集成和无障碍规范改进。',
|
|
url:'https://vercel.com/design', source:'vercel.com', tags:['设计','参考资料'],
|
|
date:'05-16', starred:true, rating:4,
|
|
note:'重点关注他们的 Motion 实现方案,和我们的项目需求很贴近。',
|
|
summary:'文章详细介绍了 Vercel 如何重构其设计系统,核心思路是「组件即文档」——每个组件自带使用示例和无障碍说明。动效方面引入了基于物理的弹簧动画替代传统缓动曲线。',
|
|
emoji:'🌐', bg:'bg-web', badgeClass:'badge-web', accentColor:'var(--blue)'
|
|
},
|
|
{
|
|
id:2, type:'text', title:'关于「慢阅读」的思考碎片',
|
|
desc:'在信息爆炸时代,我们需要主动降速。真正的理解需要摩擦感,需要反复咀嚼,需要停下来想一想。',
|
|
url:'', source:'个人笔记', tags:['灵感','值得深读'],
|
|
date:'05-15', starred:false, rating:5,
|
|
note:'这段话在地铁上想到的,和最近读《深度工作》有关。',
|
|
summary:'这段文字探讨了现代人与信息的关系——我们消费的速度远超消化的速度。提出「慢阅读」作为一种对抗信息焦虑的实践方法。',
|
|
emoji:'📝', bg:'bg-text', badgeClass:'badge-text', accentColor:'var(--purple)'
|
|
},
|
|
{
|
|
id:3, type:'web', title:'2024 年最值得关注的 AI 设计工具合集',
|
|
desc:'涵盖 Galileo AI、Uizard、Framer AI 等 14 款工具的深度对比,从生成质量、可控性、工作流融合三个维度评分。',
|
|
url:'https://example.com/ai-tools', source:'uxdesign.cc', tags:['AI工具','设计'],
|
|
date:'05-14', starred:true, rating:4,
|
|
note:'',
|
|
summary:'文章系统比较了当前主流 AI 设计工具,结论是 Framer AI 在工作流融合上领先,但 Galileo AI 的生成质量更高。建议根据团队使用场景分开选择。',
|
|
emoji:'🤖', bg:'bg-web', badgeClass:'badge-web', accentColor:'var(--blue)'
|
|
},
|
|
{
|
|
id:4, type:'img', title:'Teenage Engineering OP-1 产品摄影参考',
|
|
desc:'极简主义硬件产品的摄影风格:高对比、单色背景、精准的光影层次,彰显工业美学。',
|
|
url:'', source:'Dribbble', tags:['灵感','设计'],
|
|
date:'05-13', starred:false, rating:3,
|
|
note:'下次产品拍摄可以参考这种风格。',
|
|
summary:'这组产品摄影展示了如何用极简布景突出产品本身的设计语言——没有任何多余道具,只有主体、光线和阴影的对话。',
|
|
emoji:'📷', bg:'bg-img', badgeClass:'badge-img', accentColor:'var(--teal)'
|
|
},
|
|
{
|
|
id:5, type:'vid', title:'3Blue1Brown: 神经网络的可视化解释',
|
|
desc:'通过动态几何可视化,直觉上理解反向传播、梯度下降和神经元激活的工作原理,无需数学公式。',
|
|
url:'https://youtube.com', source:'YouTube', tags:['AI工具','参考资料','值得深读'],
|
|
date:'05-12', starred:true, rating:5,
|
|
note:'最好的 ML 入门视频,没有之一。准备在团队分享会上播放。',
|
|
summary:'Grant 用其标志性的数学可视化风格,将反向传播从一个抽象算法变成可以「看到」的几何变换过程,极大降低了理解门槛。',
|
|
emoji:'▶️', bg:'bg-vid', badgeClass:'badge-vid', accentColor:'var(--red)'
|
|
},
|
|
{
|
|
id:6, type:'doc', title:'《设计系统:企业级 UI 的构建法则》PDF',
|
|
desc:'Nathan Curtis 的经典著作,详述如何从零搭建可维护的设计系统,包含 Figma 组织规范和开发交接流程。',
|
|
url:'', source:'PDF 文档', tags:['设计','参考资料'],
|
|
date:'05-10', starred:false, rating:4,
|
|
note:'第三章关于 Token 体系的部分要精读,跟我们的需求高度相关。',
|
|
summary:'本书是目前最系统的设计系统建设指南,核心观点:设计系统不是组件库,而是一套治理语言和协作流程。',
|
|
emoji:'📄', bg:'bg-doc', badgeClass:'badge-doc', accentColor:'var(--accent)'
|
|
},
|
|
{
|
|
id:7, type:'web', title:'Raycast 如何在产品增长上做了哪些正确的事',
|
|
desc:'从 0 到 100 万用户的增长路径复盘:产品主导增长、开发者社区、插件生态三大飞轮如何协同运转。',
|
|
url:'https://raycast.com', source:'raycast.com', tags:['灵感','值得深读'],
|
|
date:'05-09', starred:false, rating:3,
|
|
note:'',
|
|
summary:'Raycast 的增长故事印证了「产品即渠道」的理念——当工具足够好用,用户自然成为传播者。插件生态是其最聪明的护城河策略。',
|
|
emoji:'🚀', bg:'bg-web', badgeClass:'badge-web', accentColor:'var(--blue)'
|
|
},
|
|
{
|
|
id:8, type:'text', title:'设计师应该学代码吗?我的答案变了三次',
|
|
desc:'从「完全没必要」到「会一点很好」再到「这个问题本身就是错的」——真正的边界在于理解,而不在于实现。',
|
|
url:'', source:'个人笔记', tags:['灵感','设计'],
|
|
date:'05-08', starred:true, rating:4,
|
|
note:'这是我要写的下一篇文章的核心论点。',
|
|
summary:'作者认为「设计师要不要学代码」这个问题预设了一个错误的二元对立。更好的问题是:如何建立跨越学科的理解力?',
|
|
emoji:'💭', bg:'bg-text', badgeClass:'badge-text', accentColor:'var(--purple)'
|
|
}
|
|
];
|
|
|
|
let allItems = [...ITEMS];
|
|
let currentFilter = 'all';
|
|
let currentView = 'grid';
|
|
let selectedItem = null;
|
|
let selectedType = 'web';
|
|
|
|
function renderCards(items) {
|
|
const grid = document.getElementById('cardsGrid');
|
|
if (items.length === 0) {
|
|
grid.innerHTML = '<div class="empty-state" style="grid-column:1/-1;padding:60px 0"><div class="empty-state-icon">🔍</div><div>没有找到匹配的收藏</div></div>';
|
|
return;
|
|
}
|
|
grid.innerHTML = items.map((item,i) => `
|
|
<div class="card" onclick="openDetail(${item.id})" style="animation-delay:${i*0.04}s">
|
|
<div class="card-thumb ${item.bg}">
|
|
<div class="card-thumb-inner">${item.emoji}</div>
|
|
<div class="type-badge ${item.badgeClass}">${typeLabel(item.type)}</div>
|
|
<div class="fav-btn" onclick="toggleStar(event,${item.id})">
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="${item.starred?'var(--accent)':'none'}" stroke="${item.starred?'var(--accent)':'rgba(255,255,255,0.6)'}" stroke-width="2"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="card-source">
|
|
<div class="card-source-dot" style="background:${item.accentColor}22;color:${item.accentColor}">●</div>
|
|
${item.source}
|
|
</div>
|
|
<div class="card-title-text">${item.title}</div>
|
|
<div class="card-desc">${item.desc}</div>
|
|
<div class="card-meta">
|
|
<div class="card-tags">
|
|
${item.tags.slice(0,2).map(t=>`<span class="ctag" style="background:${tagBg(t)};color:${tagColor(t)}">#${t}</span>`).join('')}
|
|
</div>
|
|
<div class="card-date">${item.date}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function typeLabel(t) {
|
|
return {web:'网页',text:'文本',img:'图片',vid:'视频',doc:'文档'}[t] || t;
|
|
}
|
|
function tagBg(t) {
|
|
const m = {'灵感':'var(--purple-bg)','设计':'var(--teal-bg)','AI工具':'var(--blue-bg)','值得深读':'var(--accent-bg)','参考资料':'rgba(93,184,160,0.12)'};
|
|
return m[t]||'var(--bg4)';
|
|
}
|
|
function tagColor(t) {
|
|
const m = {'灵感':'var(--purple)','设计':'var(--teal)','AI工具':'var(--blue)','值得深读':'var(--accent)','参考资料':'var(--teal)'};
|
|
return m[t]||'var(--text2)';
|
|
}
|
|
|
|
function setFilter(type, el) {
|
|
document.querySelectorAll('.filter-chip').forEach(e=>e.classList.remove('active'));
|
|
el.classList.add('active');
|
|
currentFilter = type;
|
|
applyFilter();
|
|
}
|
|
|
|
function applyFilter() {
|
|
let items = allItems;
|
|
if (currentFilter !== 'all') {
|
|
if (currentFilter === 'starred') items = items.filter(i=>i.starred);
|
|
else items = items.filter(i=>i.type === currentFilter);
|
|
}
|
|
const q = document.getElementById('searchInput').value.toLowerCase();
|
|
if (q) items = items.filter(i=>i.title.toLowerCase().includes(q)||i.desc.toLowerCase().includes(q)||i.tags.some(t=>t.includes(q)));
|
|
renderCards(items);
|
|
document.getElementById('mainSub').textContent = `${items.length} 条收藏`;
|
|
}
|
|
|
|
function handleSearch(v) { applyFilter(); }
|
|
|
|
function setNav(el, key) {
|
|
document.querySelectorAll('.nav-item').forEach(e=>e.classList.remove('active'));
|
|
el.classList.add('active');
|
|
const titles = {inbox:'收件箱',all:'全部收藏',recent:'最近收藏',starred:'已加星标',articles:'我的文章','cat-design':'设计','cat-ui':'UI / 交互','cat-font':'字体排版','cat-ai':'AI / 技术','cat-read':'阅读 / 思考'};
|
|
document.getElementById('mainTitle').textContent = titles[key] || key;
|
|
if (key === 'starred') { currentFilter='starred'; } else { currentFilter='all'; }
|
|
applyFilter();
|
|
}
|
|
|
|
function filterByTag(tag) {
|
|
document.querySelectorAll('.nav-item').forEach(e=>e.classList.remove('active'));
|
|
let items = allItems.filter(i=>i.tags.includes(tag));
|
|
document.getElementById('mainTitle').textContent = '#' + tag;
|
|
document.getElementById('mainSub').textContent = items.length + ' 条收藏';
|
|
renderCards(items);
|
|
}
|
|
|
|
function setView(v, el) {
|
|
document.querySelectorAll('.view-tab').forEach(e=>e.classList.remove('active'));
|
|
el.classList.add('active');
|
|
currentView = v;
|
|
const grid = document.getElementById('cardsGrid');
|
|
if (v === 'list') grid.style.gridTemplateColumns = '1fr';
|
|
else grid.style.gridTemplateColumns = '';
|
|
}
|
|
|
|
function openDetail(id) {
|
|
selectedItem = allItems.find(i=>i.id===id);
|
|
if (!selectedItem) return;
|
|
const p = selectedItem;
|
|
document.getElementById('detailBody').innerHTML = `
|
|
<div class="detail-thumb ${p.bg}">${p.emoji}</div>
|
|
<div class="detail-title">${p.title}</div>
|
|
<div class="detail-source">
|
|
<div style="width:14px;height:14px;border-radius:4px;background:${p.accentColor}22;display:flex;align-items:center;justify-content:center;font-size:9px;color:${p.accentColor}">●</div>
|
|
${p.source} · ${p.date}
|
|
<span style="margin-left:auto">${'★'.repeat(p.rating)}${'☆'.repeat(5-p.rating)}</span>
|
|
</div>
|
|
<div class="detail-divider"></div>
|
|
<div class="detail-section-label">AI 摘要</div>
|
|
<div class="detail-summary">${p.summary}</div>
|
|
<div class="detail-section-label">标签</div>
|
|
<div class="detail-tags">
|
|
${p.tags.map(t=>`<span class="detail-tag" style="background:${tagBg(t)};color:${tagColor(t)}">#${t}</span>`).join('')}
|
|
<span class="detail-tag" style="background:var(--bg3);color:var(--text3);cursor:pointer" onclick="showToast('标签编辑功能开发中')">+ 添加</span>
|
|
</div>
|
|
<div class="detail-divider"></div>
|
|
<div class="detail-section-label">我的备注</div>
|
|
<textarea class="note-area" placeholder="写下你的想法...">${p.note||''}</textarea>
|
|
<div class="detail-divider"></div>
|
|
<div class="detail-section-label">相关收藏推荐</div>
|
|
${allItems.filter(i=>i.id!==p.id&&i.tags.some(t=>p.tags.includes(t))).slice(0,2).map(r=>`
|
|
<div onclick="openDetail(${r.id})" style="display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;cursor:pointer;transition:background 0.15s;margin-bottom:6px" onmouseover="this.style.background='var(--bg3)'" onmouseout="this.style.background=''">
|
|
<div style="width:32px;height:32px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0" class="${r.bg}">${r.emoji}</div>
|
|
<div style="flex:1;min-width:0">
|
|
<div style="font-size:12px;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${r.title}</div>
|
|
<div style="font-size:11px;color:var(--text3)">${r.tags[0]}</div>
|
|
</div>
|
|
</div>
|
|
`).join('')}
|
|
`;
|
|
document.getElementById('detailPanel').classList.add('open');
|
|
}
|
|
|
|
function closeDetail() {
|
|
document.getElementById('detailPanel').classList.remove('open');
|
|
selectedItem = null;
|
|
}
|
|
|
|
function toggleStar(e, id) {
|
|
e.stopPropagation();
|
|
const item = allItems.find(i=>i.id===id);
|
|
if (item) { item.starred = !item.starred; applyFilter(); showToast(item.starred?'已加入星标':'已移除星标'); }
|
|
}
|
|
|
|
function writeArticle() {
|
|
showToast('✍️ 正在打开文章编辑器...');
|
|
setTimeout(closeDetail, 800);
|
|
}
|
|
function copyLink() { showToast('🔗 链接已复制到剪贴板'); }
|
|
function deleteItem() {
|
|
if (!selectedItem) return;
|
|
allItems = allItems.filter(i=>i.id!==selectedItem.id);
|
|
closeDetail(); applyFilter(); showToast('已删除');
|
|
}
|
|
|
|
function openModal() {
|
|
document.getElementById('modalOverlay').classList.add('open');
|
|
}
|
|
function closeModal() {
|
|
document.getElementById('modalOverlay').classList.remove('open');
|
|
document.getElementById('formUrl').value='';
|
|
document.getElementById('formTitle').value='';
|
|
document.getElementById('formTags').value='';
|
|
document.getElementById('formNote').value='';
|
|
}
|
|
function handleOverlayClick(e) { if (e.target===e.currentTarget) closeModal(); }
|
|
|
|
function selectType(el, t) {
|
|
document.querySelectorAll('.type-opt').forEach(e=>e.classList.remove('selected'));
|
|
el.classList.add('selected');
|
|
selectedType = t;
|
|
}
|
|
|
|
const typeEmoji = {web:'🌐',text:'📝',img:'📷',vid:'▶️',doc:'📄'};
|
|
const typeBg = {web:'bg-web',text:'bg-text',img:'bg-img',vid:'bg-vid',doc:'bg-doc'};
|
|
const typeBadge = {web:'badge-web',text:'badge-text',img:'badge-img',vid:'badge-vid',doc:'badge-doc'};
|
|
const typeAccent = {web:'var(--blue)',text:'var(--purple)',img:'var(--teal)',vid:'var(--red)',doc:'var(--accent)'};
|
|
|
|
function saveItem() {
|
|
const url = document.getElementById('formUrl').value.trim();
|
|
const title = document.getElementById('formTitle').value.trim() || (url ? url.replace(/^https?:\/\//,'').split('/')[0] : '新收藏 · ' + new Date().toLocaleDateString());
|
|
const tagsRaw = document.getElementById('formTags').value.trim();
|
|
const tags = tagsRaw ? tagsRaw.split(/\s+/).filter(Boolean) : ['未分类'];
|
|
const note = document.getElementById('formNote').value.trim();
|
|
const now = new Date();
|
|
const dateStr = `${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')}`;
|
|
const newItem = {
|
|
id: Date.now(), type: selectedType, title, desc: note || '暂无描述。',
|
|
url, source: url ? url.replace(/^https?:\/\//,'').split('/')[0] : '手动输入',
|
|
tags, date: dateStr, starred: false, rating: 3, note,
|
|
summary: 'AI 正在分析内容,摘要即将生成...',
|
|
emoji: typeEmoji[selectedType], bg: typeBg[selectedType],
|
|
badgeClass: typeBadge[selectedType], accentColor: typeAccent[selectedType]
|
|
};
|
|
allItems.unshift(newItem);
|
|
closeModal(); applyFilter(); showToast('✅ 收藏已保存');
|
|
}
|
|
|
|
function sendAI() {
|
|
const val = document.getElementById('aiInput').value.trim();
|
|
if (!val) return;
|
|
showToast('🤖 AI 正在检索你的收藏...');
|
|
document.getElementById('aiInput').value = '';
|
|
}
|
|
|
|
function showToast(msg) {
|
|
const t = document.getElementById('toast');
|
|
document.getElementById('toastMsg').textContent = msg;
|
|
t.classList.add('show');
|
|
setTimeout(()=>t.classList.remove('show'), 2200);
|
|
}
|
|
|
|
// Init
|
|
renderCards(allItems);
|
|
</script>
|
|
</body>
|
|
</html>
|