diff --git a/app/components/DocumentList.vue b/app/components/DocumentList.vue
index 0061070..22bb95a 100644
--- a/app/components/DocumentList.vue
+++ b/app/components/DocumentList.vue
@@ -4,6 +4,7 @@
v-for="(doc, index) in documents"
:key="index"
class="document-card"
+ @click="handleClick(doc.fileId)"
>
{{ doc.title }}
@@ -36,6 +37,15 @@
},
});
+ const localePath = useLocalePath();
+
+ const handleClick = (id: string) => {
+ // 获取路由参数
+ if (id) {
+ navigateTo(localePath(`/download/${id}`));
+ }
+ };
+
const handleDownload = async (fileName: string, fileUrl: string) => {
const response = await fetch(fileUrl);
const blob = await response.blob();
@@ -59,6 +69,16 @@
width: 100%;
}
+ .document-card {
+ cursor: pointer;
+ transition: all 0.3s ease;
+ }
+
+ .document-card:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+ }
+
.document-meta {
font-size: 0.8rem;
color: var(--el-text-color-secondary);
diff --git a/app/models/mappers/documentMapper.ts b/app/models/mappers/documentMapper.ts
index cf9dec4..d5c855d 100644
--- a/app/models/mappers/documentMapper.ts
+++ b/app/models/mappers/documentMapper.ts
@@ -23,6 +23,7 @@ export function toProductDocumentView(
return {
id: raw.id,
+ fileId: fileId,
filename: file.filename_download,
title: trans.title,
url: url,
@@ -68,6 +69,7 @@ export function toDocumentListView(raw: ProductDocument): DocumentListView {
return {
id: raw.id,
+ fileId: fileId,
filename: file.filename_download,
title: trans.title,
url: url,
diff --git a/app/models/views/DocumentListView.ts b/app/models/views/DocumentListView.ts
index 4fd3bae..5b66106 100644
--- a/app/models/views/DocumentListView.ts
+++ b/app/models/views/DocumentListView.ts
@@ -25,6 +25,9 @@ export interface DocumentListView {
/** 唯一标识符 **/
id: number;
+ /** 文件UUID **/
+ fileId: string;
+
/** 文件名 **/
filename: string;
diff --git a/app/models/views/ProductDocumentView.ts b/app/models/views/ProductDocumentView.ts
index dae8aa3..36625d2 100644
--- a/app/models/views/ProductDocumentView.ts
+++ b/app/models/views/ProductDocumentView.ts
@@ -6,6 +6,9 @@ export interface ProductDocumentView {
/** 唯一标识符 **/
id: number;
+ /** 文件UUID **/
+ fileId: string;
+
/** 文件名 **/
filename: string;
diff --git a/app/pages/download/[id].vue b/app/pages/download/[id].vue
new file mode 100644
index 0000000..4183861
--- /dev/null
+++ b/app/pages/download/[id].vue
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
- 类型:
+ - {{ file.type }}
+
+
+
- 大小:
+ - {{ formatFileSize(file.filesize) }}
+
+
+
- 上传时间:
+ -
+ {{ new Date(file.uploaded_on).toLocaleDateString() }}
+
+
+
+
+
+ 下载
+ 预览
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/i18n/locales/en.json b/i18n/locales/en.json
index 336a26f..462e8e1 100644
--- a/i18n/locales/en.json
+++ b/i18n/locales/en.json
@@ -34,6 +34,7 @@
"support": "Support",
"about-us": "About Us",
"contact-info": "Contact Info",
+ "downloads": "Downloads",
"faq": "FAQ",
"documents": "Documents",
"calculator": "Calculator"
diff --git a/i18n/locales/zh.json b/i18n/locales/zh.json
index b7f1163..9fc3894 100644
--- a/i18n/locales/zh.json
+++ b/i18n/locales/zh.json
@@ -34,6 +34,7 @@
"support": "服务支持",
"about-us": "关于我们",
"contact-info": "联系信息",
+ "downloads": "文件下载",
"faq": "常见问题",
"documents": "文档资料",
"calculator": "纸管计算工具"
diff --git a/server/api/download/[id].ts b/server/api/download/[id].ts
new file mode 100644
index 0000000..02e7a62
--- /dev/null
+++ b/server/api/download/[id].ts
@@ -0,0 +1,22 @@
+import { getFileMeta } from '../../utils/file';
+
+export default defineEventHandler(async (event) => {
+ const id = getRouterParam(event, 'id');
+ if (!id) {
+ throw createError({ statusCode: 400, message: '缺少文件ID' });
+ }
+ const file = await getFileMeta(id);
+ if (!file) {
+ throw createError({ statusCode: 404, message: '文件不存在' });
+ }
+
+ const res = await $fetch
(file.url, {
+ responseType: 'arrayBuffer',
+ });
+ return new Response(res, {
+ headers: {
+ 'Content-Disposition': `attachment; filename="${encodeURIComponent(file.filename_download)}"`,
+ 'Content-Type': file.type,
+ },
+ });
+});
diff --git a/server/api/file/[id].ts b/server/api/file/[id].ts
new file mode 100644
index 0000000..c504a88
--- /dev/null
+++ b/server/api/file/[id].ts
@@ -0,0 +1,24 @@
+import { getFileMeta } from '../../utils/file';
+
+/**
+ * 用于处理文件相关的API请求
+ * 返回指定ID的文件信息
+ */
+export default defineEventHandler(async (event) => {
+ const id = getRouterParam(event, 'id');
+ if (!id)
+ throw createError({
+ statusCode: 400,
+ statusMessage: 'File ID is required',
+ });
+
+ const file = await getFileMeta(id);
+ if (!file) {
+ throw createError({
+ statusCode: 404,
+ statusMessage: 'File not found',
+ });
+ }
+
+ return file;
+});
diff --git a/server/utils/file.ts b/server/utils/file.ts
new file mode 100644
index 0000000..5d4ae83
--- /dev/null
+++ b/server/utils/file.ts
@@ -0,0 +1,53 @@
+/**
+ * 判断文件是否支持预览
+ */
+export function isPreviewable(mime: string | null | undefined): boolean {
+ if (!mime || mime === undefined) return false;
+ return (
+ mime.startsWith('image/') ||
+ mime.startsWith('video/') ||
+ mime === 'application/pdf' ||
+ mime.startsWith('text/')
+ );
+}
+
+/**
+ * 从 Directus 获取文件元信息
+ */
+export async function getFileMeta(id: string): Promise {
+ const runtimeConfig = useRuntimeConfig();
+ const baseUrl = runtimeConfig.public.directus.url;
+ const access_token = runtimeConfig.public.directus.token;
+
+ try {
+ const response = await $fetch<{ data: DirectusFile }>(
+ `${baseUrl}/files/${id}`,
+ {
+ headers: {
+ Authorization: access_token ? `Bearer ${access_token}` : '',
+ },
+ }
+ );
+
+ const file = response.data;
+ if (!file) return null;
+
+ return {
+ id: file.id,
+ title: file.filename_disk ?? '',
+ filename_download: file.filename_download ?? '',
+ type: file.type ?? '',
+ filesize: Number(file.filesize),
+ width: file.width ?? undefined,
+ height: file.height ?? undefined,
+ uploaded_on: file.uploaded_on ?? undefined,
+ url: `${baseUrl}/assets/${file.id}`,
+ previewable: isPreviewable(file.type),
+ };
+ } catch (error) {
+ if (error instanceof Error) {
+ console.error('Error fetching file metadata:', error.message);
+ }
+ return null;
+ }
+}
diff --git a/app/types/common.ts b/shared/types/common.ts
similarity index 100%
rename from app/types/common.ts
rename to shared/types/common.ts
diff --git a/app/types/directus/index.ts b/shared/types/directus/index.ts
similarity index 100%
rename from app/types/directus/index.ts
rename to shared/types/directus/index.ts
diff --git a/app/types/directus/my-schema.ts b/shared/types/directus/my-schema.ts
similarity index 100%
rename from app/types/directus/my-schema.ts
rename to shared/types/directus/my-schema.ts
diff --git a/shared/types/file.ts b/shared/types/file.ts
new file mode 100644
index 0000000..4ed289d
--- /dev/null
+++ b/shared/types/file.ts
@@ -0,0 +1,12 @@
+export interface FileMeta {
+ id: string;
+ title: string;
+ filename_download: string;
+ type: string;
+ filesize: number;
+ width?: number;
+ height?: number;
+ uploaded_on?: string;
+ url: string;
+ previewable: boolean;
+}
diff --git a/shared/types/index.ts b/shared/types/index.ts
new file mode 100644
index 0000000..f0ea818
--- /dev/null
+++ b/shared/types/index.ts
@@ -0,0 +1,2 @@
+export * from './directus';
+export * from './meilisearch';
diff --git a/app/types/meilisearch/index.ts b/shared/types/meilisearch/index.ts
similarity index 100%
rename from app/types/meilisearch/index.ts
rename to shared/types/meilisearch/index.ts
diff --git a/app/types/meilisearch/meili-index.ts b/shared/types/meilisearch/meili-index.ts
similarity index 100%
rename from app/types/meilisearch/meili-index.ts
rename to shared/types/meilisearch/meili-index.ts
diff --git a/app/types/meilisearch/search-result.ts b/shared/types/meilisearch/search-result.ts
similarity index 100%
rename from app/types/meilisearch/search-result.ts
rename to shared/types/meilisearch/search-result.ts