fix: use pdf previews for documents
This commit is contained in:
@@ -5,162 +5,65 @@
|
||||
<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>
|
||||
<title>{{ $document->title }} - PDF 预览</title>
|
||||
|
||||
<!-- Tailwind CSS -->
|
||||
@vite(['resources/css/app.css'])
|
||||
|
||||
<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>
|
||||
<body class="bg-gray-100 text-gray-900">
|
||||
<div class="mx-auto flex min-h-screen max-w-7xl flex-col gap-4 p-4">
|
||||
<header class="rounded-lg bg-white p-4 shadow-sm">
|
||||
<div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||
<div class="min-w-0 flex-1">
|
||||
<h1 class="truncate text-2xl font-bold">{{ $document->title }}</h1>
|
||||
<div class="mt-2 flex flex-wrap gap-4 text-sm text-gray-600">
|
||||
<span>{{ $document->display_file_name }}</span>
|
||||
<span>{{ $document->uploader->name }}</span>
|
||||
<span>{{ $document->created_at->format('Y年m月d日 H:i') }}</span>
|
||||
</div>
|
||||
|
||||
@if($document->description)
|
||||
<p class="mt-3 text-gray-700">{{ $document->description }}</p>
|
||||
<p class="mt-3 text-gray-700">{{ $document->description }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<div class="flex flex-wrap 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>
|
||||
class="inline-flex items-center rounded-lg bg-green-600 px-4 py-2 font-medium text-white transition-colors hover:bg-green-700">
|
||||
下载原文档
|
||||
</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>
|
||||
@if($canPreviewPdf)
|
||||
<a href="{{ $previewPdfUrl }}"
|
||||
class="inline-flex items-center rounded-lg bg-gray-700 px-4 py-2 font-medium text-white transition-colors hover:bg-gray-800"
|
||||
target="_blank"
|
||||
rel="noopener">
|
||||
打开 PDF
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Markdown 内容 -->
|
||||
<div class="preview-content">
|
||||
@if($markdownHtml)
|
||||
{!! $markdownHtml !!}
|
||||
<main class="min-h-[70vh] flex-1 overflow-hidden rounded-lg bg-white shadow-sm">
|
||||
@if($canPreviewPdf)
|
||||
<iframe
|
||||
class="w-full bg-gray-100"
|
||||
style="height: calc(100vh - 150px); min-height: 640px;"
|
||||
src="{{ $previewPdfUrl }}"
|
||||
title="{{ $document->title }} PDF 预览"
|
||||
></iframe>
|
||||
@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>
|
||||
<div class="flex min-h-[420px] flex-col items-center justify-center p-8 text-center text-gray-600">
|
||||
<h2 class="mb-2 text-xl font-semibold text-gray-800">PDF 预览暂不可用</h2>
|
||||
<p class="mb-6">该文档尚未完成转换或原文件不存在。</p>
|
||||
<a href="{{ route('documents.download', $document) }}"
|
||||
class="inline-flex items-center rounded-lg bg-green-600 px-6 py-3 font-medium text-white transition-colors hover:bg-green-700">
|
||||
下载原始文档
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
@php
|
||||
use App\Services\DocumentPreviewService;
|
||||
|
||||
$previewService = app(DocumentPreviewService::class);
|
||||
$htmlContent = null;
|
||||
$error = null;
|
||||
|
||||
try {
|
||||
$htmlContent = $previewService->convertToHtml($document);
|
||||
} catch (\Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
use App\Services\DocumentPdfPreviewService;
|
||||
|
||||
$previewService = app(DocumentPdfPreviewService::class);
|
||||
$canPreview = $previewService->canPreview($document);
|
||||
$previewUrl = $canPreview ? $previewService->previewUrl($document) : null;
|
||||
@endphp
|
||||
|
||||
<div class="document-preview-modal">
|
||||
@if ($error)
|
||||
@if (! $canPreview)
|
||||
<div class="rounded-lg bg-danger-50 p-4 text-danger-600 dark:bg-danger-400/10 dark:text-danger-400">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
@@ -21,81 +15,29 @@
|
||||
</svg>
|
||||
<div>
|
||||
<p class="font-semibold">预览失败</p>
|
||||
<p class="text-sm">{{ $error }}</p>
|
||||
<p class="text-sm">该文档尚未完成转换或原文件不存在</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@elseif ($htmlContent)
|
||||
@elseif ($previewUrl)
|
||||
<div class="rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800">
|
||||
<div class="border-b border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-700 dark:bg-gray-900">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
文档内容预览
|
||||
PDF 内容预览
|
||||
</h3>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $document->display_file_name }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-h-[600px] overflow-y-auto p-6">
|
||||
<div class="prose prose-sm max-w-none dark:prose-invert">
|
||||
{!! $htmlContent !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-700 dark:bg-gray-900">
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
提示:这是文档的预览版本,可能与原始格式略有差异。如需查看完整格式,请下载文档。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="rounded-lg bg-gray-50 p-4 text-gray-600 dark:bg-gray-800 dark:text-gray-400">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="h-5 w-5 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<p>正在加载文档预览...</p>
|
||||
</div>
|
||||
|
||||
<iframe
|
||||
class="w-full rounded-b-lg bg-gray-100 dark:bg-gray-950"
|
||||
style="height: min(82vh, 960px); min-height: 720px;"
|
||||
src="{{ $previewUrl }}"
|
||||
title="{{ $document->title }} PDF 预览"
|
||||
></iframe>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.document-preview-modal .prose {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
|
||||
.document-preview-modal .prose table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.document-preview-modal .prose table td,
|
||||
.document-preview-modal .prose table th {
|
||||
border: 1px solid #e5e7eb;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.document-preview-modal .prose table th {
|
||||
background-color: #f9fafb;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.document-preview-modal .prose img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.dark .document-preview-modal .prose table td,
|
||||
.dark .document-preview-modal .prose table th {
|
||||
border-color: #374151;
|
||||
}
|
||||
|
||||
.dark .document-preview-modal .prose table th {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
@php
|
||||
use App\Services\DocumentPreviewService;
|
||||
use App\Services\DocumentPdfPreviewService;
|
||||
|
||||
$previewService = app(DocumentPreviewService::class);
|
||||
$previewService = app(DocumentPdfPreviewService::class);
|
||||
$canPreview = $previewService->canPreview($document);
|
||||
$htmlContent = null;
|
||||
$error = null;
|
||||
|
||||
if ($canPreview) {
|
||||
try {
|
||||
$htmlContent = $previewService->convertToHtml($document);
|
||||
} catch (\Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
$previewUrl = $canPreview ? $previewService->previewUrl($document) : null;
|
||||
@endphp
|
||||
|
||||
<div class="document-preview-container">
|
||||
@@ -37,7 +28,7 @@
|
||||
<p>文档等待转换中...</p>
|
||||
</div>
|
||||
</div>
|
||||
@elseif ($error)
|
||||
@elseif (! $canPreview)
|
||||
<div class="rounded-lg bg-danger-50 p-4 text-danger-600 dark:bg-danger-400/10 dark:text-danger-400">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
@@ -45,16 +36,16 @@
|
||||
</svg>
|
||||
<div>
|
||||
<p class="font-semibold">预览加载失败</p>
|
||||
<p class="text-sm">{{ $error }}</p>
|
||||
<p class="text-sm">该文档尚未完成转换或原文件不存在</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@elseif ($htmlContent)
|
||||
@elseif ($previewUrl)
|
||||
<div class="rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800">
|
||||
<div class="border-b border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-700 dark:bg-gray-900">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
文档内容预览
|
||||
PDF 内容预览
|
||||
</h3>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ $document->display_file_name }}
|
||||
@@ -62,48 +53,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-h-[600px] overflow-y-auto p-6">
|
||||
<div class="prose prose-sm max-w-none dark:prose-invert">
|
||||
{!! $htmlContent !!}
|
||||
</div>
|
||||
</div>
|
||||
<iframe
|
||||
class="w-full rounded-b-lg bg-gray-100 dark:bg-gray-950"
|
||||
style="height: min(82vh, 960px); min-height: 720px;"
|
||||
src="{{ $previewUrl }}"
|
||||
title="{{ $document->title }} PDF 预览"
|
||||
></iframe>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.document-preview-container .prose {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
|
||||
}
|
||||
|
||||
.document-preview-container .prose table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.document-preview-container .prose table td,
|
||||
.document-preview-container .prose table th {
|
||||
border: 1px solid #e5e7eb;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.document-preview-container .prose table th {
|
||||
background-color: #f9fafb;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.document-preview-container .prose img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.dark .document-preview-container .prose table td,
|
||||
.dark .document-preview-container .prose table th {
|
||||
border-color: #374151;
|
||||
}
|
||||
|
||||
.dark .document-preview-container .prose table th {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user