af4edfc4ae
- 解析 type 字段判断资源类型 - 文章类型计算正文字数并显示 - 视频类型保持显示时长/时间
269 lines
11 KiB
PHP
269 lines
11 KiB
PHP
<?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',
|
||
'word_count' => 0,
|
||
'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]);
|
||
}
|
||
// 解析 type 字段
|
||
if (preg_match('/^type:\s*(.+?)\s*$/m', $yaml, $m)) {
|
||
$meta['type'] = trim($m[1]);
|
||
}
|
||
// 如果有 duration 但没有明确 type,则设为 video
|
||
if ($meta['duration'] && $meta['type'] === 'article') {
|
||
$meta['type'] = 'video';
|
||
}
|
||
if (preg_match('/^tags:\s*\[(.+)\]\s*$/m', $yaml, $m)) {
|
||
$tagsStr = $m[1];
|
||
preg_match_all('/"([^"]+)"/', $tagsStr, $tagMatches);
|
||
$meta['tags'] = $tagMatches[1] ?? [];
|
||
}
|
||
|
||
// 计算正文字数(文章类型)
|
||
if ($meta['type'] === 'article') {
|
||
// 移除 Markdown 标记,计算纯文字字数
|
||
$plainText = strip_tags($body);
|
||
$plainText = preg_replace('/[#*_`\[\]()]/', '', $plainText);
|
||
$meta['word_count'] = mb_strlen(preg_replace('/\s+/', '', $plainText));
|
||
}
|
||
|
||
// 提取正文概述
|
||
$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'],
|
||
'word_count' => $meta['word_count'],
|
||
'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['type'] === 'article' && $doc['word_count'] > 0): ?>
|
||
<span class="meta-item">📝 <?php echo number_format($doc['word_count']); ?> 字</span>
|
||
<?php elseif ($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>
|