diff --git a/Yi.Ai.Vue3/.env.development b/Yi.Ai.Vue3/.env.development index 2403b905..870b6d95 100644 --- a/Yi.Ai.Vue3/.env.development +++ b/Yi.Ai.Vue3/.env.development @@ -15,6 +15,9 @@ VITE_WEB_BASE_API = '/dev-api' VITE_API_URL = http://localhost:19001/api/app #VITE_API_URL=http://data.ccnetcore.com:19001/api/app +# 文件上传接口域名 +VITE_FILE_UPLOAD_API = https://ai.ccnetcore.com + # SSO单点登录url diff --git a/Yi.Ai.Vue3/.env.production b/Yi.Ai.Vue3/.env.production index b2929308..fc6b6f73 100644 --- a/Yi.Ai.Vue3/.env.production +++ b/Yi.Ai.Vue3/.env.production @@ -13,6 +13,9 @@ VITE_WEB_BASE_API = '/prod-api' # 本地接口 VITE_API_URL = http://data.ccnetcore.com:19001/api/app +# 文件上传接口域名 +VITE_FILE_UPLOAD_API = https://ai.ccnetcore.com + # 是否在打包时开启压缩,支持 gzip 和 brotli VITE_BUILD_COMPRESS = gzip diff --git a/Yi.Ai.Vue3/src/api/file/index.ts b/Yi.Ai.Vue3/src/api/file/index.ts new file mode 100644 index 00000000..64b568f7 --- /dev/null +++ b/Yi.Ai.Vue3/src/api/file/index.ts @@ -0,0 +1,34 @@ +import type { UploadFileResponse } from './types'; + +/** + * 上传文件 + * @param file 文件对象 + * @returns 返回文件ID数组 + */ +export async function uploadFile(file: File): Promise { + const formData = new FormData(); + formData.append('file', file); + + const uploadApiUrl = import.meta.env.VITE_FILE_UPLOAD_API; + + const response = await fetch(`${uploadApiUrl}/prod-api/file`, { + method: 'POST', + body: formData, + }); + + if (!response.ok) { + throw new Error('文件上传失败'); + } + + const result = await response.json(); + return result; +} + +/** + * 生成文件URL + * @param fileId 文件ID + * @returns 文件访问URL + */ +export function getFileUrl(fileId: string): string { + return `https://ccnetcore.com/prod-api/file/${fileId}/true`; +} diff --git a/Yi.Ai.Vue3/src/api/file/types.ts b/Yi.Ai.Vue3/src/api/file/types.ts new file mode 100644 index 00000000..8a9d2f9f --- /dev/null +++ b/Yi.Ai.Vue3/src/api/file/types.ts @@ -0,0 +1,3 @@ +export interface UploadFileResponse { + id: string; +} diff --git a/Yi.Ai.Vue3/src/api/index.ts b/Yi.Ai.Vue3/src/api/index.ts index 224904a0..4bfb8e92 100644 --- a/Yi.Ai.Vue3/src/api/index.ts +++ b/Yi.Ai.Vue3/src/api/index.ts @@ -1,6 +1,7 @@ export * from './announcement' export * from './auth'; export * from './chat'; +export * from './file'; export * from './model'; export * from './pay'; export * from './session'; diff --git a/Yi.Ai.Vue3/src/components/FilesSelect/index.vue b/Yi.Ai.Vue3/src/components/FilesSelect/index.vue index 40fd9620..dabba464 100644 --- a/Yi.Ai.Vue3/src/components/FilesSelect/index.vue +++ b/Yi.Ai.Vue3/src/components/FilesSelect/index.vue @@ -1,18 +1,17 @@ @@ -421,6 +501,28 @@ function copy(item: any) { overflow: hidden; border-radius: 12px; } + .user-content-wrapper { + display: flex; + flex-direction: column; + gap: 8px; + } + .user-images { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 4px; + } + .user-image { + max-width: 200px; + max-height: 200px; + border-radius: 8px; + object-fit: cover; + cursor: pointer; + transition: transform 0.2s; + &:hover { + transform: scale(1.05); + } + } .user-content { // 换行 white-space: pre-wrap; diff --git a/Yi.Ai.Vue3/src/stores/modules/files.ts b/Yi.Ai.Vue3/src/stores/modules/files.ts index 3c40bc4b..8f0a5420 100644 --- a/Yi.Ai.Vue3/src/stores/modules/files.ts +++ b/Yi.Ai.Vue3/src/stores/modules/files.ts @@ -2,11 +2,19 @@ import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard'; // 对话聊天的文件上传列表 import { defineStore } from 'pinia'; +export interface FileItem extends FilesCardProps { + file: File; + fileId?: string; // 上传后返回的文件ID + isUploaded?: boolean; // 是否已上传 + uploadProgress?: number; // 上传进度 + base64?: string; // 图片的base64编码 +} + export const useFilesStore = defineStore('files', () => { - const filesList = ref([]); + const filesList = ref([]); // 设置文件列表 - const setFilesList = (list: FilesCardProps & { file: File }[]) => { + const setFilesList = (list: FileItem[]) => { filesList.value = list; }; @@ -15,9 +23,24 @@ export const useFilesStore = defineStore('files', () => { filesList.value.splice(index, 1); }; + // 更新文件上传状态 + const updateFileUploadStatus = (index: number, fileId: string) => { + if (filesList.value[index]) { + filesList.value[index].fileId = fileId; + filesList.value[index].isUploaded = true; + } + }; + + // 清空文件列表 + const clearFilesList = () => { + filesList.value = []; + }; + return { filesList, setFilesList, deleteFileByIndex, + updateFileUploadStatus, + clearFilesList, }; }); diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 5a818758..d7f8e8d4 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -27,6 +27,7 @@ declare module 'vue' { ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] + ElDrawer: typeof import('element-plus/es')['ElDrawer'] ElEmpty: typeof import('element-plus/es')['ElEmpty'] ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] @@ -42,6 +43,8 @@ declare module 'vue' { ElPagination: typeof import('element-plus/es')['ElPagination'] ElProgress: typeof import('element-plus/es')['ElProgress'] ElRow: typeof import('element-plus/es')['ElRow'] + ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] + ElSegmented: typeof import('element-plus/es')['ElSegmented'] ElSelect: typeof import('element-plus/es')['ElSelect'] ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] ElSwitch: typeof import('element-plus/es')['ElSwitch'] diff --git a/Yi.Ai.Vue3/types/import_meta.d.ts b/Yi.Ai.Vue3/types/import_meta.d.ts index d8a60d41..c98d612e 100644 --- a/Yi.Ai.Vue3/types/import_meta.d.ts +++ b/Yi.Ai.Vue3/types/import_meta.d.ts @@ -6,6 +6,7 @@ interface ImportMetaEnv { readonly VITE_WEB_ENV: string; readonly VITE_WEB_BASE_API: string; readonly VITE_API_URL: string; + readonly VITE_FILE_UPLOAD_API: string; readonly VITE_BUILD_COMPRESS: string; readonly VITE_SSO_SEVER_URL: string; readonly VITE_APP_VERSION: string;