
简介面向自媒体内容创作者与前端开发者的 Vue 3 版 Coze 工作流合集覆盖内容规划、设计、开发到发布全流程帮助独立博主或团队在最新技术栈上高效搭建自媒体项目尤其适合需要将创意快速落地的场景。资源包共 130 个文件压缩后仅 484KB主体为 80 个 Vue 组件配合 TypeScript 类型定义、CSS/SCSS 样式、HTML 入口页面及 JSON 配置等结构简洁清晰便于按模块裁剪与复用。已有 186 人学习下载适合具备一定 Vue 基础、希望借助成熟工作流提升开发效率的开发者。其中不仅包含界面交互模板、自动化测试与持续集成配置还提供了可实际操作的功能流程示例便于理解工作流的运行机制借助 Vue 3 Composition API 的组织方式可灵活管理组件状态与逻辑复用减少重复劳动快速搭建出契合自身需求的自媒体开发环境。1. 为什么需要一个 Vue3 版的 Coze workflow 客户端一个 Coze workflow 发布之后真正拿它交付业务的时候多数团队要的不是把用户导到扣子平台里点按钮而是把它嵌进自己的后台管理系统用户在工单页点一下内部系统要自动调用这个工作流跑完流程结果直接渲染在页面表格里。这就是 vue3 版 coze workflow 这件事的实质——用 Vue3 写一个最小可用的前端把 Coze 工作流的节点编排、参数入口和结果输出搬进自己的产品界面。它解决的核心问题是让 workflow 不再只是一个独立页面里的演示功能而是一个可以被异步调用、按参数返回结果的技术端点。适合那些已经搭好 workflow、需要前端落地页的开发者也适合想在一套 Vue3 后台里统一管理多个 Bot 和对话流的架构师。后面所有内容都围绕一个目标展开在本地把 Vue3 前端跑起来然后让它和 Coze workflow API 完成一次真实对话。2. Coze workflow 的运行机制与 API 结构2.1 agent 和 workflow什么时候该用 workflow 而不是让模型自由发挥Coze 平台提供 agent 和 workflow 两种主要编排方式很多人一开始分不清。agent 适合开放式的问答场景大模型自己决定调用哪些工具、按什么顺序执行写起来快但结果不可控返佣、超时、脏数据这些问题都很难在事后排查。workflow 则相反它把处理路径固化成一串节点每个节点做什么、输入输出是什么在设计阶段就已经确定。我的经验是凡是涉及结构化数据的场景——按单据号查状态、把 markdown 转 word、批量审核内容、多系统数据汇总——都优先用 workflow。它的可观测性远好于 agent每个节点的输入输出都可以单独追踪账单能算清楚出问题能定位到具体节点。表格里这几点值得对比维度workflowagent执行路径预先编排节点顺序固定大模型在运行中动态决定适合场景结构化任务、多系统串接、必须复现结果开放问答、需要临场推理调试成本单节点断点结果可复现相同输入结果可能不同输出控制由结束节点明确定义需要额外指令约束格式所以当你拿到一个既有的 Coze workflow 时前端要做的事情其实很纯粹按它开始节点定义的参数把用户输入送进去再把结束节点的输出渲染出来。不要在前端里尝试重新解释业务逻辑那部分越薄越好。2.2 workflow/run 的请求参数与返回结构Coze 开放平台对外暴露的 workflow 调用接口是POST /v1/workflow/runVue3 这一侧的所有封装最终都落在这个端点上。这个接口的鉴权方式是 Bearer Token也就是请求头里的Authorization: Bearer {token}token 在平台个人访问令牌页面生成。请求体里最关键的四个字段如下字段类型必填说明bot_idstring是工作流对应的 Bot ID在 Bot 编排页的发布信息里复制user_idstring是业务侧的用户标识Coze 用它做调用日志检索parametersobject按需工作流开始节点定义的输入参数键名要与节点变量一致is_asyncboolean否是否异步执行前端交互一般用同步模式非流式调用的返回值结构比较固定。HTTP 状态 200 不代表业务成功判断结果的字段是codecode为 0 才表示执行成功失败时msg里会有具体错误信息。真正要展示的内容都在data字段里注意这个字段的值是一个被 JSON 字符串化的对象前端必须先JSON.parse再使用。比如工作流结束节点的输出是{ answer: ... }那么data会变成{\answer\:\...\}直接取data.answer会拿到 undefined。2.3 流式响应与普通响应的选择如果你只是做一个内部工具用普通响应就够了实现简单一个axios.post就能拿到完整结果。但如果你面对的是对话类 workflow或者用户需要长时间等待多节点执行就必须考虑流式响应。流式模式下 Coze 服务端返回的是text/event-stream前端打开一个可读流逐段读取模型生成的内容做出打字机效果这在体验上比让用户盯着 loading 转圈强很多。流式响应的事件类型主要有两类message和done。message事件里的data是一个字符串化的 JSON其中content字段是本次增量文本done事件表示整个流程结束。需要注意的是流式响应并非全部文本一次性到达中间可能穿插节点开始、结束这些事件前端解析时要按event字段分流不能把每个data:都当作最终答案拼接否则会把节点元信息也混进对话内容里。3. 用 Vite 创建 Vue3 项目并接通 workflow API3.1 创建 Vue3 项目与最小依赖安装这一步用 Vite 完成。标题里的 vue3 版最终要落在一个能跑的项目里而 Vite 是当前 Vue3 项目最常规的脚手架创建命令如下npm create vitelatest coze-workflow-vue -- --template vue-ts cd coze-workflow-vue npm install npm install element-plus axios pinia--template vue-ts会把项目模板指定为 Vue3 加 TypeScript后面的 workflow 调用逻辑需要定义类型TS 能帮你在编译期就发现参数名写错的问题。element-plus不是必须项但你如果要做后台管理系统风格的界面直接用它是成本最低的方案。axios用于普通 HTTP 调用pinia用来管理对话状态后续内容都会以这两个库为基础。项目生成后先到src/main.ts里挂上 pinia 和 Element Plus再定义一个src/types/coze.ts文件把 Coze 接口相关类型放在一起。// src/types/coze.ts export interface CozeWorkflowRequest { bot_id: string user_id: string parameters: Recordstring, string | number | boolean } export interface CozeWorkflowResponse { code: number msg: string data: string } export interface CozeStreamEvent { event: message | done | error data?: string }CozeWorkflowResponse.data特意保留为string而不是object原因就是上一节说的——Coze 返回的 data 是字符串化的在代码里明确这个类型使用前就不会忘记做一次JSON.parse。3.2 用 TypeScript 封装 workflow 调用模块创建一个src/api/coze.ts作为调用入口底部导出两个函数runWorkflow走普通请求streamWorkflow走流式。先看普通请求的封装这是整个接入过程的最小可运行代码。// src/api/coze.ts import axios from axios import type { CozeWorkflowRequest, CozeWorkflowResponse } from ../types/coze const BASE_URL https://api.coze.cn/v1/workflow/run const TOKEN import.meta.env.VITE_COZE_TOKEN export async function runWorkflow( params: CozeWorkflowRequest, timeoutMs 15000 ): PromiseCozeWorkflowResponse { const { data } await axios.postCozeWorkflowResponse( BASE_URL, params, { headers: { Authorization: Bearer ${TOKEN}, Content-Type: application/json, }, timeout: timeoutMs, } ) if (data.code ! 0) { throw new Error(workflow 执行失败: ${data.msg}) } return data }这里把timeout单独暴露给调用方是刻意的。workflow 的节点数量决定执行耗时三五个节点的轻量流程通常在两秒内返回但包含大模型生成节点的流程很可能超过十秒。把超时时间做成参数UI 层就可以根据具体工作流的复杂度来设置不需要为最慢的场景统一调大所有请求的超时时间。VITE_COZE_TOKEN这个环境变量从.env文件读取。项目根目录下新建.env.local写入VITE_COZE_TOKEN你的tokenVite 启动时会自动加载。注意VITE_前缀不能省没有这个前缀的变量不会暴露给前端代码。3.3 用 fetch 解析流式输出实现打字机效果流式调用不能使用 axios因为 axios 对text/event-stream的支持不完整对增量数据的处理需要自己拼 buffer。最干净的方式是直接用浏览器原生 fetch把响应体当作一个ReadableStream来消费。这里给出一个按行解析的版本。export async function streamWorkflow( params: CozeWorkflowRequest, onDelta: (chunk: string) void, timeoutMs 60000 ): Promisevoid { const controller new AbortController() const timer setTimeout(() controller.abort(), timeoutMs) try { const resp await fetch(BASE_URL, { method: POST, headers: { Authorization: Bearer ${TOKEN}, Content-Type: application/json, }, body: JSON.stringify(params), signal: controller.signal, }) if (!resp.ok || !resp.body) { throw new Error(workflow 请求异常: ${resp.status}) } const reader resp.body.getReader() const decoder new TextDecoder(utf-8) let buffer while (true) { const { done, value } await reader.read() if (done) break buffer decoder.decode(value, { stream: true }) const lines buffer.split(\n) buffer lines.pop() ?? for (const line of lines) { const trimmed line.trim() if (!trimmed.startsWith(data:)) continue const payload JSON.parse(trimmed.slice(5).trim()) if (payload.event message) { const inner JSON.parse(payload.data ?? {}) if (typeof inner.content string) { onDelta(inner.content) } } if (payload.event error) { throw new Error(JSON.stringify(payload)) } } } } finally { clearTimeout(timer) } }这段代码的三个关键点。第一line.startsWith(data:)限定只处理 SSE 数据行兼容event:字段交错出现。第二返回的data字段是 JSON 字符串inner才是真正的模型输出对象里面才有content。第三controller.abort()会在超时时打断整个流式链路避免用户在 workflow 卡住时无限等待。调用方只需要把onDelta里拿到的字符串片段追加到响应式消息上打字机效果自然就出现了。4. 把 workflow 对话流接进 Vue3 组件状态与 UI 编排4.1 用 Pinia 管理会话消息与运行状态对话流在界面上表现为一组消息核心状态只有两个消息数组以及“是否正在运行”。Pinia 的 store 设计应该保持精简不要把 workflow 参数都塞进去那是 API 层的事情。下面这个 store 足够撑起一个单轮直出的 workflow 页面。// src/stores/chat.ts import { defineStore } from pinia export interface ChatMessage { role: user | assistant content: string } export const useChatStore defineStore(chat, { state: () ({ messages: [] as ChatMessage[], running: false, }), actions: { async send(text: string) { const userMessage: ChatMessage { role: user, content: text } const botMessage: ChatMessage { role: assistant, content: } this.messages.push(userMessage, botMessage) this.running true try { await streamWorkflow( { bot_id: import.meta.env.VITE_COZE_BOT_ID, user_id: web-${Date.now()}, parameters: { query: text }, }, (delta) { botMessage.content delta } ) } finally { this.running false } }, }, })user_id这里用时间戳临时生成生产环境应该换成登录态里的真实用户 ID。Coze 的日志系统会按 user_id 聚合调用记录如果所有人共享一个固定值出问题时就无法区分具体是哪个用户触发的失败。parameters里的query是开始节点上定义的变量名你在 Coze 编排页给节点起的变量名是什么这里就写什么不能自己另起名字。4.2 消息列表与输入框的组件组织组件拆分建议按三条路径展开ChatView.vue负责整体布局MessageList.vue负责渲染消息流ChatInput.vue负责采集输入与触发发送。父组件只持有 store 引用不需要自己维护 props 下发。!-- src/views/ChatView.vue -- script setup langts import { useChatStore } from ../stores/chat import MessageList from ../components/MessageList.vue import ChatInput from ../components/ChatInput.vue const chat useChatStore() /script template div classchat-container MessageList :messageschat.messages / ChatInput :disabledchat.running sendchat.send / /div /templateMessageList里渲染每条消息时用一个v-for遍历messages根据role决定气泡靠左还是靠右。assistant消息的容器必须使用white-space: pre-wrap否则大模型返给用户的换行和缩进会全部消失。还有一个容易忽略的问题流式追加内容时Vue 默认会把每次内容变更当成一次 DOM 更新如果你的消息体很长需要在MessageList的根节点上监听滚动让新内容出现时列表自动滚到底部。这里不要用定时器轮询scrollTop直接在消息 push 后的 next tick 里用scrollIntoView即可。4.3 把 workflow 节点状态可视化成步骤卡片很多运营类 workflow 由十几个串行节点组成用户点击执行后界面最好能展示“当前已经跑到哪一步”。做法是在 workflow 的开始节点里把节点名称作为参数传入同时 Coze 在流式响应里会返回节点执行事件前端监听这些事件并更新一个步骤卡片列表。script setup langts const stepState refRecordstring, pending | running | done({}) function markNodeRunning(nodeName: string) { stepState.value[nodeName] running } /script template div classstep-cards div v-for(state, name) in stepState :keyname classstep-card :data-statestate {{ name }} /div /div /template这里的核心价值是让等待过程可见用户知道工作流在哪个环节耗时而不是面对一个空洞的转圈图标。实现时需要你在streamWorkflow的解析逻辑里把event对应的节点名称事件也回调出来仅处理你关心的几个节点即可不要试图渲染全部系统内部事件那会把界面搞成一张调试日志表。4.4 流式失败时的降级策略流式接口偶发断流是常态。设置一个降级开关如果 8 秒内一个message事件都没收到自动重新发起一次普通请求用非流式结果补全消息内容。这里的关键是前端在发起 flow 请求前记录开始时间第一个块到达时清除定时器。这个策略不需要复杂的状态机一个setTimeout加一个receivedFirstChunk布尔值就能实现但它是实际使用中保证页面可用性最重要的一道保险。5. workflow 接入的鉴权、部署与验证5.1 在服务端加一层转发别把 token 发给浏览器上一章代码里把VITE_COZE_TOKEN直接写在环境变量里这只适合本地开发。真正部署时前端代码里的环境变量会被打包进 JS 文件浏览器用户打开开发者工具就能看到你的 token这会带来调用额度被刷的风险。上线前必须移除前端 token改为在服务端保留凭证。常见做法是在 Node.js 服务端加一个/api/coze路由前端只请求你自己的域名由服务端把请求转发到 Coze。Node 侧示例// server/coze.js import express from express import process from process const router express.Router() router.post(/run, async (req, res) { const upstream await fetch(https://api.coze.cn/v1/workflow/run, { method: POST, headers: { Authorization: Bearer ${process.env.COZE_TOKEN}, Content-Type: application/json, }, body: JSON.stringify({ bot_id: process.env.COZE_BOT_ID, user_id: req.body.userId, parameters: req.body.parameters, }), }) res.status(upstream.status) for await (const chunk of upstream.body) res.write(chunk) res.end() }) export default routertoken 只存在于服务端环境变量里前端代码无论如何也读不到。这一层还能顺便做请求频率限制、IP 白名单、调用日志记录。不要省这一步工作流一个节点的成本可能是一次大模型调用token 泄露直接等于钱包泄露。5.2 Vue3 项目在 win 服务器 Nginx 上的路由回退配置Vue3 项目部署时最容易在服务器上遇到 404原因是 SPA 路由刷新时 Nginx 会把/chat这类路径当成文件去找。vite build 之后把dist目录放到服务器上再给站点加一条路由回退配置即可location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; }try_files先检查路径是否存在物理文件没有就回退到 index.html交给 Vue Router 接管。如果你的页面里嵌了 iframe注意给 iframe 的父容器显式设置宽高否则 Vue3 组件里外层 div 的点击事件可能因为 iframe 把事件吞掉而无法触发这类问题多检查一下嵌套层级的pointer-events和遮挡关系比从框架代码里找原因更快。5.3 三个最常踩的 workflow 对接坑与验证命令对接流程跑不通九成问题出在三个地方。第一是bot_id填错从 Coze 平台复制时带了空格或换行这种错误接口会直接返回bot not found。第二是parameters里的键名和开始节点变量名不一致Coze 对多余参数不报错但你的业务字段永远不会出现在后续节点里表现就是节点输出为空排查起来很隐蔽。第三是流式模式判断失误接口返回的 content-type 是text/event-stream前端如果用 axios 接收拿到的 response.data 会被解析成字符串而不是对象。逐层排查时先别急着打开页面调 UI直接用 curl 验证一条链路curl -sS -X POST https://api.coze.cn/v1/workflow/run \ -H Authorization: Bearer YOUR_TOKEN \ -H Content-Type: application/json \ -d {bot_id:YOUR_BOT_ID,user_id:debug-user,parameters:{query:你好}}能看到code: 0并且data里有可解析的 JSON再回到 Vue3 项目里查前端问题。如果 curl 这一步都报鉴权错误先回平台重新生成一次 token确认复制时没有隐藏字符。这条链路通了剩下的工作只是把流式解析的 UI 细节打磨到位。本文还有配套的精品资源点击获取