Flask+Vue构建医院康复预约系统:设计与实现
1. 项目概述与整体设计思路1.1 医院康复预约到底在解决什么问题康复科这个场景很有意思它跟普通门诊挂号有本质区别。普通挂号只需要科室、医生、时间三个信息康复预约则多出了项目归属和疗程连续性。一个脑卒中恢复期的患者可能一周要去三次理疗每次一小时且最好固定同一个治疗师否则康复方案容易断档。如果仍然用传统的人工排班电话预约会出现三个典型痛点工作日高峰时段治疗师被约满、迟到和临时取消导致时段空转、治疗师之间时间撞车无人查重。我在设计这个系统时把核心需求拆成了三类角色。患者端要能查看康复项目的介绍、看到某治疗师某天的可用时段、在线上完成预约并查看我的预约记录治疗师端要能设置本人可接诊的时段、确认或取消预约、查看当日排班管理员端则负责患者基础信息维护、治疗师与项目建档、全科预约数据看板。这套角色权限划分直接决定了后面的接口设计也决定了前端页面路由的区分策略所以第一步不是写代码而是把角色和状态边界画清楚。1.2 技术选型为什么是 Flask 和 Vue而不是其他方案这个项目当时有四种组合可选DjangoVue、FlaskVue、FastAPIReact、SpringBootVue。我最终选了 FlaskVue核心原因偏务实。Django 确实自带 Admin 和 ORM但对前后端分离场景来说Django 的复杂度有一半用不上而且模板和表单的理念会诱导新手继续写服务端渲染偏离了我想要的 API 优先架构。FastAPI 的异步性能和自动文档确实香但它对 SQLAlchemy 事务和 Flas 生态里很多现成插件的兼容方式和 Flask 不完全一样项目组其他人不熟。这里顺便对比一下 Flask 和 FastAPI也是群里被问得最多的Flask 的请求模型是同步阻塞但实际业务里预约系统属于低频事务型接口单机每秒几十个请求很够用FastAPI 的优势在 IO 密集和并发规模大的场景比如实时推送、流式接口这里有后面接入康复设备的 IoT 数据采集但初期根本没必要引入异步复杂度。FlaskSQLAlchemy 的搭配经过十年以上大规模项目检验出问题网上都有答案FastAPI 的 Pydantic 确实让参数校验更优雅但 Flask 配合 Marshmallow 同样能做最终选了 Flask核心因素就是团队熟练度、插件生态和可维护性。表格对比一下不同技术选型的适配场景方案上手难度适合场景不适合场景Flask Vue低中小型管理类系统、传统多点业务大规模高并发实时系统Django Vue中后台复杂、权限模型繁重的系统轻量 API 为主的项目FastAPI Vue中高高并发、流式接口、AI 推理服务强耦合 Django Admin 思维的项目SpringBoot Vue高企业级多系统集成、Java 团队小团队快速原型验证前端选 Vue 的理由也很简单生态中庸不激进中文资料非常全从 Vue2 过渡到 Vue3 组成都可以接受而且招聘市场上会 Vue 的工程师比会 React 的多以后接手的人压力小。组件库我用 Element Plus样式统一又省时间后面所有页面的表格、表单、弹窗不用自己写 CSS。1.3 整体架构与系统模块规划系统结构分三层。最底层是 MySQL 数据库存储患者、治疗师、项目、时段、预约五类核心数据外加管理员账号。中间层是 Flask 提供的 RESTful API统一返回 JSON 格式通过 JWT 做登录态校验通过装饰器做角色权限限制。最上层是 Vue 单页应用用 Vue Router 管理页面跳转用 Pinia 管理用户会话状态用 Axios 跟后端通信。前后端分离的关键约束是后端不关心页面长什么样前端不关心数据从哪个表来。接口返回什么字段前端就消费什么字段两边维持一个约定好的契约格式。这个项目里我定义了统一的响应结构——{code:200, msg:success, data:{...}}——所有接口都遵守前端拦截器统一处理code不要在页面里写一层层判断。模块规划上按业务拆成六大块用户认证模块、患者信息管理、康复项目管理、治疗师排班管理、预约记录管理、统计看板。每一个模块在后端都是独立的 Flask Blueprint前端对应独立的 Vue 页面目录这样的好处是开发后期可以多人并行一个人负责预约模块一个人负责排班模块互不覆盖。2. 核心业务逻辑与数据库设计2.1 数据模型的五张核心表数据库是整个系统的地基我在这里花的时间比写接口还多。康复预约系统的核心表有八张但主干链路是五张patient患者表、therapist治疗师表、rehab_item康复项目表、time_slot时段表、appointment预约记录表。另外还有admin管理员表、therapist_item关联表表示治疗师擅长哪些项目。患者表字段并不复杂id、name、phone、gender、age、medical_record_no病历号、created_at。注意要加唯一索引在phone或medical_record_no上预约系统的患者凭证就是电话号码或病历号后面查询记录全靠它。治疗师表除了基本字段外要有title职称、department科室、max_appointments_per_day每日最大预约量这个字段是后面时间冲突校验的一个兜底条件。康复项目表要存name、category分类、duration_minutes单次时长、price价格、description描述。时长很重要它不直接存在预约里而是让系统计算治疗师的时间被占用几格。时段表time_slot是提前用脚本生成的把每一天从 8:30 到 17:30 切成 30 分钟一格的时段slot_date、start_time、end_time、therapist_id、is_booked五个字段。时段表的优势是预约冲突检测从日期时间运算简化成了查记录这是这个项目最核心的一个设计决策。预约记录表appointment包含patient_id、therapist_id、rehab_item_id、slot_id、status、remark。status是字符串枚举取值有pending待确认、confirmed已确认、completed已完成、cancelled已取消、noshow爽约。为什么不直接用布尔字段因为预约的全生命周期管理需要一个状态机后面讲。2.2 预约时段与冲突检测的实现预约系统最怕撞车一个治疗师四点到五点的那格时段同时被两个患者约走这种数据事故一旦发生康复科现场就得争执打架。设计上我用两层防护。第一层是数据库唯一约束。在appointment表上建一个联合唯一索引字段是therapist_id slot_id status其中status限定为有效状态pending 和 confirmed。也就是说同一治疗师同一天同一时段只允许存在一条未取消的预约记录。这层防护是最终底线任何代码层面的逻辑 bug 都没办法绕过数据库约束。第二层是业务层的预校验。在插入预约之前先把待选时段通过SELECT ... FOR UPDATE锁定再检查is_booked如果已经预约就返回提示该时段已被预约。有人会问直接用唯一索引就够了为什么还要加业务锁实际原因是用户体验如果你等到插入时报唯一约束错误前端拿到的报错信息很难做到友好但如果你在查询阶段把已被占的时段直接置灰患者根本不会约到已经满的时段。前者是兜底后者是体验。时段表生成脚本的做法很简单循环日期范围加时间区间用 Python 的datetime和timedelta组合生成。要注意节假日不上班所以排班表里还要排除周六周日这个可以在生成时段时配置is_weekend参数也方便管理员后续按节假日手动调整。2.3 预约状态机的流转设计状态机是预订类系统里最容易做乱的地方。我见过很多系统把状态字段随便拼如把取消状态和设备状态搞成一锅粥。这次预约系统只用了五种状态但五状态之间的流转关系必须收敛清楚。初始化是pending患者提交预约后进入待确认。治疗师或管理员操作确认后进入confirmed这意味着时段的is_booked置为 true其他患者看到该时段已满。到达治疗当天治疗师点击完成状态转向completed同时如果未进行治疗则标记为noshow爽约。pending和confirmed状态都可以发起cancelled取消但completed和noshow是终态不能回退。状态机的实现可以写在Appointment模型里也可以写成独立的 service 层。我在这个项目里用的是 Flask-SQLAlchemy 的模型方法在Appointment类上定义cancel()、confirm()、complete()、noshow()四个方法每个方法内部先检查当前状态是否允许跳转不允许就抛异常允许则执行状态变更并同步更新时段表的is_booked字段。这样做的好处是状态逻辑收敛在一处不是散落在各个视图函数里未来加新状态只需要改这一个模型。3. Flask 后端开发接口从定义到落地3.1 项目初始化与依赖配置后端我用的是经典的 Flask 应用工厂模式项目结构如下rehab-backend/ ├── app/ │ ├── __init__.py # 应用工厂创建 app 实例 │ ├── models/ # SQLAlchemy 模型 │ │ ├── __init__.py │ │ ├── patient.py │ │ ├── therapist.py │ │ ├── rehab_item.py │ │ ├── time_slot.py │ │ └── appointment.py │ ├── blueprints/ # 蓝图路由 视图 │ │ ├── __init__.py │ │ ├── auth.py # 认证登录 │ │ ├── patient.py # 患者模块 │ │ ├── therapist.py # 治疗师模块 │ │ ├── appointment.py # 预约模块 │ │ ├── slot.py # 时段查询 │ │ └── dashboard.py # 看板统计 │ ├── utils/ # 工具函数 │ │ ├── response.py # 统一响应封装 │ │ ├── decorators.py # 角色权限装饰器 │ │ └── validators.py # 参数校验 │ ├── config.py # 配置文件 │ └── extensions.py # db, jwt, cors 等扩展实例 ├── migrations/ # Flask-Migrate 迁移目录 ├── requirements.txt └── run.py # 启动入口依赖文件requirements.txt里核心打包如下flask2.3.3 flask-sqlalchemy3.1.2 flask-cors4.0.0 flask-jwt-extended4.5.2 flask-migrate4.0.5 mysqlclient2.2.0 marshmallow3.20.1 gunicorn21.2.0这里有个版本坑要注意Flask 3 开始把before_first_request移除了网上很多老教程用这个钩子初始化数据照着抄会直接报错。我选 Flask 2.3 主要是为了稳定兼容后面不需要升级也能跑完整个项目等你熟练了再往 Flask 3 迁移不迟。另一个坑是mysqlclient在 Windows 上安装比较麻烦如果你用 Windows 开发建议配置pymysql作为替代驱动连接字符串写法不一样但功能完全等价。3.2 JWT 登录认证与角色权限控制登录模块是整个安全链路的入口。我用 flask-jwt-extended 生成 JWT Token用户登录成功后返回access_token和refresh_token。access_token生命周期设两小时refresh_token设七天前端在 Axios 响应拦截器里发现 401 时自动用刷新 Token 重新换取实现无感续期。Token 里除了存用户 id还要存role角色字段。这个项目有三种角色patient患者、therapist治疗师、admin管理员。为什么不在鉴权时每次查数据库拿角色因为每一次请求都多一次数据库查询性能就差在细微处尤其是预约列表这种高频接口。JWT 里的role字段是签发时写死的角色变更在有效期内的概率极低可以容忍少量延迟。权限控制使用自定义装饰器。flask-jwt-extended 提供了jwt_required()校验登录但角色校验要自己写我用一个简单版本实现from functools import wraps from flask import jsonify from flask_jwt_extended import verify_jwt_in_request, get_jwt def role_required(*roles): def wrapper(fn): wraps(fn) def decorator(*args, **kwargs): verify_jwt_in_request() claims get_jwt() if claims.get(role) not in roles: return jsonify({code: 403, msg: 权限不足}), 403 return fn(*args, **kwargs) return decorator return wrapper访问预约接口时在视图函数上方加上role_required(patient)即可。注意装饰器的顺序jwt_required()要在role_required下方否则get_jwt取不到 claims这个顺序我踩过好几次。3.3 CORS 跨域配置与全局异常处理前后端分离后跨域问题几乎必现。前端跑在 localhost:5173后端跑在 localhost:5000两者端口不同浏览器默认阻止跨域请求。最简单的处理是使用 flask-cors 扩展一行代码允许所有来源from flask_cors import CORS CORS(app, supports_credentialsTrue, resources{r/api/*: {origins: *}})实际开发中不要一味用通配符生产环境要限定前端域名否则潜在安全隐患有人写脚本带着你的 Token 去请求接口。更稳妥的写法是把origins配置为一个列表来自.env环境变量部署时修改即可。另外注意如果接口需要携带 Cookiesupports_credentialsTrue必须开启同时前端 Axios 也要设置withCredentials: true否则取不到 Cookie。全局异常处理是 Flask 项目里不可或缺的一环。默认情况下 Flask 的异常会返回 HTML 错误页前后端分离后前端根本解析不了。我写了一个统一的异常处理器from flask import jsonify app.errorhandler(Exception) def handle_exception(e): if isinstance(e, HTTPException): code e.code msg e.description else: code 500 msg 服务器内部错误 # 记录完整堆栈到日志 app.logger.error(fException: {e}, exc_infoTrue) return jsonify({code: code, msg: msg, data: None}), code这样做以后前端拦截器只需要判断response.data.code不等于 200 就在页面上弹出msg提示因此所有接口的错误提示格式统一排查问题的时候也能在后端日志里留痕。3.4 核心预约接口的实现细节预约接口是核心交互我拆成三个接口查可用时段、提交预约、取消预约。查询可用时段接口的返回格式是前端渲染日历的关键设计时字段必须完整。我给前端的 JSON 里包含slot_id、date、start_time、end_time、is_booked五个字段前端的日历组件根据is_booked把时段渲染成灰色或绿色。创建预约的接口实现里面业务逻辑按四个步骤走第一步校验患者 id 和治疗师 id 是否存在第二步校验该时段是否属于该治疗师且未被预约第三步校验预约日期是否在未来第四步插入预约记录并把时段is_booked改为 true。查询和插入之间我用数据库事务包裹配合SELECT ... FOR UPDATE防止并发问题这个细节后面单独讲。取消预约的实际写法如下用commit包裹两个数据变更bp.route(/appointments/int:appointment_id/cancel, methods[POST]) role_required(patient) def cancel_appointment(appointment_id): appointment Appointment.query.get(appointment_id) if not appointment: return resp(404, 预约不存在) try: appointment.cancel() # 方法内改变状态并释放时段 db.session.commit() return resp(200, 取消成功) except StateTransitionError as e: return resp(400, str(e))状态机的好处在这里体现视图函数不需要写一堆 if 判断当前状态是否允许取消模型方法内部统一处理。3.5 数据库迁移 Flask-Migrate 使用开发过程中表结构必然要改比如我一开始设计的time_slot表没有is_booked字段后来在预约状态流转时需要就通过 Flask-Migrate 平滑升级没有手动删表重建。先初始化迁移环境flask db init flask db migrate -m add is_booked to time_slot flask db upgrade不用 Flask-Migrate 的团队往往直接修改模型定义然后db.create_all()这样一旦有线上数据就会全部丢失。有了迁移机制以后每次改模型生成一个新的迁移版本线上执行flask db upgrade就能完成增量更新团队协作也不会出现你加了字段我没加的窘况。有一点必须在团队里统一迁移文件是受版本控制的不要直接改已经生成过的迁移脚本每一次表结构变更都通过新迁移来完成。否则一旦有人执行过旧迁移再拉取新脚本就会出现差异校验失败。4. Vue 前端开发从脚手架到页面交互4.1 环境搭建与项目初始化前端部分我用 Vite 搭建 Vue3 项目相比 Vue CLI 的热更新速度快很多。在搭建之前先确认 Node 环境node -v建议 18 以上npm -v6.9 以上。如果电脑还没装 Node去官网下载 LTS 版本即可记得勾选Add to PATH之后终端才能直接执行node命令。初始化命令是在后端项目同级目录下创建的npm create vitelatest rehab-frontend -- --template vue cd rehab-frontend npm install npm install axios vue-router pinia element-plus安装完成以后在main.js里注册 Element Plusimport { createApp } from vue import App from ./App.vue import ElementPlus from element-plus import element-plus/dist/index.css import router from ./router import { createPinia } from pinia const app createApp(App) app.use(ElementPlus) app.use(router) app.use(createPinia()) app.mount(#app)这里有个体验细节Element Plus 全量引入会让首屏包体变大但开发效率高。这个项目的用户都是医院内部和患者使用不需要追求极致的首屏加载速度全量引入换来的是不用按需配置取舍是合理的。如果是面向 C 端大流量场景就用unplugin-vue-components做按需引入。4.2 路由配置与动态路由守卫项目采用两种路由混合方式。固定路由是登录页、注册页、项目介绍页等对所有人生效的页面动态路由是登录后根据角色动态添加的页面。以患者角色为例登录成功后返回的 user 信息里有role字段前端根据角色把对应页面路由注册进 Vue Router。这样做的好处是患者不会看到管理端的菜单和路由直接在路由层面隔断了越权访问而不仅仅靠按钮隐藏来做界面安全。路由守卫设置如下router.beforeEach((to, from, next) { const userStore useUserStore() if (to.meta.public) { next() } else { if (!userStore.token) { next({ path: /login, query: { redirect: to.fullPath } }) } else if (to.meta.roles !to.meta.roles.includes(userStore.role)) { ElMessage.error(无权限访问该页面) next({ path: / }) } else { next() } } })动态路由的调试是新手最容易迷茫的环节明明在菜单里点击跳转没问题但刷新页面后路由突然失效。原因很简单Vue Router 的 addRoute 添加的路由是存在内存中的刷新后重新初始化就丢失了。解决方案是刷新后从后端重新拉取用户信息和权限再重新动态注册路由。我在 Pinia 的 user store 中做了一个fetchUserAndRoutes方法在应用初始化时调用确保刷新不掉线。4.3 状态管理与 Axios 封装前端的状态管理用 Pinia主要管理三块用户的 Token 和基本信息、当前选择的预约筛选条件、页面加载状态。Token 存储用 localStorage每次请求在 Axios 请求拦截器里自动附带同时如果 Token 不存在直接拦截并跳到登录页。Axios 封装是一个独立模块request.js里面做三件事import axios from axios import { ElMessage } from element-plus import { useUserStore } from ../stores/user import router from ../router const service axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) service.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers[Authorization] Bearer ${userStore.token} } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.msg || 请求失败) if (res.code 401) { userStore.logout() router.push(/login) } return Promise.reject(new Error(res.msg)) } return res }, error { ElMessage.error(error.response?.data?.msg || 网络异常) return Promise.reject(error) } ) export default service封装好以后每个页面组件不需要关心 Token 怎么带、错误怎么提示只要写业务代码调用request.get(/api/slots)即可。时间一长你就会发现Axios 封装质量决定了前端的开发效率因为业务代码里 70% 都是请求和回显。4.4 预约页面的组件实现预约页面是系统里交互复杂度最高的页面拆成三个子组件日期选择器、时段日历、预约表单弹窗。日期选择器用 Element Plus 的el-calendar组件默认显示整月日期点击某天时触发接口查询该天的时段数据。时段日历用表格渲染行为逻辑是已约满的时段渲染成 disabled 状态点击无效当前时间已过的时段标记为过期同样不可选可用时段高亮点击后弹出预约表单。这里有一个关键细节后端返回的时段是后端时区下的日期时间前端必须确认时区一致否则会出现日期偏移。我开发时统一用08:00时区前端不做本地时区转换直接用字符串比较避免 DST 类的麻烦。预约表单的字段包括患者姓名、联系电话、备注等患者端登录后自动填充如果是管理员代预约还需要手动选择患者。提交成功后再调一次时段列表接口刷新日历状态。整个链路不长但把用户操作逻辑串起来需要前后端接口定义得足够清晰。5. 前后端联调、部署与排错5.1 联调过程中最容易踩的坑联调阶段往往是整个项目最痛苦的阶段前后端各写各的一对接各种一一不符。我总结了三类高频问题。第一类是字段命名不一致后端习惯snake_case前端习惯camelCase比如后端返回user_id前端却用的是userId页面一直拿不到值。解决办法是在后端定义序列化输出时就统一改成前端想要的格式或者直接约定所有接口字段都用snake_case前端配合不转。无所谓哪种关键是白纸黑字写进接口文档别靠口头约定。第二类是时间格式的坑。后端用 Python 的datetime在 JSON 序列化时默认格式是2024-05-20T14:30:00前端如果用new Date(res.date)解析这个格式在现代浏览器能解析但在一些 App 内嵌 WebView 里就可能报 Invalid Date。稳妥做法是后端统一输出2024-05-20 14:30:00格式前端把它当字符串处理或手动转new Date(str.replace( , T))。第三类是接口 404 问题。Flask 路由默认的 404 是 HTML 响应前端 axios 响应拦截器里的错误提示可能拿到的是 HTML 字符串。如果后端没有做统一的异常处理前端就会弹出一堆乱码排查时第一直觉是跨域或地址写错但实际往往是路由拼写差异。5.2 生产环境部署方案本地跑通以后部署是另一个战场。前端用npm run build打包后会生成一个dist目录里面是纯静态文件。后端着用 Gunicorn 作为 WSGI 服务启动 Flask 应用。部署架构用 Nginx 做反向代理短板路径分开/指向前端静态目录/api转发到后端 Gunicorn 监听的本机端口。Nginx 配置核心部分server { listen 80; server_name your.domain.com; # 前端静态文件 root /var/www/rehab-frontend/dist; index index.html; # 后端 API 反向代理 location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 前端路由 history 模式需要回退到 index.html location / { try_files $uri $uri/ /index.html; } }Gunicorn 启动命令如下gunicorn -w 4 -b 127.0.0.1:8000 run:app-w 4表示开四个 worker 进程对应四核 CPU 的典型配置。这里注意 Gunicorn 只适合部署在 Unix 系服务器上如果公司服务器是 Windows得用 waitress 替代两者启动命令格式基本一样。很多朋友问 Vue 打包后为什么打开是空白页面。这基本是静态资源路径问题要么是 Vite 配置里的base没设置为/要么是 Nginxlocation路径配错。解决方案是在vite.config.js中设置base: /同时确保 Nginx 的 root 路径指向dist/index.html所在目录本地部署前也可以用serve dist先做一个自测。5.3 常见问题速查表在实际开发里我记录了下面这份问题排查速查表很多问题不是一次就能定位到根因的需要按现象逐层排查常见现象可能原因排查步骤页面请求接口报 401Token 过期、未携带检查 localstorage 是否有 token检查请求头是否有 Authorization接口返回 403角色权限不足核对登录用户的 role检查装饰器 roles 配置预约时报时段冲突时段已被占、状态未变查 datetime 的时区查时段is_booked字段是否变更前端刷新路由 404Vue Router history 模式未配 fallbackNginxtry_files配置缺失中文乱码数据库字符集不是 utf8mb4建库时指定utf8mb4连接串加charsetutf8mb4CORS 跨域报错后端未加 CORS 配置检查flask-cors是否配置了目标域名Gunicorn 启动报端口占用端口被其他进程占用lsof -i :8000查进程kill 后重启第三行这种报错往往是业务逻辑 bug不要因为是偶发就跳过在数据层面查一下硬件表结构看到底有没有多条预约同时插进去。5.4 并发预约与数据一致性处理预约系统的并发问题集中体现在同一时刻多个患者抢同一个治疗师时段。如果不用任何手段处理两个请求同时读到时段未预约然后同时插入预约记录数据库里就会出现两条有效预约这个后果很严重。我在设计里用了三层对策。第一层是数据库唯一约束也就是前面讲过的联合唯一索引这是最底层的保险。第二层是事务内行级锁在 Flask 视图函数里手动开启事务执行查询时加上with_for_update()锁定时段行注意 SQLite 不支持行锁所以生产环境必须用 MySQL。第三层是前端层面的防重复提交提交预约按钮单击后立刻变成 loading禁用二次点击。三层叠加基本可以防住多数并发场景。这里分享一个调优点把with_for_update()尽量放在事务开始后的第一条语句锁定的时间越短越好锁住的行越少越好。比如你在读患者信息时不需要锁时段行那就在查询时段之前先查患者最后才锁时段避免长时间持有锁影响其他患者的预约。锁的范围要及时释放事务提交后锁自动解除。我在实际压测时模拟过同时 50 个请求抢同一个时段最终只有一条能预约成功其余全部返回该时段已被预约。这离不开数据库层的兜底单靠业务逻辑是有破绽的分布式环境尤其如此。写在最后整个系统从设计到上线前后搞了三周中间改过两版数据库结构踩过的坑基本都记录在前面几节里了。如果只能给出一条建议那就是项目开工前先花两天时间把数据模型和接口文档定清楚剩下的 CRUD 只是体力活。很多开发同学一上来就想着写接口、写页面最后往往在数据库字段对不上和状态逻辑混乱中反复返工磨掉的是整个团队的耐心。关于后续扩展这个项目还可以在三个方向迭代一是增加消息通知预约状态变化时通过公众号或短信触达患者否则患者可能不知道预约被确认二是引入治疗师工作量统计按项目、时段、治疗师维度输出康复科运营报表三是接入电子病历数据让治疗师在查看预约时能看到患者的康复进度。这三个方向都不需要动已有核心架构都是在现有接口上扩展这也印证了前期设计的重要性。最后再分享一个小技巧如果你也是从 Flask 后端起手学 Vue先别急着把所有组件都抽象成通用高阶组件等页面重复出现三次以上再抽象也不迟。前期多写一点重复代码反而能让你更快摸清每个组件的真实用法后面再重构效率高得多。

相关新闻

基于Spring Boot+Vue的校园互助交易平台开发实战

基于Spring Boot+Vue的校园互助交易平台开发实战

1. 选题拆解:校园互助交易平台到底在研究什么1.1 从一个很朴素的痛点说起高校里的闲置物品交易、二手教材买卖、代取快递、拼车拼单、技能互助,这些需求在校园里是真实存在且高频发生的。但当前大部分信息的流转方式是QQ群、微信群、表白墙、朋友圈&…

2026/10/10 20:34:19 阅读更多 →
Linux命令高效学习:从知识框架到组合实战

Linux命令高效学习:从知识框架到组合实战

1. 先建立整体认识:Linux命令到底该怎么学很多人一提到Linux命令,第一反应就是“记不住”,然后是“命令太多太杂,不知从何下手”。这其实是刚入门时最常见的误区。Linux命令看起来成千上万,但抛开那些一年都用不上一两…

2026/10/10 20:34:19 阅读更多 →
一个人 = 50人游戏工作室?我发现了AI游戏开发的终极答案

一个人 = 50人游戏工作室?我发现了AI游戏开发的终极答案

文章目录 一、你有没有想过,一个人也能运营一家游戏公司? 二、功能全景图:这不是一个工具,是一整套操作系统 三、三层代理架构:完美复刻游戏大厂的组织设计 3.1 架构总览 3.2 每一层到底干什么? 3.3 协作规则:AI不会乱打架的秘密 四、模块架构图:五大组件如何协同工作 …

2026/10/10 20:33:18 阅读更多 →

最新新闻

Jev设计哲学实践:类型安全、概率校准与代码掌舵

Jev设计哲学实践:类型安全、概率校准与代码掌舵

1. 从一个“反直觉”的设计选择说起第一次接触 Jev 这套设计思路的时候,我其实是有点抗拒的。原因很简单——它把“概率”这件事摆到了台面上,而且要求开发者主动去“校准”它。这跟我们过去十几年写业务代码的习惯完全相反。以前我们写代码,…

2026/10/10 21:20:07 阅读更多 →
分布式任务调度核心原理与实战:从定时任务到分片、幂等与选型

分布式任务调度核心原理与实战:从定时任务到分片、幂等与选型

1. 从单机定时任务说起:为什么需要分布式任务调度1.1 你曾经写过的那些定时任务很多人的分布式任务调度之路,都是从一段简单的cron表达式开始的。我自己刚工作那会儿,项目里最常见的就是 SpringScheduled注解,或者干脆在服务器上挂…

2026/10/10 21:20:07 阅读更多 →
「比 Codex 省 40% token」刷屏 GitHub:Unreal Agent 性能零损耗是真香还是 PPT?

「比 Codex 省 40% token」刷屏 GitHub:Unreal Agent 性能零损耗是真香还是 PPT?

「比 Codex 省 40% token」刷屏 GitHub:Unreal Agent 性能零损耗是真香还是 PPT? 【免费下载链接】unreal-agent Async-first agent harness 项目地址: https://gitcode.com/gh_mirrors/un/unreal-agent 九月底,一个名为 Unreal Agent…

2026/10/10 21:20:07 阅读更多 →
uni-app x `uni.getAppBaseInfo` 应用基本信息获取指南:API 用法、多端字段与源码实现解析

uni-app x `uni.getAppBaseInfo` 应用基本信息获取指南:API 用法、多端字段与源码实现解析

示例工程前端移动开发跨平台 【免费下载链接】uni-app A cross-platform framework using Vue.js 项目地址: https://gitcode.com/gh_mirrors/un/uni-app 点击查看 免费下载 uni.getAppBaseInfo 是 uni-app x(以及 uni-app)中用于获取应用基…

2026/10/10 21:20:07 阅读更多 →
端侧小模型双子星选型指南:星火 X2.5 的 1.7B 与 4B,该带哪个上生产

端侧小模型双子星选型指南:星火 X2.5 的 1.7B 与 4B,该带哪个上生产

端侧小模型双子星选型指南:星火 X2.5 的 1.7B 与 4B,该带哪个上生产 【免费下载链接】Spark-X2.5-4B Spark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智…

2026/10/10 21:20:07 阅读更多 →
647回文子串与516最长回文子序列:区间DP两种典型玩法全解析

647回文子串与516最长回文子序列:区间DP两种典型玩法全解析

各位打卡代码随想录的伙计们,第四十五天来了。今天这两道题——647 回文子串、516 最长回文子序列——看起来名字只差两个字,实际上一个是把字符串切成一段段判断"是不是回文",另一个是允许跳跃地凑出"最长回文有多长"。…

2026/10/10 21:19:06 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →