资讯动态

基于Vue的银行预约管理系统:从预约到叫号的完整实现

发布时间:2026/9/12 2:06:01 来源:尧图企业网站定制
简介这是一套基于JavaScript与Vue开发的银行预约管理系统前后台源码属于高分毕业设计项目评审得分95分面向计算机、自动化等相关专业学生可直接用于毕业设计、课程大作业或期末实训。系统包含人员管理、银行管理、业务管理和预约管理四大核心模块前端采用Vue管理后台模板后端逻辑清晰适合学习前后端分离开发、预约流程设计与权限控制。压缩包共374个文件以vue组件、js脚本、json配置为主另含png图片、scss样式及markdown文档等整体体积仅3.26MB目录结构规整便于快速定位与小范围修改。截至目前已有136人学习使用。资源已调试至可运行状态解压后按启动命令即可体验适合在此基础上扩展预约类型、增加数据统计或对接后端接口进一步提升项目完整度与创新性。1. 银行预约管理系统用 Vue 把「排队」变成「预约」银行网点排队是刚需场景但传统现场取号在高峰时段经常把大堂挤满用户等一小时办三分钟是常态。预约制的思路是把「人到现场再排队」改成「线上预约、按时到店、窗口直接办理」省掉无效等待。这套系统在前端由 JavaScript Vue 承担用户前台负责预约取号、时段选择和订单查询管理后台负责窗口调度、叫号与统计前后台共用一套组件与接口封装。对做毕业设计的人来说它的价值在于业务链路完整从用户预约到柜员办理再到报表输出每个环节都有可展示的代码对小型网点或政务大厅的信息化改造这套前后台结构也能直接裁剪复用。无论是自己从零实现还是拿到源码后二次梳理下面的方案都按「能复现、能答辩、能扩展」三条线展开。2. 预约管理系统的前后台拆分与 Vue 路由权限设计2.1 用户前台和管理后台共用工程还是两套独立应用做银行预约管理系统第一步要定工程结构。常见做法是「一个 Vue 工程、两个入口」把用户预约端前台和网点管理端后台放在同一个仓库里通过路由模块和目录区分。这样做的直接好处是能共用 axios 封装、基础表单组件和样式变量答辩时也能讲清楚「哪些代码被复用了、复用的依据是什么」。如果团队要求前后台独立部署也可以拆成两个 Vue 项目代价是公共逻辑要抽成 npm 包或复制两份日常维护成本明显更高。我一般会用这样的目录组织前后台代码src/ ├─ api/ # 接口层 │ ├─ portal.js # 前台预约接口 │ └─ admin.js # 后台管理接口 ├─ router/ │ ├─ index.js # 路由总表与全局守卫 │ ├─ portal.routes.js # 前台路由 │ └─ admin.routes.js # 后台路由 ├─ views/ │ ├─ portal/ # 预约页、订单详情 │ └─ admin/ # 窗口管理、叫号台、报表 └─ components/ ├─ common/ # 跨前后台复用 └─ admin/ # 后台专用组件这段结构里最关键的一点是用户看的预约页和柜员看的窗口页从路由层就分开而不是在同一个页面里用 v-if 切来切去否则菜单权限、组件懒加载和路由参数会互相污染。前后台真正共用的只有按钮、弹窗、表单这类基础组件预约表单和叫号队列这种业务组件不要跨域复用因为它们的接口协议和状态流转完全不同。2.2 用 Vue Router 守卫把角色鉴权做在进页面前银行预约系统对权限的敏感点不在页面隐藏而在路由和接口双把关。前台只需要校验用户是否登录后台则要区分管理员、柜员、网点主管三种角色。把判断收敛在 router.beforeEach 里比在每个页面 mounted 里各写一遍登录判断更省事也能覆盖「手动输入 URL 直达后台页面」的越权路径。// router/index.js const whiteList [/portal/home, /login] // 免登录白名单 router.beforeEach((to, from, next) { const token localStorage.getItem(bank_token) const role localStorage.getItem(bank_role) // user / admin / teller if (!token) { if (whiteList.includes(to.path)) return next() return next({ path: /login, query: { redirect: to.fullPath } }) } // 页面 meta.roles 配置了角色要求时才校验 if (to.meta.roles !to.meta.roles.includes(role)) { return next({ path: /403 }) } // 预约完成后带回来源网点页 if (to.query.redirect) { return next({ path: to.query.redirect }) } next() })这个守卫分三段逻辑。第一段处理未登录白名单里的页面直接放行其余跳登录页并用 redirect 参数记住用户原本想去的完整路径登录成功后再跳回来。这里有个细节redirect 要放在 query 上而不是 path 上因为登录页本身可能也带 query用 vue 路由参数传递后在登录页取出再放回跳转目标即可。第二段做角色校验前台路由的 meta.roles 配成[user]后台路由配成[admin,teller]就算手输 /admin/window 也会被拦到 /403。第三段是预约场景的特例预约成功后从订单详情返回网点首页避免用户点浏览器后退丢表单状态。注意 role 不能只信 localStorage每次进入后台前要调一次 /user/info 刷新真实角色防止改本地缓存绕过校验。真正的兜底在后端接口token 解析出的角色才作数前端校验只是体验层。2.3 预约系统的核心数据模型与预约订单表设计预约管理系统的数据模型是典型的「业务类型 → 窗口 → 号源 → 订单」四层关系表结构直接决定代码复杂度。下面是一套经过验证的最小表结构表名关键字段说明business_typeid, name, avg_duration业务类型如开户、挂失、对公window_infoid, window_no, biz_type_id, status窗口动态绑定一种业务类型appointment_orderid, order_no, user_id, biz_type_id, window_id, appoint_date, time_slot, queue_no, status预约主表queue_ticketid, order_no, window_id, call_time, finish_time, status叫号轨迹一个预约对应一条这里最容易疏忽的是 appointment_order 要存 window_id 而不是只存 biz_type_id。用户预约的是「某个窗口的某个时段」而非「某个业务」窗口和业务的绑定关系是后台随时可能改的若只存业务类型改绑后已生成的预约号就会错乱。queue_ticket 单独建表是为了叫号时不动预约主表、每次叫号办结都留一条轨迹统计平均办理时长直接查这张表不用在预约订单上堆状态字段。3. 预约取号流程用 JavaScript 切号源、做校验与排队号分配3.1 把一天切成时间段号源生成策略预约系统第一个难点是「号什么时候算有」。银行窗口不是电影院座位不能只控制全天总量要精确到时间段。常见做法是按业务类型的平均办理时长切分营业时间一个窗口 9:00–12:00、14:00–17:00 营业业务平均办理 15 分钟就把上午切成 12 个时段每个时段放 2 个号避免某一小时挤进一堆人、其他时段空转。号源不必提前落库前台组件按日期动态生成即可切段逻辑如下// utils/slot.js export function buildSlots(bizType, windows, date) { const slots [] for (const win of windows) { for (const segment of bizType.workSegments) { // [{ start: 09:00, end: 12:00 }] let cur segment.start while (cur segment.end) { const end addMinutes(cur, bizType.avgDuration) // 按平均办理时长步进 if (end segment.end) break // 超营业段直接截断 slots.push({ windowId: win.id, date, start: cur, capacity: bizType.slotCapacity, // 每个时段放几个号 booked: loadBookedCount(win.id, date, cur) }) cur end } } } return slots }这段代码的重点在 while 循环的步进与截断cur 每次累加 avgDuration若 end 超过营业段末尾就 break这样最后一个时段不会出现「开始正常、结束超营业时间」的脏数据。booked 字段用于前端置灰把 capacity 减 booked 小于等于 0 的时段设置 disabled。前端置灰只是体验层真正的并发控制在接口后端在同一事务里用条件更新扣减号源防止两个用户同时锁住最后一个号。3.2 预约表单的表单校验、防重复提交与时间选择预约表单通常只有业务类型、时间段、手机号三个必填项加一个备注选填。但越简单越容易出重复提交——用户点击提交后等接口响应时又点了一次就会产生两条订单。Vue 里防两层按钮 loading 提交函数内标志位。// views/portal/AppointmentForm.vue const submitting ref(false) async function handleSubmit() { if (submitting.value) return submitting.value true try { const { orderNo } await createOrder({ bizTypeId: form.bizTypeId, windowId: form.windowId, appointDate: form.appointDate, timeSlot: form.timeSlot, mobile: form.mobile.trim() }) ElMessage.success(预约成功排队号 ${orderNo}) router.push({ path: /portal/detail, query: { orderNo } }) } finally { submitting.value false } }submitting 是响应式标志位进入提交立即置 true后续点击直接 returnfinally 保证失败后能复位。它比单纯给按钮加 disabled 可靠因为 disabled 在组件重新渲染的间隙会短暂失效而标志位不依赖 DOM 状态。接口层再配合一个请求拦截器对 5 秒内相同参数的请求做去重双保险才算闭环。校验规则里手机号要做 1 开头 11 位的正则校验同时去掉首尾空格再判断不能只看长度时间选择用 el-date-picker 配合 disabledDate 把过去的日期禁掉再结合时段列表做二次限制。表单整体用 el-form 的 rules 声明式配置提交时 validate 通过才发请求。3.3 排队号生成规则与预约状态流转预约提交成功后要生成排队号。排队号必须满足「同一天、同窗口唯一且可读」常见规则是日期 窗口号 2 位 当日序号 3 位例如 20250611-03-007。序号必须由后端基于 appointment_order 表里同窗口、同日期最大序号加一生成并在表上加唯一索引兜底不能靠前端时间戳或随机数拼否则并发下会产生重复号。号源扣减与订单创建要在同一事务里完成任一步失败都要回滚并释放号源。状态流转是预约系统的第二个易错点。预约创建后是「已预约」柜员叫号变「已到号」办理中、已完成、已取消是后续态。需要特别区分「用户主动取消」和「窗口过号自动取消」主动取消要立刻释放号源过号自动取消则要结合网点策略决定是否允许重排到队尾。完整状态机如下当前状态触发动作下一状态已预约柜员点击叫号已到号已到号用户 3 分钟内未签到已过号已到号柜员点击开始办理办理中办理中柜员点击完成已完成已预约 / 已到号用户取消已取消已过号不一定直接作废很多网点允许用户重新排队所以 queue_ticket 里额外留一个 reorder 字段记录重排次数前端的队列列表按「未叫号 已过号」排序展示两个分组之间用 Vue 的 computed 派生不要手动改数组。提示取消预约的接口要做幂等处理用户重复点击取消或后台重试时第二次请求应直接返回成功而不是报错否则前端容易出现「明明取消了还提示取消失败」。4. 管理后台的窗口调度、叫号逻辑与 Vue 统计报表4.1 窗口与业务类型的动态绑定管理后台的核心操作是窗口管理。网点可能有 8 个窗口但高峰只开 4 个、低峰只开 2 个所以窗口要支持动态启停和改绑业务。管理员的操作流程是选窗口、选业务类型、保存。保存时后端除了更新 window_info还要检查该窗口是否存在未完成预约若有则前端弹确认框提示「还有 3 个预约未办理停用后将转入待分配池」这是银行网点「窗口可以关、业务不能断」的运营要求。窗口与业务绑定关系对应三种状态窗口状态含义是否可被预约active正常营业是closed临时关闭否relocating停用且清理未完成预约中否窗口状态变更的代码本身不复杂但检查顺序不能乱async function toggleWindow(win) { if (win.status active) { // 先查未完成预约再决定是否允许停用 const pending await api.getPendingOrderCount(win.id) if (pending 0) { const confirm await ElMessageBox.confirm( 该窗口还有 ${pending} 个预约未办理停用后将转入待分配确认停用 ) if (!confirm) return } } const res await api.setWindowStatus(win.id, win.status active ? closed : active) win.status res.status }这里调用的关键参数是 windowId所有接口都要以窗口维度查询而不是以业务类型维度。停用后转入待分配的具体做法把 appointment_order 中该窗口未完成记录的 window_id 置空由管理员按业务类型手动重绑或由系统按「同业务类型、最早开始时间」自动分配。注意这个更新必须是带条件 UPDATE只更新仍指向旧窗口的记录避免把别的窗口的预约一起改掉。4.2 叫号逻辑与队列的实时轮询柜员端的叫号页面是后台里交互最频繁的模块。流程是柜员登录后看到本窗口待办理列表点「叫号」广播给用户端大屏用户到窗口后点「开始办理」办完点「完成」。这里要区分叫号和签到叫号是柜员发起的广播动作签到是用户到窗口的确认动作两者之间是一个时间窗口过了窗口就算过号。实时性是这个模块的难点。银行网点网络环境复杂叫号模块不建议依赖 WebSocket更稳的是轮询加短连接柜员端每 2 秒拉一次队列用户端大屏每 3 秒拉一次叫号。Vue 组件在 onBeforeUnmount 里清理定时器避免路由切换后请求堆积let poll null onMounted(() { poll setInterval(async () { const { data } await api.getWindowQueue(windowId) queue.value data // 重新拉取队列并覆盖 }, 2000) }) onBeforeUnmount(() clearInterval(poll)) // 页面销毁必须停掉轮询轮询间隔要按端区分柜员端 2 秒、网点大屏 3 秒、用户手机 5 秒。间隔太短接口压力大太长用户觉得卡。2 秒轮询、100 个并发连接时QPS 大约 50普通后端毫无压力。叫号动作本身是一次条件更新把 queue_ticket 从「已预约」改成「已叫号」并记录 call_timeSQL 里必须带 status 已预约 条件防止两个柜员同时叫到同一个号。4.3 用 ECharts 输出网点忙闲度报表管理后台最有说服力的加分项是忙闲度报表。数据从 queue_ticket 聚合按小时统计叫号量和平均办理时长再用 ECharts 渲染。这个模块在答辩里能同时展示数据建模与可视化两个能力而且数据链路完全来自前面的订单与叫号表逻辑自洽。Vue 里安装 echarts 依赖时用npm i echarts -S装进 dependencies 而不是 devDependencies因为图表渲染属于运行时逻辑。聚合查询通常是这个形态SELECT HOUR(call_time) AS h, COUNT(*) AS cnt, AVG(TIMESTAMPDIFF(MINUTE, call_time, finish_time)) AS avg_min FROM queue_ticket WHERE DATE(call_time) ? AND status IN (done, cancel) GROUP BY HOUR(call_time)过滤条件用 status IN (done,cancel)是为了把「已过号」这类被叫号但未办理的记录也纳入整体统计而不是丢弃否则平均办理时长会偏高。前端配置 ECharts 时要点是双 y 轴const chart echarts.init(document.getElementById(busyChart)) const option { tooltip: { trigger: axis }, legend: { data: [叫号量, 平均办理时长] }, xAxis: { type: category, data: hours }, yAxis: [ { type: value, name: 叫号量 }, { type: value, name: 平均时长(分钟) } ], series: [ { name: 叫号量, type: bar, data: counts }, { name: 平均办理时长, type: line, yAxisIndex: 1, data: avgMinutes } ] } chart.setOption(option)最常画错的地方是第二个 series 必须写 yAxisIndex: 1否则折线和柱状图共用同一个量纲平均时长会被叫号量带成一条横线。图表容器的高度要显式设置组件里的样式用 scoped 隔离避免和其他页面的 canvas 互相覆盖。报表页加一个日期选择器默认显示最近 7 天按天切换结果并做前端缓存避免重复请求。5. 银行预约管理系统的高分演示与验证技巧拿到这类系统源码后第一步不是逐行读代码而是先按「一个演示脚本 一个异常验证清单」把它跑通。演示要在五分钟内讲完开两个无痕窗口一个模拟用户、一个模拟柜员无痕窗口之间 localStorage 完全隔离不用反复退出登录。演示顺序固定为「用户预约 → 后台窗口叫号 → 用户端看到叫号 → 开始办理 → 完成 → 报表数字变化」每步停 10 秒左右让评审看清状态变化。答辩追问通常集中在三处追问点回答要点并发时会不会重复发号后端同窗口同日最大序号加一唯一索引兜底token 过期怎么处理axios 拦截器捕获 401 后用 refreshToken 刷新失败再跳登录停用窗口时未办理预约怎么办window_id 置空进入待分配池管理员按业务类型重绑验证部分建议打开浏览器开发者工具的 Network 面板把网络切到 Slow 3G重点看三件事预约提交时按钮 loading 态是否全程生效、接口失败后表单数据有没有丢失、取消预约后重新预约同一时段号源是否立刻释放并恢复可见。这三条顺序执行基本能把状态机、防重复提交、号源释放三个最容易出问题的环节全部覆盖。最后补一个数据层的验证查 queue_ticket 表确认每一条叫号记录都有对应的 call_time 和 finish_time且 finish_time 不小于 call_time时间差落在该业务类型的 avgDuration 合理范围内。遇到时间倒挂的记录直接回查对应的 appointment_order 状态通常能定位到叫号接口没做状态校验的代码分支。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价