Vue3 + Coze API 实战:一键将宠物变冰球明星,代码全解析

102 阅读7分钟

🔥Vue3 + Coze API 实战:一键将宠物变冰球明星,代码全解析

摘要:本文带你从0到1开发一款“上传宠物照片生成冰球运动员形象”的AI趣味应用。使用 Vue3 组合式API + fetch 调用 Coze 文件上传与工作流API,实现图片预览、状态反馈、错误处理等完整交互流程。附完整可运行代码,助你快速掌握现代前端+AI集成开发范式!


在AI技术席卷各行各业的今天,前端开发者不再只是页面搬运工——我们也可以借助成熟的AI平台,打造极具传播力的创意应用。

本文将以一个真实项目为蓝本:
👉 「上传宠物照 → 生成穿队服的冰球运动员形象」
这是一个为冰球协会设计的社交裂变小工具,用户上传自家猫狗的照片,系统自动生成它们身穿队服、手持球杆的“职业球员写真”,一键分享朋友圈,趣味性拉满!

我们将基于 Vue3 + Vite + Coze AI 平台 API 完整实现该功能,并深入拆解每一个关键技术点。文末还提供完整源码结构和部署建议,确保你能复现并扩展成自己的AI产品。


🧩 一、项目背景与核心架构

✅项目目标页面:

image.png

✅ 应用目标

  • 用户上传本地宠物图片
  • 填写队服编号、颜色、位置、持杆习惯、艺术风格等参数
  • 点击“生成”按钮,调用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 文件:

image.png image.png

⚠️ 重要提醒

  • .env 加入 .gitignore,可以禁止提交到Git仓库
  • VITE_ 前缀是 Vite 的约定,只有以此开头的变量才会被暴露给前端
  • 生产环境中建议通过后端代理API,避免前端直接暴露token

🛠 四、关键技术点深度解析

1. ✅ 文件预览:FileReader + Base64

image.png

  • FileReader 是 HTML5 提供的异步读取本地文件的API
  • readAsDataURL 将文件转为 Base64 编码字符串
  • 可直接作为 <img src> 显示,无需上传即可预览

💡 优势:提升用户体验,减少无效请求,让用户立刻看到所选内容。


2. ✅ 文件上传:FormData + fetch

image.png

  • FormData 专用于表单数据(尤其适合文件上传)
  • 不需要手动设置 Content-Type,浏览器会自动设为 multipart/form-data
  • Coze 要求携带 Authorization: Bearer xxx 进行身份验证

3. ✅ 工作流调用:JSON 参数传递技巧

image.png

⚠️ 注意:Coze API 要求某些字段必须是 字符串化的JSON,否则无法识别!

例如 picture 字段虽然逻辑上是对象,但必须用 JSON.stringify() 包一层。


4. ✅ 错误处理与状态反馈

image.png

良好的错误提示能显著提升用户体验,避免用户“点了没反应”的困惑。

✅ 5.调用Coze工作流

image.png

image.png


🐞 五、常见问题与解决方案(实战经验)

问题原因解决方案
上传失败 code=401PAT Token 错误或过期登录 Coze 平台重新生成 Token
工作流不执行workflow_id 错误检查是否复制正确的工作流ID
生成图片为空parameters 格式不对特别注意 picture 必须是字符串化JSON
CORS 跨域报错浏览器限制确认 Coze API 支持 CORS(目前支持)
图片太大上传慢文件体积过大前端增加 size > 10MB 判断并提示

🌟 六、可扩展方向(让你的应用更出彩)

✅ 功能升级建议

方向实现方式
添加图片裁剪功能使用 vue-cropper 组件优化构图
显示生成进度条轮询工作流状态接口(如有)
多图批量生成支持多文件上传 + 循环调用
社交分享按钮集成微信SDK、Twitter分享链接
个性化推荐记录用户偏好,推荐热门组合

✅ 性能优化建议

  • 使用 compressorjs 在前端压缩大图再上传
  • 对生成图片做懒加载
  • 添加 loading 动画提升等待体验

📦 七、如何部署上线?

推荐方案:

  1. 构建打包:npm run build
  2. 部署静态资源:
    • GitHub Pages(免费)
    • Vercel / Netlify(全球CDN加速)
    • 阿里云OSS + CDN(国内访问更快)

示例命令(Vercel):

npm install -g vercel
vercel --prod

上线后即可生成专属链接,发给冰球协会成员玩起来!


🎯 八、总结:前端如何拥抱AI时代?

通过这个项目,你可以学到:

技能维度收获
Vue3 实战<script setup>refonMounted、响应式编程
文件操作FileReader、FormData、input[file]
API集成fetch、POST请求、错误处理
安全实践环境变量、token保护
AI融合调用第三方AI平台,零模型基础也能做AI应用

🔥 核心思想
前端不再是“切图仔”,而是“AI应用组装师”
只要你会调API、懂交互、会状态管理,就能做出爆款AI产品!


📢 最后呼吁:立即动手,抢占AI流量红利!

别再观望了!
现在就 clone 项目、申请 Coze 账号、跑通第一个 AI 请求!

👉 你的下一个大厂offer,可能就来自这样一个小小的创意应用