ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Cursor + 蓝湖 MCP 协议:Vue 3 设计稿还原效率提升实战

Cursor + 蓝湖 MCP 协议:Vue 3 设计稿还原效率提升实战

1. 设计稿还原这件事,到底卡在哪

做前端的人对“还原了吗”这四个字应该都不陌生。设计师在蓝湖上标注好了间距、字号、色值,切图也上传了,然后甩过来一句“这个页面什么时候能还原完”。你打开设计稿,对着标注一个一个量,margin-left: 24px、font-size: 14px、line-height: 22px、color: #333333,写完之后本地跑起来截图对比,发现差了 2px,回去翻标注发现是padding不是margin。改完再截图,颜色又不对,因为设计稿用的是#333但项目里 SCSS 变量定义的是$text-primary: #2c2c2c。

这种来回拉扯的消耗,做过中后台系统或者 C 端页面的人应该都深有体会。一个中等复杂度的页面,从拿到设计稿到像素级还原,熟练工也得花上大半天,中间还要经历“截图-对比-修改-再截图”的循环。设计师那边也难受,他们不知道你改到哪一步了,只能隔一会儿问一句“还原了吗”,问多了你烦,不问他们又不放心。

我最近把 Cursor 和蓝湖通过 MCP 协议接了起来,整个流程变成了:在 Cursor 里选中设计稿对应的组件代码,AI 直接读取蓝湖上的标注数据,自动生成符合项目规范的 Vue 3 组件和 SCSS 样式。设计师在蓝湖上更新了设计稿,我这边重新拉一次标注就能同步。这篇文章就把这套方案的完整落地过程拆开讲清楚,包括 MCP 服务怎么部署、Cursor 怎么配置、Vue 3 + SCSS 的代码怎么生成、遇到问题怎么排查。如果你也在做前端还原,或者想了解 MCP 协议在实际项目里怎么用,这篇内容应该能帮你省下不少折腾的时间。

2. 整体方案设计与技术选型思路

2.1 为什么是 MCP 而不是直接调 API

蓝湖本身提供了开放平台 API,理论上你可以写个脚本去拉标注数据,然后手动或者半自动地生成代码。但这样做有几个问题:第一,你需要自己处理鉴权、分页、数据格式转换,写一堆胶水代码;第二,每次拉数据都要切换上下文,从编辑器跳到终端或者浏览器,思路容易断;第三,AI 没法直接感知到这些数据,你只能把拉下来的 JSON 复制粘贴给 AI,效率很低。

MCP 协议解决的就是这个问题。它的全称是 Model Context Protocol,你可以把它理解成 AI 和外部工具之间的一个标准接口。Cursor 作为 MCP 客户端,可以连接到实现了 MCP 协议的服务端,服务端负责去调蓝湖的 API 拿数据,然后把数据以 AI 能理解的方式暴露出来。这样你在 Cursor 里跟 AI 对话的时候,AI 就能直接读取蓝湖上的设计稿信息,不需要你手动搬运数据。

提示:MCP 是一个软件协议层面的概念,跟硬件协议没有关系。它的核心作用是让 AI 模型能够安全、可控地访问外部数据源和工具。

2.2 为什么选 Vue 3 + SCSS 这套组合

这个方案对技术栈其实没有硬性限制,React、Vue 2、甚至原生 HTML 都能做。我选 Vue 3 + SCSS 是因为当前项目本身就是这套技术栈,而且 Vue 3 的<script setup>语法写起来比较简洁,AI 生成代码的准确率也更高。SCSS 的好处是支持变量、嵌套、mixin,可以把设计稿里的色值、字号、间距统一管理起来,避免硬编码。

具体来说,我会在项目里维护一个_variables.scss文件,把蓝湖上常用的设计 token 定义成 SCSS 变量。AI 生成样式的时候,优先引用这些变量而不是直接写死数值。这样做的好处是,设计师改了主色调,我只需要改一个变量值,所有页面自动生效,不需要全局搜索替换。

2.3 整体数据流是怎样的

整个链路是这样的:蓝湖设计稿 → 蓝湖开放平台 API → MCP 服务端(本地运行)→ Cursor MCP 客户端 → AI 模型 → 生成 Vue 3 组件代码 + SCSS 样式。

MCP 服务端跑在本地,通过标准输入输出或者 SSE 跟 Cursor 通信。Cursor 在启动的时候会读取配置文件,自动拉起 MCP 服务。你在 Cursor 的 AI 对话框里输入指令,比如“帮我还原这个设计稿的卡片组件”,AI 就会通过 MCP 协议去调服务端暴露的工具方法,拿到标注数据,然后结合你项目里的代码规范生成对应的组件代码。

这套方案的核心优势在于:数据是实时拉取的,设计师更新了设计稿,你重新拉一次就能拿到最新标注;AI 直接读取结构化数据,不需要你手动转录;生成的代码可以按照你预设的模板和规范来,减少后期调整的工作量。

3. 核心细节解析与实操要点

3.1 蓝湖 MCP 服务怎么部署

蓝湖官方目前没有提供现成的 MCP 服务,需要自己写一个。我用的方案是基于 Node.js 写一个轻量级的 MCP 服务端,核心逻辑就是调蓝湖开放平台的接口拿标注数据,然后按照 MCP 协议的格式暴露给 Cursor。

先说一下蓝湖开放平台的鉴权方式。你需要在蓝湖的团队设置里生成一个访问令牌,然后每次调接口的时候在请求头里带上这个令牌。具体的接口文档蓝湖开放平台上有,核心接口就两个:一个是获取设计稿列表,一个是获取指定设计稿的标注详情。

MCP 服务端的代码结构大概是这样:

// mcp-server.js import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; const server = new Server({ name: 'lanhu-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, }); // 注册工具:获取设计稿标注 server.setRequestHandler('tools/list', async () => ({ tools: [ { name: 'get_design_spec', description: '获取蓝湖设计稿的标注数据', inputSchema: { type: 'object', properties: { designId: { type: 'string', description: '设计稿 ID' }, pageName: { type: 'string', description: '页面名称' }, }, required: ['designId'], }, }, ], })); server.setRequestHandler('tools/call', async (request) => { if (request.params.name === 'get_design_spec') { const { designId, pageName } = request.params.arguments; const spec = await fetchLanhuSpec(designId, pageName); return { content: [{ type: 'text', text: JSON.stringify(spec, null, 2) }], }; } }); const transport = new StdioServerTransport(); await server.connect(transport);

fetchLanhuSpec这个函数就是去调蓝湖的 API,拿到标注数据之后做一层清洗,把 AI 不需要的字段去掉,只保留图层名称、位置、尺寸、颜色、字体、间距这些关键信息。

注意:蓝湖 API 返回的数据结构比较深,图层是嵌套的。我在清洗数据的时候会把嵌套结构拍平,每个图层保留一个path字段记录它的层级关系,这样 AI 理解起来更直观。

3.2 Cursor 这边怎么配置 MCP

Cursor 的 MCP 配置入口在设置里,找到 Features 下面的 MCP Servers,点添加。配置文件的格式是这样的:

{ "mcpServers": { "lanhu": { "command": "node", "args": ["/path/to/your/mcp-server.js"], "env": { "LANHU_TOKEN": "your_token_here" } } } }

command填node,args填你 MCP 服务端脚本的绝对路径,env里把蓝湖的访问令牌传进去。保存之后 Cursor 会自动拉起这个服务,你可以在 MCP Servers 列表里看到它的状态,绿色圆点表示连接正常。

这里有个细节要注意:Cursor 启动 MCP 服务的时候,工作目录可能不是你项目所在的目录。所以脚本里如果用相对路径读文件,可能会找不到。我的做法是在脚本里用process.env或者__dirname来定位路径,避免踩这个坑。

配置好之后,你在 Cursor 的 AI 对话框里输入指令,AI 就能感知到这个 MCP 服务提供的工具。你可以直接说“帮我拉一下蓝湖上这个设计稿的标注”,AI 会问你设计稿 ID 和页面名称,然后调工具拿数据。

3.3 Vue 3 组件代码的生成规范怎么定

AI 生成代码的质量,很大程度上取决于你给的约束条件。我在项目根目录放了一个.cursorrules文件,里面写清楚了组件命名规范、目录结构、SCSS 变量引用规则、以及一些代码风格要求。

比如组件命名,我要求所有组件文件用 PascalCase,比如UserProfileCard.vue,而不是user-profile-card.vue。SCSS 类名用 BEM 风格,块名跟组件名保持一致。样式里引用变量而不是硬编码色值,比如color: $text-primary而不是color: #333。

.cursorrules里还可以写一些示例代码,让 AI 参考。比如我给了一个标准的卡片组件模板:

<template> <div class="user-profile-card"> <div class="user-profile-card__avatar"> <img :src="avatar" :alt="name" /> </div> <div class="user-profile-card__info"> <h3 class="user-profile-card__name">{{ name }}</h3> <p class="user-profile-card__desc">{{ description }}</p> </div> </div> </template> <script setup lang="ts"> interface Props { avatar: string; name: string; description: string; } defineProps<Props>(); </script> <style lang="scss" scoped> .user-profile-card { display: flex; align-items: center; padding: $spacing-md; background: $bg-white; border-radius: $radius-md; &__avatar { width: 48px; height: 48px; border-radius: 50%; overflow: hidden; } &__info { margin-left: $spacing-sm; } &__name { font-size: $font-size-lg; color: $text-primary; } &__desc { font-size: $font-size-sm; color: $text-secondary; } } </style>

有了这个模板,AI 生成新组件的时候就会参照这个结构来,命名风格、变量引用、BEM 写法都能保持一致。

3.4 SCSS 变量体系怎么跟设计稿对齐

这一步是整个方案里最关键的。如果 SCSS 变量跟设计稿的标注对不上,AI 生成的代码还是得手动改。我的做法是先把蓝湖上常用的设计 token 整理出来,在项目里建一个styles/_variables.scss文件。

具体操作是:打开蓝湖的设计稿,把常用的色值、字号、间距、圆角、阴影这些属性列出来,然后定义成 SCSS 变量。比如:

// 颜色 $text-primary: #1f1f1f; $text-secondary: #666666; $text-placeholder: #999999; $bg-white: #ffffff; $bg-gray: #f5f5f5; $border-color: #e0e0e0; $primary-color: #1890ff; // 字号 $font-size-xs: 12px; $font-size-sm: 14px; $font-size-md: 16px; $font-size-lg: 18px; $font-size-xl: 24px; // 间距 $spacing-xs: 4px; $spacing-sm: 8px; $spacing-md: 16px; $spacing-lg: 24px; $spacing-xl: 32px; // 圆角 $radius-sm: 4px; $radius-md: 8px; $radius-lg: 12px;

定义好之后,在.cursorrules里告诉 AI:生成样式时优先使用这些变量,如果设计稿的标注值跟变量值有偏差,以变量值为准,并在注释里说明偏差。这样做的好处是,整个项目的视觉风格统一,不会出现这个页面用#333那个页面用#2c2c2c的情况。

提示:如果设计稿的标注值跟变量值偏差超过 2px 或者色差肉眼可见,建议先跟设计师确认是不是设计稿更新了但变量没同步,不要直接硬编码。

4. 实操过程与核心环节实现

4.1 从蓝湖拉取标注数据的完整流程

假设设计师在蓝湖上给了一个新的页面设计稿,我需要还原成 Vue 3 组件。第一步是拿到设计稿的 ID。打开蓝湖的设计稿页面,URL 里有一串 ID,比如https://lanhuapp.com/web/#/item/project/detail?pid=xxxxx,这个pid就是设计稿 ID。

拿到 ID 之后,在 Cursor 的 AI 对话框里输入:

帮我拉取蓝湖设计稿 pid=xxxxx 的标注数据,页面名称是"用户列表"

AI 会通过 MCP 服务去调蓝湖的 API,把标注数据拉下来。返回的数据大概长这样:

{ "pageName": "用户列表", "layers": [ { "name": "卡片容器", "type": "frame", "x": 0, "y": 0, "width": 375, "height": 200, "backgroundColor": "#ffffff", "borderRadius": 8, "padding": { "top": 16, "right": 16, "bottom": 16, "left": 16 } }, { "name": "头像", "type": "image", "x": 16, "y": 16, "width": 48, "height": 48, "borderRadius": 24 }, { "name": "用户名", "type": "text", "x": 80, "y": 20, "width": 100, "height": 22, "fontSize": 16, "fontWeight": 500, "color": "#1f1f1f", "content": "张三" } ] }

拿到数据之后,我会让 AI 先分析一下图层结构,确认它理解得对不对。比如我会问:“这个设计稿的布局方式是什么?用 flex 还是 grid 更合适?”AI 会根据图层的位置和尺寸关系给出建议。这一步很重要,因为如果 AI 对布局的理解有偏差,后面生成的代码结构就会有问题。

4.2 生成 Vue 3 组件代码的实操记录

确认布局方式之后,我让 AI 生成组件代码。指令大概是这样的:

根据刚才拉取的标注数据,生成一个 Vue 3 组件,组件名是 UserListCard,使用 script setup 语法,样式用 SCSS,引用项目里的变量文件。

AI 生成的代码:

<template> <div class="user-list-card"> <div class="user-list-card__avatar"> <img :src="avatar" :alt="name" /> </div> <div class="user-list-card__content"> <h3 class="user-list-card__name">{{ name }}</h3> <p class="user-list-card__desc">{{ description }}</p> </div> </div> </template> <script setup lang="ts"> interface Props { avatar: string; name: string; description: string; } defineProps<Props>(); </script> <style lang="scss" scoped> .user-list-card { display: flex; align-items: flex-start; padding: $spacing-md; background: $bg-white; border-radius: $radius-md; &__avatar { width: 48px; height: 48px; border-radius: 50%; overflow: hidden; flex-shrink: 0; img { width: 100%; height: 100%; object-fit: cover; } } &__content { margin-left: $spacing-md; flex: 1; } &__name { font-size: $font-size-md; font-weight: 500; color: $text-primary; line-height: 22px; } &__desc { margin-top: $spacing-xs; font-size: $font-size-sm; color: $text-secondary; line-height: 20px; } } </style>

这段代码基本可以直接用,但有几个细节我做了调整。第一,设计稿里头像和内容的间距是 16px,我用了$spacing-md,正好对应。第二,用户名的行高设计稿标注是 22px,我直接写了line-height: 22px,因为 SCSS 变量里没有定义行高变量,这种一次性的值可以直接写。第三,描述文字的行高是 20px,同样直接写。

实操心得:AI 生成的代码不要直接复制粘贴就用,一定要过一遍。重点检查三个方面:布局方式是否符合预期、变量引用是否正确、边界情况有没有处理。比如头像加载失败的时候显示什么,文字过长的时候怎么截断,这些 AI 不一定能考虑到。

4.3 样式还原的精度校验方法

代码写完之后,怎么确认还原精度?我的做法是在本地跑起来,用浏览器的开发者工具截图,然后跟蓝湖的设计稿做对比。具体操作是:在 Chrome 里打开页面,按 F12 打开开发者工具,用设备模拟器切成设计稿的尺寸(比如 375px 宽),然后截图。把截图和蓝湖的设计稿并排放在一起,肉眼对比。

如果发现偏差,我会用开发者工具的元素检查功能,看具体是哪个属性不对。比如间距差了 4px,就去看是margin还是padding的问题。颜色不对,就用取色器吸一下实际渲染的颜色,跟设计稿的色值对比。

这里有个小技巧:在 Chrome 开发者工具里,选中元素之后按Ctrl+Shift+C可以快速定位到对应的 DOM 节点,然后在 Styles 面板里直接修改样式值,实时看效果。确认没问题之后,再把修改同步回代码里。

对于精度要求高的项目,我会用 Playwright 写一个截图对比脚本,自动截取页面截图跟设计稿做像素级对比。不过这个方案搭建成本比较高,适合对还原精度要求极高的场景。日常开发用肉眼对比加开发者工具微调就够了。

4.4 设计师更新设计稿后的同步流程

设计师改了设计稿之后,我这边不需要重新生成整个组件,只需要让 AI 对比新旧标注数据的差异,然后针对性地修改代码。具体操作是:重新拉一次标注数据,然后跟 AI 说:

这是更新后的标注数据,跟之前的对比一下,告诉我哪些地方变了,然后修改对应的代码。

AI 会对比两份数据,找出差异点,比如某个间距从 16px 变成了 12px,某个色值从#333变成了#666,然后定位到代码里对应的位置进行修改。这个流程比重新生成整个组件要高效得多,而且不会引入不必要的改动。

注意:如果设计稿的改动比较大,比如布局结构变了,那还是重新生成组件比较稳妥。差异对比适合微调场景,结构性的改动还是走完整流程。

5. 常见问题与排查技巧实录

5.1 MCP 服务连接失败的排查思路

最常见的问题是 Cursor 启动之后 MCP 服务显示红色,连接不上。排查步骤是这样的:

第一,检查 Node.js 版本。MCP SDK 对 Node.js 版本有要求,建议用 18 以上的版本。在终端里跑node -v确认一下。

第二,检查脚本路径。Cursor 配置里的args必须是绝对路径,相对路径可能会因为工作目录的问题找不到文件。在终端里手动跑一下node /path/to/mcp-server.js,看能不能正常启动。

第三,检查环境变量。蓝湖的访问令牌如果没传进去,服务启动的时候可能不会报错,但调接口的时候会返回 401。在脚本里加一行console.error(process.env.LANHU_TOKEN)确认令牌有没有读到。

第四,看 Cursor 的日志。Cursor 的 MCP 日志在输出面板里可以找到,切换到 MCP 相关的频道,能看到服务启动的详细日志和报错信息。

5.2 标注数据解析出来对不上的情况

有时候拉下来的标注数据跟设计稿上看到的不一致,比如某个图层的尺寸对不上,或者颜色值有偏差。这种情况通常是以下几个原因:

一是设计稿有多个版本,你拉的是旧版本的数据。确认一下设计稿 ID 是不是最新的。

二是图层被锁定了或者隐藏了,蓝湖 API 默认可能不返回隐藏图层的数据。在设计稿里把图层解锁并显示出来。

三是标注数据的单位问题。蓝湖的标注数据默认是 px,但如果你在设计稿里用了其他单位,API 返回的值可能需要转换。检查一下设计稿的设置。

四是颜色值的格式问题。蓝湖返回的颜色可能是 RGB 格式,也可能是 HEX 格式,需要统一转换。我在 MCP 服务里做了一层处理,统一转成 HEX 格式再返回给 AI。

5.3 AI 生成的代码不符合项目规范怎么办

这个问题通常是因为.cursorrules文件写得不够具体。AI 对规范的理解依赖于你给的示例和约束条件。如果生成的代码命名风格不对,就在.cursorrules里明确写清楚命名规则,并给出正例和反例。如果变量引用不对,就把变量文件的路径和可用变量列表写进去。

另外,Cursor 的 AI 模型对上下文的理解能力有限,如果项目很大,它可能记不住所有的规范。我的做法是把最常用的规范放在.cursorrules的前面,确保 AI 优先读到。不常用的规范可以放到单独的文档里,需要的时候手动提醒 AI。

还有一个技巧是在对话里直接给 AI 反馈。比如它生成的代码用了#333而不是$text-primary,你就说“样式里不要硬编码色值,用 SCSS 变量”,AI 会记住这个反馈并在后续生成中调整。

5.4 常见问题速查表

问题现象可能原因排查方法解决方案
MCP 服务显示红色Node 版本过低终端跑node -v升级到 Node 18+
MCP 服务显示红色脚本路径错误手动跑node 脚本路径改用绝对路径
调接口返回 401令牌未传入打印process.env.LANHU_TOKEN检查 Cursor 配置的 env 字段
标注数据对不上设计稿版本旧确认设计稿 ID用最新版本的设计稿 ID
标注数据对不上图层被隐藏在设计稿里检查图层状态解锁并显示图层
颜色值格式不对API 返回 RGB打印原始数据在 MCP 服务里统一转 HEX
代码命名不规范.cursorrules不具体检查规则文件补充命名规则和示例
变量引用错误变量文件未告知 AI检查.cursorrules写入变量文件路径和变量列表
布局方式不对AI 理解偏差先让 AI 分析布局确认后再生成代码
样式精度不够未做截图对比开发者工具截图对比用元素检查微调

5.5 几个踩过的坑和避坑建议

第一个坑是蓝湖 API 的调用频率限制。蓝湖开放平台对接口调用有频率限制,短时间内大量请求可能会被限流。我的做法是在 MCP 服务里加一个简单的缓存,同一个设计稿 ID 的标注数据缓存 5 分钟,避免重复请求。

第二个坑是 MCP 服务的日志输出。MCP 协议通过标准输入输出通信,如果你在脚本里用console.log打印调试信息,可能会干扰协议通信。调试信息应该用console.error输出到标准错误流,这样不会影响 MCP 的正常通信。

第三个坑是 AI 生成的代码里图片路径问题。设计稿里的图片在蓝湖上是一个 URL,但项目里通常会把图片下载到本地。AI 生成代码的时候可能会直接用蓝湖的 URL,你需要手动替换成本地路径。我的做法是在.cursorrules里约定图片路径的格式,让 AI 生成占位路径,然后手动替换。

第四个坑是 SCSS 变量的作用域问题。如果变量文件没有在组件里正确引入,编译的时候会报变量未定义的错误。Vue 3 项目里通常用@use或者@import引入变量文件,需要在vite.config.js里配置additionalData,让每个 SCSS 文件自动引入变量。

// vite.config.js export default defineConfig({ css: { preprocessorOptions: { scss: { additionalData: `@use "@/styles/variables" as *;`, }, }, }, });

这样配置之后,组件里的 SCSS 就可以直接用变量,不需要每个文件都手动引入。

6. 这套方案的实际效果和后续扩展

用这套方案跑了大概两周,最直观的感受是还原一个中等复杂度页面的时间从原来的 3-4 小时压缩到了 1 小时左右。省下来的时间主要是在“量标注”和“写样式”这两个环节,AI 直接读数据生成代码,我只需要做校验和微调。设计师那边也不再频繁追问“还原了吗”,因为我在蓝湖上更新设计稿之后,重新拉一次数据就能同步,他们能看到进度。

后续我打算在这几个方向继续优化:一是把截图对比做成自动化,用 Playwright 定时跑截图任务,跟设计稿做像素级 diff,有偏差自动告警;二是把常用的组件模板沉淀到.cursorrules里,让 AI 生成的代码更贴近项目风格;三是探索把 MCP 服务部署到团队内部,让其他前端同事也能用上这套流程,减少重复配置的成本。

如果你也在做前端还原,建议先从一个小页面开始试,把 MCP 服务和 Cursor 配置跑通,感受一下整个流程。跑通之后再逐步把 SCSS 变量体系和代码规范补全,效果会越来越明显。遇到问题优先看 Cursor 的 MCP 日志和蓝湖 API 的返回数据,大部分问题都能从日志里找到线索。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进