基于Flask+Vue的过程性评价系统开发实战与踩坑记录

基于Flask+Vue的过程性评价系统开发实战与踩坑记录 你还别小看这个“课程学习过程性评价系统”它听着像学校教务里那种冷冰冰的管理软件但真做起来业务逻辑、技术选型、前后端配合的复杂度一点不少。我最近刚好用 Python Flask Vue 这套组合完整落地了一个开发工具用的 PyCharm写前端时又切到 VS Code 顺手调了调样式。整个过程踩了不少坑也积累了一些实际经验今天把它整理出来供准备做类似系统、或者正在纠结“到底用 Flask 还是 Django”的朋友参考。先说清楚这系统解决什么问题。传统评价方式基本看期末考试成绩属于“一考定终身”但学习是个持续过程平时表现、作业提交、课堂参与、阶段测验这些数据更能反映真实水平。过程性评价系统就是把平时表现量化、汇总、可视化老师可以随时记录和调整评分学生可以看到自己的成长曲线教务可以拿到班级整体分析。所以它不是一个简单的 CRUD而是带业务规则的评价引擎。我选的组合是 Flask Vue 3 MySQLPyCharm 做后端开发前端用 Vite 搭建。整体做下来从数据库设计到接口开发再到前端联调一个完整周期大概三周。下面把设计思路、核心实现、踩坑记录都拆开讲。1. 项目整体设计与技术选型1.1 过程性评价的核心需求拆解开始写代码前我先把需求拆成了四层这决定了后面的数据库设计和接口划分。第一层是基础数据层包含学生、课程、教师、班级这些主数据。这一层没什么特殊逻辑就是常规的增删改查但表结构一定要提前想到后续扩展比如学生表里预留学号、邮箱、头像字段课程表里预留学期、学分字段不然数据量一上来再改表结构很痛苦。第二层是过程记录层也就是评价数据的来源。出勤记录、课堂表现打分、作业提交和批改记录、阶段测验成绩、项目实践评分这些都属于这一层。所有记录都要关联到学生和课程同时保留打分人和打分时间方便后续追溯。第三层是评价规则层这是系统的核心业务逻辑。每种评价项都对应一个权重系统要根据权重把各类得分折算成综合得分。比如出勤占 10%作业占 25%阶段测验占 30%项目实践占 35%权重可以按课程类型灵活配置。这里不能用硬编码必须做成配置表因为不同老师、不同课程的评价维度完全不一样。第四层是展示与分析层学生端要能看到自己的各项得分趋势和扣分明细教师端要能看到全班成绩分布教务端要有汇总统计。我当时用 ECharts 做了雷达图和柱状图效果还比较直观。需求拆到这种程度数据库表基本就出来了。一共设计了八张表学生表、教师表、课程表、选课表、出勤表、作业表、测验表、项目评分表另外还有评价项配置表和权重配置表。后两张表用来存评价维度是什么各维度占比多少这类系统参数。1.2 技术选型思路Flask 和 Django 到底怎么选标题里把 Flask 和 Django 都写上了说明不少人在这两个框架之间纠结。我的观点很直接追求快速落地、接口数量少、系统边界清晰的选 Flask模块多、需要后台管理、开发团队规模大的选 Django。我这个项目用的是 Flask。为什么首先是接口数量。过程性评价系统大概就三四十个接口Flask 用蓝图分模块组织完全够用路由注册直观请求入口清晰没有 Django 那一套完整的 MTV 约束。其次是数据模型。Flask SQLAlchemy 的 ORM 用起来很灵活模型定义简单直接。Django 的 ORM 很强大但我在这个项目里用不到那么复杂的查询和关联选 Flask 可以把项目结构控制得很轻。第三是后台管理。Flask 有 flask-admin 插件几行代码就能把数据表的增删改查页面生成出来对内部管理来说非常省事。Django 自带 admin 也不错但整个框架的体量对我来说偏重。选 Vue 3 作为前端框架主要因为它是当前生态最活跃的前端方案之一。组合式 API 写业务逻辑比选项式 API 顺滑得多配合 Vite 的开发服务器热更新速度比 Vue 2 时代的 Webpack 方案快非常多。再加上 Element Plus 组件库表格、表单、弹窗这些后台管理场景通用的组件不用自己从零写了。有一点要提醒Flask 和 Django 的部署方式差别不小。Django 用 runserver 做开发调试没问题但生产环境一般要配合 Gunicorn 或 uWSGI。Flask 也是一样的道理开发时跑内置服务器部署时如果没有 WSGI 服务器扛并发很容易被流量打垮。我在项目里用的是 WaitressWindows 服务器上跑得很稳配合 Nginx 做反向代理后端并发压力小了很多。1.3 开发环境配置与项目目录组织开发工具我用的是 PyCharm 专业版后端代码几乎都在 PyCharm 里完成。Flask 项目的解释器一定要在虚拟环境里跑我习惯用python -m venv venv创建虚拟环境然后在 PyCharm 的 Settings 里指定解释器路径。这样项目依赖隔离得很干净换机器部署也不会有乱七八糟的版本冲突。前端部分我会切到 VS Code因为写 Vue 单文件组件时 VS Code 的 Volar 插件比 PyCharm 的前端支持更顺手。两个工具来回切一开始觉得麻烦后来觉得这是效率最高的搭配。目录结构我做了前后端完全分离backend/ app.py # 入口文件 config.py # 配置信息 models/ __init__.py student.py course.py evaluation.py api/ __init__.py student_api.py course_api.py evaluation_api.py auth_api.py utils/ __init__.py response.py decorators.py requirements.txt frontend/ index.html vite.config.js # 开发代理配置 src/ main.js App.vue router/ index.js stores/ user.js api/ request.js # axios 封装 student.js course.js evaluation.js views/ Login.vue StudentList.vue CourseDetail.vue EvaluationForm.vue Dashboard.vue前后端完全分离的好处是后端只提供 API前端只关心页面和交互两边可以并行开发。我在开发阶段用 Vite 的 proxy 代理解决跨域问题上线后由 Nginx 统一转发整体非常清爽。2. 后端开发模型、接口、评价算法2.1 数据模型与数据库实战我用 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)) email db.Column(db.String(50)) class_name db.Column(db.String(50)) create_time db.Column(db.DateTime, defaultdatetime.now)这个模型里有个字段值得多说一句student_no我加了唯一约束。实际开发中经常出现重复导入学生数据的问题有了唯一约束插入数据时用ON DUPLICATE KEY UPDATE或先查询再插入就能避免产生重复记录。课程模型和选课关系模型类似核心是建立学生和课程的多对多关系。选课表里除了两个外键还有一个状态字段比如已选已退选用来处理学期中调课的情况。状态字段平时用不到但一旦班主任说某个学生退课了这个字段就派上用场了。评价相关表是系统的关键。class EvaluationItem(db.Model): __tablename__ evaluation_item id db.Column(db.Integer, primary_keyTrue) course_id db.Column(db.Integer, db.ForeignKey(course.id)) item_name db.Column(db.String(100)) # 出勤/作业/测验/项目 weight db.Column(db.Float) # 权重0.10 表示 10% sort_order db.Column(db.Integer) # 排序字段每条课程记录对应多个评价项每个评价项有独立的权重。算综合得分时系统遍历该课程的所有评价项把各项得分乘以权重再相加就得到最终的过程性评价总分。权重之和我规定必须等于 1在保存配置时做一次校验不通过就拦截避免后台配置出错导致计算异常。评分记录表设计成通用结构class ScoreRecord(db.Model): __tablename__ score_record id db.Column(db.Integer, primary_keyTrue) student_id db.Column(db.Integer, db.ForeignKey(student.id)) course_id db.Column(db.Integer, db.ForeignKey(course.id)) item_id db.Column(db.Integer, db.ForeignKey(evaluation_item.id)) score db.Column(db.Float) comment db.Column(db.Text) create_by db.Column(db.Integer) # 打分教师ID create_time db.Column(db.DateTime, defaultdatetime.now)这种通用设计好处是添加新的评价维度不用改表结构只要在评价项配置表加一条记录就行。如果哪天学校说还要加一个小组互评维度数据层完全不用动接口层加一个项类型就完事。2.2 Flask 接口开发与蓝图路由Flask 的路由用蓝图来组织非常清晰。我在__init__.py里注册蓝图from flask import Flask from flask_cors import CORS from flask_sqlalchemy import SQLAlchemy db SQLAlchemy() def create_app(): app Flask(__name__) app.config.from_object(config.Config) db.init_app(app) CORS(app) from api.student_api import student_bp from api.evaluation_api import evaluation_bp from api.auth_api import auth_bp app.register_blueprint(student_bp, url_prefix/api/student) app.register_blueprint(evaluation_bp, url_prefix/api/evaluation) app.register_blueprint(auth_bp, url_prefix/api/auth) return app接口返回格式我统一封装了所有接口都返回{code: 200, data: ..., message: success}这种结构。这样前端axios拦截器里只需判断code字段不用每个接口单独处理异常。这个习惯是从做 App 后端时学来的前后端联调时统一格式能节省大量沟通成本。一个典型的列表接口student_bp.route(/list, methods[GET]) def get_student_list(): page request.args.get(page, 1, typeint) size request.args.get(size, 10, typeint) keyword request.args.get(keyword, ) query Student.query if keyword: query query.filter( or_(Student.name.like(f%{keyword}%), Student.student_no.like(f%{keyword}%)) ) pagination query.paginate(pagepage, per_pagesize, error_outFalse) items [s.to_dict() for s in pagination.items] return response_success({ items: items, total: pagination.total, page: page, size: size })分页参数用request.args.get取出来同时设置默认值这样前端不传参数也不会报错。搜索用like模糊查询数据量不大时完全够用但数据量过万之后建议换Elasticsearch或数据库全文索引不过这种学校内部系统一般不会走到这一步。2.3 评价指标计算与权重算法评价算法是这个系统最有含金量的部分。我的设计思路是先定义评价维度然后通过配置好的评分标准和权重矩阵把学生的过程表现映射成量化得分。具体计算逻辑分三步。第一步根据课程 ID 获取所有评价项配置items EvaluationItem.query.filter_by(course_idcourse_id).all()第二步对每个评价项取该学生在该项下的所有得分记录算平均分def get_item_score(student_id, course_id, item_id): records ScoreRecord.query.filter_by( student_idstudent_id, course_idcourse_id, item_iditem_id ).all() if not records: return 0 total sum(r.score for r in records) return round(total / len(records), 2)第三步加权求和def calculate_total_score(student_id, course_id): items EvaluationItem.query.filter_by(course_idcourse_id).all() total 0 for item in items: score get_item_score(student_id, course_id, item.id) total score * item.weight return round(total, 2)这段逻辑看起来简单实际开发中有几个细节值得注意。第一个细节是缺省记录的处理。如果一个学生一次作业都没交ScoreRecord表里没有任何作业记录此时get_item_score返回 0 还是返回默认分我这边经过和老师确认采用了未参与不计算、已参与取平均的策略如果没有任何记录该评价项在加权时按 0 分处理同时在前端标注缺数据。这样能真实反映情况但也会让综合分看起来偏低老师认为这是合理的毕竟没交作业本来就该扣分。第二个细节是权重和的校验。如果配置时权重加起来不是 1计算结果就会存在系统性偏差。我在保存配置时做了校验if abs(sum(i.weight for i in items) - 1.0) 0.001: return response_error(权重之和必须为 1)这里用 0.001 做精度控制因为浮点数直接比较等于很容易出问题。实际开发中碰过一次权重配置成 0.3、0.3、0.3加起来 0.9所有学生总分普遍低于预期排查了半天才找到原因。这种低级错误靠人工检查很难发现必须靠代码拦截。第三个细节是历史数据的追溯。老师经常会遇到这种情况学生反映分数不对。如果打分记录是不可变更的纠纷就没法处理。我在设计中加了一个modify_log表专门记录每次修改的旧值和新值。这样解释分数时老师可以看到完整的变更轨迹。这个表一开始完全没在计划里是跟老师对需求时对方提的说以前用 Excel 统计时经常有家长质疑分数有了变更记录就方便解释了。2.4 认证授权与文件处理系统涉及三种角色学生、教师、管理员。权限控制如果做得太粗学生就能改自己的成绩这是绝对不行的。我在后端用装饰器实现权限校验def role_required(role): def decorator(f): wraps(f) def decorated(*args, **kwargs): user get_current_user() if not user or user.role ! role: return response_error(无权限访问, code403) return f(*args, **kwargs) return decorated return decorator接口上直接加装饰器student_bp.route(/int:student_id/score, methods[POST]) role_required(teacher) def add_score(student_id): pass文件处理这块我处理了学生提交项目作业的场景。上传文件用 Flask 自带的request.files接收保存路径按日期分目录文件名用 UUID 重命名避免中文文件名和重名导致的问题。upload_dir os.path.join(app.config[UPLOAD_FOLDER], datetime.now().strftime(%Y%m%d)) os.makedirs(upload_dir, exist_okTrue) filename f{uuid.uuid4().hex}.{file_ext} file.save(os.path.join(upload_dir, filename))上传文件路径会存入数据库下载时通过一个路由发送文件内容。在线预览视频时前端拿到的是文件流的 URL。这里提一下热词里反复出现的StreamingHttpResponse那是 Django 的写法Flask 对应的方案是from flask import send_file app.route(/file/filename) def send_file_route(filename): file_path os.path.join(UPLOAD_FOLDER, filename) return send_file(file_path, as_attachmentFalse)配合响应头里的Content-Disposition和Content-Type参数可以控制浏览器是直接预览还是下载。我在项目里把这两个头单独处理了预览视频时Content-Type设置为video/mp4浏览器就能直接播放而不触发下载行为。当时后端返回视频流时前端播放不了排查好久最后发现问题出在响应头缺了Accept-Ranges加上之后 HLS 切片拉取就正常了。2.5 项目部署开发完的 Flask 项目不能直接扔到服务器上用flask run。我在生产环境用了 Waitress一个纯 Python 的 WSGI 服务器Windows 和 Linux 都能用配置简单几百行代码就能托管 Flask 应用。from waitress import serve from app import create_app app create_app() if __name__ __main__: serve(app, host0.0.0.0, port5000, threads8)Nginx 再做一次反向代理用户访问的是 80 端口Nginx 按路径转发到后端 5000 端口前端静态文件由 Nginx 直接托管。整个部署过程比 Django 还要快一些没有那么多中间件和配置项。3. 前端 Vue 开发页面、组件与联调3.1 前端环境与工程初始化前端我用 Vite 创建 Vue 3 项目npm create vitelatest frontend -- --template vue cd frontend npm install npm install axios vue-router4 pinia element-plus element-plus/icons-vue echartsVite 的依赖安装速度比 Webpack 时代的npm install快非常多这在日常迭代中感受很明显。安装完依赖第一件事就是配置vite.config.js里的开发代理解决跨域问题import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://localhost:5000, changeOrigin: true } } } })这样前端请求/api/student/list时Vite 开发服务器会把请求转发到http://localhost:5000避免浏览器直接跨域。上线后 Nginx 完全按照这个思路配置路径规则都不用改。3.2 核心页面实现系统的核心页面是学生列表、课程详情、评分表单和分析看板。学生列表页用 Element Plus 的el-table展示加上搜索框和分页组件。列表数据的交互逻辑基本一致页面加载时调用fetchList()完成后把数据绑定到tableData数组分页切换时重新请求对应页码。课程详情页包括课程基本信息、评价项配置列表、班级学生列表。评价项配置直接前端弹窗修改权重变化后立即调后端接口保存。这个页面的核心是展示评价维度老师看到的是每个维度当前的权重和平均分可以快速了解班级整体情况。评分表单页是老师使用频率最高的页面。页面上方是当前课程和学生的信息中间是各个评价项右侧是打分输入框和评语。每次打分提交后我加了一个 toast 提示并自动刷新该学生的综合得分预览。这里我特意做了得分预览的实时更新老师改一个权重或补一条记录总分立刻就能看到反馈非常直接。这个功能一开始没有计划是演示时老师看到后主动要求的说明过程性评价系统对即时反馈的需求很高。分析看板页是给管理员和老师看的用 ECharts 渲染。我实现了三个图表班级成绩分布柱状图、学生个人雷达图各评价项得分对比、课程总体趋势折线图。ECharts 的配置项非常灵活雷达图的indicator数组直接用当前课程的评价项动态生成。const radarOption { radar: { indicator: evaluationItems.map(item ({ name: item.item_name, max: 100 })) }, series: [{ type: radar, data: [{ value: studentScores.map(item item.score), name: studentName }] }] }evaluationItems是课程的评价项配置studentScores是学生在各评价项上的得分。这个组件写好后换课程、换学生时只需要更新这两个数组图表自动重绘。3.3 axios 封装与请求拦截axios的封装直接决定前后端联调效率。我在src/api/request.js里做统一配置import axios from axios import { ElMessage } from element-plus import router from ../router const service axios.create({ baseURL: /api, timeout: 15000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer ${token} } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) if (res.code 401) { router.push(/login) } return Promise.reject(new Error(res.message)) } return res.data }, error { ElMessage.error(error.message || 网络异常) return Promise.reject(error) } ) export default service响应拦截器里后端返回的code不为 200 时统一弹错误提示。数据直接从res.data取页面里不需要再写res.data.data这种冗长的链式调用。统一错误提示也避免了每个页面重复写try-catch。我记得还有一点很关键timeout一定要写。有一次后端处理大文件导出耗时较长前端没有设置超时一直转圈等了几分钟用户以为系统卡死了。设置 15 秒超时后虽然导出大文件时偶尔会超时但至少系统不会一直悬挂用户能明确知道请求失败了。3.4 Vue 中播放 m3u8 视频项目里的过程性评价包含项目实践环节学生要提交视频演示。视频文件统一转成 HLS 格式也就是.m3u8加一堆.ts切片然后传回服务器。前端播放用hls.js这个库封装了一个视频播放组件template video refvideoRef controls classvideo-player/video /template script setup import Hls from hls.js import { onMounted, ref, watch } from vue const props defineProps({ src: { type: String, required: true } }) const videoRef ref(null) function initPlayer() { const video videoRef.value if (Hls.isSupported()) { const hls new Hls() hls.loadSource(props.src) hls.attachMedia(video) } else if (video.canPlayType(application/vnd.apple.mpegurl)) { video.src props.src } } onMounted(initPlayer) watch(() props.src, () { initPlayer() }) /script这里做了一个兼容判断桌面 Chrome、Firefox 不支持原生 m3u8 播放用hls.jsSafari 支持原生 HLS直接设置video.src。这样一套代码兼容所有主流浏览器。前端播放过程中如果有人问Content-Type和Content-Disposition的问题我上面已经用 Flask 的send_file处理过了关键是后端返回.m3u8文件时一定要设置正确的 MIME 类型application/vnd.apple.mpegurl不然浏览器可能无法识别直接触发下载。3.5 vue-router 与整体路由组织路由层面我按角色区分了页面权限const routes [ { path: /login, component: Login }, { path: /, component: Layout, meta: { requiresAuth: true }, children: [ { path: /students, component: StudentList, meta: { roles: [admin, teacher] } }, { path: /course/:id, component: CourseDetail, meta: { roles: [admin, teacher] } }, { path: /evaluation/:studentId, component: EvaluationForm, meta: { roles: [teacher] } }, { path: /dashboard, component: Dashboard, meta: { roles: [admin] } } ] } ]路由守卫里做登录态验证和角色校验。学生角色登录后只能看到自己的成绩页面教师能看到课程和学生列表管理员额外能看到分析看板。一开始我想把所有页面都展示给所有人后来演示时学校信息中心主任说这样学生可以看到所有打分细节容易引发纠纷。前端的meta.roles和后端接口的role_required双重控制才算把权限问题彻底堵住。4. 常见问题与排查实战4.1 跨域问题开发代理与 Nginx 配置跨域是前后端分离项目里最容易遇到的问题。开发阶段 Vite 代理配好之后基本没遇麻烦上线部署时反而被 Nginx 配置坑了一回。当时 Nginx 配置写成了location /api { proxy_pass http://localhost:5000; }前端所有请求都正常但一直报 404。排查了半天发现proxy_pass后面的 URL 少了结尾的/导致 Nginx 替换路径时把/api/student/list转发成了http://localhost:5000/api/student/list而我的 Flask 蓝图注册时 url_prefix 已经是/api/student所以整个路径变成了/api/api/student后端自然 404。改成location /api { proxy_pass http://localhost:5000/; }问题才解决。这个proxy_pass末尾斜杠的坑我估计做过前后端分离的人多少都碰上过。4.2 开发中的双框架摇摆问题很多人在 Flask 和 Django 之间摇摆不定导致项目代码写了一半推倒重来。我建议在做技术选型时画一张简单的表格把项目需求逐项列出来对比两个框架哪边更容易实现。我的项目需求是接口数量 30 个左右后台管理要快速搭建数据库表 8 张权限模型三种角色。Flask 加 flask-admin 能覆盖这些需求Django 也可以但 Django 需要创建 8 个 app 还是 1 个 app需要创建自定义用户模型这些配置花的时间比 Flask 多。如果你有现成的 Django 团队选 Django 没问题如果和我一样单兵作战想快速出成果Flask 更合适。4.3 视频播放不了Content-Type 与视频编码的坑前端播放视频时遇到的一个典型问题后端返回了视频文件但浏览器打开直接触发了下载而不是播放。排查时发现响应头里Content-Type是application/octet-stream浏览器收到这个 MIME 类型会默认走下载逻辑。解决方案是后端明确设置response send_file(file_path, mimetypevideo/mp4)如果视频本身是.mov或其他格式MIME 类型也要同步调整。另外如果浏览器直接播放.mp4失败很可能是视频编码不是 H.264比如是 HEVC 编码Chrome 就播放不了。这个不在后端代码能解决的范围内需要视频转换工具转码后才能播放。4.4 数据库中文乱码Flask 连接 MySQL 时如果连接字符串没加字符集参数插入中文数据会变成乱码。这个问题第一次出现时我还以为前端传参编码错了最后发现是数据库连接配置漏了charsetSQLALCHEMY_DATABASE_URI mysqlpymysql://root:passwordlocalhost/evaluation?charsetutf8mb4同时数据库表本身的建表语句也要指定utf8mb4两个地方都对了才能保证中文正常。utf8mb4 比 utf8 更推荐能完整显示 Emoji 和生僻字。4.5 分页和统计数据不一致系统里分页查询返回的total是总记录数。刚开始我的做法是前端拿到所有数据后在前端分页结果数据量一大页面就卡死。后来改成后端分页total由数据库count查询返回前端只展示当页数据。这个改动虽然简单但对性能提升很明显。统计图表的数据也是一样全部由后端聚合接口一次返回前端只管渲染。如果前端用map、filter处理大量明细数据浏览器性能会直线下降。我一开始用 ECharts 图表时直接在浏览器里用reduce算均值数据量几百条还好等课程覆盖几百个学生、每个学生几千条记录后浏览器直接卡死。后来改成后端用 SQLGROUP BY聚合前端只接收整理好的数组性能就完全没问题了。5. 一些实战心得这个系统做完后我最大的一点体会是过程性评价系统的难点不在技术而在评价规则本身。技术上的东西数据库设计、接口开发、前端组件都是标准动作网上资料一大把。真正花精力的是和老师反复确认出勤一次算多少分缺作业扣不扣分阶段测验占比多少这些业务细节。开发前一定要把评价维度、权重分配、缺省策略逐条确认清楚画成表格让老师签字确认再动手否则后面每个细节都可能引发需求变更。技术实现上的几个体会值得单独说说。版本管理一定要用 Git前后端仓库分开管理。我习惯后端一个仓库前端一个仓库各自由语义化版本号推进。有一次需求大改前端代码回滚到了三天前的版本如果没版本控制改动就全丢了。早期手动复制备份的习惯现在基本废了。接口文档要随代码更新。我用 Postman 同步接口集合开发阶段每写完一个接口就在 Postman 里发一次请求测试顺手把接口保存到集合里。这样一个项目做完接口文档也就同步更新完了以后维护的时候随时可以查看。密码管理要注意安全。数据库里的用户密码绝对不能明文存必须用哈希算法处理。Flask 里用werkzeug.security.generate_password_hash和check_password_hash就够了比你自己写加密逻辑安全得多。出现任何问题时先看日志。后端把日志写到文件里按天滚动前端开发时打开控制台保留网络请求的响应信息。很多问题两边对一下日志和请求参数原因就清楚了。调试跨域问题尤其如此。从接到需求到完整上线这个项目前后花了大概三周时间。中间最耗时的不是写代码而是跟老师反复确认评价规则的细节。系统本身不复杂只要把评价规则理解透技术实现是水到渠成的事。如果你正在做类似系统我的建议是先花两天把评价规则设计清楚再动手写代码进度反而会更快。