feat: 初始化知识库系统项目

- 实现基于 Laravel 11 和 Filament 3.X 的文档管理系统
- 添加用户认证和分组管理功能
- 实现文档上传、分类和权限控制
- 集成 Word 文档自动转换为 Markdown
- 集成 Meilisearch 全文搜索引擎
- 实现文档在线预览功能
- 添加安全日志和审计功能
- 完整的简体中文界面
- 包含完整的项目文档和部署指南

技术栈:
- Laravel 11.x
- Filament 3.X
- Meilisearch 1.5+
- Pandoc 文档转换
- Redis 队列系统
- Pest PHP 测试框架
This commit is contained in:
Knowledge Base System
2025-12-05 14:44:44 +08:00
commit acf549c43c
165 changed files with 32838 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{ $document->title }} - Markdown 预览</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background-color: #f3f4f6;
}
.preview-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.preview-header {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.preview-content {
background: white;
border-radius: 8px;
padding: 40px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
min-height: 400px;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #6b7280;
}
.empty-state-icon {
font-size: 64px;
margin-bottom: 20px;
}
@media print {
.preview-header {
display: none;
}
.preview-content {
box-shadow: none;
padding: 0;
}
}
/* 移动端优化 */
@media (max-width: 768px) {
.preview-container {
padding: 10px;
}
.preview-header {
padding: 15px;
}
.preview-content {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="preview-container">
<!-- 头部信息 -->
<div class="preview-header">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div class="flex-1">
<h1 class="text-2xl font-bold text-gray-900 mb-2">{{ $document->title }}</h1>
<div class="flex flex-wrap gap-4 text-sm text-gray-600">
<div class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
</svg>
<span>{{ $document->type === 'global' ? '全局知识库' : '专用知识库' }}</span>
</div>
@if($document->group)
<div class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
<span>{{ $document->group->name }}</span>
</div>
@endif
<div class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
<span>{{ $document->uploader->name }}</span>
</div>
<div class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
<span>{{ $document->created_at->format('Y年m月d日 H:i') }}</span>
</div>
</div>
@if($document->description)
<p class="mt-3 text-gray-700">{{ $document->description }}</p>
@endif
</div>
<div class="flex gap-2">
<a href="{{ route('documents.download', $document) }}"
class="inline-flex items-center gap-2 px-4 py-2 bg-green-600 hover:bg-green-700 text-white font-medium rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
<span>下载原文档</span>
</a>
<button onclick="window.print()"
class="inline-flex items-center gap-2 px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white font-medium rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"></path>
</svg>
<span>打印</span>
</button>
</div>
</div>
</div>
<!-- Markdown 内容 -->
<div class="preview-content">
@if($markdownHtml)
{!! $markdownHtml !!}
@else
<div class="empty-state">
<div class="empty-state-icon">📄</div>
<h2 class="text-xl font-semibold text-gray-700 mb-2">Markdown 内容为空</h2>
<p class="text-gray-600 mb-6">该文档的 Markdown 内容尚未生成或为空</p>
<a href="{{ route('documents.download', $document) }}"
class="inline-flex items-center gap-2 px-6 py-3 bg-green-600 hover:bg-green-700 text-white font-medium rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
<span>下载原始文档</span>
</a>
</div>
@endif
</div>
</div>
</body>
</html>