【免费】基于Python的微信小程序共享单车系统(FastAPI+Vue3) 锋哥原创出品,必属精品

发布时间:2026/8/15 22:39:47
【免费】基于Python的微信小程序共享单车系统(FastAPI+Vue3) 锋哥原创出品,必属精品 大家好我是Java1234_小锋老师分享一套锋哥原创的基于Python的微信小程序共享单车系统(FastAPIVue3) 。项目介绍共享单车作为城市绿色出行的重要组成部分能够有效衔接公交、地铁等大运量公共交通缓解短距离出行不便和道路拥堵问题。传统共享单车平台功能集中、接口封闭难以直接作为高校实践教学和中小规模运营场景的完整解决方案。针对这一现状本文设计并实现了一套面向实际业务闭环的微信小程序共享单车系统。系统采用前后端分离的三层架构。用户端基于微信小程序开发提供地图找车、扫码解锁、骑行计时、规范还车、余额充值、押金管理、骑行卡购买、订单查询、故障报修、骑行评价以及个人资料与密码维护等功能管理端基于 Vue3、Vite 与 Element Plus 构建实现用户与车辆管理、停车点维护、计费规则与骑行卡配置、订单与资金流水查询、故障处理、公告发布以及带统计图表的运营首页服务端采用 Python 语言和 FastAPI 框架结合 SQLAlchemy 2.x 与 MySQL 8 完成数据持久化使用 JWT 实现管理员与普通用户的身份鉴别。数据库设计遵循第三范式建立 db_bike 库及 t_admin、t_user、t_bike、t_ride_order 等 12 张业务表。骑行计费采用“起步价 超时按分钟计费 单次上限”模型并结合 Haversine 公式估算里程支付环节支持余额支付、骑行卡抵扣和模拟微信支付。测试表明系统主要业务流程运行稳定接口响应及时能够满足毕业设计对完整性、规范性与可演示性的要求对同类校园或社区共享出行系统具有参考价值。源码下载链接: https://pan.baidu.com/s/1X2m0WnWBA8Vfoa4IzfehlQ?pwd1234提取码: 1234系统展示核心代码 骑行卡服务 from sqlalchemy.orm import Session from app.exceptions import BusinessException from app.models import RideCard def page_list(db: Session, current: int, size: int, keyword: str None): 分页查询骑行卡 query db.query(RideCard) if keyword: query query.filter(RideCard.name.like(f%{keyword}%)) total query.count() records ( query.order_by(RideCard.create_time.desc()) .offset((current - 1) * size) .limit(size) .all() ) return records, total def save_card(db: Session, data: dict) - None: 保存骑行卡 card_id data.get(id) if card_id: card db.query(RideCard).filter(RideCard.id card_id).first() if not card: raise BusinessException(骑行卡不存在) for key, val in data.items(): if key ! id and val is not None: setattr(card, key, val) else: card RideCard( namedata[name], card_typedata[card_type], valid_daysdata.get(valid_days, 30), total_timesdata.get(total_times), free_minutesdata.get(free_minutes, 30), pricedata[price], descriptiondata.get(description), statusdata.get(status, 1), ) db.add(card) db.commit() def delete_card(db: Session, card_id: int) - None: 删除骑行卡 card db.query(RideCard).filter(RideCard.id card_id).first() if card: db.delete(card) db.commit() def list_active(db: Session): 查询上架骑行卡 return ( db.query(RideCard) .filter(RideCard.status 1) .order_by(RideCard.price.asc()) .all() )template div classpage-container div classsearch-bar el-select v-modelquery.status placeholder处理状态 clearable el-option v-for(item, key) in faultStatusMap :keykey :labelitem.label :valueNumber(key) / /el-select el-button typeprimary clickloadDatael-iconSearch //el-icon 搜索/el-button /div div classtable-card el-table :datatableData v-loadingloading stripe border el-table-column propid labelID min-width70 / el-table-column propuserId label用户ID min-width80 / el-table-column propbikeId label单车ID min-width80 / el-table-column propfaultType label故障类型 min-width120 / el-table-column propdescription label描述 min-width180 show-overflow-tooltip / el-table-column label状态 min-width90 template #default{ row } el-tag :typefaultStatusMap[row.status]?.type sizesmall {{ faultStatusMap[row.status]?.label }} /el-tag /template /el-table-column el-table-column label报修时间 min-width170 template #default{ row }{{ formatDateTime(row.createTime) }}/template /el-table-column el-table-column label操作 min-width120 fixedright template #default{ row } el-button v-ifrow.status ! 2 typeprimary link clickopenHandle(row)处理/el-button el-button typeinfo link clickshowDetail(row)详情/el-button /template /el-table-column /el-table div classpagination-wrap el-pagination v-model:current-pagequery.current v-model:page-sizequery.size :totaltotal :page-sizes[10, 20, 50] layouttotal, sizes, prev, pager, next, jumper background size-changeloadData current-changeloadData / /div /div !-- 处理弹窗 -- el-dialog v-modelhandleVisible title故障处理 width520px destroy-on-close el-form refformRef :modelhandleForm label-width100px el-form-item label处理状态 el-radio-group v-modelhandleForm.status el-radio :value1处理中/el-radio el-radio :value2已处理/el-radio /el-radio-group /el-form-item el-form-item label回复内容 el-input v-modelhandleForm.reply typetextarea :rows4 placeholder请输入处理回复 / /el-form-item /el-form template #footer el-button clickhandleVisible false取消/el-button el-button typeprimary :loadingsubmitLoading clicksubmitHandle确定/el-button /template /el-dialog !-- 详情弹窗 -- el-dialog v-modeldetailVisible title故障详情 width560px destroy-on-close el-descriptions :column1 border v-ifdetailRow el-descriptions-item label故障类型{{ detailRow.faultType }}/el-descriptions-item el-descriptions-item label描述{{ detailRow.description }}/el-descriptions-item el-descriptions-item label图片 div v-ifdetailRow.images classimage-list el-image v-for(img, idx) in detailRow.images.split(,) :keyidx :srcimg :preview-src-listdetailRow.images.split(,) fitcover stylewidth: 80px; height: 80px; margin-right: 8px; border-radius: 6px / /div span v-else-/span /el-descriptions-item el-descriptions-item label回复{{ detailRow.reply || - }}/el-descriptions-item el-descriptions-item label处理时间{{ formatDateTime(detailRow.handleTime) }}/el-descriptions-item /el-descriptions /el-dialog /div /template script setup import { ref, reactive, onMounted } from vue import { ElMessage } from element-plus import request from /utils/request import { formatDateTime, faultStatusMap } from /utils/date /** * 故障报修管理页面 */ const loading ref(false) const submitLoading ref(false) const handleVisible ref(false) const detailVisible ref(false) const detailRow ref(null) const tableData ref([]) const total ref(0) const query reactive({ current: 1, size: 10, status: null }) const handleForm reactive({ id: null, status: 2, reply: }) async function loadData() { loading.value true try { const res await request.get(/admin/fault/page, { params: query }) tableData.value res.data.records total.value res.data.total } finally { loading.value false } } /** 打开处理弹窗 */ function openHandle(row) { handleForm.id row.id handleForm.status row.status 0 ? 1 : 2 handleForm.reply row.reply || handleVisible.value true } /** 提交处理 */ async function submitHandle() { submitLoading.value true try { await request.put(/admin/fault/handle/${handleForm.id}, null, { params: { status: handleForm.status, reply: handleForm.reply } }) ElMessage.success(处理成功) handleVisible.value false loadData() } finally { submitLoading.value false } } /** 查看详情 */ function showDetail(row) { detailRow.value row detailVisible.value true } onMounted(() loadData()) /script style scoped .image-list { display: flex; flex-wrap: wrap; gap: 8px; } /style

相关新闻