From 07b75dc03e9f0a92ce2f83687acc15f6a3fbb4b3 Mon Sep 17 00:00:00 2001 From: Seven Date: Mon, 19 Jan 2026 19:40:02 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=96=B0=E9=97=BB?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85=E6=8B=AC?= =?UTF-8?q?=E6=96=B0=E9=97=BB=E5=88=97=E8=A1=A8=E5=92=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=96=B0=E9=97=BB=E8=A1=A8=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/upload/index.vue | 41 ++++----- src/locales/langs/en-us.ts | 3 +- src/locales/langs/zh-cn.ts | 3 +- src/router/elegant/imports.ts | 1 + src/router/elegant/routes.ts | 9 ++ src/router/elegant/transform.ts | 1 + src/typings/elegant-router.d.ts | 3 + src/typings/treaty.d.ts | 4 + src/utils/aws/s3.ts | 7 +- src/views/news/components/add.vue | 142 ++++++++++++++++++++++++++++++ src/views/news/index.vue | 95 ++++++++++++++++++++ 11 files changed, 279 insertions(+), 30 deletions(-) create mode 100644 src/typings/treaty.d.ts create mode 100644 src/views/news/components/add.vue create mode 100644 src/views/news/index.vue diff --git a/src/components/upload/index.vue b/src/components/upload/index.vue index 1f5d142..1fa1f1a 100644 --- a/src/components/upload/index.vue +++ b/src/components/upload/index.vue @@ -36,27 +36,16 @@ const fileList = ref([]); const loading = ref(false); async function initFileList() { - const fileIds = props.modelValue.filter(id => id && id.trim() !== ''); + const fileIds = props.modelValue.filter(url => url && url.trim() !== ''); if (!props.modelValue || fileIds.length === 0) { fileList.value = []; - return; - } - loading.value = true; - try { - const { data } = await safeClient(client.api.file_storage.access_urls.post({ fileIds })); - - fileList.value = - data.value?.map(item => ({ - id: item.id, - name: item.fileName || item.id, - status: 'finished' as const, - url: item.url, - file: null as any - })) || []; - } catch (error) { - window.$message?.error('加载文件列表失败'); - } finally { - loading.value = false; + } else { + fileList.value = props.modelValue.map(url => ({ + id: url, + name: url.split('/').pop() || 'file', + url, + status: 'finished' + })); } } @@ -96,19 +85,19 @@ async function handleCustomRequest({ file, onProgress, onFinish, onError }: Uplo ...props.fetchOptions } as UploadFetchOptions; - const fileId = await uploadToS3(file.file as File, { + const res = await uploadToS3(file.file as File, { fetchOptions: options, onProgress: percent => { onProgress({ percent }); } }); // 直接修改 file 对象的 id,NaiveUI 会自动同步到 fileList - file.id = fileId; + file.id = res.fileId; file.status = 'finished'; - file.url = fileId; + file.url = res.publicUrl; - const newFileIds = [...props.modelValue, fileId].filter(Boolean); - emit('update:modelValue', newFileIds); + const values = [...props.modelValue, res.publicUrl].filter(Boolean); + emit('update:modelValue', values); onFinish(); window.$message?.success(`文件 ${file.name} 上传成功`); @@ -119,8 +108,8 @@ async function handleCustomRequest({ file, onProgress, onFinish, onError }: Uplo function handleRemove({ file }: { file: UploadFileInfo }) { // 只删除上传成功的文件(有有效 id 的文件) - if (file.id && typeof file.id === 'string' && props.modelValue.includes(file.id)) { - const newFileIds = props.modelValue.filter(id => id !== file.id); + if (file.url && typeof file.url === 'string' && props.modelValue.includes(file.url)) { + const newFileIds = props.modelValue.filter(url => url !== file.url); emit('update:modelValue', newFileIds); } return true; diff --git a/src/locales/langs/en-us.ts b/src/locales/langs/en-us.ts index a1f3420..1b63e95 100644 --- a/src/locales/langs/en-us.ts +++ b/src/locales/langs/en-us.ts @@ -230,7 +230,8 @@ const local: App.I18n.Schema = { 'iframe-page': 'Iframe', home: 'Home', product: 'Product', - user: ' User' + user: ' User', + news: 'News' }, page: { login: { diff --git a/src/locales/langs/zh-cn.ts b/src/locales/langs/zh-cn.ts index 1892b64..b9a4232 100644 --- a/src/locales/langs/zh-cn.ts +++ b/src/locales/langs/zh-cn.ts @@ -226,7 +226,8 @@ const local: App.I18n.Schema = { 'iframe-page': '外链页面', home: '首页', product: '产品管理', - user: '用户管理' + user: '用户管理', + news: '新闻管理' }, page: { login: { diff --git a/src/router/elegant/imports.ts b/src/router/elegant/imports.ts index 6831b64..3454b50 100644 --- a/src/router/elegant/imports.ts +++ b/src/router/elegant/imports.ts @@ -21,6 +21,7 @@ export const views: Record Promise import("@/views/_builtin/iframe-page/[url].vue"), login: () => import("@/views/_builtin/login/index.vue"), home: () => import("@/views/home/index.vue"), + news: () => import("@/views/news/index.vue"), product: () => import("@/views/product/index.vue"), user: () => import("@/views/user/index.vue"), }; diff --git a/src/router/elegant/routes.ts b/src/router/elegant/routes.ts index 3887a35..94a4b37 100644 --- a/src/router/elegant/routes.ts +++ b/src/router/elegant/routes.ts @@ -73,6 +73,15 @@ export const generatedRoutes: GeneratedRoute[] = [ hideInMenu: true } }, + { + name: 'news', + path: '/news', + component: 'layout.base$view.news', + meta: { + title: 'news', + i18nKey: 'route.news' + } + }, { name: 'product', path: '/product', diff --git a/src/router/elegant/transform.ts b/src/router/elegant/transform.ts index 8a1453a..97bb034 100644 --- a/src/router/elegant/transform.ts +++ b/src/router/elegant/transform.ts @@ -169,6 +169,7 @@ const routeMap: RouteMap = { "home": "/home", "iframe-page": "/iframe-page/:url", "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?", + "news": "/news", "product": "/product", "user": "/user" }; diff --git a/src/typings/elegant-router.d.ts b/src/typings/elegant-router.d.ts index 4eab429..4c2911d 100644 --- a/src/typings/elegant-router.d.ts +++ b/src/typings/elegant-router.d.ts @@ -23,6 +23,7 @@ declare module "@elegant-router/types" { "home": "/home"; "iframe-page": "/iframe-page/:url"; "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"; + "news": "/news"; "product": "/product"; "user": "/user"; }; @@ -62,6 +63,7 @@ declare module "@elegant-router/types" { | "home" | "iframe-page" | "login" + | "news" | "product" | "user" >; @@ -86,6 +88,7 @@ declare module "@elegant-router/types" { | "iframe-page" | "login" | "home" + | "news" | "product" | "user" >; diff --git a/src/typings/treaty.d.ts b/src/typings/treaty.d.ts new file mode 100644 index 0000000..0ca9145 --- /dev/null +++ b/src/typings/treaty.d.ts @@ -0,0 +1,4 @@ +declare namespace Treaty { + type Query = T extends (...args: any[]) => any ? NonNullable[0]>['query']> : never; + type Body = T extends (...args: any[]) => any ? NonNullable[0]> : never; +} diff --git a/src/utils/aws/s3.ts b/src/utils/aws/s3.ts index d8ff7a3..ba556b7 100644 --- a/src/utils/aws/s3.ts +++ b/src/utils/aws/s3.ts @@ -26,7 +26,7 @@ export async function uploadToS3(file: File, options: UploadOptions) { const { fileId, uploadUrl, method, headers, publicUrl } = toRefs(data.value); // 2. 上传文件到 S3 - return new Promise((resolve, reject) => { + return new Promise<{ fileId: string; publicUrl: string }>((resolve, reject) => { const xhr = new XMLHttpRequest(); // 监听上传进度 @@ -40,7 +40,10 @@ export async function uploadToS3(file: File, options: UploadOptions) { // 上传成功 xhr.addEventListener('load', () => { if (xhr.status >= 200 && xhr.status < 300) { - resolve(fileId.value); + resolve({ + fileId: fileId.value, + publicUrl: publicUrl!.value! + }); } else { reject(new Error(`上传失败: ${xhr.status} ${xhr.statusText}`)); } diff --git a/src/views/news/components/add.vue b/src/views/news/components/add.vue new file mode 100644 index 0000000..fe60e12 --- /dev/null +++ b/src/views/news/components/add.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/src/views/news/index.vue b/src/views/news/index.vue new file mode 100644 index 0000000..63138ba --- /dev/null +++ b/src/views/news/index.vue @@ -0,0 +1,95 @@ + + + + +