
这几年AI辅助编程的讨论越来越多Cursor算是真正把我从“到处搜代码、复制粘贴再改半天”的状态里解放出来的工具。这篇文章我就拿一个刚跑通的练手项目来复盘——用Cursor配合Claude Opus 4.6从空目录开始生成一个Flask2 Vue3 Vite的学生信息管理系统。整个系统包括后端接口、数据库表设计、前端页面、前后端联调最后能完整跑起来增删改查、分页、关键词搜索、跨域处理这些常见需求全都有。想了解AI编程实际效果的、正在学Python全栈的、或者想把一条技术栈完整串起来做项目的同学这篇内容对你有用。我会把给AI写的提示词、生成的代码结构、踩过的坑、以及一些常规教程里不会写的细节全部摊开讲。你不需要有很深的编程基础跟着走一遍也能用Cursor搭出类似的小系统。1. 项目概述为什么选这个组合1.1 需求拆解一个学生信息管理系统到底要做什么很多教程喜欢一上来就建一堆表、写一堆接口结果新手直接劝退。我在动手之前先把这个项目拆成了最小可用版本也就是MVP学生信息的增删改查、分页列表、按姓名或学号的关键词搜索、按班级或状态过滤。界面就两个页面一个学生列表页一个新增/编辑弹窗。登录权限这种偏运营后台的功能第一版完全可以不做等主体跑通了再说。数据表方面第一版只建一张学生表就够了字段包括学号、姓名、性别、年龄、班级、手机号、邮箱、入学时间、状态。课程表、成绩表这类关联表属于二期需求因为一旦引入多表关联前端页面和接口复杂度会直线上升。先把单表CRUD跑通再往上加东西这个节奏对于用AI辅助开发尤其重要——每轮对话只解决一组问题AI给出的代码质量会高很多。1.2 技术选型Flask2、Vue3、Vite各承担什么角色后端选Flask2理由很直接项目体量小不需要Django那种全家桶Flask轻量、灵活路由写法简单数据库操作用Flask-SQLAlchemy接管什么表都能建。而且Flask的代码量少很适合让AI一口气生成核心部分人再去逐行审阅。前端选Vue3 Vite是当前新项目的标配。Vue3的组合式APIsetup语法写业务逻辑比Options API更集中一个页面的请求、响应、Loading状态、刷新列表这些逻辑可以收敛在几段清晰的代码里。Vite负责开发服务器和打包构建冷启动速度、热更新体验比老一代Webpack好太多。Vite底层的esbuild转换速度极快一个学生管理项目的前端规模构建时间基本是秒级。我习惯把项目分为server和client两个目录后端和前端彻底分开这样概念清晰后面部署也方便。server放Flask代码client放Vue3代码两个进程独立启动开发时通过Vite的代理转发请求。1.3 Cursor和Claude Opus 4.6在项目里扮演的角色Cursor本质上是VSCode的一个分支保留了VSCode的生态插件、快捷键、设置同步额外集成了AI能力。在这个项目里我主要用了几个能力行内补全Tab补全、多文件对话Chat/Ask、以及在对话里让AI直接读写文件、执行命令。模型方面我选的是Claude Opus 4.6。代码生成场景里Opus系列对长上下文的把握、对技术栈规范的理解明显比普通模型更稳。让它生成Flask路由它知道要写错误处理让它写Vue3页面它知道要处理Loading状态和空数据。不过这也有个前提提示词必须把业务规则说清楚AI才能给出高质量结果。2. 环境准备把Cursor调教成趁手工具2.1 Python和Node环境安装系统里Python版本比较老的话建议装3.10以上。Windows用户在官网下载安装包时记得勾选“Add Python to PATH”否则后面终端里输入python会提示找不到命令。macOS/Linux下一般自带Python3但版本可能不是最新的用python3 --version确认一下。Node.js建议装18以上的LTS版本Vite 5、Vue 3的生态对Node版本有要求太旧了安装依赖会报错。装完之后建议顺手把包管理器梳理一下。Python侧用venv建虚拟环境依赖隔离很重要不然不同项目的包互相冲突够你折腾一上午。Node侧我推荐用pnpm相比npm它的安装速度快、磁盘占用小。如果机器上还没装执行npm install -g pnpm即可。2.2 Cursor安装与中文设置Cursor的安装很无脑官网下载对应系统的安装包一路下一步。装完启动之后第一次用的人大概率会觉得界面是英文的。想切成中文有个最简单的方法左侧扩展商店搜索“Chinese (Simplified)”安装Microsoft的中文语言包装完按提示重启编辑器界面就变成中文了。网上有些汉化教程会让你改配置文件、设置locale其实在Cursor这种基于VSCode内核的编辑器里装语言包是最标准的方案也不用担心版本升级后失效。设置中文之后再顺手把Settings Sync登录一下这样在多台机器上复用同一套配置包括AI设置、快捷键、主题。2.3 模型选择与项目初始化进入Cursor先按快捷键打开模型切换面板确认当前使用的是Claude Opus 4.6。不同版本的模型对同样一段提示词的输出质量差异很大所以如果你之前用过Claude 3.5或者Sonnet系列这次换到Opus很多代码生成结果会有明显变化。随后在本地建好项目骨架。我在某个工作目录下执行了mkdir student-system然后cd进去在Cursor里打开这个目录。接下来的大部分工作都是选中文件、给AI提需求、让它直接修改文件内容。建议先手动创建server和client两个空目录并各自建好虚拟环境或初始化的脚手架再让AI在对应目录里填充代码这样AI对项目结构的理解会更准确。3. 用Claude Opus生成Flask2后端3.1 数据模型设计最少要建哪些表第一版我只建了一个Student模型。字段设计如下字段类型说明idInteger, 主键自增student_noString(20), 唯一学号nameString(50)姓名genderString(10)性别ageInteger年龄class_nameString(50)班级phoneString(20)手机号emailString(100)邮箱enrollment_dateDate入学时间statusString(10)状态在读/休学/毕业这个表的设计逻辑是常用查询条件姓名、学号、班级、状态都有对应字段列表展示的信息也都在同一张表里不需要JOIN查询对新手非常友好。AI生成代码时我明确告诉它“用Flask-SQLAlchemy定义上面这张表”它就能直接产出正确的模型代码。3.2 告诉AI怎么写后端一套高命中率的提示词模板给AI下需求最忌讳一句话丢过去然后等结果。Claude Opus 4.6理解能力强但Prompt里信息越明确代码可用率越高。我的提示词大概是这么写的你是资深Python后端工程师请用Flask 2.x和Flask-SQLAlchemy实现一个学生信息管理系统的后端。 数据库使用SQLite文件放在instance/student.db。 Student模型字段id, student_no, name, gender, age, class_name, phone, email, enrollment_date, status。 需要实现的接口 1. GET /api/students分页查询参数page、page_size、keyword模糊匹配姓名/学号、status按状态过滤 2. POST /api/students新增学生校验必填字段和学号唯一性 3. PUT /api/students/id修改学生信息 4. DELETE /api/students/id删除单个学生 5. DELETE /api/students/batch批量删除接收JSON数组 统一返回格式{code: 0, message: success, data: {...}} 项目结构使用app.py、models.py、routes.py分离并附上requirements.txt注意几个关键点指明框架版本、数据库类型、字段类型、接口URL、返回格式、项目结构。这些约束给到位AI基本不会跑偏。如果让它自由发挥它可能给你生成marshmallow序列化、flask-migrate迁移、JWT认证这些偏重的组件对一个小项目来说是过度设计。3.3 核心接口实现与代码解读生成出来的代码结构大概是这样的server/ app.py # 应用入口 models.py # 数据模型 routes.py # 路由和视图函数 requirements.txtmodels.py里面定义Student模型核心代码类似下面这样from flask_sqlalchemy import SQLAlchemy db SQLAlchemy() class Student(db.Model): __tablename__ student id db.Column(db.Integer, primary_keyTrue) student_no db.Column(db.String(20), uniqueTrue, nullableFalse) name db.Column(db.String(50), nullableFalse) gender db.Column(db.String(10)) age db.Column(db.Integer) class_name db.Column(db.String(50)) phone db.Column(db.String(20)) email db.Column(db.String(100)) enrollment_date db.Column(db.Date) status db.Column(db.String(10), default在读) def to_dict(self): return { id: self.id, student_no: self.student_no, name: self.name, gender: self.gender, age: self.age, class_name: self.class_name, phone: self.phone, email: self.email, enrollment_date: self.enrollment_date.strftime(%Y-%m-%d) if self.enrollment_date else None, status: self.status }routes.py里面主要是分页搜索接口稍微有点逻辑的代码如下bp.route(/students, methods[GET]) def get_students(): page request.args.get(page, 1, typeint) page_size request.args.get(page_size, 10, typeint) keyword request.args.get(keyword, , typestr).strip() status request.args.get(status, , typestr).strip() query Student.query if keyword: like_keyword f%{keyword}% query query.filter( db.or_(Student.name.like(like_keyword), Student.student_no.like(like_keyword)) ) if status: query query.filter(Student.status status) pagination query.paginate(pagepage, per_pagepage_size, error_outFalse) items [s.to_dict() for s in pagination.items] return jsonify({ code: 0, message: success, data: { total: pagination.total, items: items, page: page, page_size: page_size } })这里有个容易踩坑的细节接收page和page_size参数时一定要用typeint做类型转换否则前端传个字符串SQLAlchemy会在比较时直接报错。keyword为空字符串时不加筛选条件这个判断要放在构造like之前否则LIKE %%会查全部结果虽然一样但多一次无谓的查询。app.py里要做三件事创建Flask实例、注册蓝图、初始化数据库。另外一定要加上Flask-CORS的配置因为开发时前端在5173端口后端在5000端口跨域请求是必然的。from flask import Flask from flask_cors import CORS from models import db from routes import bp def create_app(): app Flask(__name__) app.config[SQLALCHEMY_DATABASE_URI] sqlite:///student.db app.config[SQLALCHEMY_TRACK_MODIFICATIONS] False db.init_app(app) CORS(app) app.register_blueprint(bp, url_prefix/api) return app app create_app() app.cli.command(init-db) def init_db(): 初始化数据库运行: flask init-db db.create_all() print(Database initialized!) if __name__ __main__: app.run(debugTrue, port5000)小项目用db.create_all()建表就够了不需要引入Flask-Migrate。迁移工具解决的问题是表结构上线后的增量变更学生管理系统开发阶段推倒重建成本很低没必要增加复杂度。我把init-db写成了Flask的CLI命令运行一次flask --app app init-db就能完成建表。启动后端后用浏览器或者curl验证一下接口。先访问http://127.0.0.1:5000/api/students?page1page_size10正常情况下应该返回一个code为0的JSON。这一步务必在写前端之前验证后端接口有问题越早知道越好否则前后端联调时会多一堆干扰项。4. 用Claude Opus生成Vue3 Vite前端4.1 创建Vite项目并安装依赖前端部分我让Cursor新建了一个Vite项目。手动创建也很简单在client目录下执行pnpm create vitelatest . -- --template vue然后安装核心依赖pnpm add element-plus element-plus/icons-vue axios vue-router pinia选Element Plus作为UI组件库原因是它和Vue3的配合成熟度最高表格、表单、弹窗、消息提示这些后台管理场景的组件都有现成的样式也统一。学生信息管理这种页面本质就是“表格 表单弹窗 查询条件”用Element Plus能省掉大量样式代码。依赖装完之后我直接打开.vue文件让Claude Opus推荐目录结构。它给出的方案很标准src/views放页面、src/api放请求封装、src/router放路由配置、src/components放复用组件。目录结构定了再逐个生成文件。4.2 Element Plus 图标自动注册减少样板代码Element Plus的图标是单独一个包element-plus/icons-vue。如果你在所有需要图标的地方都手动import代码会很啰嗦。第一次让AI生成时它默认在每个组件里单独引用了图标我看着太冗余就要求它改成自动注册方案。具体做法是安装unplugin-icons和unplugin-vue-components这两个Vite插件然后在vite.config.js里配置import { defineConfig } from vite import vue from vitejs/plugin-vue import AutoImport from unplugin-auto-import/vite import Components from unplugin-vue-components/vite import { ElementPlusResolver } from unplugin-vue-components/resolvers import IconsResolver from unplugin-icons/resolver import Icons from unplugin-icons/vite export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver(), IconsResolver()] }), Icons({ autoInstall: true }) ] })这段配置的作用是页面模板里直接写el-table、el-button不需要手动import插件会在编译阶段自动按需引入组件。图标同理直接写i-ep-add /这种组件名插件自动解析成对应的图标组件。按需引入之后打包体积会小不少开发时也不用关心组件注册顺序。配置这种Vite插件刚开始很容易报错。如果出现“IconsResolver is not a function”这类问题八成是版本不对检查一下package.json里的unplugin-icons和unplugin-vue-components版本Vite 5项目用最新版基本没坑。4.3 核心页面与交互逻辑实现学生列表页是整个前端最核心的内容。我让AI按如下规则实现顶部是搜索栏包含关键词输入框、状态下拉框、查询按钮、重置按钮中间是表格展示学生信息右上角是“新增学生”按钮表格行操作包含“编辑”和“删除”底部是分页组件。这里我重点说一下computed的使用。筛选条件有两个keyword和status它们和分页参数同时影响表格数据。但是搜索和分页的触发时机不同如果一输入关键词就发请求接口压力很大。我设计成点击查询按钮时重置page为1再调用getStudents而keyword本身用ref保存不直接驱动请求。再用一个computed去计算表格里需要展示的年龄区间分布这个属于演示了computed的典型用法——对现有数据进行派生计算而不需要额外的接口请求。const ageStats computed(() { const list students.value if (!list.length) return { total: 0, avg: 0 } const total list.reduce((sum, s) sum (s.age || 0), 0) return { total: list.length, avg: (total / list.length).toFixed(1) } })新增和编辑弹窗用同一个Dialog组件通过修改dialogTitle和form对象来区分模式。提交方法共用新增调POST接口编辑调PUT接口。这里有个小技巧编辑的时候要把当前行数据深拷贝到form里不要直接引用原有对象否则弹窗里的修改会立刻反映到表格行上用户取消编辑时数据已经被污染了。API请求封装也值得说一下。我把axios实例单独放在src/api/index.js里统一设置baseURL: /api和超时时间再用一个拦截器统一处理返回结构只把data.data暴露给业务代码省得每个页面都去解包。import axios from axios const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.response.use( (response) { const res response.data if (res.code ! 0) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data }, (error) { ElMessage.error(error.message || 网络异常) return Promise.reject(error) } ) export const getStudents (params) service.get(/students, { params }) export const addStudent (data) service.post(/students, data) export const updateStudent (id, data) service.put(/students/${id}, data) export const deleteStudent (id) service.delete(/students/${id}) export const batchDeleteStudents (ids) service.delete(/students/batch, { data: { ids } })这样封装的好处是页面组件里只需要关心业务逻辑比如“调用addStudent成功后弹Message并刷新列表”不需要关心HTTP状态码和响应体结构。4.4 Vite构建配置esbuild还是terser开发阶段完全不用管构建配置Vite默认就能跑。但是到打包上线之前项目里会出现一个常见选择minify用esbuild还是terser。Vite默认的压缩器是esbuild压缩速度快构建一个学生管理系统这种体量的项目基本毫秒级完成。但esbuild做不了太复杂的压缩选项比如它不支持在压缩时自动移除所有console.log和debugger。如果你希望生产环境的代码里没有调试输出就要换成terser。terser是老牌的JavaScript压缩器支持更细粒度的压缩控制。在vite.config.js里配置export default defineConfig({ build: { minify: terser, terserOptions: { compress: { drop_console: true, drop_debugger: true } } } })代价是构建时间会变长因为terser是纯JavaScript实现的没有esbuild那种原生代码的速度。我的建议是开发环境永远保持esbuild只有发布正式版、明确要清理调试代码的时候再临时切到terser。后面我在问题排查章节还会提到一个和esbuild相关的报错到时候你会理解为什么这个选择如此重要。另外提一句Vite 6相关的变化Vite团队正在把底层构建工具从Rollup切换到RolldownRolldown是用Rust写的构建性能比Rollup强很多。学生管理系统暂时用不到但如果你看到网上文章说Vite 6构建更快那说的就是这个方向未来升级构建工具链的时候现有vite.config.js配置大概率还是兼容的。前端页面生成完之后在client目录跑一下pnpm dev浏览器打开Vite输出的本地地址就能看到页面了不过现在还没有数据下一步就是前后端联调。5. 前后端联调与完整走通5.1 开发环境跨域方案Vite代理开发时前端跑在5173端口后端跑在5000端口两个不同的源浏览器会拦截跨域请求。虽然我已经在后端配了Flask-CORS但更好的做法是让Vite代为转发请求这样前端请求的URL看起来是同源的。在vite.config.js里添加server.proxy配置export default defineConfig({ server: { proxy: { /api: { target: http://127.0.0.1:5000, changeOrigin: true } } } })配置完成后前端组件里一律使用相对路径/api/studentsVite开发服务器收到请求后转发到5000端口。这个方案的好处是浏览器控制台里看不到跨域报错Cookie和Headers的传递也顺畅。后端保留Flask-CORS不是为了现在而是为了避免以后分开发布时忘了补。5.2 数据流验证从新增到列表刷新联调第一步先在后端加一条测试数据可以直接用POST接口。然后刷新前端页面看看表格里是否能显示出这条记录。如果列表显示正常说明GET接口和网络链路是通的。第二步验证新增流程。点“新增学生”填完整表单提交。这里有个高频问题日期格式不匹配。前端Element Plus的DatePicker组件返回的是Date对象或特定格式字符串后端Student模型里enrollment_date是Date类型。Flask接收JSON后如果直接把字符串塞给Date字段会报TypeError或者ValueError。解决方法是前端先格式化成YYYY-MM-DD字符串后端用date.fromisoformat()解析。我在让AI生成代码时特意加了这条规则所以联调时一次通过。第三步验证编辑和删除。编辑时要注意学号唯一性校验如果改成和别的学生重复的学号后端要返回明确的报错信息。删除建议加上二次确认弹出框防止误操作。5.3 打包与部署思路前后端开发完最后一步是构建。在client目录执行pnpm buildVite会生成dist目录里面是纯静态文件。部署方式有两种第一种最简单的方式把dist目录里所有文件复制到Flask的static目录然后写一个根路由返回index.html。这种方式适合个人项目或课程设计演示一个Python进程就全部搞定。第二种正式点的方式前端dist放到Nginx里后端Flask用Gunicorn启动Nginx配置反向代理把/api请求转发到Gunicorn监听的端口。这种方式负载能力更强前后端物理分离但部署复杂度也高一些。学生信息管理系统不涉及高并发我个人倾向第一种方案。如果只是为了交作业或者自己学习完全没有必要上Nginx。部署前记得把debug模式关掉数据库换成MySQL也不是必须的SQLite文件就够了。6. 常见问题与避坑实录6.1 Cursor生成代码时的典型问题AI生成的代码不是百发百中我这次遇到最多的三类问题第一字段和接口不一致。比如AI在models.py里定义了email字段但routes.py的to_dict里忘了解析它导致前端表格里邮箱列永远为空。解决方法是生成代码后逐个字段过一遍尤其是序列化和反序列化的部分。这个检查步骤不能省我大概花了两分钟就发现了这个遗漏。第二AI会把简单事情复杂化。有一次它给我的分页接口里加了flask-migrate迁移脚本还生成了三四个我根本没提的配置文件。对于小项目我明确告诉它“不需要迁移不要JWT不要权限中间件”重新生成一次就干净了。第三上下文漂移。当对话轮数变多、文件改动频繁时AI偶尔会遗忘之前定好的规则比如把返回格式从{code, message, data}改成直接返回数组或者把接口路径从/api/students改成/students。应对办法是随时把关键约定粘贴回对话里或者新建一个约定的.md文件让AI每次写代码前都先读一下。6.2 Vite构建报错排查构建时最容易遇到的报错就是[vite:esbuild-transpile] transform failed with 2 errors: static/js/general-9.js这个报错通常意味着esbuild在转换某个JavaScript文件时失败了。常见原因有两种一种是某个依赖包里含有超出目标浏览器版本支持的语法比如用了比较新的空值合并运算符??或者可选链?.而你的build.target设置成了比较老的浏览器。另一种是包版本不兼容比如某个插件升级后语法变化导致的转换失败。排查步骤建议如下先把build.target调整成es2018以上或者把build.minify改成terser试试。如果注释里还有更详细的错误行号打开对应文件看是哪一行出了问题。实在不行升级Vite和相关插件版本大概率能解决。我这次遇到的报错就出现在切换minify配置时最后用升级插件版本加调整target解决的。如果你也在这一步被卡住不要慌这类构建问题通常不是业务代码的错而是工具链版本之间的兼容性摩擦。6.3 环境与依赖问题速查我把这一轮实操里遇到的环境类问题整理成了一个速查表后面再搭建类似项目可以直接对照场景现象解决方案终端输入python提示找不到Windows未加PATH重装Python时勾选Add to PATHPython 3.11虚拟环境激活失败执行venv命令后activate无反应Windows用Scripts\activatemacOS/Linux用bin/activateNode版本过旧Vite启动报错提示Node版本不支持装Node 18建议用nvm管理5000端口被占用Flask启动即报Address already in use修改端口为5001或在macOS上检查AirPlay相关服务前端页面请求返回404Nginx未配置或代理路径错误检查vite.config.js的proxy规则确认后端路由前缀学号重复数据库唯一约束报错在业务层先判断唯一性再插入SQLite的IntegrityError要捕获页面样式丢失自动导入插件未生效重启Vite dev server检查vite.config.js插件顺序以上都是实际操作中高频出现的问题每一条我都至少踩过一次。AI生成的代码能解决90%的“怎么写”问题剩下10%的“为什么不行”还得靠人来看日志、查环境、调配置这也是这轮项目最有价值的部分。最后分享一个我现在的习惯让Cursor生成代码之后不要直接无脑接受至少要通读一遍核心文件理解每一步在干什么。AI帮你把重复劳动干了但业务逻辑的归属、边界条件的设计、字段和接口的约定最终都是你说了算。第一次跑通这个学生管理系统前前后后大概花了一个下午。换做手写这个时间至少翻倍。工具提效是真实存在的但前提是你懂这个工具在帮你干什么以及出了问题该去哪里查。