简介:一套面向毕业设计的民航售票管理系统完整资料包,基于ASP与SQL Server开发,采用B/S结构,适合计算机相关专业学生用来完成课程设计或毕业论文参考。系统涵盖会员注册管理、管理员后台、留言管理、航班查询、网上订票与网上退票等核心功能,从需求分析、数据库设计到编码实现均有完整支持。整个压缩包共96个文件,以35个ASP页面、42张GIF图片、7个SWF动画为主,另含数据库文件及毕业论文文档,整体仅1.14MB,轻量便于本地运行与调试。目前已有139人学习下载。资料包含可直接部署的源代码、配套数据库和完整毕业论文,并附有系统设计与实现文档,可帮助读者快速理解民航售票业务的模块划分、数据库表设计与ASP编程思路,是毕业设计备题和ASP技术练手的实用参考。 我们来做一版纯前端的方案。核心思路:把配送至页保存的地址写进 localStorage,商品详情页的“由我享用 / 送TA礼物”列表从 localStorage 读取并支持勾选删除,订单确认页再从 localStorage 读取被勾选的地址来完成回填。
整体流程:
- 用户在 A 商品详情页点击“由我享用 / 送TA礼物”进入配送至页;
- 配送至页选择配送方式、填写收货地址,点击保存;
- 保存后地址写入 localStorage,并跳转回 A 商品详情页;
- A 商品详情页的列表展示已保存地址,可勾选“由我享用/送TA礼物”,也可删除;
- 用户进入 B 商品详情页,同样能看到同一份地址列表(因为 localStorage 是全局共享的),勾选某条地址后点击“立即购买”;
- 跳转订单确认页时,从 localStorage 读取勾选状态,把地址回填到订单确认页。
下面给出最简实现,你可以直接复制到现有项目里对照修改。
1. localStorage 数据结构设计
建议统一用两个 key:
shippingAddresses:保存全部地址列表,数组对象。selectedShippingId:保存当前选中的地址 id(用于“由我享用/送TA礼物”的勾选状态以及订单确认页回填)。
每条地址对象结构:
{ id: Date.now() + Math.random().toFixed(4), // 确保唯一,方便删除 receiver: '张三', phone: '13800000000', province: '浙江省', city: '杭州市', district: '西湖区', detail: '文一西路 100 号', deliveryType: 'express', // express 快递 / pickup 门店自提 tag: '由我享用' // 或 '送TA礼物' }当然你也可以把 tag 单独拆出去,但最简单的做法是把它作为地址的一个属性保存。这样后续勾选时可以区分是“由我享用”还是“送TA礼物”。
2. 配送至页:保存地址并回写 localStorage
页面流程里,用户填完表单点击“保存”,我们做这些事:
- 校验表单必填项;
- 组装地址对象;
- 从 localStorage 读出旧列表(或初始化为空数组);
- 往数组头部 unshift 新地址(方便用户看到最新保存的);
- 把新地址的 id 设置为当前选中项,写入
selectedShippingId; - 跳转回来源商品详情页,可以用
window.history.back()或location.href。
代码示例:
function saveAddress(formData) { // 1. 基本校验,不通过则 return if (!formData.receiver || !formData.phone || !formData.detail) { alert('请填写完整收货信息'); return false; } // 2. 构造地址对象 const newAddress = { id: Date.now() + '_' + Math.floor(Math.random() * 1000), receiver: formData.receiver, phone: formData.phone, province: formData.province, city: formData.city, district: formData.district, detail: formData.detail, deliveryType: formData.deliveryType || 'express', tag: formData.tag === '送TA礼物' ? '送TA礼物' : '由我享用' }; // 3. 读取旧列表 const existing = JSON.parse(localStorage.getItem('shippingAddresses') || '[]'); // 4. 新增到头部,并保存 existing.unshift(newAddress); localStorage.setItem('shippingAddresses', JSON.stringify(existing)); // 5. 设置当前选中地址 id localStorage.setItem('selectedShippingId', newAddress.id); // 6. 跳回商品详情页 history.back(); // 或者 location.href = '商品详情页地址'; return true; }逻辑说明:
- 用
unshift保持最新地址在最上面,后续列表展示时直接按数组顺序渲染; - 新保存的地址默认设为选中,这是为了贴近用户操作习惯:刚填完的地址大概率就是想用的;
id使用时间戳+随机数,不需要依赖后端返回;- 如果你希望保留“由我享用”和“送TA礼物”两个独立入口对应两套独立标签,可以在
tag字段区分,后续在商品详情页根据按钮类型筛选显示。
3. 商品详情页:“由我享用 / 送TA礼物”列表读写与删除
商品详情页打开时,需要:
- 读取 localStorage 里的地址列表;
- 判断当前商品详情页是从哪个入口进来的(由我享用 / 送TA礼物),可以通过链接参数或者按钮点击时记录的方式;
- 列表里展示符合条件的地址(例如 tag 等于当前入口的值);
- 支持删除某条地址;
- 支持勾选某条地址,并把选中 id 写入
selectedShippingId。
这里给一个列表渲染与事件处理的示例:
let currentTag = '由我享用'; // 实际可根据页面参数或点击入口动态设置 function getAddressList() { return JSON.parse(localStorage.getItem('shippingAddresses') || '[]'); } function getSelectedId() { return localStorage.getItem('selectedShippingId') || ''; } // 渲染地址列表到容器 function renderAddressList() { const list = getAddressList().filter(item => item.tag === currentTag); const selectedId = getSelectedId(); const container = document.getElementById('addressList'); container.innerHTML = list.map(item => ` <div class="address-item"> <label> <input type="radio" name="selectedAddress" value="${item.id}" ${item.id === selectedId ? 'checked' : ''} onchange="selectAddress('${item.id}')"> <span>${item.receiver} ${item.phone}</span> <span>${item.province}${item.city}${item.district}${item.detail}</span> </label> <button onclick="deleteAddress('${item.id}')">删除</button> </div> `).join(''); } // 勾选地址 function selectAddress(id) { localStorage.setItem('selectedShippingId', id); } // 删除地址 function deleteAddress(id) { let list = getAddressList(); list = list.filter(item => item.id !== id); localStorage.setItem('shippingAddresses', JSON.stringify(list)); // 如果删除的是当前选中项,需要把选中项清空或重设为 null if (getSelectedId() === id) { localStorage.removeItem('selectedShippingId'); } renderAddressList(); }代码说明:
- 通过
tag过滤地址列表,这样“由我享用”入口和“送TA礼物”入口看到的列表是分开的; - 使用 radio 单选,更贴合“勾选一条地址去下单”的语义;
- 删除时从数组中过滤掉对应的 id,如果删掉的是当前选中项,需要同步清空
selectedShippingId,否则后续下单时会读到已经不复存在的地址; renderAddressList里的onchange事件依赖selectAddress是全局函数,如果你在模块化开发中,建议改成事件委托或绑定函数引用。
4. 立即购买 与订单确认页回填
当用户点击“立即购买”时,需要把当前选中的地址 id 和商品信息一起带到订单确认页。
最简做法有两种:
- URL query 传参:
order.html?addressId=xxx&skuId=123 - sessionStorage 传递:把订单相关数据暂存到 sessionStorage
由于商品详情页可能已经存在于历史栈中,且订单确认页刷新后 query 参数还在,用 URL query 会更直观。
示例代码:
function buyNow() { const selectedId = localStorage.getItem('selectedShippingId'); if (!selectedId) { alert('请先选择收货地址'); return false; } // 携带地址 id 跳转到订单确认页 location.href = './orderConfirm.html?addressId=' + selectedId + '&skuId=' + skuId; }订单确认页侧:
// 从 query 获取 addressId function getQueryParam(key) { const url = new URL(window.location.href); return url.searchParams.get(key); } function loadSelectedAddress() { const addressId = getQueryParam('addressId'); if (!addressId) return null; const list = JSON.parse(localStorage.getItem('shippingAddresses') || '[]'); const address = list.find(item => item.id === addressId); if (address) { // 回填到订单确认页的地址区域 showAddress(orderAddressEl, address); } else { alert('地址不存在或已被删除,请重新选择'); redirectToCart(); // 回到商品详情页重新选 } } function showAddress(el, address) { el.innerHTML = ` <div>收货人:${address.receiver}</div> <div>手机号:${address.phone}</div> <div>地址:${address.province}${address.city}${address.district}${address.detail}</div> <div>配送方式:${address.deliveryType === 'pickup' ? '门店自提' : '快递'}</div> `; }整体流程到这里就闭环了。订单确认页提交订单时,也不需要额外传地址,直接从 localStorage 里根据 addressId 再读一次即可。
5. 避坑要点
这套方案整体不难,但有几个坑容易翻车,提前注意:
radio 的 onchange 和 label 冲突
在渲染时如果label包裹了input和文本,在部分浏览器里点击文本不会触发 radio 的 change 事件。建议在容器上用事件委托,或者给label加onclick手动赋值。删除当前选中地址后没有清空 selectedShippingId
不删除的话,订单确认页会 read 到一个不存在的地址,导致回填缺失。上面的代码里已经处理,但如果你用框架(Vue/React),需要在删除时同步setSelectedId(null)。多个入口共用 localStorage 时的 key 冲突
如果项目里其他地方也用shippingAddresses,会互相覆盖。建议给 key 加前缀,例如mall_shipping_addresses。异步渲染时序问题
如果你在 Vue 的mounted里读取 localStorage,注意renderAddressList要等在 DOM 渲染后再执行,或者用nextTick。否则事件绑定可能找不到元素。订单确认页刷新后 addressId 还在,但地址已被删除
页面 load 时调用loadSelectedAddress发现查不到地址时,要不就自动跳到商品详情页重新选,要不弹窗提示。避免拿着一个失效的地址继续提交。
6. 扩展建议
如果后续要对接真实接口,这套前端方案可以平滑迁移:
- 删除时调用后端接口,删除成功后再更新 localStorage;
- 保存地址时把后端返回的数据结构(如 addressId)一并保存;
- 订单确认页提交时向后端传
addressId,后端再去查地址,而不是信任前端传的完整地址文本。
纯前端方案先跑通交互,后续接后端只改数据源,界面和流程不需要大改。
希望这套最简方案能帮到你,有问题可以随时对照调试。
本文还有配套的精品资源,点击获取