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
starmen1265 af4edfc4ae feat: 文章类型显示原文字数代替时间
- 解析 type 字段判断资源类型
- 文章类型计算正文字数并显示
- 视频类型保持显示时长/时间
2026-02-28 23:19:31 +08:00

269 lines
11 KiB
PHP
Raw Permalink 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',
'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>