Web-Dev-For-Beginners 银行项目实战:实现与状态管理无缝集成的“添加交易“(Add Transaction)对话框

发布时间:2026/9/10 23:34:47
Web-Dev-For-Beginners 银行项目实战:实现与状态管理无缝集成的“添加交易“(Add Transaction)对话框 Web-Dev-For-Beginners 银行项目实战实现与状态管理无缝集成的添加交易Add Transaction对话框【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners本篇技术指南基于 Web-Dev-For-Beginners 仓库中银行应用课程7-bank-project第 4 课《状态管理概念》的课后作业英文原版作业 及其 丹麦语翻译版展开核心任务是在已具备状态管理与数据持久化能力的银行应用中实现一个完整的添加交易Add Transaction对话框。该作业综合了银行系列四课的全部知识点HTML 模板化、表单处理、API 集成与状态管理。读完本文你将掌握从 UI 触发按钮、模态对话框实现、可访问性增强、表单校验到后端 API 通信与前端状态同步的完整实战链路并能在仓库提供的 完整参考实现 与 API 服务 的印证下独立复刻这一专业级功能。背景与前置准备在银行系列前几课中你的应用已经完成了模板化页面路由第 1 课模板与路由、表单处理第 2 课表单、数据获取第 3 课数据以及集中式状态管理第 4 课状态管理。本作业在此基础上补上真实银行应用最关键的缺口允许用户自己录入交易并将新交易即时反映到仪表盘Dashboard上。动手前请确认以下环境就绪已完成第 3 课应用能正常加载并展示账户数据本机已安装 Node.js 以运行后端 API已启动本地 API 服务详见 7-bank-project/api/README.md进入7-bank-project/api目录执行npm install再运行npm start服务默认监听 5000 端口。启动后可用如下命令验证服务正常curl http://localhost:5000/api # - 应返回 Bank API v1.0.0 之类的结果该命令向本地 API 发送 GET 请求测试连接并确认服务正在响应成功时返回 API 版本信息。学习目标完成本作业后你将能够创建用户友好的数据录入对话框界面实现支持键盘与屏幕阅读器的可访问表单设计将新功能与既有状态管理系统无缝集成练习 API 通信与错误处理将现代 Web 开发模式应用到真实场景功能中。第一步添加Add Transaction按钮在仪表盘页面创建一个用户容易发现和访问的添加交易按钮。需求如下位置放置在仪表盘的逻辑合理位置如交易列表标题栏右侧文案使用清晰、面向动作的按钮文字样式与现有 UI 设计保持一致可访问性保证按钮可通过键盘访问。仓库参考实现中该按钮位于仪表盘模板的交易区标题旁是一个标准的button typebutton并以内联onclick调用addTransaction()见 7-bank-project/solution/index.htmlbutton classbtn btn-primary typebutton onclickaddTransaction() i classfa-solid fa-plus aria-hiddentrue/i Add transaction /button使用原生button而非div或span可免费获得 Tab 聚焦与回车/空格激活能力这是键盘可访问性的第一步。装饰性图标需加aria-hiddentrue避免屏幕阅读器朗读无意义内容。第二步对话框实现两种方案实现对话框有两种可选方案方案 A独立页面Separate Page为交易表单创建新的 HTML 模板在路由系统中新增一条路由实现表单页与仪表盘之间的导航跳转。此方案适合偏好一页一表单导航模型的应用但切换页面会打断用户操作流且需要额外的路由状态维护。方案 B模态对话框Modal Dialog推荐使用 JavaScript 在不离开仪表盘的情况下显示/隐藏对话框基于hidden属性 或 CSS 类实现显隐配合正确的焦点管理创造流畅的用户体验。参考实现采用方案 B核心机制分三层1. HTML 标记默认隐藏 无障碍语义对话框模板位于仪表盘模板内部初始带hidden属性并对内容区声明了完整的 ARIA 角色见 7-bank-project/solution/index.htmlsection idtransactionDialog classdialog hidden div classdialog-backdrop>.dialog { display: none; position: fixed; inset: 0; background-color: rgba(0,0,0,.45); z-index: 1000; } .dialog.show { display: flex; } .dialog-content { max-width: 520px; animation: dialogIn .25s ease-out both; }3. JavaScript开关逻辑与事件绑定7-bank-project/solution/app.js 中的addTransaction()负责打开对话框为容器添加.show类、重置表单并默认填入当天日期form.date.valueAsDate new Date()、将焦点移入第一个输入框同时挂载点击背板与按键监听function addTransaction() { const dialog qs(transactionDialog); dialog.classList.add(show); const form qs(transactionForm); if (form) { form.reset(); form.date.valueAsDate new Date(); form.date.focus(); } const backdrop dialog.querySelector([data-dismiss]) || dialog; backdrop.addEventListener(click, onDialogDismissClick); dialog.addEventListener(keydown, onDialogKeydown); }第三步可访问性实现对话框必须满足模态对话框的可访问性标准键盘导航与屏幕阅读器支持参考实现中的落地细节如下。键盘导航Escape 键关闭onDialogKeydown监听容器上的keydown按 Escape 即调用cancelTransaction()app.js焦点管理打开时焦点移入第一个字段关闭时通过cancelTransaction()将焦点归还给触发按钮——实现方式是查询button[onclickaddTransaction()]并调用.focus()app.js焦点陷阱关闭时同步移除keydown监听dialog.removeEventListener(keydown, onDialogKeydown)避免残留事件污染后续交互。屏幕阅读器支持ARIA 角色与标签内容区声明roledialog、aria-modaltrue用aria-labelledby指向标题txDialogTitle、用aria-describedby指向说明文本txDialogDesc让读屏软件正确播报对话框标题与用途打开/关闭播报对话框及其内部错误区域使用rolealert与aria-livepolite如transactionError容器使状态变化被及时宣告清晰的字段标签与错误信息每个输入框都有显式label错误信息以rolealert容器呈现并自动聚焦见下文第四步的setFormError。第四步表单创建与校验设计一个收集交易数据的 HTML 表单包含三个必填字段字段含义取值约定日期Date交易发生时间input typedate默认当天描述Object交易用途文本输入参考实现限制 50 字符金额Amount交易金额正数表示收入credit负数表示支出debit参考实现index.html的表单要点label fordateDate/label input iddate namedate typedate required label forobjectObject/label input idobject nameobject typetext maxlength50 required label foramountAmount (negative for debit)/label input idamount nameamount typenumber value0 stepany inputmodedecimal required div idtransactionError classerror rolealert aria-livepolite/div提交前的客户端校验表单使用novalidate关闭浏览器原生气泡改为在confirmTransaction()中做内联校验app.jsconst amountVal Number(form.amount.value); const objectVal String(form.object.value || ).trim(); if (!Number.isFinite(amountVal)) { setFormError(transactionError, Amount must be a valid number); return; } if (!objectVal) { setFormError(transactionError, Object is required); return; }校验失败时setFormError()写入错误文本并将焦点移动到错误容器确保屏幕阅读器与键盘用户都能第一时间获知问题校验通过后clearFormError()清空旧错误function setFormError(id, message) { updateElement(id, message); const el qs(id); if (el) el.focus(); }提示typenumberstepanyinputmodedecimal的组合在移动端会调出数字键盘同时允许任意小数步长适用于金额录入。第五步API 集成表单需要与后端 API 对接正确端点与数据格式见 7-bank-project/api/README.md路由方法说明/api/GET获取服务器信息/api/accounts/POST创建账户如{ user: Yohan, description: My budget, currency: EUR, balance: 100 }/api/accounts/:userGET获取指定账户全部数据/api/accounts/:user/transactionsPOST添加交易如{ date: 2020-07-23T18:25:43.511Z, object: Bought a book, amount: -20 }/api/accounts/:user/transactions/:idDELETE删除指定交易本作业使用的端点POST /api/accounts/:user/transactions请求体为 JSON必填date、object、amount三个字段。服务端行为作为对照依据7-bank-project/api/server.js 展示了该端点的服务端语义账户不存在返回404 { error: User does not exist }缺少date/object/amount返回400 { error: Missing parameters }amount非数字返回400 { error: Amount must be a number }用crypto.createHash(md5)对date object amount生成交易 ID重复交易返回409 { error: Transaction already exists }成功时201返回新交易对象并同步累加账户余额account.balance transaction.amount。注意API 数据存于内存服务重启后数据即丢失但本作业的客户端逻辑不受影响。客户端集成步骤审查 API 规格确认端点 URL 与数据格式从表单输入构造 JSONJSON.stringify(Object.fromEntries(new FormData(form)))发送数据并妥善处理错误向用户展示成功/失败消息优雅处理网络错误。参考实现中的confirmTransaction()展示了完整闭环app.jsconst jsonData JSON.stringify(Object.fromEntries(new FormData(form))); const data await createTransaction(state.account.user, jsonData); if (data.error) { setFormError(transactionError, data.error); return; }其中createTransaction()是对 API 的封装解析 JSON若请求体非法或账户不存在则返回{ error: ... }而非抛异常客户端据此统一走错误 → 表单内联提示路径将服务端400/404/409语义映射为用户可见的友好文案。第六步状态管理集成成功添加交易后必须让仪表盘立即反映新数据且全程保持状态一致性交易添加成功后更新账户数据不刷新页面即可更新仪表盘视图新交易即时可见全程维护正确的状态一致性。不可变状态更新与自动持久化本课建立的状态管理模式是核心支撑。state对象通过Object.freeze()冻结所有变更必须走唯一的updateState()函数——它用展开运算符生成新对象、再整体冻结并自动把当前账户写入localStorageapp.jslet state Object.freeze({ account: null }); function updateState(property, newData) { state Object.freeze({ ...state, [property]: newData }); localStorage.setItem(storageKey, JSON.stringify(state.account)); }交易提交后的本地状态同步在confirmTransaction()中API 成功后并不重新拉取整份数据而是基于当前state.account做一次不可变合并用展开运算符复制账户、累加余额、追加新交易到transactions数组再经updateState()一次性写回app.jsconst newAccount { ...state.account, balance: (Number(state.account.balance) || 0) data.amount, transactions: [...(state.account.transactions || []), data] }; updateState(account, newAccount); cancelTransaction(); updateDashboard();由于updateState()内部已自动持久化刷新页面后新交易依然存在而关闭对话框后立即调用updateDashboard()则保证交易无需刷新即可出现在列表中达到即时可见的要求。数据新鲜度与仪表盘刷新作业要求还隐含了持久化与数据新鲜度平衡的考量本课实现的refresh()会在每次进入仪表盘路由时触发先调用updateAccountData()从服务端拉取最新账户数据无会话则自动登出再刷新视图app.jsasync function updateAccountData() { const account state.account; if (!account) return logout(); const data await getAccount(account.user); if (data.error) return logout(); updateState(account, data); } async function refresh() { await updateAccountData(); updateDashboard(); }路由表将/dashboard的init指向refreshapp.js确保每次导航到仪表盘都会同步服务端最新数据避免localStorage缓存陈旧导致用户看到过期余额。下图展示了本课状态管理数据流的核心结构——用户动作经事件处理器汇入受控的updateState()再驱动状态校验、UI 更新与持久化技术规格与预期结果API 端点细节完整 JSON 格式、HTTP 方法与端点 URL、预期响应格式、错误响应处理均以 7-bank-project/api/README.md 与 7-bank-project/api/server.js 为准。完成作业后你的银行应用应具备一个外观专业、行为完善的添加交易功能最终效果参考下图对应仓库 对话框截图测试你的实现功能测试确认Add Transaction按钮清晰可见且可访问测试对话框能否正确打开与关闭确认所有必填字段的表单校验均生效检查成功提交的交易是否立即出现在仪表盘上确保无效数据与网络异常时的错误处理工作正常。可访问性测试仅使用键盘走完整个流程Tab 进入按钮、回车打开、填写、Escape 关闭并返回焦点使用屏幕阅读器验证播报内容正确验证焦点管理打开时进入、关闭时归还正确检查所有表单元素都有恰当标签。评估标准Rubric标准优秀Exemplary合格Adequate需改进Needs Improvement功能性添加交易功能完美运行用户体验出色遵循课程全部最佳实践功能正确但可能未遵循部分最佳实践或有轻微可用性问题功能部分可用或存在显著可用性问题代码质量代码组织良好遵循既有模式包含正确的错误处理与现有状态管理无缝集成代码可运行但可能存在组织问题或与既有代码库模式不一致代码存在显著结构问题或未与既有模式良好集成可访问性完整的键盘导航支持、屏幕阅读器兼容遵循 WCAG 指南且焦点管理出色实现了基础可访问性但可能缺少部分键盘导航或屏幕阅读器特性可访问性考量很少或完全没有用户体验界面直观精致反馈清晰交互流畅外观专业用户体验良好但在反馈或视觉设计上有可改进的小瑕疵用户体验差界面令人困惑或缺少用户反馈额外挑战可选完成基础要求后可尝试以下增强以练习更高级的 Web 开发概念并打造更完整的银行应用增强功能添加交易分类餐饮、交通、娱乐等实现带实时反馈的输入校验为高级用户创建键盘快捷键增加交易的编辑与删除能力。高级集成为新增交易实现撤销Undo功能支持从 CSV 文件批量导入交易提供交易搜索与筛选实现数据导出功能。小结本作业是一次典型的全栈小闭环练习从仪表盘按钮触发经模态对话框完成数据录入与校验调用后端 API 落账再通过不可变updateState()同步本地状态并即时重绘视图同时借助localStorage实现持久化、通过路由级refresh()保障数据新鲜度。仓库中的 参考实现 完整演示了上述每一条路径如果你在阅读 第 4 课正文 时掌握了集中式状态对象、Object.freeze()不可变更新与持久化恢复这三板斧本作业的实现过程将水到渠成——这也正是课程设计者把添加交易对话框作为状态管理课后作业的用意所在。【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考