Compare commits
3 Commits
5d199c2cdb
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b37e40db3 | |||
| e69bc67823 | |||
| 49392b47fc |
@@ -18,7 +18,7 @@ const router = createRouter({
|
|||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'login',
|
name: 'login',
|
||||||
component: () => import('@/views/Login/index.vue'),
|
component: () => import('@/views/login/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const REDIRECT_MAIN: RouteRecordRaw = {
|
|||||||
{
|
{
|
||||||
path: '/redirect/:path',
|
path: '/redirect/:path',
|
||||||
name: REDIRECT_ROUTE_NAME,
|
name: REDIRECT_ROUTE_NAME,
|
||||||
component: () => import('@/views/Redirect/index.vue'),
|
component: () => import('@/views/redirect/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref, computed, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { getBook, type Book, retryFailed, softDeleteBook } from '@/api/book';
|
import { getBook, type Book, retryFailed, softDeleteBook } from '@/api/book';
|
||||||
import DocumentItem from '../components/DocumentItem.vue';
|
import { getDocumentUrl } from '@/api/document';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -11,16 +11,57 @@ const bookId = Number(route.params.id);
|
|||||||
|
|
||||||
const book = ref<Book & { documents: any[] } | null>(null);
|
const book = ref<Book & { documents: any[] } | null>(null);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
const selectedDocId = ref<string | null>(null);
|
||||||
|
const activeTab = ref<'word' | 'html' | 'json'>('html');
|
||||||
|
|
||||||
|
type FileKind = 'docx' | 'html' | 'json' | 'error_log';
|
||||||
|
const fileTypeMap: Record<typeof activeTab.value, FileKind> = {
|
||||||
|
word: 'docx',
|
||||||
|
html: 'html',
|
||||||
|
json: 'json',
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedDoc = computed(() => {
|
||||||
|
if (!book.value || !selectedDocId.value) return null;
|
||||||
|
return book.value.documents.find((d) => d.doc_id === selectedDocId.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileUrl = ref<string>('');
|
||||||
|
const fileLoading = ref(false);
|
||||||
|
|
||||||
|
const fetchFileUrl = async () => {
|
||||||
|
if (!selectedDoc.value) {
|
||||||
|
fileUrl.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileLoading.value = true;
|
||||||
|
try {
|
||||||
|
const r = await getDocumentUrl(
|
||||||
|
selectedDoc.value.doc_id,
|
||||||
|
fileTypeMap[activeTab.value],
|
||||||
|
);
|
||||||
|
fileUrl.value = r?.url ?? '';
|
||||||
|
} catch (e) {
|
||||||
|
fileUrl.value = '';
|
||||||
|
} finally {
|
||||||
|
fileLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fetchBook = async () => {
|
const fetchBook = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
book.value = await getBook(bookId);
|
book.value = await getBook(bookId);
|
||||||
|
if (book.value?.documents.length && !selectedDocId.value) {
|
||||||
|
selectedDocId.value = book.value.documents[0].doc_id;
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch([selectedDocId, activeTab], fetchFileUrl, { immediate: false });
|
||||||
|
|
||||||
const onRetry = async () => {
|
const onRetry = async () => {
|
||||||
const r = await retryFailed(bookId);
|
const r = await retryFailed(bookId);
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
@@ -39,129 +80,476 @@ const onDelete = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatBytes = (n: number): string => {
|
const openInNewTab = (url: string) => {
|
||||||
if (n < 1024) return `${n} B`;
|
window.open(url, '_blank');
|
||||||
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
||||||
return `${(n / 1024 / 1024).toFixed(2)} MB`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const statusColor: Record<string, string> = {
|
|
||||||
pending: 'gray',
|
|
||||||
processing: 'arcoblue',
|
|
||||||
done: 'green',
|
|
||||||
failed: 'red',
|
|
||||||
partial: 'orange',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusText: Record<string, string> = {
|
const statusText: Record<string, string> = {
|
||||||
pending: '待处理',
|
pending: '待处理',
|
||||||
processing: '处理中',
|
processing: '处理中',
|
||||||
|
html_ready: 'HTML 已就绪',
|
||||||
|
json_ready: 'JSON 已就绪',
|
||||||
done: '完成',
|
done: '完成',
|
||||||
failed: '失败',
|
failed: '失败',
|
||||||
partial: '部分失败',
|
partial: '部分失败',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const statusColor: Record<string, string> = {
|
||||||
|
pending: 'gray',
|
||||||
|
processing: 'arcoblue',
|
||||||
|
html_ready: 'green',
|
||||||
|
json_ready: 'green',
|
||||||
|
failed: 'red',
|
||||||
|
partial: 'orange',
|
||||||
|
done: 'green',
|
||||||
|
};
|
||||||
|
|
||||||
|
const completionRate = computed(() => {
|
||||||
|
if (!book.value || !book.value.total_count) return 0;
|
||||||
|
return Math.round((book.value.success_count / book.value.total_count) * 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
const truncate = (s: string, len = 8) => (s ? `${s.slice(0, len)}…` : '');
|
||||||
|
|
||||||
onMounted(fetchBook);
|
onMounted(fetchBook);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="book-detail">
|
<div class="book-detail">
|
||||||
<a-card :bordered="false">
|
<a-skeleton v-if="loading" :rows="8" />
|
||||||
<a-skeleton v-if="loading" :rows="6" />
|
|
||||||
<template v-else-if="book">
|
<template v-else-if="book">
|
||||||
<a-page-header
|
<!-- 顶部:标题 + 操作 -->
|
||||||
:title="book.name"
|
<a-card :bordered="false" class="header-card">
|
||||||
:subtitle="`ID: ${book.id} | ${book.subject || ''} ${book.grade || ''} ${book.version || ''}`"
|
<div class="header-row">
|
||||||
>
|
<div class="header-info">
|
||||||
<template #extra>
|
<div class="book-name">{{ book.name }}</div>
|
||||||
<a-space>
|
<div class="book-meta">
|
||||||
<a-tag :color="statusColor[book.status]" size="medium">
|
<a-tag :color="statusColor[book.status]" size="small">
|
||||||
{{ statusText[book.status] || book.status }}
|
{{ statusText[book.status] || book.status }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<a-button
|
<span class="meta-text">ID: {{ book.id }}</span>
|
||||||
v-if="book.failed_count > 0"
|
<span v-if="book.subject" class="meta-text">{{ book.subject }}</span>
|
||||||
@click="onRetry"
|
<span v-if="book.grade" class="meta-text">{{ book.grade }}</span>
|
||||||
>
|
<span v-if="book.version" class="meta-text">{{ book.version }}</span>
|
||||||
<icon-refresh /> 重试失败
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-space>
|
||||||
|
<a-button
|
||||||
|
v-if="book.failed_count > 0"
|
||||||
|
size="small"
|
||||||
|
@click="onRetry"
|
||||||
|
>
|
||||||
|
<icon-refresh /> 重试失败
|
||||||
|
</a-button>
|
||||||
|
<a-popconfirm content="确定软删该教辅?" @ok="onDelete">
|
||||||
|
<a-button size="small" status="danger">
|
||||||
|
<icon-delete /> 删除
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-popconfirm
|
</a-popconfirm>
|
||||||
content="确定软删该教辅?"
|
</a-space>
|
||||||
@ok="onDelete"
|
</div>
|
||||||
>
|
</a-card>
|
||||||
<a-button status="danger">
|
|
||||||
<icon-delete /> 删除
|
|
||||||
</a-button>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
</a-page-header>
|
|
||||||
|
|
||||||
<a-descriptions
|
<!-- 4 张统计卡 -->
|
||||||
:data="[]"
|
<a-row :gutter="16" class="stat-row">
|
||||||
:column="3"
|
<a-col :span="6">
|
||||||
class="meta"
|
<a-card :bordered="false" class="stat-card">
|
||||||
size="medium"
|
<div class="stat-label">文档总数</div>
|
||||||
|
<div class="stat-value">{{ book.total_count }}</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-card :bordered="false" class="stat-card stat-ok">
|
||||||
|
<div class="stat-label">成功</div>
|
||||||
|
<div class="stat-value">{{ book.success_count }}</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-card :bordered="false" class="stat-card stat-fail">
|
||||||
|
<div class="stat-label">失败</div>
|
||||||
|
<div class="stat-value">{{ book.failed_count }}</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-card :bordered="false" class="stat-card">
|
||||||
|
<div class="stat-label">完成率</div>
|
||||||
|
<div class="stat-value">{{ completionRate }}%</div>
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div
|
||||||
|
class="progress-fill"
|
||||||
|
:style="{
|
||||||
|
width: completionRate + '%',
|
||||||
|
background: completionRate === 100 ? '#00b42a' : '#165dff',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<!-- 左侧目录 + 右侧 Tabs -->
|
||||||
|
<div class="detail-body">
|
||||||
|
<a-card
|
||||||
|
:bordered="false"
|
||||||
|
class="doc-list-card"
|
||||||
>
|
>
|
||||||
<a-descriptions-item label="zip sha256">
|
<template #title>
|
||||||
<a-typography-text code :ellipsis="{ showTooltip: true }">
|
<div class="card-title">
|
||||||
{{ book.zip_sha256 }}
|
<icon-list />
|
||||||
</a-typography-text>
|
<span>文档目录({{ book.documents.length }})</span>
|
||||||
</a-descriptions-item>
|
</div>
|
||||||
<a-descriptions-item label="zip 大小">
|
</template>
|
||||||
{{ formatBytes(book.zip_size) }}
|
<a-scrollbar style="height: 640px">
|
||||||
</a-descriptions-item>
|
<a-empty
|
||||||
<a-descriptions-item label="类型">
|
v-if="!book.documents.length"
|
||||||
{{ book.type || '-' }}
|
description="暂无文档"
|
||||||
</a-descriptions-item>
|
/>
|
||||||
<a-descriptions-item label="文档数">
|
<ul v-else class="doc-list">
|
||||||
<a-typography-text bold>
|
<li
|
||||||
{{ book.total_count }}
|
v-for="(doc, idx) in book.documents"
|
||||||
</a-typography-text>
|
:key="doc.doc_id"
|
||||||
(成功 {{ book.success_count }},失败 {{ book.failed_count }})
|
:class="['doc-item', { active: selectedDocId === doc.doc_id }]"
|
||||||
</a-descriptions-item>
|
@click="selectedDocId = doc.doc_id"
|
||||||
<a-descriptions-item label="创建">
|
>
|
||||||
{{ book.created_at?.slice(0, 19).replace('T', ' ') }}
|
<div class="doc-item-main">
|
||||||
</a-descriptions-item>
|
<div class="doc-item-title">
|
||||||
<a-descriptions-item label="更新">
|
<span class="doc-num">{{ idx + 1 }}.</span>
|
||||||
{{ book.updated_at?.slice(0, 19).replace('T', ' ') }}
|
<span class="doc-name">{{ doc.original_filename.replace('.docx', '') }}</span>
|
||||||
</a-descriptions-item>
|
</div>
|
||||||
<a-descriptions-item
|
<div class="doc-item-meta">
|
||||||
v-if="book.error_msg"
|
<span class="doc-id">{{ truncate(doc.doc_id, 10) }}</span>
|
||||||
label="错误"
|
</div>
|
||||||
:span="3"
|
</div>
|
||||||
>
|
<a-tag
|
||||||
<a-typography-text type="error">
|
size="small"
|
||||||
{{ book.error_msg }}
|
:color="statusColor[doc.status] || 'gray'"
|
||||||
</a-typography-text>
|
>
|
||||||
</a-descriptions-item>
|
{{ statusText[doc.status] || doc.status }}
|
||||||
</a-descriptions>
|
</a-tag>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</a-scrollbar>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
<a-divider orientation="left">
|
<a-card :bordered="false" class="preview-card">
|
||||||
文档({{ book.documents.length }})
|
<template #title>
|
||||||
</a-divider>
|
<div class="card-title" v-if="selectedDoc">
|
||||||
|
<icon-eye />
|
||||||
|
<span>{{ selectedDoc.original_filename }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-title" v-else>
|
||||||
|
<icon-eye />
|
||||||
|
<span>请选择文档</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="selectedDoc">
|
||||||
|
<a-tabs v-model:active-key="activeTab" type="rounded-card">
|
||||||
|
<a-tab-pane key="word">
|
||||||
|
<template #title>
|
||||||
|
<icon-file /> Word 源文件
|
||||||
|
</template>
|
||||||
|
<div class="preview-pane">
|
||||||
|
<a-empty
|
||||||
|
v-if="!fileUrl"
|
||||||
|
description="文件未上传"
|
||||||
|
/>
|
||||||
|
<div v-else class="file-info">
|
||||||
|
<icon-file :size="48" style="color: #165dff" />
|
||||||
|
<div class="file-text">
|
||||||
|
<div class="file-title">Word 源文件</div>
|
||||||
|
<div class="file-hint">docx 格式不能直接在浏览器中预览</div>
|
||||||
|
</div>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
@click="openInNewTab(fileUrl)"
|
||||||
|
>
|
||||||
|
<icon-download /> 下载 .docx
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
|
||||||
<a-collapse v-if="book.documents.length">
|
<a-tab-pane key="html">
|
||||||
<DocumentItem
|
<template #title>
|
||||||
v-for="doc in book.documents"
|
<icon-code /> HTML 预览
|
||||||
:key="doc.doc_id"
|
</template>
|
||||||
:doc="doc"
|
<div class="preview-pane">
|
||||||
/>
|
<a-spin v-if="fileLoading" tip="加载中..." />
|
||||||
</a-collapse>
|
<a-empty
|
||||||
<a-empty v-else description="教辅中暂无文档" />
|
v-else-if="!fileUrl"
|
||||||
</template>
|
description="HTML 未生成或文档未就绪"
|
||||||
<a-empty v-else description="教辅不存在或已删除" />
|
/>
|
||||||
</a-card>
|
<iframe
|
||||||
|
v-else
|
||||||
|
:src="fileUrl"
|
||||||
|
class="preview-iframe"
|
||||||
|
:title="selectedDoc.original_filename"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
|
||||||
|
<a-tab-pane key="json">
|
||||||
|
<template #title>
|
||||||
|
<icon-storage /> JSON 结构
|
||||||
|
</template>
|
||||||
|
<div class="preview-pane">
|
||||||
|
<a-spin v-if="fileLoading" tip="加载中..." />
|
||||||
|
<a-empty
|
||||||
|
v-else-if="!fileUrl"
|
||||||
|
description="JSON 未生成"
|
||||||
|
/>
|
||||||
|
<div v-else class="file-info">
|
||||||
|
<icon-code :size="48" style="color: #165dff" />
|
||||||
|
<div class="file-text">
|
||||||
|
<div class="file-title">JSON 结构数据</div>
|
||||||
|
<div class="file-hint">点击下载查看完整的题目结构</div>
|
||||||
|
</div>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
@click="openInNewTab(fileUrl)"
|
||||||
|
>
|
||||||
|
<icon-download /> 下载 .json
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</template>
|
||||||
|
<a-empty v-else description="请在左侧选择一份文档" />
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-empty v-else description="教辅不存在或已删除" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.book-detail {
|
.book-detail {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
background: var(--color-fill-1);
|
||||||
|
min-height: 100%;
|
||||||
|
|
||||||
|
.header-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.book-name {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-text {
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-row {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
:deep(.arco-card-body) {
|
||||||
|
padding: 16px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
margin-top: 8px;
|
||||||
|
height: 6px;
|
||||||
|
background: var(--color-fill-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
transition: width 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.stat-ok .stat-value {
|
||||||
|
color: #00b42a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.stat-fail .stat-value {
|
||||||
|
color: #f53f3f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 380px 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-list-card {
|
||||||
|
align-self: start;
|
||||||
|
|
||||||
|
.doc-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 12px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-fill-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: rgb(var(--primary-1));
|
||||||
|
border-color: rgb(var(--primary-3));
|
||||||
|
|
||||||
|
.doc-name {
|
||||||
|
color: rgb(var(--primary-6));
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-item-main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-item-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-num {
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-family: var(--font-family-mono);
|
||||||
|
font-size: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-name {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-item-meta {
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--color-text-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-id {
|
||||||
|
font-family: var(--font-family-mono);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-card {
|
||||||
|
min-height: 700px;
|
||||||
|
|
||||||
|
.preview-pane {
|
||||||
|
min-height: 600px;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
padding: 32px;
|
||||||
|
background: var(--color-fill-1);
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-text {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.file-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-hint {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 600px;
|
||||||
|
border: 1px solid var(--color-border-2);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.meta {
|
|
||||||
margin-top: 16px;
|
@media (max-width: 1024px) {
|
||||||
}
|
.book-detail .detail-body {
|
||||||
:deep(.arco-descriptions-item-label) {
|
grid-template-columns: 1fr;
|
||||||
width: 100px;
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user