从 UI 工程师到 AI 应用架构者
13 年前,我的工作是让按钮在 IE6 上对齐;
13 年后,我用 fetch-event-source 订阅大模型的"思维流",用 OCR 解锁图片中的文字------前端,正在成为 AI 产品的第一道体验防线。
最近,我基于 Vue 3 + Vite + TypeScript + Ant Design Vue 开发了一款企业级 AI 多语翻译平台。它支持:
-
- ✅ 文本/图片/文档多模态输入
- ✅ 大模型动态切换(通义千问、DeepSeek 等)
- ✅ 行业领域定制(医疗、法律、金融)
- ✅ SSE 流式输出 (使用
fetch-event-source)
今天,我将逐行拆解核心代码,带你复现这一高价值 AI 应用的前端架构。
一、技术选型:为什么是 fetch-event-source?
虽然浏览器原生支持 EventSource,但在现代前端工程中,我们更倾向使用 @microsoft/fetch-event-source:
-
- ✅ 基于
fetch,天然支持 AbortController(可取消请求) - ✅ 支持 自定义 headers(用于鉴权)
- ✅ 更好的 TypeScript 类型支持
- ✅ 与 Axios/Vite 生态无缝集成
- ✅ 基于
安装:
bash
npm install @microsoft/fetch-event-source
二、OCR 图片识别:前端只管"传"和"显"
用户上传一张英文菜单截图,系统自动提取文字并翻译。前端不碰 OCR 算法,但要管理全流程状态。
🔧 真实代码实现(来自 文本_r9uv.txt)
1. 系统配置加载(含文件大小限制)
ts
// src/views/translation/index.vue
const loadSystemPublicConfig = async () => {
try {
const response = await findSystemPublicConfig({ configKey: 'upload_file_max_limit_mb' });
const config = response.data[0];
systemConfig.value.uploadFileMaxLimit = parseInt(config.value) || 10; // 默认10MB
} catch (error) {
ElMessage.error('加载系统配置失败');
}
};
2. 文件选择与校验
ts
const handleFileSelect = (event: Event) => {
const input = event.target as HTMLInputElement;
if (input.files && input.files.length > 0) {
const file = input.files[0];
const maxSize = systemConfig.value.uploadFileMaxLimit * 1024 * 1024;
if (file.size > maxSize) {
message.error(`文件不能超过 ${systemConfig.value.uploadFileMaxLimit} MB`);
return;
}
currentFile.value = file;
startParsing(file);
}
};
3. 调用 OCR 接口(Ant Design Vue 组件反馈)
ts
const handleObtainFileContentText = async () => {
if (!currentFile.value) return;
const formData = new FormData();
formData.append('file', currentFile.value);
try {
const response = await obtainFileContentText(formData);
if (response.success) {
sourceLanguageText.value = response.data.contentText;
currentUploadStatus.value = 'success';
} else {
currentUploadStatus.value = 'failed';
message.error(response.message || '解析失败');
}
} catch (error) {
currentUploadStatus.value = 'failed';
message.error('网络错误,请重试');
}
};
💡 关键设计 :状态分为
'idle' | 'parsing' | 'success' | 'failed',对应四个 UI 区块,避免用户困惑。
三、大模型动态集成:让选择"无感而智能"
AI 模型不是越多越好,而是要默认最优 + 动态适配。
🔧 真实代码实现
1. 加载模型与语种字典
ts
// 加载大模型列表
const loadLargeModelDictionary = async () => {
const response = await findLargeModelDictionary({ keyword: '' });
largeModelList.value = response.data;
if (largeModelList.value.length > 0) {
const firstModel = largeModelList.value[0];
selectedModelId.value = firstModel.id;
await loadLargeModelLanguageSupport(firstModel.id); // 关键:联动语种
}
};
// 加载某模型支持的语种
const loadLargeModelLanguageSupport = async (modelId: string) => {
const response = await findLargeModelLanguageSupport({ largeModelId: modelId });
languageSupportList.value = response.data;
};
2. 行业领域初始化
ts
const loadIndustrySectorDictionary = async () => {
const response = await findIndustrySectorDictionary({ keyword: '' });
industrySectorList.value = response.data;
// 业务逻辑:若默认为 'general',则跳过
if (defaultIndustrySector.value === 'general' && industrySectorList.value.length > 1) {
defaultIndustrySector.value = industrySectorList.value[1].code;
}
};
3. Ant Design Vue 下拉框绑定
vue
<!-- 模型选择 -->
<a-select
v-model:value="selectedModelId"
style="width: 100%"
@change="handleModelChange"
>
<a-select-option
v-for="item in largeModelList"
:key="item.id"
:value="item.id"
>
{{ item.name }}
</a-select-option>
</a-select>
四、SSE 流式翻译:用 fetch-event-source 实现"打字机效果"
这是体验升级的核心!我们使用 @microsoft/fetch-event-source 替代原生 EventSource。
🔧 完整流式翻译实现(结合你的项目结构)
ts
import { fetchEventSource } from '@microsoft/fetch-event-source';
// 存储控制器,用于取消请求
let abortController: AbortController | null = null;
const handleTranslate = async () => {
if (isTranslating.value) return;
// 重置结果
translationResult.value = '';
isTranslating.value = true;
// 创建 AbortController
abortController = new AbortController();
const params = new URLSearchParams({
largeModelId: selectedModelId.value,
sourceLangCode: selectedSourceLangCode.value,
targetLangCode: selectedTargetLangCode.value,
industrySector: defaultIndustrySector.value,
enableDeepThinking: String(enableDeepThinking.value),
content: sourceLanguageText.value.trim(),
});
try {
await fetchEventSource(`/api/v1/large-model/translate/stream?${params}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${getToken()}`, // 从 Pinia 或 localStorage 获取
},
signal: abortAssistant.signal, // 支持取消
onmessage(event) {
if (event.data) {
translationResult.value += event.data;
nextTick(() => {
scrollToBottom();
});
}
},
onclose() {
isTranslating.value = false;
},
onerror(err) {
console.error('SSE Error:', err);
message.error('翻译服务异常,请稍后重试');
isTranslating.value = false;
abortController?.abort(); // 主动关闭
},
});
} catch (error) {
if (abortController?.signal.aborted) {
console.log('翻译已取消');
} else {
message.error('连接失败');
}
isTranslating.value = false;
}
};
// 取消翻译
const cancelTranslation = () => {
if (abortController) {
abortController.abort();
isTranslating.value = false;
message.info('翻译已取消');
}
};
🔧 自动滚动到底部
ts
const scrollToBottom = () => {
const container = document.querySelector('.translation-result-content');
if (container) {
container.scrollTop = container.scrollHeight;
}
};
💡 为什么不用 WebSocket?
- SSE 是 单向流(服务端 → 客户端),完美匹配"AI 生成文本"场景
fetch-event-source提供 Promise 风格 API,更符合现代前端习惯
五、工程化亮点:13年经验的沉淀
✅ 1. 响应式布局(PC/Mobile 适配)
ts
const updateMainHeight = () => {
const mainEl = document.querySelector('.main') as HTMLElement;
if (!mainEl) return;
if (isMobile.value) {
mainEl.style.minHeight = '100vh';
mainEl.style.height = 'auto';
} else {
mainEl.style.height = '100vh';
mainEl.style.minHeight = 'auto';
}
};
✅ 2. 内存清理(防止泄漏)
ts
onUnmounted(() => {
if (abortController) {
abortController.abort();
}
window.removeEventListener('resize', checkIsMobile);
});
✅ 3. 防重复提交 + 加载状态
vue
<a-button
type="primary"
:loading="isTranslating"
@click="handleTranslate"
:disabled="!sourceLanguageText.trim()"
>
{{ isTranslating ? '翻译中...' : '开始翻译' }}
</a-button>
结语:前端的价值,在"AI 与人之间"
这个项目让我确信:AI 时代,前端工程师的不可替代性在于"体验设计"。
-
- 我们用
fetch-event-source把冰冷的 token 流变成温暖的"打字机"; - 我们用 Ant Design Vue 让复杂配置变得简单;
- 我们用状态机管理 OCR 的每一步,不让用户迷失。
- 我们用
如果你也想从"切图仔"转型为"AI 应用架构者",不妨从一个流式翻译工具开始。技术会过时,但解决问题的能力永远稀缺。
项目技术栈 :Vue 3.4 + Vite 5 + TypeScript + Ant Design Vue 4 + fetch-event-source
代码来源 :本文所有逻辑均来自实际生产项目(已脱敏)
互动 :你用过fetch-event-source吗?欢迎分享你的 SSE 实践!