添加标签和完整元数据显示
列表页: - 显示标签(最多5个) - 显示作者、发布时间、平台、时长 - 显示源链接 - 显示类型(视频/文章) - 平台颜色徽章 文章页: - 新增元数据卡片 - 显示所有元数据信息 - 显示所有标签 - 源链接可点击跳转
This commit is contained in:
+63
-33
@@ -18,6 +18,9 @@ function parseMarkdownWithMeta($filePath) {
|
|||||||
'date' => '',
|
'date' => '',
|
||||||
'platform' => '',
|
'platform' => '',
|
||||||
'url' => '',
|
'url' => '',
|
||||||
|
'tags' => [],
|
||||||
|
'duration' => '',
|
||||||
|
'type' => 'article',
|
||||||
'summary' => ''
|
'summary' => ''
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -42,8 +45,17 @@ function parseMarkdownWithMeta($filePath) {
|
|||||||
if (preg_match('/^url:\s*(.+?)\s*$/m', $yaml, $m)) {
|
if (preg_match('/^url:\s*(.+?)\s*$/m', $yaml, $m)) {
|
||||||
$meta['url'] = trim($m[1]);
|
$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));
|
$lines = preg_split('/\r?\n/', trim($body));
|
||||||
$summaryLines = [];
|
$summaryLines = [];
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
@@ -53,17 +65,6 @@ function parseMarkdownWithMeta($filePath) {
|
|||||||
if (count($summaryLines) >= 2) break;
|
if (count($summaryLines) >= 2) break;
|
||||||
}
|
}
|
||||||
$meta['summary'] = implode(' ', $summaryLines);
|
$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;
|
return $meta;
|
||||||
@@ -91,7 +92,6 @@ function scanDirectory($dir, $baseDir, $search) {
|
|||||||
|
|
||||||
$content = file_get_contents($fullPath);
|
$content = file_get_contents($fullPath);
|
||||||
|
|
||||||
// 搜索过滤
|
|
||||||
if ($search) {
|
if ($search) {
|
||||||
$searchLower = mb_strtolower($search);
|
$searchLower = mb_strtolower($search);
|
||||||
$contentLower = mb_strtolower($content);
|
$contentLower = mb_strtolower($content);
|
||||||
@@ -100,19 +100,21 @@ function scanDirectory($dir, $baseDir, $search) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析元数据
|
|
||||||
$meta = parseMarkdownWithMeta($fullPath);
|
$meta = parseMarkdownWithMeta($fullPath);
|
||||||
|
|
||||||
$docs[] = [
|
$docs[] = [
|
||||||
'name' => $item,
|
'name' => $item,
|
||||||
'path' => $relativePath,
|
'path' => $relativePath,
|
||||||
'folder' => dirname($relativePath) === '.' ? '' : dirname($relativePath),
|
'folder' => dirname($relativePath) === '.' ? '' : dirname($relativePath),
|
||||||
'size' => filesize($fullPath),
|
|
||||||
'mtime' => filemtime($fullPath),
|
'mtime' => filemtime($fullPath),
|
||||||
'title' => $meta['title'],
|
'title' => $meta['title'],
|
||||||
'author' => $meta['author'],
|
'author' => $meta['author'],
|
||||||
'date' => $meta['date'],
|
'date' => $meta['date'],
|
||||||
'platform' => $meta['platform'],
|
'platform' => $meta['platform'],
|
||||||
|
'url' => $meta['url'],
|
||||||
|
'tags' => $meta['tags'],
|
||||||
|
'duration' => $meta['duration'],
|
||||||
|
'type' => $meta['type'],
|
||||||
'summary' => $meta['summary']
|
'summary' => $meta['summary']
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -122,32 +124,35 @@ function scanDirectory($dir, $baseDir, $search) {
|
|||||||
|
|
||||||
$documents = scanDirectory($docDir, $docDir, $search);
|
$documents = scanDirectory($docDir, $docDir, $search);
|
||||||
|
|
||||||
// 按日期排序(新文件在前)
|
// 排序
|
||||||
usort($documents, function($a, $b) {
|
usort($documents, function($a, $b) {
|
||||||
// 优先按日期排序
|
|
||||||
if ($a['date'] && $b['date']) {
|
if ($a['date'] && $b['date']) {
|
||||||
return strcmp($b['date'], $a['date']);
|
return strcmp($b['date'], $a['date']);
|
||||||
}
|
}
|
||||||
// 其次按修改时间
|
|
||||||
return $b['mtime'] - $a['mtime'];
|
return $b['mtime'] - $a['mtime'];
|
||||||
});
|
});
|
||||||
|
|
||||||
// 平台图标映射
|
// 平台配置
|
||||||
function getPlatformIcon($platform) {
|
function getPlatformInfo($platform) {
|
||||||
$icons = [
|
$platforms = [
|
||||||
'bilibili' => '📺',
|
'bilibili' => ['name' => 'Bilibili', 'icon' => '📺', 'color' => '#fb7299'],
|
||||||
'youtube' => '▶️',
|
'youtube' => ['name' => 'YouTube', 'icon' => '▶️', 'color' => '#ff0000'],
|
||||||
'zhihu' => '💡',
|
'zhihu' => ['name' => '知乎', 'icon' => '💡', 'color' => '#0084ff'],
|
||||||
'wechat' => '💬',
|
'wechat' => ['name' => '微信', 'icon' => '💬', 'color' => '#07c160'],
|
||||||
];
|
];
|
||||||
return $icons[$platform] ?? '📄';
|
return $platforms[$platform] ?? ['name' => $platform ?: '未知', 'icon' => '📄', 'color' => '#666'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型图标
|
||||||
|
function getTypeIcon($type) {
|
||||||
|
return $type === 'video' ? '🎬' : '📝';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>知识库 - <?php echo htmlspecialchars($username); ?></title>
|
<title>知识库 - <?php echo htmlspecialchars($username); ?></title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
</head>
|
</head>
|
||||||
@@ -189,23 +194,48 @@ function getPlatformIcon($platform) {
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($documents as $doc): ?>
|
<?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) : ''; ?>"
|
<a href="view.php?file=<?php echo urlencode($doc['path']); ?><?php echo $search ? '&q=' . urlencode($search) : ''; ?>"
|
||||||
class="document-card">
|
class="document-card">
|
||||||
<div class="doc-icon"><?php echo getPlatformIcon($doc['platform']); ?></div>
|
<div class="doc-icon"><?php echo $platformInfo['icon']; ?></div>
|
||||||
<div class="doc-info">
|
<div class="doc-info">
|
||||||
<div class="doc-title"><?php echo htmlspecialchars($doc['title']); ?></div>
|
<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">
|
<div class="doc-meta">
|
||||||
<?php if ($doc['author']): ?>
|
<?php if ($doc['author']): ?>
|
||||||
<span class="meta-author">👤 <?php echo htmlspecialchars($doc['author']); ?></span>
|
<span class="meta-item">👤 <?php echo htmlspecialchars($doc['author']); ?></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if ($doc['date']): ?>
|
<?php if ($doc['date']): ?>
|
||||||
<span class="meta-date">📅 <?php echo htmlspecialchars($doc['date']); ?></span>
|
<span class="meta-item">📅 <?php echo htmlspecialchars($doc['date']); ?></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if ($doc['platform']): ?>
|
<?php if ($doc['platform']): ?>
|
||||||
<span class="meta-platform">🏷️ <?php echo htmlspecialchars($doc['platform']); ?></span>
|
<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; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</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']): ?>
|
<?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>
|
<div class="doc-summary"><?php echo htmlspecialchars(mb_substr($doc['summary'], 0, 150)); ?><?php echo mb_strlen($doc['summary']) > 150 ? '...' : ''; ?></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
@@ -846,3 +846,189 @@ mark {
|
|||||||
background: rgba(56, 189, 248, 0.3);
|
background: rgba(56, 189, 248, 0.3);
|
||||||
color: var(--text-primary);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,11 +6,9 @@ $user = getCurrentUser();
|
|||||||
$username = $user['username'];
|
$username = $user['username'];
|
||||||
$docDir = getUserDocDir($username);
|
$docDir = getUserDocDir($username);
|
||||||
|
|
||||||
// 获取文件名
|
|
||||||
$file = $_GET['file'] ?? '';
|
$file = $_GET['file'] ?? '';
|
||||||
$search = $_GET['q'] ?? '';
|
$search = $_GET['q'] ?? '';
|
||||||
|
|
||||||
// 安全检查
|
|
||||||
if (empty($file) || strpos($file, '..') !== false) {
|
if (empty($file) || strpos($file, '..') !== false) {
|
||||||
die('无效的文件名');
|
die('无效的文件名');
|
||||||
}
|
}
|
||||||
@@ -30,12 +28,15 @@ if (!file_exists($filePath)) {
|
|||||||
$content = file_get_contents($filePath);
|
$content = file_get_contents($filePath);
|
||||||
|
|
||||||
// 解析元数据
|
// 解析元数据
|
||||||
$title = basename($file);
|
|
||||||
$meta = [
|
$meta = [
|
||||||
'title' => $title,
|
'title' => basename($file),
|
||||||
'author' => '',
|
'author' => '',
|
||||||
'date' => '',
|
'date' => '',
|
||||||
'platform' => ''
|
'platform' => '',
|
||||||
|
'url' => '',
|
||||||
|
'tags' => [],
|
||||||
|
'duration' => '',
|
||||||
|
'type' => 'article'
|
||||||
];
|
];
|
||||||
|
|
||||||
// 提取并移除 YAML front matter
|
// 提取并移除 YAML front matter
|
||||||
@@ -43,10 +44,8 @@ if (preg_match('/^---\s*\n(.*?)\n---\s*\n(.*)$/s', $content, $matches)) {
|
|||||||
$yaml = $matches[1];
|
$yaml = $matches[1];
|
||||||
$content = $matches[2];
|
$content = $matches[2];
|
||||||
|
|
||||||
// 解析元数据(用于页面标题)
|
|
||||||
if (preg_match('/^title:\s*["\']?(.+?)["\']?\s*$/m', $yaml, $m)) {
|
if (preg_match('/^title:\s*["\']?(.+?)["\']?\s*$/m', $yaml, $m)) {
|
||||||
$title = trim($m[1], '"\'');
|
$meta['title'] = trim($m[1], '"\'');
|
||||||
$meta['title'] = $title;
|
|
||||||
}
|
}
|
||||||
if (preg_match('/^author:\s*(.+?)\s*$/m', $yaml, $m)) {
|
if (preg_match('/^author:\s*(.+?)\s*$/m', $yaml, $m)) {
|
||||||
$meta['author'] = trim($m[1]);
|
$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)) {
|
if (preg_match('/^platform:\s*(.+?)\s*$/m', $yaml, $m)) {
|
||||||
$meta['platform'] = trim($m[1]);
|
$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) {
|
function markdownToHtml($text) {
|
||||||
// 标题
|
|
||||||
$text = preg_replace('/^#### (.*$)/m', '<h4>$1</h4>', $text);
|
$text = preg_replace('/^#### (.*$)/m', '<h4>$1</h4>', $text);
|
||||||
$text = preg_replace('/^### (.*$)/m', '<h3>$1</h3>', $text);
|
$text = preg_replace('/^### (.*$)/m', '<h3>$1</h3>', $text);
|
||||||
$text = preg_replace('/^## (.*$)/m', '<h2>$1</h2>', $text);
|
$text = preg_replace('/^## (.*$)/m', '<h2>$1</h2>', $text);
|
||||||
$text = preg_replace('/^# (.*$)/m', '<h1>$1</h1>', $text);
|
$text = preg_replace('/^# (.*$)/m', '<h1>$1</h1>', $text);
|
||||||
|
|
||||||
// 粗体和斜体
|
|
||||||
$text = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $text);
|
$text = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $text);
|
||||||
$text = preg_replace('/\*(.+?)\*/', '<em>$1</em>', $text);
|
$text = preg_replace('/\*(.+?)\*/', '<em>$1</em>', $text);
|
||||||
|
|
||||||
// 代码块
|
|
||||||
$text = preg_replace('/```(\w*)\n(.*?)```/s', '<pre><code class="language-$1">$2</code></pre>', $text);
|
$text = preg_replace('/```(\w*)\n(.*?)```/s', '<pre><code class="language-$1">$2</code></pre>', $text);
|
||||||
$text = preg_replace('/`(.+?)`/', '<code>$1</code>', $text);
|
$text = preg_replace('/`(.+?)`/', '<code>$1</code>', $text);
|
||||||
|
|
||||||
// 链接
|
|
||||||
$text = preg_replace('/\[(.+?)\]\((.+?)\)/', '<a href="$2" target="_blank">$1</a>', $text);
|
$text = preg_replace('/\[(.+?)\]\((.+?)\)/', '<a href="$2" target="_blank">$1</a>', $text);
|
||||||
|
|
||||||
// 列表
|
|
||||||
$text = preg_replace('/^- (.*$)/m', '<li>$1</li>', $text);
|
$text = preg_replace('/^- (.*$)/m', '<li>$1</li>', $text);
|
||||||
$text = preg_replace('/(<li>.*<\/li>\n?)+/', '<ul>$0</ul>', $text);
|
$text = preg_replace('/(<li>.*<\/li>\n?)+/', '<ul>$0</ul>', $text);
|
||||||
|
|
||||||
// 段落
|
|
||||||
$text = preg_replace('/\n\n/', '</p><p>', $text);
|
$text = preg_replace('/\n\n/', '</p><p>', $text);
|
||||||
$text = '<p>' . $text . '</p>';
|
$text = '<p>' . $text . '</p>';
|
||||||
|
|
||||||
return $text;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 高亮搜索词
|
|
||||||
function highlightSearch($text, $search) {
|
function highlightSearch($text, $search) {
|
||||||
if (empty($search)) return $text;
|
if (empty($search)) return $text;
|
||||||
return preg_replace('/(' . preg_quote($search, '/') . ')/i',
|
return preg_replace('/(' . preg_quote($search, '/') . ')/i', '<mark>$1</mark>', $text);
|
||||||
'<mark>$1</mark>', $text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$htmlContent = markdownToHtml(htmlspecialchars($content));
|
$htmlContent = markdownToHtml(htmlspecialchars($content));
|
||||||
@@ -102,13 +105,26 @@ if ($search) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$folder = dirname($file) !== '.' ? dirname($file) : '';
|
$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']);
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title><?php echo htmlspecialchars($title); ?> - 知识库</title>
|
<title><?php echo htmlspecialchars($meta['title']); ?> - 知识库</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -117,12 +133,68 @@ $folder = dirname($file) !== '.' ? dirname($file) : '';
|
|||||||
<a href="dashboard.php<?php echo $search ? '?q=' . urlencode($search) : ''; ?>" class="btn-back">
|
<a href="dashboard.php<?php echo $search ? '?q=' . urlencode($search) : ''; ?>" class="btn-back">
|
||||||
← 返回列表
|
← 返回列表
|
||||||
</a>
|
</a>
|
||||||
<h1><?php echo htmlspecialchars($title); ?></h1>
|
<h1><?php echo htmlspecialchars($meta['title']); ?></h1>
|
||||||
<?php if ($folder): ?>
|
<?php if ($folder): ?>
|
||||||
<div class="folder-info">📁 <?php echo htmlspecialchars($folder); ?></div>
|
<div class="folder-info">📁 <?php echo htmlspecialchars($folder); ?></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<!-- 元数据卡片 -->
|
||||||
|
<div class="meta-card">
|
||||||
|
<div class="meta-row">
|
||||||
|
<?php if ($meta['author']): ?>
|
||||||
|
<div class="meta-field">
|
||||||
|
<span class="meta-label">作者</span>
|
||||||
|
<span class="meta-value">👤 <?php echo htmlspecialchars($meta['author']); ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($meta['date']): ?>
|
||||||
|
<div class="meta-field">
|
||||||
|
<span class="meta-label">发布时间</span>
|
||||||
|
<span class="meta-value">📅 <?php echo htmlspecialchars($meta['date']); ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($meta['platform']): ?>
|
||||||
|
<div class="meta-field">
|
||||||
|
<span class="meta-label">平台</span>
|
||||||
|
<span class="meta-value platform-badge" style="--platform-color: <?php echo $platformInfo['color']; ?>">
|
||||||
|
<?php echo $platformInfo['icon']; ?> <?php echo $platformInfo['name']; ?>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($meta['duration']): ?>
|
||||||
|
<div class="meta-field">
|
||||||
|
<span class="meta-label">时长</span>
|
||||||
|
<span class="meta-value">⏱️ <?php echo htmlspecialchars($meta['duration']); ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="meta-field">
|
||||||
|
<span class="meta-label">类型</span>
|
||||||
|
<span class="meta-value"><?php echo $meta['type'] === 'video' ? '🎬 视频' : '📝 文章'; ?></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($meta['url']): ?>
|
||||||
|
<div class="meta-source">
|
||||||
|
<span class="meta-label">源链接</span>
|
||||||
|
<a href="<?php echo htmlspecialchars($meta['url']); ?>" target="_blank" class="source-url">
|
||||||
|
🔗 <?php echo htmlspecialchars($meta['url']); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($meta['tags'])): ?>
|
||||||
|
<div class="meta-tags">
|
||||||
|
<span class="meta-label">标签</span>
|
||||||
|
<div class="tags-list">
|
||||||
|
<?php foreach ($meta['tags'] as $tag): ?>
|
||||||
|
<span class="tag"><?php echo htmlspecialchars($tag); ?></span>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="document-content">
|
<div class="document-content">
|
||||||
<?php echo $htmlContent; ?>
|
<?php echo $htmlContent; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user