diff --git a/dashboard.php b/dashboard.php index d48692c..b5b6349 100644 --- a/dashboard.php +++ b/dashboard.php @@ -18,6 +18,9 @@ function parseMarkdownWithMeta($filePath) { 'date' => '', 'platform' => '', 'url' => '', + 'tags' => [], + 'duration' => '', + 'type' => 'article', 'summary' => '' ]; @@ -42,8 +45,17 @@ function parseMarkdownWithMeta($filePath) { if (preg_match('/^url:\s*(.+?)\s*$/m', $yaml, $m)) { $meta['url'] = trim($m[1]); } + if (preg_match('/^duration:\s*(.+?)\s*$/m', $yaml, $m)) { + $meta['duration'] = trim($m[1]); + $meta['type'] = 'video'; + } + if (preg_match('/^tags:\s*\[(.+)\]\s*$/m', $yaml, $m)) { + $tagsStr = $m[1]; + preg_match_all('/"([^"]+)"/', $tagsStr, $tagMatches); + $meta['tags'] = $tagMatches[1] ?? []; + } - // 提取正文概述(前两行非空内容) + // 提取正文概述 $lines = preg_split('/\r?\n/', trim($body)); $summaryLines = []; foreach ($lines as $line) { @@ -53,17 +65,6 @@ function parseMarkdownWithMeta($filePath) { if (count($summaryLines) >= 2) break; } $meta['summary'] = implode(' ', $summaryLines); - } else { - // 没有 front matter,从正文提取 - $lines = preg_split('/\r?\n/', trim($content)); - $summaryLines = []; - foreach ($lines as $line) { - $line = trim($line); - if (empty($line) || strpos($line, '#') === 0) continue; - $summaryLines[] = $line; - if (count($summaryLines) >= 2) break; - } - $meta['summary'] = implode(' ', $summaryLines); } return $meta; @@ -91,7 +92,6 @@ function scanDirectory($dir, $baseDir, $search) { $content = file_get_contents($fullPath); - // 搜索过滤 if ($search) { $searchLower = mb_strtolower($search); $contentLower = mb_strtolower($content); @@ -100,19 +100,21 @@ function scanDirectory($dir, $baseDir, $search) { } } - // 解析元数据 $meta = parseMarkdownWithMeta($fullPath); $docs[] = [ 'name' => $item, 'path' => $relativePath, 'folder' => dirname($relativePath) === '.' ? '' : dirname($relativePath), - 'size' => filesize($fullPath), 'mtime' => filemtime($fullPath), 'title' => $meta['title'], 'author' => $meta['author'], 'date' => $meta['date'], 'platform' => $meta['platform'], + 'url' => $meta['url'], + 'tags' => $meta['tags'], + 'duration' => $meta['duration'], + 'type' => $meta['type'], 'summary' => $meta['summary'] ]; } @@ -122,32 +124,35 @@ function scanDirectory($dir, $baseDir, $search) { $documents = scanDirectory($docDir, $docDir, $search); -// 按日期排序(新文件在前) +// 排序 usort($documents, function($a, $b) { - // 优先按日期排序 if ($a['date'] && $b['date']) { return strcmp($b['date'], $a['date']); } - // 其次按修改时间 return $b['mtime'] - $a['mtime']; }); -// 平台图标映射 -function getPlatformIcon($platform) { - $icons = [ - 'bilibili' => '📺', - 'youtube' => '▶️', - 'zhihu' => '💡', - 'wechat' => '💬', +// 平台配置 +function getPlatformInfo($platform) { + $platforms = [ + 'bilibili' => ['name' => 'Bilibili', 'icon' => '📺', 'color' => '#fb7299'], + 'youtube' => ['name' => 'YouTube', 'icon' => '▶️', 'color' => '#ff0000'], + 'zhihu' => ['name' => '知乎', 'icon' => '💡', 'color' => '#0084ff'], + 'wechat' => ['name' => '微信', 'icon' => '💬', 'color' => '#07c160'], ]; - return $icons[$platform] ?? '📄'; + return $platforms[$platform] ?? ['name' => $platform ?: '未知', 'icon' => '📄', 'color' => '#666']; +} + +// 类型图标 +function getTypeIcon($type) { + return $type === 'video' ? '🎬' : '📝'; } ?> - + 知识库 - <?php echo htmlspecialchars($username); ?> @@ -189,23 +194,48 @@ function getPlatformIcon($platform) { - + -
+
-
+
+ + +
- 👤 + 👤 - 📅 + 📅 - 🏷️ + + + + + + ⏱️ + + + + 🔗 源链接 +
+ +
+ + + + 5): ?> + + + +
+
150 ? '...' : ''; ?>
diff --git a/style.css b/style.css index 5d27123..08698ca 100644 --- a/style.css +++ b/style.css @@ -846,3 +846,189 @@ mark { background: rgba(56, 189, 248, 0.3); color: var(--text-primary); } + +/* ============ 文档卡片头部 ============ */ +.doc-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; +} + +.doc-type { + font-size: 16px; + opacity: 0.8; +} + +/* ============ 标签样式 ============ */ +.doc-tags { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-bottom: 10px; +} + +.tag { + display: inline-block; + padding: 3px 10px; + background: rgba(56, 189, 248, 0.1); + border: 1px solid rgba(56, 189, 248, 0.3); + border-radius: 12px; + font-size: 11px; + color: var(--accent-cyan); + font-family: 'JetBrains Mono', monospace; + transition: all 0.2s ease; +} + +.tag:hover { + background: rgba(56, 189, 248, 0.2); + border-color: var(--accent-cyan); +} + +.tag-more { + display: inline-block; + padding: 3px 8px; + background: rgba(168, 85, 247, 0.1); + border-radius: 12px; + font-size: 11px; + color: var(--accent-purple); + font-family: 'JetBrains Mono', monospace; +} + +/* ============ 元数据项 ============ */ +.meta-item { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 0; +} + +.platform-badge { + padding: 2px 8px; + background: color-mix(in srgb, var(--platform-color, #666) 15%, transparent); + border: 1px solid color-mix(in srgb, var(--platform-color, #666) 40%, transparent); + border-radius: 4px; + font-size: 11px; + font-weight: 500; + color: var(--platform-color, #fff); +} + +.source-link { + cursor: pointer; + color: var(--accent-cyan); + transition: color 0.2s; +} + +.source-link:hover { + color: var(--accent-purple); +} + +/* ============ 文章页元数据卡片 ============ */ +.meta-card { + background: var(--bg-card); + backdrop-filter: blur(10px); + border: 1px solid var(--border); + border-radius: 12px; + padding: 20px 24px; + margin-bottom: 24px; + animation: fade-up 0.5s ease; +} + +.meta-row { + display: flex; + flex-wrap: wrap; + gap: 24px; + margin-bottom: 16px; +} + +.meta-field { + display: flex; + flex-direction: column; + gap: 4px; +} + +.meta-label { + font-size: 11px; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 1px; + font-family: 'JetBrains Mono', monospace; +} + +.meta-value { + font-size: 14px; + color: var(--text-primary); +} + +.meta-source { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 0; + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); + margin-bottom: 16px; +} + +.source-url { + color: var(--accent-cyan); + text-decoration: none; + font-size: 13px; + font-family: 'JetBrains Mono', monospace; + word-break: break-all; + transition: color 0.2s; +} + +.source-url:hover { + color: var(--accent-purple); + text-decoration: underline; +} + +.meta-tags { + display: flex; + align-items: flex-start; + gap: 12px; +} + +.meta-tags .meta-label { + padding-top: 3px; +} + +.tags-list { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.tags-list .tag { + padding: 4px 12px; + font-size: 12px; +} + +/* ============ 响应式补充 ============ */ +@media (max-width: 640px) { + .meta-row { + flex-direction: column; + gap: 16px; + } + + .meta-source { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + + .meta-tags { + flex-direction: column; + gap: 8px; + } + + .doc-tags { + gap: 4px; + } + + .tag { + font-size: 10px; + padding: 2px 8px; + } +} diff --git a/view.php b/view.php index b886cdd..bfc53d0 100644 --- a/view.php +++ b/view.php @@ -6,11 +6,9 @@ $user = getCurrentUser(); $username = $user['username']; $docDir = getUserDocDir($username); -// 获取文件名 $file = $_GET['file'] ?? ''; $search = $_GET['q'] ?? ''; -// 安全检查 if (empty($file) || strpos($file, '..') !== false) { die('无效的文件名'); } @@ -30,12 +28,15 @@ if (!file_exists($filePath)) { $content = file_get_contents($filePath); // 解析元数据 -$title = basename($file); $meta = [ - 'title' => $title, + 'title' => basename($file), 'author' => '', 'date' => '', - 'platform' => '' + 'platform' => '', + 'url' => '', + 'tags' => [], + 'duration' => '', + 'type' => 'article' ]; // 提取并移除 YAML front matter @@ -43,10 +44,8 @@ if (preg_match('/^---\s*\n(.*?)\n---\s*\n(.*)$/s', $content, $matches)) { $yaml = $matches[1]; $content = $matches[2]; - // 解析元数据(用于页面标题) if (preg_match('/^title:\s*["\']?(.+?)["\']?\s*$/m', $yaml, $m)) { - $title = trim($m[1], '"\''); - $meta['title'] = $title; + $meta['title'] = trim($m[1], '"\''); } if (preg_match('/^author:\s*(.+?)\s*$/m', $yaml, $m)) { $meta['author'] = trim($m[1]); @@ -57,43 +56,47 @@ if (preg_match('/^---\s*\n(.*?)\n---\s*\n(.*)$/s', $content, $matches)) { if (preg_match('/^platform:\s*(.+?)\s*$/m', $yaml, $m)) { $meta['platform'] = trim($m[1]); } + if (preg_match('/^url:\s*(.+?)\s*$/m', $yaml, $m)) { + $meta['url'] = trim($m[1]); + } + if (preg_match('/^duration:\s*(.+?)\s*$/m', $yaml, $m)) { + $meta['duration'] = trim($m[1]); + $meta['type'] = 'video'; + } + if (preg_match('/^tags:\s*\[(.+)\]\s*$/m', $yaml, $m)) { + $tagsStr = $m[1]; + preg_match_all('/"([^"]+)"/', $tagsStr, $tagMatches); + $meta['tags'] = $tagMatches[1] ?? []; + } } -// 简单的 Markdown 转换 +// Markdown 转换 function markdownToHtml($text) { - // 标题 $text = preg_replace('/^#### (.*$)/m', '

$1

', $text); $text = preg_replace('/^### (.*$)/m', '

$1

', $text); $text = preg_replace('/^## (.*$)/m', '

$1

', $text); $text = preg_replace('/^# (.*$)/m', '

$1

', $text); - // 粗体和斜体 $text = preg_replace('/\*\*(.+?)\*\*/', '$1', $text); $text = preg_replace('/\*(.+?)\*/', '$1', $text); - // 代码块 $text = preg_replace('/```(\w*)\n(.*?)```/s', '
$2
', $text); $text = preg_replace('/`(.+?)`/', '$1', $text); - // 链接 $text = preg_replace('/\[(.+?)\]\((.+?)\)/', '
$1', $text); - // 列表 $text = preg_replace('/^- (.*$)/m', '
  • $1
  • ', $text); $text = preg_replace('/(
  • .*<\/li>\n?)+/', '', $text); - // 段落 $text = preg_replace('/\n\n/', '

    ', $text); $text = '

    ' . $text . '

    '; return $text; } -// 高亮搜索词 function highlightSearch($text, $search) { if (empty($search)) return $text; - return preg_replace('/(' . preg_quote($search, '/') . ')/i', - '$1', $text); + return preg_replace('/(' . preg_quote($search, '/') . ')/i', '$1', $text); } $htmlContent = markdownToHtml(htmlspecialchars($content)); @@ -102,13 +105,26 @@ if ($search) { } $folder = dirname($file) !== '.' ? dirname($file) : ''; + +// 平台配置 +function getPlatformInfo($platform) { + $platforms = [ + 'bilibili' => ['name' => 'Bilibili', 'icon' => '📺', 'color' => '#fb7299'], + 'youtube' => ['name' => 'YouTube', 'icon' => '▶️', 'color' => '#ff0000'], + 'zhihu' => ['name' => '知乎', 'icon' => '💡', 'color' => '#0084ff'], + 'wechat' => ['name' => '微信', 'icon' => '💬', 'color' => '#07c160'], + ]; + return $platforms[$platform] ?? ['name' => $platform ?: '未知', 'icon' => '📄', 'color' => '#666']; +} + +$platformInfo = getPlatformInfo($meta['platform']); ?> - <?php echo htmlspecialchars($title); ?> - 知识库 + <?php echo htmlspecialchars($meta['title']); ?> - 知识库 @@ -117,12 +133,68 @@ $folder = dirname($file) !== '.' ? dirname($file) : ''; ← 返回列表 -

    +

    📁
    + +
    +
    + +
    + 作者 + 👤 +
    + + +
    + 发布时间 + 📅 +
    + + +
    + 平台 + + + +
    + + +
    + 时长 + ⏱️ +
    + +
    + 类型 + +
    +
    + + +
    + 源链接 + + 🔗 + +
    + + + +
    + 标签 +
    + + + +
    +
    + +
    +