This repository has been archived on 2026-03-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
kb-web/dashboard.php
T
starmen1265 cd38932eb0 添加标签和完整元数据显示
列表页:
- 显示标签(最多5个)
- 显示作者、发布时间、平台、时长
- 显示源链接
- 显示类型(视频/文章)
- 平台颜色徽章

文章页:
- 新增元数据卡片
- 显示所有元数据信息
- 显示所有标签
- 源链接可点击跳转
2026-02-15 21:25:06 +08:00

250 lines
9.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once 'config.php';
checkLogin();
$user = getCurrentUser();
$username = $user['username'];
$docDir = getUserDocDir($username);
// 搜索关键词
$search = trim($_GET['q'] ?? '');
// 解析 Markdown 文件的 YAML front matter
function parseMarkdownWithMeta($filePath) {
$content = file_get_contents($filePath);
$meta = [
'title' => basename($filePath),
'author' => '',
'date' => '',
'platform' => '',
'url' => '',
'tags' => [],
'duration' => '',
'type' => 'article',
'summary' => ''
];
// 解析 YAML front matter
if (preg_match('/^---\s*\n(.*?)\n---\s*\n(.*)$/s', $content, $matches)) {
$yaml = $matches[1];
$body = $matches[2];
// 解析 YAML 字段
if (preg_match('/^title:\s*["\']?(.+?)["\']?\s*$/m', $yaml, $m)) {
$meta['title'] = trim($m[1], '"\'');
}
if (preg_match('/^author:\s*(.+?)\s*$/m', $yaml, $m)) {
$meta['author'] = trim($m[1]);
}
if (preg_match('/^date:\s*(.+?)\s*$/m', $yaml, $m)) {
$meta['date'] = trim($m[1]);
}
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] ?? [];
}
// 提取正文概述
$lines = preg_split('/\r?\n/', trim($body));
$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;
}
// 递归获取文档列表
$documents = [];
function scanDirectory($dir, $baseDir, $search) {
$docs = [];
if (!is_dir($dir)) return $docs;
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$fullPath = $dir . '/' . $item;
$relativePath = str_replace($baseDir . '/', '', $fullPath);
if (is_dir($fullPath)) {
$docs = array_merge($docs, scanDirectory($fullPath, $baseDir, $search));
} elseif (is_file($fullPath)) {
$ext = strtolower(pathinfo($item, PATHINFO_EXTENSION));
if (!in_array($ext, ['md', 'txt', 'markdown'])) continue;
$content = file_get_contents($fullPath);
if ($search) {
$searchLower = mb_strtolower($search);
$contentLower = mb_strtolower($content);
if (strpos($contentLower, $searchLower) === false) {
continue;
}
}
$meta = parseMarkdownWithMeta($fullPath);
$docs[] = [
'name' => $item,
'path' => $relativePath,
'folder' => dirname($relativePath) === '.' ? '' : dirname($relativePath),
'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']
];
}
}
return $docs;
}
$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 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'];
}
// 类型图标
function getTypeIcon($type) {
return $type === 'video' ? '🎬' : '📝';
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>知识库 - <?php echo htmlspecialchars($username); ?></title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="dashboard">
<header class="header">
<h1>📚 我的知识库</h1>
<div class="user-info">
<span><?php echo htmlspecialchars($username); ?></span>
<a href="logout.php" class="btn-logout">退出</a>
</div>
</header>
<div class="search-bar">
<form method="GET" action="">
<input type="text" name="q" placeholder="搜索文档..."
value="<?php echo htmlspecialchars($search); ?>">
<button type="submit">搜索</button>
<?php if ($search): ?>
<a href="dashboard.php" class="btn-clear">清除</a>
<?php endif; ?>
</form>
</div>
<div class="stats">
共 <?php echo count($documents); ?> 个文档
<?php if ($search): ?>
<span class="search-hint">(搜索: "<?php echo htmlspecialchars($search); ?>")</span>
<?php endif; ?>
</div>
<div class="document-list">
<?php if (empty($documents)): ?>
<div class="empty">
<?php if ($search): ?>
没有找到匹配的文档
<?php else: ?>
暂无文档
<?php endif; ?>
</div>
<?php else: ?>
<?php foreach ($documents as $doc):
$platformInfo = getPlatformInfo($doc['platform']);
?>
<a href="view.php?file=<?php echo urlencode($doc['path']); ?><?php echo $search ? '&q=' . urlencode($search) : ''; ?>"
class="document-card">
<div class="doc-icon"><?php echo $platformInfo['icon']; ?></div>
<div class="doc-info">
<div class="doc-header">
<span class="doc-type"><?php echo getTypeIcon($doc['type']); ?></span>
<span class="doc-title"><?php echo htmlspecialchars($doc['title']); ?></span>
</div>
<div class="doc-meta">
<?php if ($doc['author']): ?>
<span class="meta-item">👤 <?php echo htmlspecialchars($doc['author']); ?></span>
<?php endif; ?>
<?php if ($doc['date']): ?>
<span class="meta-item">📅 <?php echo htmlspecialchars($doc['date']); ?></span>
<?php endif; ?>
<?php if ($doc['platform']): ?>
<span class="meta-item platform-badge" style="--platform-color: <?php echo $platformInfo['color']; ?>">
<?php echo $platformInfo['name']; ?>
</span>
<?php endif; ?>
<?php if ($doc['duration']): ?>
<span class="meta-item">⏱️ <?php echo htmlspecialchars($doc['duration']); ?></span>
<?php endif; ?>
<?php if ($doc['url']): ?>
<span class="meta-item source-link" onclick="event.stopPropagation(); event.preventDefault(); window.open('<?php echo htmlspecialchars($doc['url']); ?>', '_blank');">
🔗 源链接
</span>
<?php endif; ?>
</div>
<?php if (!empty($doc['tags'])): ?>
<div class="doc-tags">
<?php foreach (array_slice($doc['tags'], 0, 5) as $tag): ?>
<span class="tag"><?php echo htmlspecialchars($tag); ?></span>
<?php endforeach; ?>
<?php if (count($doc['tags']) > 5): ?>
<span class="tag-more">+<?php echo count($doc['tags']) - 5; ?></span>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($doc['summary']): ?>
<div class="doc-summary"><?php echo htmlspecialchars(mb_substr($doc['summary'], 0, 150)); ?><?php echo mb_strlen($doc['summary']) > 150 ? '...' : ''; ?></div>
<?php endif; ?>
</div>
</a>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</body>
</html>