🔥Vue3 + Coze API 实战:一键将宠物变冰球明星,代码全解析
摘要:本文带你从0到1开发一款“上传宠物照片生成冰球运动员形象”的AI趣味应用。使用 Vue3 组合式API +
fetch调用 Coze 文件上传与工作流API,实现图片预览、状态反馈、错误处理等完整交互流程。附完整可运行代码,助你快速掌握现代前端+AI集成开发范式!
在AI技术席卷各行各业的今天,前端开发者不再只是页面搬运工——我们也可以借助成熟的AI平台,打造极具传播力的创意应用。
本文将以一个真实项目为蓝本:
👉 「上传宠物照 → 生成穿队服的冰球运动员形象」
这是一个为冰球协会设计的社交裂变小工具,用户上传自家猫狗的照片,系统自动生成它们身穿队服、手持球杆的“职业球员写真”,一键分享朋友圈,趣味性拉满!
我们将基于 Vue3 + Vite + Coze AI 平台 API 完整实现该功能,并深入拆解每一个关键技术点。文末还提供完整源码结构和部署建议,确保你能复现并扩展成自己的AI产品。
🧩 一、项目背景与核心架构
✅项目目标页面:
✅ 应用目标
- 用户上传本地宠物图片
- 填写队服编号、颜色、位置、持杆习惯、艺术风格等参数
- 点击“生成”按钮,调用AI模型生成拟人化冰球运动员图像
- 支持预览、下载、分享
📦 技术选型
| 模块 | 技术 |
|---|---|
| 前端框架 | Vue3 + <script setup> + Vite |
| 图片处理 | FileReader + Base64 预览 |
| HTTP 请求 | fetch + async/await |
| AI能力 | Coze 工作流 API(字节跳动出品) |
| 安全配置 | .env 环境变量管理 |
💡 为什么选择 Coze?
Coze 是字节推出的低代码AI应用平台,支持可视化编排工作流(Workflow),并通过开放API供外部调用。无需训练模型,只需上传图片+传参即可触发AI绘图,非常适合前端快速集成AI能力。
🚀 二、完整代码实现(Vue3 单文件组件)
以下是可在 Vite 项目中直接运行的完整 APP.vue 文件代码:
<template>
<div class="container">
<div class="input">
<div class="file-input">
<input
type="file"
ref="uploadImage"
accept="image/*"
@change="updateImageData"
required
/>
</div>
<img :src="imgPreview" alt="" v-if="imgPreview"/>
<div class="settings">
<div class="selection">
<label >队服编号:</label>
<input type="number" v-model="uniform_number"/>
</div>
<div class="selection">
<label >队服颜色:</label>
<select v-model="uniform_color">
<option value="红">红</option>
<option value="蓝">蓝</option>
<option value="绿">绿</option>
<option value="白">白</option>
<option value="黑">黑</option>
</select>
</div>
</div>
<div class="settings">
<div class="selection">
<label >位置:</label>
<select v-model="position">
<option value="0">守门员</option>
<option value="1">前锋</option>
<option value="2">后卫</option>
</select>
</div>
<div class="selection">
<label>持杆:</label>
<select v-model="shooting_hand">
<option value="0">左手</option>
<option value="1">右手</option>
</select>
</div>
<div class="selection">
<label>风格:</label>
<select v-model="style">
<option value="写实">写实</option>
<option value="乐高">乐高</option>
<option value="国漫">国漫</option>
<option value="日漫">日漫</option>
<option value="油画">油画</option>
<option value="涂鸦">涂鸦</option>
<option value="素描">素描</option>
</select>
</div>
</div>
<div class="generate">
<button @click="generate">生成</button>
</div>
</div>
<div class="output">
<div class="generated">
<img :src="imgUrl" alt="" v-if="imgUrl"/>
<div v-if="status">{{ status }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref ,onMounted} from 'vue'
// script+setuo 是vue3 最好的代码组织方式
//composition api 组合
// 直接在 script setup 中定义函数,就可以在模板中直接调用
//用于标记一个DOM 对象,如果要做就用ref
//未挂载前null,
const patToken = import.meta.env.VITE_PAT_TOKEN;
const uploadUrl = 'https://api.coze.cn/v1/files/upload';
const workflowUrl = 'https://api.coze.cn/v1/workflow/run';
const workflow_id ='7584046136927862794'
console.log(patToken);
const uniform_number = ref(10);
const uniform_color = ref('红');
const position = ref(0);
const shooting_hand = ref(0);
const style = ref('写实');
// 数据状态
const status = ref('');// 空->图片上传中...->图片生成中...->图片生成成功
const imgUrl = ref('');// 生成的图片url
// 生成图片模块
const generate =async ()=>{
status.value = '图片上传中...';
const file_id =await uploadFile();
if(!file_id){
return;
}
status.value = '图片上传成功,正在生成图片...';
// workflow 调用
const parameters ={
picture : JSON.stringify({
file_id // 安全问题
}),
style:style.value,
uniform_number:uniform_number.value,
uniform_color:uniform_color.value,
position:position.value,
shooting_hand:shooting_hand.value,
}
const res = await fetch(workflowUrl,{
method:'POST',
headers:{
//请求头
'Authorization':`Bearer ${patToken}`,// 授权信息 Bearer 令牌
'Content-Type':'application/json',// 内容类型 json 字符串
},
body:JSON.stringify({
workflow_id,
parameters
})
});
const ret =await res.json();
if(ret.code !== 0){//如果出错了
status.value = ret.msg;//msg 错误消息
return;
}
const data =JSON.parse(ret.data);
console.log(data);
status.value='';
imgUrl.value=data.data;
}
//先上传到coze 服务器
const uploadFile =async ()=>{
// post 请求体 http 协议
const formData =new FormData();// 表单提交数据
const input =uploadImage.value;
if(!input.files||input.files.length===0)
return;
formData.append('file',input.files[0]);// 向请求体里加上文件
//向coze 发送http 请求 上传
const res =await fetch(uploadUrl,{
method:'POST',
headers:{
//请求头
'Authorization':`Bearer ${patToken}`,// 授权信息 Bearer 令牌
},
body:formData
})
const ret =await res.json();
console.log(ret);
if(ret.code !== 0){//如果出错了
status.value = ret.msg;//msg 错误消息
return;
}
return ret.data.id;
}
// 图片预览模块
const uploadImage = ref(null);
const imgPreview = ref('');//声明了响应式对象
// 挂载了 null -> dom 对象 变化
onMounted(()=>{
console.log(uploadImage.value);
})
const updateImageData = ()=>{
//html5 文件对象
//console.log(uploadImage.value.files[0]);
const input =uploadImage.value;
if(!input.files||input.files.length===0){
return;
}
const file =input.files[0];//文件对象 html5新特性
console.log(file);
// FileReader 文件阅读对象
const reader =new FileReader();
reader.readAsDataURL(file);//url 异步
reader.onload =(e)=>{//读完之后
// console.log(e.target.result);
imgPreview.value = e.target.result;
}
}
</script>
<style scoped>
.container {
display: flex;
flex-direction: row;
align-items: start;
justify-content: start;
height: 100vh;
font-size: .85rem;
}
.preview {
max-width: 300px;
margin-bottom: 20px;
}
.settings {
display: flex;
flex-direction: row;
align-items: start;
justify-content: start;
margin-top: 1rem;
}
.selection {
width: 100%;
text-align: left;
}
.selection input {
width: 50px;
}
.input {
display: flex;
flex-direction: column;
min-width: 330px;
}
.file-input {
display: flex;
margin-bottom: 16px;
}
.output {
margin-top: 10px;
min-height: 300px;
width: 100%;
text-align: left;
}
button {
padding: 10px;
min-width: 200px;
margin-left: 6px;
border: solid 1px black;
}
.generate {
width: 100%;
margin-top: 16px;
}
.generated {
width: 400px;
height: 400px;
border: solid 1px black;
position: relative;
display: flex;
justify-content: center;
/* 水平居中 */
align-items: center;
/* 垂直居中 */
}
.output img {
width: 100%;
}
</style>
🔐 三、环境变量配置(安全必做)
在项目根目录创建 .env 文件:
⚠️ 重要提醒:
- 将
.env加入.gitignore,可以禁止提交到Git仓库VITE_前缀是 Vite 的约定,只有以此开头的变量才会被暴露给前端- 生产环境中建议通过后端代理API,避免前端直接暴露token
🛠 四、关键技术点深度解析
1. ✅ 文件预览:FileReader + Base64
FileReader是 HTML5 提供的异步读取本地文件的APIreadAsDataURL将文件转为 Base64 编码字符串- 可直接作为
<img src>显示,无需上传即可预览
💡 优势:提升用户体验,减少无效请求,让用户立刻看到所选内容。
2. ✅ 文件上传:FormData + fetch
FormData专用于表单数据(尤其适合文件上传)- 不需要手动设置
Content-Type,浏览器会自动设为multipart/form-data - Coze 要求携带
Authorization: Bearer xxx进行身份验证
3. ✅ 工作流调用:JSON 参数传递技巧
⚠️ 注意:Coze API 要求某些字段必须是 字符串化的JSON,否则无法识别!
例如 picture 字段虽然逻辑上是对象,但必须用 JSON.stringify() 包一层。
4. ✅ 错误处理与状态反馈
良好的错误提示能显著提升用户体验,避免用户“点了没反应”的困惑。
✅ 5.调用Coze工作流
🐞 五、常见问题与解决方案(实战经验)
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 上传失败 code=401 | PAT Token 错误或过期 | 登录 Coze 平台重新生成 Token |
| 工作流不执行 | workflow_id 错误 | 检查是否复制正确的工作流ID |
| 生成图片为空 | parameters 格式不对 | 特别注意 picture 必须是字符串化JSON |
| CORS 跨域报错 | 浏览器限制 | 确认 Coze API 支持 CORS(目前支持) |
| 图片太大上传慢 | 文件体积过大 | 前端增加 size > 10MB 判断并提示 |
🌟 六、可扩展方向(让你的应用更出彩)
✅ 功能升级建议
| 方向 | 实现方式 |
|---|---|
| 添加图片裁剪功能 | 使用 vue-cropper 组件优化构图 |
| 显示生成进度条 | 轮询工作流状态接口(如有) |
| 多图批量生成 | 支持多文件上传 + 循环调用 |
| 社交分享按钮 | 集成微信SDK、Twitter分享链接 |
| 个性化推荐 | 记录用户偏好,推荐热门组合 |
✅ 性能优化建议
- 使用
compressorjs在前端压缩大图再上传 - 对生成图片做懒加载
- 添加 loading 动画提升等待体验
📦 七、如何部署上线?
推荐方案:
- 构建打包:
npm run build - 部署静态资源:
- GitHub Pages(免费)
- Vercel / Netlify(全球CDN加速)
- 阿里云OSS + CDN(国内访问更快)
示例命令(Vercel):
npm install -g vercel
vercel --prod
上线后即可生成专属链接,发给冰球协会成员玩起来!
🎯 八、总结:前端如何拥抱AI时代?
通过这个项目,你可以学到:
| 技能维度 | 收获 |
|---|---|
| Vue3 实战 | <script setup>、ref、onMounted、响应式编程 |
| 文件操作 | FileReader、FormData、input[file] |
| API集成 | fetch、POST请求、错误处理 |
| 安全实践 | 环境变量、token保护 |
| AI融合 | 调用第三方AI平台,零模型基础也能做AI应用 |
🔥 核心思想:
前端不再是“切图仔”,而是“AI应用组装师”。
只要你会调API、懂交互、会状态管理,就能做出爆款AI产品!
📢 最后呼吁:立即动手,抢占AI流量红利!
别再观望了!
现在就 clone 项目、申请 Coze 账号、跑通第一个 AI 请求!
👉 你的下一个大厂offer,可能就来自这样一个小小的创意应用。