diff --git a/Yi.Ai.Vue3/src/api/chat/index.ts b/Yi.Ai.Vue3/src/api/chat/index.ts index 74db9744..aa9cdbba 100644 --- a/Yi.Ai.Vue3/src/api/chat/index.ts +++ b/Yi.Ai.Vue3/src/api/chat/index.ts @@ -1,7 +1,7 @@ import type { ChatMessageVo, GetChatListParams, SendDTO } from './types'; import { get, post } from '@/utils/request'; -// 发送消息 +// 发送消息(旧接口) export function send(data: SendDTO) { const url = data.sessionId !== 'not_login' ? `/ai-chat/send/?sessionId=${data.sessionId}` @@ -9,6 +9,12 @@ export function send(data: SendDTO) { return post(url, data); } +// 统一发送消息接口,支持4种API类型 +export function unifiedSend(data: any, apiType: string, modelId: string, sessionId: string) { + const url = `/ai-chat/unified/send?apiType=${apiType}&modelId=${modelId}&sessionId=${sessionId}`; + return post(url, data); +} + // 新增对应会话聊天记录 export function addChat(data: ChatMessageVo) { return post('/system/message', data).json(); diff --git a/Yi.Ai.Vue3/src/api/model/types.ts b/Yi.Ai.Vue3/src/api/model/types.ts index 699f06d8..bece537f 100644 --- a/Yi.Ai.Vue3/src/api/model/types.ts +++ b/Yi.Ai.Vue3/src/api/model/types.ts @@ -14,6 +14,7 @@ export interface GetSessionListVO { modelId?: string; isFree?: boolean; // 是否为免费模型 isPremiumPackage?: boolean; // 是否为尊享套餐模型 + modelApiType?: string; // API 格式类型: Completions | Messages | Responses | GenerateContent } // 模型类型枚举 diff --git a/Yi.Ai.Vue3/src/components/ModelSelect/index.vue b/Yi.Ai.Vue3/src/components/ModelSelect/index.vue index d9691670..0f2139ca 100644 --- a/Yi.Ai.Vue3/src/components/ModelSelect/index.vue +++ b/Yi.Ai.Vue3/src/components/ModelSelect/index.vue @@ -29,8 +29,31 @@ onMounted(async () => { const currentModelName = computed( () => modelStore.currentModelInfo && modelStore.currentModelInfo.modelName, ); -const popoverList = computed(() => modelStore.modelList); +// API 类型映射 +const apiTypeNameMap: Record = { + Completions: 'OpenAI Chat Completion', + Responses: 'OpenAI Responses API', + Messages: 'Anthropic Claude API', + GenerateContent: 'Google Gemini API', +}; + +// 按 API 类型分组的模型列表 +const groupedModelList = computed(() => { + const groups: Record = {}; + + modelStore.modelList.forEach((model) => { + const apiType = model.modelApiType || 'Completions'; + if (!groups[apiType]) { + groups[apiType] = []; + } + groups[apiType].push(model); + }); + + return groups; +}); + +console.log('groupedModelList---', groupedModelList.value); /* 弹出面板 开始 */ const popoverStyle = ref({ width: '200px', @@ -46,7 +69,8 @@ const popoverRef = ref(); // 显示 async function showPopover() { // 获取最新的模型列表 - await modelStore.requestModelList(); + + return await modelStore.requestModelList(); } // 点击 @@ -176,39 +200,48 @@ function getWrapperClass(item: GetSessionListVO) {
-
- - -
- {{ item.remark }} -
-
+ +
@@ -229,6 +262,20 @@ function getWrapperClass(item: GetSessionListVO) { height: 300px; overflow: hidden auto; + // 分组标题样式 + .group-title { + font-size: 12px; + font-weight: 600; + color: #409eff; + padding: 8px 8px 4px 8px; + margin-top: 4px; + border-bottom: 1px solid #e4e7ed; + position: sticky; + top: 0; + background: var(--el-bg-color, #fff); + z-index: 1; + } + :deep(.popover-trigger-item-text) { width: 100%; } diff --git a/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue b/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue index 05ec0f7c..87ab0151 100644 --- a/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue +++ b/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue @@ -11,9 +11,10 @@ import { useHookFetch } from 'hook-fetch/vue'; import { computed, nextTick, ref, watch } from 'vue'; import { Sender } from 'vue-element-plus-x'; import { useRoute } from 'vue-router'; -import { send } from '@/api'; +import { unifiedSend } from '@/api'; import ModelSelect from '@/components/ModelSelect/index.vue'; import Collapse from '@/layouts/components/Header/components/Collapse.vue'; +import { parseStreamChunk, convertToApiFormat, type UnifiedMessage } from '@/utils/apiFormatConverter'; import CreateChat from '@/layouts/components/Header/components/CreateChat.vue'; import TitleEditing from '@/layouts/components/Header/components/TitleEditing.vue'; @@ -63,8 +64,17 @@ const bubbleItems = ref([]); const bubbleListRef = ref(null); const isSending = ref(false); +// 记录当前请求使用的 API 格式类型,用于正确解析响应 +const currentRequestApiType = ref(''); + +// 创建统一发送请求的包装函数 +const unifiedSendWrapper = (params: any) => { + const { data, apiType, modelId, sessionId } = params; + return unifiedSend(data, apiType, modelId, sessionId); +}; + const { stream, loading: isLoading, cancel } = useHookFetch({ - request: send, + request: unifiedSendWrapper, onError: async (error) => { isLoading.value = false; @@ -133,51 +143,49 @@ watch( */ function handleDataChunk(chunk: AnyObject) { try { - // 安全获取 delta 和 content - const delta = chunk.choices?.[0]?.delta; - const reasoningChunk = delta?.reasoning_content; - const parsedChunk = delta?.content; + // 使用统一的解析器,根据当前请求的 API 格式类型解析响应 + const parsed = parseStreamChunk(chunk, currentRequestApiType.value || 'Completions'); + console.log('✅ [解析结果]:', parsed); - // usage 处理(可以移动到 startSSE 里也可以写这里) - if (chunk.usage) { - const { prompt_tokens, completion_tokens, total_tokens } = chunk.usage; - const latest = bubbleItems.value[bubbleItems.value.length - 1]; + const latest = bubbleItems.value[bubbleItems.value.length - 1]; + + // 处理 token 使用情况 + if (parsed.usage) { latest.tokenUsage = { - prompt: prompt_tokens, - completion: completion_tokens, - total: total_tokens, + prompt: parsed.usage.prompt_tokens || 0, + completion: parsed.usage.completion_tokens || 0, + total: parsed.usage.total_tokens || 0, }; } - if (reasoningChunk) { - const latest = bubbleItems.value[bubbleItems.value.length - 1]; + // 处理推理内容 + if (parsed.reasoning_content) { latest.thinkingStatus = 'thinking'; latest.loading = true; latest.thinlCollapse = true; - latest.reasoning_content += reasoningChunk; + latest.reasoning_content += parsed.reasoning_content; } - if (parsedChunk) { - const thinkStart = parsedChunk.includes(''); - const thinkEnd = parsedChunk.includes(''); + // 处理普通内容 + if (parsed.content) { + const thinkStart = parsed.content.includes(''); + const thinkEnd = parsed.content.includes(''); if (thinkStart) isThinking = true; if (thinkEnd) isThinking = false; - const latest = bubbleItems.value[bubbleItems.value.length - 1]; - if (isThinking) { latest.thinkingStatus = 'thinking'; latest.loading = true; latest.thinlCollapse = true; - latest.reasoning_content += parsedChunk.replace('', '').replace('', ''); + latest.reasoning_content += parsed.content.replace('', '').replace('', ''); } else { latest.thinkingStatus = 'end'; latest.loading = false; - latest.content += parsedChunk; + latest.content += parsed.content; } } } @@ -212,6 +220,15 @@ async function startSSE(chatContent: string) { isSending.value = true; + // 记录当前请求使用的 API 格式类型,用于后续正确解析响应 + currentRequestApiType.value = modelStore.currentModelInfo.modelApiType || 'Completions'; + console.log('🚀 [发送请求] 当前模型信息:', { + modelId: modelStore.currentModelInfo.modelId, + modelName: modelStore.currentModelInfo.modelName, + modelApiType: modelStore.currentModelInfo.modelApiType, + currentRequestApiType: currentRequestApiType.value, + }); + try { // 清空输入框 inputValue.value = ''; @@ -309,14 +326,33 @@ async function startSSE(chatContent: string) { return baseMessage; }); - // 使用 for-await 处理流式响应 + // 获取当前模型的 API 类型 + const apiType = modelStore.currentModelInfo.modelApiType || 'Completions'; + console.log('📤 [转换请求] API 类型:', apiType); + console.log('📤 [转换前] 消息内容:', messagesContent); + + // 根据 API 类型转换请求格式 + const convertedRequest = convertToApiFormat( + messagesContent as UnifiedMessage[], + apiType, + modelStore.currentModelInfo.modelId ?? '', + true, + ); + + console.log('📤 [转换后] 请求体:', convertedRequest); + + // 使用新的统一接口发送请求 + const sessionId = route.params?.id !== 'not_login' ? String(route.params?.id) : 'not_login'; + const modelId = modelStore.currentModelInfo.modelId ?? ''; + for await (const chunk of stream({ - messages: messagesContent, - sessionId: route.params?.id !== 'not_login' ? String(route.params?.id) : 'not_login', - stream: true, - userId: userStore.userInfo?.userId, - model: modelStore.currentModelInfo.modelId ?? '', + data: convertedRequest, + apiType, + modelId, + sessionId, })) { + console.log('📦 [接收响应] 原始数据块:', chunk.result); + console.log('🔧 [解析响应] 使用格式:', currentRequestApiType.value); handleDataChunk(chunk.result as AnyObject); } } diff --git a/Yi.Ai.Vue3/src/utils/apiFormatConverter.ts b/Yi.Ai.Vue3/src/utils/apiFormatConverter.ts new file mode 100644 index 00000000..868ea190 --- /dev/null +++ b/Yi.Ai.Vue3/src/utils/apiFormatConverter.ts @@ -0,0 +1,492 @@ +/** + * API 格式转换工具 + * 支持 OpenAI Completions、OpenAI Responses、Anthropic Claude、Google Gemini 四种格式的相互转换 + */ + +// API 格式类型枚举 +export enum ApiFormatType { + Completions = 'Completions', // OpenAI Chat Completion + Responses = 'Responses', // OpenAI Responses API + Messages = 'Messages', // Anthropic Claude API + GenerateContent = 'GenerateContent', // Google Gemini API +} + +// 统一的消息格式(内部使用) +export interface UnifiedMessage { + role: 'system' | 'user' | 'assistant' | 'model'; + content: string | MessageContent[]; +} + +// 消息内容块 +export interface MessageContent { + type: 'text' | 'image_url'; + text?: string; + image_url?: { + url: string; + name?: string; + }; +} + +// OpenAI Completions 格式 +export interface CompletionsMessage { + role: 'system' | 'user' | 'assistant'; + content: string | MessageContent[]; +} + +export interface CompletionsRequest { + model: string; + messages: CompletionsMessage[]; + stream?: boolean; + max_tokens?: number; +} + +// OpenAI Responses 格式 +export interface ResponsesMessage { + role: 'system' | 'user' | 'assistant'; + content: string | MessageContent[]; +} + +export interface ResponsesRequest { + model: string; + input: ResponsesMessage[]; + stream?: boolean; +} + +// Anthropic Claude 格式 +export interface ClaudeMessage { + role: 'user' | 'assistant'; + content: string | ClaudeContent[]; +} + +export interface ClaudeContent { + type: 'text' | 'image'; + text?: string; + source?: { + type: 'base64'; + media_type: string; + data: string; + }; +} + +export interface ClaudeRequest { + model: string; + messages: ClaudeMessage[]; + max_tokens: number; + stream?: boolean; + system?: string; +} + +// Google Gemini 格式 +export interface GeminiPart { + text?: string; + inlineData?: { + mimeType: string; + data: string; + }; +} + +export interface GeminiContent { + role: 'user' | 'model'; + parts: GeminiPart[]; +} + +export interface GeminiRequest { + contents: GeminiContent[]; + generationConfig?: { + maxOutputTokens?: number; + }; +} + +/** + * 将统一格式的消息转换为 OpenAI Completions 格式 + */ +export function toCompletionsFormat(messages: UnifiedMessage[]): CompletionsMessage[] { + return messages.map((msg) => { + const role = msg.role === 'model' ? 'assistant' : msg.role; + return { + role: role as 'system' | 'user' | 'assistant', + content: msg.content, + }; + }); +} + +/** + * 将统一格式的消息转换为 OpenAI Responses 格式 + */ +export function toResponsesFormat(messages: UnifiedMessage[]): ResponsesMessage[] { + return messages.map((msg) => { + const role = msg.role === 'model' ? 'assistant' : msg.role; + return { + role: role as 'system' | 'user' | 'assistant', + content: msg.content, + }; + }); +} + +/** + * 将统一格式的消息转换为 Anthropic Claude 格式 + */ +export function toClaudeFormat(messages: UnifiedMessage[]): { messages: ClaudeMessage[]; system?: string } { + let systemPrompt: string | undefined; + const claudeMessages: ClaudeMessage[] = []; + + for (const msg of messages) { + // Claude 的 system 消息需要单独提取 + if (msg.role === 'system') { + systemPrompt = typeof msg.content === 'string' ? msg.content : msg.content.map(c => c.text || '').join(''); + continue; + } + + const role = msg.role === 'model' ? 'assistant' : msg.role; + + // 处理内容格式 + let content: string | ClaudeContent[]; + if (typeof msg.content === 'string') { + content = msg.content; + } + else { + content = msg.content.map((item) => { + if (item.type === 'text') { + return { type: 'text', text: item.text || '' }; + } + else if (item.type === 'image_url' && item.image_url) { + // 将 base64 图片转换为 Claude 格式 + const base64Data = item.image_url.url.replace(/^data:image\/\w+;base64,/, ''); + const mimeType = item.image_url.url.match(/^data:(image\/\w+);base64,/)?.[1] || 'image/jpeg'; + return { + type: 'image', + source: { + type: 'base64', + media_type: mimeType, + data: base64Data, + }, + }; + } + return { type: 'text', text: '' }; + }); + } + + claudeMessages.push({ + role: role as 'user' | 'assistant', + content, + }); + } + + return { messages: claudeMessages, system: systemPrompt }; +} + +/** + * 将统一格式的消息转换为 Google Gemini 格式 + */ +export function toGeminiFormat(messages: UnifiedMessage[]): GeminiContent[] { + const geminiContents: GeminiContent[] = []; + + for (const msg of messages) { + // Gemini 不支持 system 角色,跳过或转换为 user + if (msg.role === 'system') { + continue; + } + + const role = msg.role === 'assistant' ? 'model' : 'user'; + const parts: GeminiPart[] = []; + + if (typeof msg.content === 'string') { + parts.push({ text: msg.content }); + } + else { + for (const item of msg.content) { + if (item.type === 'text') { + parts.push({ text: item.text || '' }); + } + else if (item.type === 'image_url' && item.image_url) { + // 将 base64 图片转换为 Gemini 格式 + const base64Data = item.image_url.url.replace(/^data:image\/\w+;base64,/, ''); + const mimeType = item.image_url.url.match(/^data:(image\/\w+);base64,/)?.[1] || 'image/jpeg'; + parts.push({ + inlineData: { + mimeType, + data: base64Data, + }, + }); + } + } + } + + geminiContents.push({ role, parts }); + } + + return geminiContents; +} + +/** + * 统一的响应数据接口(用于流式和非流式) + */ +export interface UnifiedStreamChunk { + content?: string; + reasoning_content?: string; + usage?: { + prompt_tokens?: number; + completion_tokens?: number; + total_tokens?: number; + }; + finish_reason?: string; +} + +/** + * 解析 OpenAI Completions 格式的流式响应 + */ +export function parseCompletionsStreamChunk(chunk: any): UnifiedStreamChunk { + const delta = chunk.choices?.[0]?.delta; + const result: UnifiedStreamChunk = {}; + + if (delta?.reasoning_content) { + result.reasoning_content = delta.reasoning_content; + } + + if (delta?.content) { + result.content = delta.content; + } + + if (chunk.usage) { + result.usage = { + prompt_tokens: chunk.usage.prompt_tokens, + completion_tokens: chunk.usage.completion_tokens, + total_tokens: chunk.usage.total_tokens, + }; + } + + if (chunk.choices?.[0]?.finish_reason) { + result.finish_reason = chunk.choices[0].finish_reason; + } + + return result; +} + +/** + * 解析 OpenAI Responses 格式的流式响应 + * 注意:Responses API 需要跟踪当前输出项的类型来区分 reasoning 和 message + */ +export function parseResponsesStreamChunk(chunk: any): UnifiedStreamChunk { + const result: UnifiedStreamChunk = {}; + + // Responses API 使用事件驱动的流式响应 + + // 处理 response.output_item.added 事件 - 记录输出项类型 + // 这个事件告诉我们接下来的内容是 reasoning 还是 message + if (chunk.type === 'response.output_item.added' && chunk.item) { + // 暂时不返回内容,只是标记 + // 实际内容会在后续的 delta 事件中 + } + + // 处理 response.output_text.delta 事件 - 包含文本增量 + if (chunk.type === 'response.output_text.delta' && chunk.delta) { + // 根据 output_index 判断: + // output_index 0 通常是 reasoning(思考链) + // output_index 1 通常是 message(正常回复) + // 但更准确的方式是检查之前的 output_item.added 事件 + // 这里我们简化处理:所有 delta 都作为正常内容 + result.content = chunk.delta; + } + + // 处理 response.completed 事件 - 包含 usage 信息 + if (chunk.type === 'response.completed' && chunk.response?.usage) { + result.usage = { + prompt_tokens: chunk.response.usage.input_tokens, + completion_tokens: chunk.response.usage.output_tokens, + total_tokens: chunk.response.usage.total_tokens, + }; + } + + return result; +} + +/** + * 解析 Anthropic Claude 格式的流式响应 + */ +export function parseClaudeStreamChunk(chunk: any): UnifiedStreamChunk { + const result: UnifiedStreamChunk = {}; + + // Claude 流式响应格式 - 处理 content_block_delta 事件 + if (chunk.type === 'content_block_delta') { + // text_delta 类型包含文本内容 + if (chunk.delta?.type === 'text_delta' && chunk.delta?.text) { + result.content = chunk.delta.text; + } + } + + // 处理 message_delta 事件 - 包含 usage 和 stop_reason + if (chunk.type === 'message_delta') { + if (chunk.usage) { + result.usage = { + prompt_tokens: chunk.usage.input_tokens, + completion_tokens: chunk.usage.output_tokens, + total_tokens: (chunk.usage.input_tokens || 0) + (chunk.usage.output_tokens || 0), + }; + } + if (chunk.delta?.stop_reason) { + result.finish_reason = chunk.delta.stop_reason; + } + } + + return result; +} + +/** + * 解析 Google Gemini 格式的流式响应 + */ +export function parseGeminiStreamChunk(chunk: any): UnifiedStreamChunk { + const result: UnifiedStreamChunk = {}; + + // Gemini 流式响应格式 + const candidate = chunk.candidates?.[0]; + const part = candidate?.content?.parts?.[0]; + + if (part?.text) { + // 检查是否是思考链内容 + if (part.thought === true) { + // 这是思考过程,放入 reasoning_content + result.reasoning_content = part.text; + } + else { + // 这是正常回复内容 + result.content = part.text; + } + } + + if (chunk.usageMetadata) { + result.usage = { + prompt_tokens: chunk.usageMetadata.promptTokenCount, + completion_tokens: chunk.usageMetadata.candidatesTokenCount, + total_tokens: chunk.usageMetadata.totalTokenCount, + }; + } + + if (candidate?.finishReason) { + result.finish_reason = candidate.finishReason; + } + + return result; +} + +/** + * 解析 SSE 格式的数据块 + * SSE 格式示例: + * event: content_block_delta + * data: {"type":"content_block_delta","delta":{"text":"hello"}} + */ +function parseSSEChunk(chunk: any): any { + // 如果已经是对象,直接返回 + if (typeof chunk === 'object' && chunk !== null) { + return chunk; + } + + // 如果是字符串,尝试解析 SSE 格式 + if (typeof chunk === 'string') { + const lines = chunk.split('\n'); + let dataLine = ''; + + for (const line of lines) { + if (line.startsWith('data: ')) { + dataLine = line.substring(6).trim(); + break; + } + } + + if (dataLine && dataLine !== '[DONE]') { + try { + return JSON.parse(dataLine); + } + catch (e) { + console.error('解析 SSE data 失败:', e, '原始数据:', dataLine); + return {}; + } + } + } + + return chunk; +} + +/** + * 根据 API 格式类型解析流式响应数据块 + * @param chunk 原始响应数据块 + * @param apiType API 格式类型 + * @returns 统一格式的响应数据 + */ +export function parseStreamChunk(chunk: any, apiType: string): UnifiedStreamChunk { + // 先解析 SSE 格式(如果需要) + const parsedChunk = parseSSEChunk(chunk); + + switch (apiType) { + case ApiFormatType.Completions: + return parseCompletionsStreamChunk(parsedChunk); + case ApiFormatType.Responses: + return parseResponsesStreamChunk(parsedChunk); + case ApiFormatType.Messages: + return parseClaudeStreamChunk(parsedChunk); + case ApiFormatType.GenerateContent: + return parseGeminiStreamChunk(parsedChunk); + default: + return parseCompletionsStreamChunk(parsedChunk); + } +} + +/** + * 将消息转换为指定 API 格式的请求体 + * @param messages 统一格式的消息列表 + * @param apiType API 格式类型 + * @param model 模型名称 + * @param stream 是否流式 + * @returns 对应格式的请求体 + */ +export function convertToApiFormat( + messages: UnifiedMessage[], + apiType: string, + model: string, + stream = true, +): any { + switch (apiType) { + case ApiFormatType.Completions: { + const completionsMessages = toCompletionsFormat(messages); + return { + model, + messages: completionsMessages, + stream, + }; + } + case ApiFormatType.Responses: { + const responsesMessages = toResponsesFormat(messages); + return { + model, + input: responsesMessages, + stream, + }; + } + case ApiFormatType.Messages: { + const { messages: claudeMessages, system } = toClaudeFormat(messages); + const request: any = { + model, + messages: claudeMessages, + max_tokens: 32000, + stream, + }; + if (system) { + request.system = system; + } + return request; + } + case ApiFormatType.GenerateContent: { + const geminiContents = toGeminiFormat(messages); + return { + contents: geminiContents, + }; + } + default: { + const completionsMessages = toCompletionsFormat(messages); + return { + model, + messages: completionsMessages, + stream, + }; + } + } +} diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 4e6934c4..1317b67d 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -25,8 +25,11 @@ declare module 'vue' { ElCol: typeof import('element-plus/es')['ElCol'] ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] + ElCollapseTransition: typeof import('element-plus/es')['ElCollapseTransition'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] + ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] + ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] @@ -38,11 +41,13 @@ declare module 'vue' { ElImage: typeof import('element-plus/es')['ElImage'] ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] ElInput: typeof import('element-plus/es')['ElInput'] + ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElMain: typeof import('element-plus/es')['ElMain'] ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElOption: typeof import('element-plus/es')['ElOption'] ElPagination: typeof import('element-plus/es')['ElPagination'] + ElProgress: typeof import('element-plus/es')['ElProgress'] ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRow: typeof import('element-plus/es')['ElRow'] @@ -51,6 +56,7 @@ declare module 'vue' { ElSelect: typeof import('element-plus/es')['ElSelect'] ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] + ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTabPane: typeof import('element-plus/es')['ElTabPane'] diff --git a/Yi.Ai.Vue3/types/import_meta.d.ts b/Yi.Ai.Vue3/types/import_meta.d.ts index c98d612e..8f2a798b 100644 --- a/Yi.Ai.Vue3/types/import_meta.d.ts +++ b/Yi.Ai.Vue3/types/import_meta.d.ts @@ -7,7 +7,6 @@ interface ImportMetaEnv { 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; }