新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于Python与Flask的救援队救助管理系统设计与实现

发布时间:2026/9/26 18:38:12来源:尧图网络
基于Python与Flask的救援队救助管理系统设计与实现
作为一个电子信息/计算机方向的老学长每年毕业季前后都会碰到不少学弟学妹问“毕业设计到底做什么”。今天就想借着这个项目聊聊我实际开发“基于Python的救援队救助管理系统”的完整过程。这个项目和我以往做的纯粹图书管理、学生管理系统不一样它带有一点“流程性”和“紧迫性”的业务特征处理的是救援任务从接收、派单、执行到反馈的全链路数据。我尽量把设计和实现拆开讲包括代码结构、数据表设计、接口逻辑以及踩过的坑希望给正在选题或者卡在中期实现的同学一个可以参照的模板。1. 系统整体设计与思路拆解1.1 项目定位与技术选型逻辑先说说为什么选这个题目以及后台实现方案为什么是“Python Flask MySQL”这套组合。救援队救助管理系统本质上属于典型的MIS管理信息系统核心诉求就三个把人员管好、把任务派好、把物资和救助记录留痕。所以后端Web框架我选了Flask。相比DjangoFlask更轻量路由和ORM的接入方式对新手更透明改起来也容易而且做毕设足够了。前端没有采用前后端分离方案直接用Jinja2模板配合Bootstrap动静分离但不用额外架设Node服务部署时一个服务带起全部页面省掉很多环境问题。MySQL 5.7存业务数据Redis缓存会话和部分热点数据不过为了降低环境复杂度会话先放在Flask服务端里Redis只用来做救援地图的临时坐标缓存。之前我也纠结过是否用Django自带的Admin生成后台后来放弃了。救援队系统最核心的任务分派、救助状态回传、物资出入库审核这三个环节Admin没法覆盖业务动态逻辑如果硬在Admin上改会造成一堆钩子方法复杂度反而上升。自己动手写蓝图Blueprint更可控分模块管理代码量也不多。1.2 业务角色与核心流程梳理这个系统有四类角色系统管理员、调度员、救援队员和普通求助者。管理员负责基础数据维护和账号审核调度员负责接收求助信息、评估优先级、生成任务并指派队员队员通过系统接收任务、上报执行进度求助者可以提交救助申请并查询反馈。为了让救援流程有完整的闭环我把核心业务状态设计成了几个阶段待受理、已派单、执行中、待复核、已完成、已归档。整个任务生命周期里每一次状态变更都会追加一条流程记录方便追溯“谁在什么时间做了什么操作”。爬取了相关项目后发现很多类似系统的通病是“录入视角大于业务视角”每个模块都在做增删改查但跨模块的数据联动很弱。我设计系统时就把任务和救助记录绑定一条求助记录可以生成多个救援任务一个救援任务完成后自动回写求助单的状态和救援结果。这样在首页统计时可以直接展示本月受理求助数、出队次数、各类事故占比让数据之间形成关联而不是各模块孤立。1.3 功能模块地图用户认证与权限管理登录验证、不同角色菜单渲染、管理员对队员账号的启用/禁用求助受理模块求助人信息登记、求助描述、位置标记、紧迫程度评估任务调度模块调度员创建任务、指派队员或小队、设定截止时间执行反馈模块队员上报执行情况、现场照片上传轨迹记录、任务里程碑物资管理模块装备入库出库、物资申领审批、库存预警数据统计模块救援趋势、事故类型分布、队员出队次数排名日志审计关键操作留痕、异常登录监测提示毕设项目中不建议功能模块铺太开但都做不深。比如“数据统计”用一个Dashboard展示核心指标即可没必要单独做一套BI工具。2. 环境准备与项目工程结构2.1 基础环境搭建整套系统我在Windows 11上开发Python版本用的是3.10.x。依赖的第三方库我都固定了版本避免将来部署到别的机器时版本冲突。核心依赖如下Flask2.3.3Web框架Flask-SQLAlchemy3.0.5ORMPyMySQL1.1.0MySQL驱动Flask-Login0.6.2用户会话管理Flask-WTF1.2.1表单校验与CSRF保护Werkzeug2.3.7密码哈希和路由底层依赖pandas2.0.3导出统计报表用openpyxl3.1.2Excel文件读写安装命令建议统一用pip install flask2.3.3 flask-sqlalchemy3.0.5 ...这样固定版本不建议直接pip install flask因为最新版Flask的接口变化容易带崩旧代码。2.2 项目目录划分开发时我按功能模块拆分了包结构而不是所有文件堆在根目录下rescue_system/ ├── app/ │ ├── __init__.py # 创建Flask实例、注册蓝图、初始化扩展 │ ├── config.py # 项目配置 │ ├── models/ # ORM模型 │ │ ├── user.py │ │ ├── rescue_order.py │ │ ├── task.py │ │ └── material.py │ ├── forms/ # 表单类Flask-WTF │ ├── views/ # 蓝图路由 │ │ ├── auth.py │ │ ├── dispatch.py │ │ └── statistic.py │ ├── utils/ # 工具函数 │ │ └── decorators.py │ └── templates/ # Jinja2模板 ├── tests/ # 简单单元测试 ├── run.py # 启动入口 └── requirements.txt从开发体验讲这种结构让后续写数据库迁移、增加接口都不至于手忙脚乱。很多未分层的毕设代码虽然能跑但答辩时老师问“优化空间在哪里”你至少能答出模块化拆分已做了一部分。3. 数据库表设计与核心字段说明3.1 数据表概览救援救助系统的数据表我总共设计了9张核心表6张辅助表3张。这里选几张最重要的展开讲用户表tt_userid主键自增username唯一登录名password_hash存哈希不存明文role_type0管理员 1调度员 2队员 3求助者real_name / phone / status求助登记表tt_rescue_orderid / order_no业务编号例如 RS20241215001applicant_name / applicant_phonelocation事故地点描述longitude / latitude经纬度disaster_type事故类型火灾、水灾、山地迷路、交通事故等urgency_level紧急性0一般 1紧急 2特急status当前状态1待受理 2已派单 3执行中 4已完成 5已归档 6已取消create_time救援任务表tt_taskid / task_noorder_id外键关联求助登记表team_leader带队人member_ids队员ID组合逗号分隔task_content任务描述start_time / deadlinecomplete_timestatus0未开始 1进行中 2已完成 3无法完成feedback完成反馈物资表tt_materialid / material_name / material_typetotal_count / available_countunit / locationthreshold库存预警阈值物资出入库记录表tt_material_logid / material_id / change_count / change_type入库或出库operator_id / create_time / remark3.2 表关系设计要注意的三个点救援任务和求助登记之间的外键关联要有但别滥用物理外键。我建议在ORM层面用relationship和foreign key维护一致性数据库物理外键可以不加因为后期测试造数据或者误操作删除时物理外键会带来很多麻烦。Aliyun、腾讯云上的MySQL实例开了外键检查后删除归档数据会连续报错很浪费时间。时间和状态字段尽量使用系统当前时间不要靠前端传。例如任务状态变更的时间应当在后端datetime.now()直接写防止篡改。我在写更新接口时约定凡是涉及状态变更的接口只允许后端代码修改update_time字段接口入参里出现该字段则直接忽略。经纬度字段统一存DECIMAL(10,6)不要存成字符串。否则做距离排序、地图标注时还要转换而且Python端读到字符串类型的Decimal也容易出意想不到的精度问题。3.3 救助单编号的生成规则订单编号我设计了RS 年月日 三位当天流水的组合比如RS20250301001。生成方式是在同一个事务里先查当天已有单数再加一然后生成编号。这样能避免并发时编号冲突。数据库层面再加一个唯一索引就算极端情况并发插入也会因为唯一约束而报错方便排查。4. 后端核心模块实现4.1 登录认证与权限控制用户密码使用Werkzeug的generate_password_hash加密登录时用check_password_hash校验。Flask-Login负责维护session。Flask-Login的UserMixin需要自己处理get_id方法我们在ORM模型里直接继承from flask_login import UserMixin, current_user from werkzeug.security import generate_password_hash, check_password_hash from app.extensions import db class User(UserMixin, db.Model): __tablename__ tt_user id db.Column(db.Integer, primary_keyTrue) username db.Column(db.String(64), uniqueTrue, nullableFalse) password_hash db.Column(db.String(256), nullableFalse) role_type db.Column(db.Integer, default0) status db.Column(db.Integer, default1) create_time db.Column(db.DateTime, defaultdb.func.now()) def set_password(self, password): self.password_hash generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) # Flask-Login需要返回主键的字符串形式 def get_id(self): return str(self.id)权限控制我写了一个装饰器role_required(1, 2)放在app/utils/decorators.py里。判断当前用户角色是否在允许列表里不在就重定向到首页并闪现错误提示。管理员请求队员页面、队员请求调度页面的问题就被统一拦截了。自定义装饰器里要注意修饰视图函数时不能用functools.wraps包裹否则会丢失视图名称导致请求/logout时报View function mapping is overwriting an existing endpoint function的错误。正确的写法from functools import wraps from flask import redirect, url_for, flash from flask_login import current_user def role_required(*roles): def decorator(func): wraps(func) def wrapper(*args, **kwargs): if not current_user.is_authenticated: return redirect(url_for(auth.login)) if current_user.role_type not in roles: flash(当前账号无权访问该页面, warning) return redirect(url_for(main.index)) return func(*args, **kwargs) return wrapper return decorator4.2 救助受理的业务实现救助受理页面其实就是一个表单求助人姓名、电话、事件描述、位置坐标、灾害类型、紧急程度。保存时要同时做两件事插入求助记录、写入一条初始状态流转日志。日志我单独放在tt_order_log表里每条记录对应order_id、from_status、to_status、operator_id、remark和create_time。这里有个业务节点很关键受理时要根据系统计算出一个“建议队组”。计算逻辑不复杂就是查一下当前所有状态为“空闲”的队员按照最近30天参与任务次数升序排序取前三个作为推荐队员。真正把队员分配到任务里还是由调度员确认只是系统给出推荐值减少人为挑人的主观偏差。4.3 任务分派与状态流转调度员看到待受理的求助单点击“派单”选择任务类型、描述、截止时间、队员提交后任务状态置为“待执行”求助单状态同步变为“已派单”。队员端登录后会看到一个待执行任务列表。队员点击“开始执行”任务状态变为“执行中”同时求助单变为“执行中”。执行过程中队员可以上报进度插入进度记录。任务完成后队员填写反馈任务状态变为“已完成”求助单状态变为“待复核”。最后管理员复核无误后把单据归档。这段状态流转必须有一个独立的Service类管理不要在视图函数里到处直接改status字段。我写了个TaskService.change_status(task_id, target_status, operator, remark)方法里面统一检查状态是否合法、记录时间、写流转日志。非法跳转比如“已归档”忽然改成“执行中”会直接抛出异常控制器捕获后返回错误提示。通过这种方式状态逻辑集中在服务层视图代码非常干净。4.4 物资库存预警物资模块里每一次出库都减少available_count入库增加。库存低于threshold时在物资列表页标记为“偏低”并在首页Dashboard右上角显示一个红色警告数量。实现上我用了一个简单的SQLAlchemy查询low_stock Material.query.filter(Material.available_count Material.threshold).count()需要注意的一点是并发扣减库存时可能会造成超卖。这个业务场景虽然没有秒杀那么夸张但如果两个队员同时出库同一物资事务隔离级别不当就会造成库存负值。解决办法是使用乐观锁出库时在代码里判断available_count need_count然后UPDATE tt_material SET available_count available_count - %s WHERE id %s AND available_count %s这条SQL自带条件影响行数为0则说明库存不足中止操作。5. 前端页面与交互细节5.1 页面布局与模板继承前端我用的Bootstrap 4 AdminLTE的简洁风格。所有页面的壳子抽到了base.html里侧边栏菜单根据current_user.role_type动态渲染。模板里使用Jinja2的{% if current_user.role_type 1 %}判断菜单项是否展示。这里的坑是Jinja2模板里直接调用current_user.role_type偶尔会报UndefinedError。原因是在非登录态访问一些视图时Flask-Login的current_user是一个AnonymousUserMixin对象并没有role_type属性。解决方法是给匿名用户类添加属性默认值或者模板里写成{% if current_user.is_authenticated and current_user.role_type 1 %}确保短路判断。5.2 救援地图坐标展示系统里有一块地图展示页面用来标记当前待救援的任务位置。原本打算嵌入高德地图JS API后来觉得演示环境不一定有外网就用了纯Leaflet OpenStreetMap切片效果也不错。后端在页面加载时返回未完成任务列表的JSONdispatch_bp.route(/api/task/map_data) role_required(1, 2) def map_data(): tasks Task.query.filter(Task.status.in_([0, 1])).all() data [{ task_no: t.task_no, lat: t.latitude, lng: t.longitude, content: t.task_content, status: t.status } for t in tasks] return jsonify(code0, datadata)前端用fetch拉取数据后在map上循环添加Marker。地图页面还做了个小交互Marker点击后弹出任务详情可以直接跳转到任务处理页面。这样调度员看地图时能快速处理任务不用来回翻列表。整体展示效果比单纯表格直观很多答辩时加分会很明显。5.3 表单校验与前端防抖对求助表单里的紧急程度选择前端做了一点点交互优化选“特急”时页面背景会变红提示调度员注意选“一般”时不作特殊处理。这只是心理提醒但实际值班人员反馈比较受用。提交按钮在发送请求期间置为disabled防止用户重复点击导致同一求助单被创建两次。服务端表单验证用Flask-WTF自带验证器必填项缺失时直接返回错误。不要依赖前端校验服务端校验永远是最后一道防线。6. 数据统计与图表可视化实现6.1 统计指标设计统计页面主要展示四类数据今日新增求助数、本月累计出队次数、任务完成率、物资库存预警数量。除预警数量外全部用SQL聚合计算得出。任务完成率是已完成/总任务数乘以100注意任务总数不要只算未归档的需要把已经取消的排除掉否则分母会虚高。6.2 可视化图表图表库我用的是ECharts通过CDN引入。后端返回最近30天每天的新增求助数量前端生成折线图app.route(/api/statistic/monthly) role_required(0, 1) def monthly_statistic(): days 30 date_list [(datetime.now() - timedelta(daysi)).strftime(%Y-%m-%d) for i in range(days-1, -1, -1)] result [] for d in date_list: count RescueOrder.query.filter(db.func.date(create_time) d).count() result.append({date: d, count: count}) return jsonify(code0, dataresult)直接循环查库性能一般但演示数据量小毫无压力。如果数据量上来了可以改成一条GROUP BY DATE(create_time)SQL拿到全部数据再用Python补齐缺失日期能减少循环次数。用ECharts时建议配置tooltip的trigger: axis这样一个时间点上有多个数据系列时悬浮能看到完整信息演示效果好。6.3 导出Excel报表调度员可能需要把某段时间的救助记录导出Excel用于线下汇报。后端用pandas生成DataFrame再通过openpyxl写入Excel文件最后用send_file返回给前端下载。需要注意是send_file时一定要把文件名做编码处理不然浏览器下载的中文文件名会乱码。写法是from urllib.parse import quote filename quote(救助记录报表.xlsx) return send_file(BytesIO(output), download_namefilename, as_attachmentTrue, mimetypeapplication/octet-stream)生成Excel之前要先检查临时目录是否有写权限。Windows下如果直接写入os.path.join(os.getcwd(), tmp)而没提前创建目录会抛FileNotFoundError。项目里我在启动入口处用os.makedirs(tmp, exist_okTrue)做了保障。7. 常见问题与排查技巧实录开发过程中真正浪费时间的不是业务逻辑而是一些环境问题和框架的小坑。我整理一下这几个高频问题提前帮大家踩平。7.1 数据库连接报错“Access denied for user”MySQL用户授权问题。用root连数据库时电信云、本地环境可能默认只允许localhost连接。解决方法是登录MySQL后执行USE mysql; CREATE USER rescuelocalhost IDENTIFIED BY 123456; GRANT ALL PRIVILEGES ON rescue_db.* TO rescuelocalhost; FLUSH PRIVILEGES;另外连接串要指定charsetutf8mb4避免中文乱码SQLALCHEMY_DATABASE_URI mysqlpymysql://rescue:123456localhost:3306/rescue_db?charsetutf8mb47.2 Flask-SQLAlchemy查询结果不是JSON可序列化写JSON接口时直接返回ORM对象会报TypeError: Object of type User is not JSON serializable。习惯性做法是先转字典再返回。我在模型基类里加了to_dict()方法所有模型继承它这样每个接口的输出格式都统一了。class BaseModel(db.Model): __abstract__ True def to_dict(self): return {c.name: getattr(self, c.name) for c in self.__table__.columns}7.3 页面刷新404发现是蓝图的URL前缀问题注册蓝图时前缀写错了。例如“dispatches”译为中文习惯应该挂在/dispatch下但模板里链接写的是/dispatches/task_list两边不一致刷新就404。注册蓝图时注意模板里的路径一定要匹配前缀建议统一用一个常量保存蓝图前缀避免手打失误。7.4 任务并发更新导致状态错乱两个浏览器标签页同时打开任务更新页面都提交了“开始任务”的POST请求后面的覆盖了前面的状态。解决方案很简单更新任务状态的接口里用带有状态条件的更新SQL比如UPDATE tt_task SET status1 WHERE id? AND status0。如果更新影响行数为0就给用户提示“任务已被其他操作员处理请刷新页面”。7.5 中文乱码问题字符集不一致的坑最常见。MySQL连接串、建表语句、前端HTML三处都要统一UTF-8。建库时直接指定CREATE DATABASE rescue_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;HTML里放meta charsetutf-8。模板里渲染中文若出现?去检查MySQL配置文件my.ini的default-character-setutf8mb4。Windows下MySQL 8默认服务端字符集可能不是utf8mb4需要手动修改后重启。7.6 DEBUG模式下的静态文件缓存开发调试前端改了CSS但浏览器缓存刷新不出来按下F12打开开发者工具勾选“Disable cache”再刷新即可。有些按钮样式改了没生效其实是浏览器缓存了旧的css文件用CtrlF5强制刷新解决。部署到生产环境后建议给静态文件URL后加版本参数?v20250301彻底杜绝缓存问题。7.7 Linux部署时的Python环境问题测试完毕要部署到服务器时尽量用python3 -m venv venv建虚拟环境不要直接全局安装依赖。pip安装时如果提示“Running setup.py install for XXX... error”多半是缺少编译工具直接apt install build-essential libssl-dev libffi-dev后重装。上一个项目里还遇到过mysqlclient编译失败的问题换成pymysql纯Python驱动后就没有这类困扰了。8. 系统测试与答辩准备建议8.1 测试数据准备为了让系统演示有说服力至少准备三组不同状态的数据一条待受理求助、一条执行中的任务、一条已完成并归档的记录。另外物资库存特意调一件低于阈值方便演示红色预警。演示前把队员账号密码提前写在便签上免得现场登录卡壳。8.2 单元测试与逻辑自测我写了几个简单测试用例来验证核心业务不会自断后路主要是测试状态流转合法性和编号唯一性import unittest from app import create_app from app.extensions import db from app.services.task_service import TaskService class TaskServiceTest(unittest.TestCase): def setUp(self): self.app create_app(testing) self.app_context self.app.app_context() self.app_context.push() def test_status_exception(self): with self.assertRaises(ValueError): TaskService.change_status(1, archived, 1, 非法状态跳转)虽然毕设对完整测试覆盖率要求不高但基础断言能帮你提前发现一半以上的低级错误。用unittest discover跑起来也很方便写两个核心用例表达态度就够了。8.3 老旧数据清理系统运行一段时间后会有大量调试阶段产生的脏数据。建议写个简单的定时脚本清除30天前、状态为“已归档”的所有任务日志和临时图片压缩数据库体积。也可以顺手生成一个统计报表归档到Excel文件里然后物理删除数据库中的归档记录保证数据库看的都是“活跃数据”。这部分的实现用了Flask内置的CLI命令在manage.py里加一条flask clean_archive命令后台定时任务用Windows计划任务或Linux cron每日执行都属于加分项。8.4 答辩讲解侧的思路拿着这套代码去答辩讲的时候抓住三个点数据联动、状态闭环、权限分层。一般老师会问“你这个系统和普通信息管理系统有什么区别”直接拿求助单生成任务、任务完成回写求助状态、物资库存和任务耗材联动这几条打底说明你做了业务流程设计而不是简单的增删改查页面堆叠。如果老师深挖并发和安全重点是密码哈希存储、SQL注入防护SQLAlchemy参数化查询天然免疫、CSRF保护和角色权限校验。这几个点都经得起追问。9. 后续扩展方向与个人经验总结这套系统如果要接真实救援队来用有几个可以快速落地的改进点开放微信小程序上报入口让求助者不用填表单而是直接发定位和照片接入地图路径规划API给调度员呈现“从队部到事发地的预计车程”物资模块加一个二维码标签每件装备出入库时扫码登记减少手误。不过这些扩展方向的实现工作量都不小作为毕设二期扩展讲就够了。根据我个人的实际开发体会做这类管理系统最大的坎不是技术而是需求梳理。很多人一上来就想把所有功能做完结果每个模块都半途而废。我建议先把角色和状态机画清楚把“哪个角色在哪个状态下能做什么操作”列成表格然后再去数据库建表效率能翻倍。还有一个小技巧开发过程中坚持写启动日志print调试多了以后建议换成logging模块统一输出到文件排查线上Conda环境问题时真的救命。项目做到后面我发现系统设计里最花心思的不是地图和图表这些炫酷效果而是救助单和任务单之间的状态同步逻辑。这一块捋顺了整个系统的骨架就稳了剩下的页面都是往骨架上填肉。希望能帮到正在做同类系统的朋友。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

深入解析 Mach-O 的 __stubs_helper:懒加载符号与 dyld_stub_binder 的幕后桥梁 2026/9/26 19:25:43

深入解析 Mach-O 的 __stubs_helper:懒加载符号与 dyld_stub_binder 的幕后桥梁

第一次在otool -l输出里看到__TEXT,__stubs_helper的时候,我盯着它愣了很久。__text放业务代码,__stubs放整齐的跳板,__la_symbol_ptr负责存函数地址,这些按名字都能猜个大概。但一个名字里带 helper 的节,到底是给谁帮…

阅读更多 →
Race conditions之Limit overrun race conditions 2026/9/26 19:25:43

Race conditions之Limit overrun race conditions

一、漏洞原理购物下订单时,可以使用优惠券,但是下单和用券这两个动作不是在一次用户操作中完成的,而是分开的。首先,用户先使用优惠券减少订单金额,此时调用了/cart/coupon接口;然后,用户点击“…

阅读更多 →
Longhorn v1.5.4 版本说明深度解读:节点排空自动驱逐副本与关键修复实践 2026/9/26 19:25:36

Longhorn v1.5.4 版本说明深度解读:节点排空自动驱逐副本与关键修复实践

云原生存储高可用容器编排 【免费下载链接】longhorn Cloud-Native distributed storage built on and for Kubernetes 项目地址: https://gitcode.com/gh_mirrors/lo/longhorn 点击查看 免费下载 导读:本文围绕 Longhorn 1.5 系列的最新稳定版本 v1.5.…

阅读更多 →
Python环境配置与PyCharm安装:从零搭建高效开发环境 2026/9/26 19:25:36

Python环境配置与PyCharm安装:从零搭建高效开发环境

1. Python 环境配置与 PyCharm 安装:从零搭建一套顺手的开发环境很多人第一次接触 Python,卡住的地方根本不是语法,而是“环境”这两个字。下载了安装包,一路下一步,结果命令行里敲python提示找不到命令;或…

阅读更多 →
Google Antigravity SDK是什么?Google官方AI Agent开发框架完整架构解析 2026/9/26 19:25:30

Google Antigravity SDK是什么?Google官方AI Agent开发框架完整架构解析

Google Antigravity SDK是什么?Google官方AI Agent开发框架完整架构解析 【免费下载链接】antigravity-sdk-python A Python library for building AI agents that leverage the full power of Google Antigravity. 项目地址: https://gitcode.com/gh_mirrors/an/…

阅读更多 →
OpenClaw 网页前端开发与优化全流程指南:TaoToken 统一 Key 接入与 settings.json 配置骨架 2026/9/26 19:25:30

OpenClaw 网页前端开发与优化全流程指南:TaoToken 统一 Key 接入与 settings.json 配置骨架

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

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉