現(xiàn)配送地址跨頁(yè)面同步方案)
簡(jiǎn)介一套面向畢業(yè)設(shè)計(jì)的民航售票管理系統(tǒng)完整資料包基于ASP與SQL Server開(kāi)發(fā)采用B/S結(jié)構(gòu)適合計(jì)算機(jī)相關(guān)專(zhuān)業(yè)學(xué)生用來(lái)完成課程設(shè)計(jì)或畢業(yè)論文參考。系統(tǒng)涵蓋會(huì)員注冊(cè)管理、管理員后臺(tái)、留言管理、航班查詢(xún)、網(wǎng)上訂票與網(wǎng)上退票等核心功能從需求分析、數(shù)據(jù)庫(kù)設(shè)計(jì)到編碼實(shí)現(xiàn)均有完整支持。整個(gè)壓縮包共96個(gè)文件以35個(gè)ASP頁(yè)面、42張GIF圖片、7個(gè)SWF動(dòng)畫(huà)為主另含數(shù)據(jù)庫(kù)文件及畢業(yè)論文文檔整體僅1.14MB輕量便于本地運(yùn)行與調(diào)試。目前已有139人學(xué)習(xí)下載。資料包含可直接部署的源代碼、配套數(shù)據(jù)庫(kù)和完整畢業(yè)論文并附有系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)文檔可幫助讀者快速理解民航售票業(yè)務(wù)的模塊劃分、數(shù)據(jù)庫(kù)表設(shè)計(jì)與ASP編程思路是畢業(yè)設(shè)計(jì)備題和ASP技術(shù)練手的實(shí)用參考。 我們來(lái)做一版純前端的方案。核心思路把配送至頁(yè)保存的地址寫(xiě)進(jìn) localStorage商品詳情頁(yè)的“由我享用 / 送TA禮物”列表從 localStorage 讀取并支持勾選刪除訂單確認(rèn)頁(yè)再?gòu)?localStorage 讀取被勾選的地址來(lái)完成回填。整體流程用戶(hù)在 A 商品詳情頁(yè)點(diǎn)擊“由我享用 / 送TA禮物”進(jìn)入配送至頁(yè)配送至頁(yè)選擇配送方式、填寫(xiě)收貨地址點(diǎn)擊保存保存后地址寫(xiě)入 localStorage并跳轉(zhuǎn)回 A 商品詳情頁(yè)A 商品詳情頁(yè)的列表展示已保存地址可勾選“由我享用/送TA禮物”也可刪除用戶(hù)進(jìn)入 B 商品詳情頁(yè)同樣能看到同一份地址列表因?yàn)?localStorage 是全局共享的勾選某條地址后點(diǎn)擊“立即購(gòu)買(mǎi)”跳轉(zhuǎn)訂單確認(rèn)頁(yè)時(shí)從 localStorage 讀取勾選狀態(tài)把地址回填到訂單確認(rèn)頁(yè)。下面給出最簡(jiǎn)實(shí)現(xiàn)你可以直接復(fù)制到現(xiàn)有項(xiàng)目里對(duì)照修改。1. localStorage 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)建議統(tǒng)一用兩個(gè) keyshippingAddresses保存全部地址列表數(shù)組對(duì)象。selectedShippingId保存當(dāng)前選中的地址 id用于“由我享用/送TA禮物”的勾選狀態(tài)以及訂單確認(rèn)頁(yè)回填。每條地址對(duì)象結(jié)構(gòu){ id: Date.now() Math.random().toFixed(4), // 確保唯一方便刪除 receiver: 張三, phone: 13800000000, province: 浙江省, city: 杭州市, district: 西湖區(qū), detail: 文一西路 100 號(hào), deliveryType: express, // express 快遞 / pickup 門(mén)店自提 tag: 由我享用 // 或 送TA禮物 }當(dāng)然你也可以把 tag 單獨(dú)拆出去但最簡(jiǎn)單的做法是把它作為地址的一個(gè)屬性保存。這樣后續(xù)勾選時(shí)可以區(qū)分是“由我享用”還是“送TA禮物”。2. 配送至頁(yè)保存地址并回寫(xiě) localStorage頁(yè)面流程里用戶(hù)填完表單點(diǎn)擊“保存”我們做這些事校驗(yàn)表單必填項(xiàng)組裝地址對(duì)象從 localStorage 讀出舊列表或初始化為空數(shù)組往數(shù)組頭部 unshift 新地址方便用戶(hù)看到最新保存的把新地址的 id 設(shè)置為當(dāng)前選中項(xiàng)寫(xiě)入selectedShippingId跳轉(zhuǎn)回來(lái)源商品詳情頁(yè)可以用window.history.back()或location.href。代碼示例function saveAddress(formData) { // 1. 基本校驗(yàn)不通過(guò)則 return if (!formData.receiver || !formData.phone || !formData.detail) { alert(請(qǐng)?zhí)顚?xiě)完整收貨信息); return false; } // 2. 構(gòu)造地址對(duì)象 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. 設(shè)置當(dāng)前選中地址 id localStorage.setItem(selectedShippingId, newAddress.id); // 6. 跳回商品詳情頁(yè) history.back(); // 或者 location.href 商品詳情頁(yè)地址; return true; }邏輯說(shuō)明用unshift保持最新地址在最上面后續(xù)列表展示時(shí)直接按數(shù)組順序渲染新保存的地址默認(rèn)設(shè)為選中這是為了貼近用戶(hù)操作習(xí)慣剛填完的地址大概率就是想用的id使用時(shí)間戳隨機(jī)數(shù)不需要依賴(lài)后端返回如果你希望保留“由我享用”和“送TA禮物”兩個(gè)獨(dú)立入口對(duì)應(yīng)兩套獨(dú)立標(biāo)簽可以在tag字段區(qū)分后續(xù)在商品詳情頁(yè)根據(jù)按鈕類(lèi)型篩選顯示。3. 商品詳情頁(yè)“由我享用 / 送TA禮物”列表讀寫(xiě)與刪除商品詳情頁(yè)打開(kāi)時(shí)需要讀取 localStorage 里的地址列表判斷當(dāng)前商品詳情頁(yè)是從哪個(gè)入口進(jìn)來(lái)的由我享用 / 送TA禮物可以通過(guò)鏈接參數(shù)或者按鈕點(diǎn)擊時(shí)記錄的方式列表里展示符合條件的地址例如 tag 等于當(dāng)前入口的值支持刪除某條地址支持勾選某條地址并把選中 id 寫(xiě)入selectedShippingId。這里給一個(gè)列表渲染與事件處理的示例let currentTag 由我享用; // 實(shí)際可根據(jù)頁(yè)面參數(shù)或點(diǎn)擊入口動(dòng)態(tài)設(shè)置 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 classaddress-item label input typeradio nameselectedAddress value${item.id} ${item.id selectedId ? checked : } onchangeselectAddress(${item.id}) span${item.receiver} ${item.phone}/span span${item.province}${item.city}${item.district}${item.detail}/span /label button onclickdeleteAddress(${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)); // 如果刪除的是當(dāng)前選中項(xiàng)需要把選中項(xiàng)清空或重設(shè)為 null if (getSelectedId() id) { localStorage.removeItem(selectedShippingId); } renderAddressList(); }代碼說(shuō)明通過(guò)tag過(guò)濾地址列表這樣“由我享用”入口和“送TA禮物”入口看到的列表是分開(kāi)的使用 radio 單選更貼合“勾選一條地址去下單”的語(yǔ)義刪除時(shí)從數(shù)組中過(guò)濾掉對(duì)應(yīng)的 id如果刪掉的是當(dāng)前選中項(xiàng)需要同步清空selectedShippingId否則后續(xù)下單時(shí)會(huì)讀到已經(jīng)不復(fù)存在的地址renderAddressList里的onchange事件依賴(lài)selectAddress是全局函數(shù)如果你在模塊化開(kāi)發(fā)中建議改成事件委托或綁定函數(shù)引用。4. 立即購(gòu)買(mǎi) 與訂單確認(rèn)頁(yè)回填當(dāng)用戶(hù)點(diǎn)擊“立即購(gòu)買(mǎi)”時(shí)需要把當(dāng)前選中的地址 id 和商品信息一起帶到訂單確認(rèn)頁(yè)。最簡(jiǎn)做法有兩種URL query 傳參order.html?addressIdxxxskuId123sessionStorage 傳遞把訂單相關(guān)數(shù)據(jù)暫存到 sessionStorage由于商品詳情頁(yè)可能已經(jīng)存在于歷史棧中且訂單確認(rèn)頁(yè)刷新后 query 參數(shù)還在用 URL query 會(huì)更直觀。示例代碼function buyNow() { const selectedId localStorage.getItem(selectedShippingId); if (!selectedId) { alert(請(qǐng)先選擇收貨地址); return false; } // 攜帶地址 id 跳轉(zhuǎn)到訂單確認(rèn)頁(yè) location.href ./orderConfirm.html?addressId selectedId skuId skuId; }訂單確認(rèn)頁(yè)側(cè)// 從 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) { // 回填到訂單確認(rèn)頁(yè)的地址區(qū)域 showAddress(orderAddressEl, address); } else { alert(地址不存在或已被刪除請(qǐng)重新選擇); redirectToCart(); // 回到商品詳情頁(yè)重新選 } } function showAddress(el, address) { el.innerHTML div收貨人${address.receiver}/div div手機(jī)號(hào)${address.phone}/div div地址${address.province}${address.city}${address.district}${address.detail}/div div配送方式${address.deliveryType pickup ? 門(mén)店自提 : 快遞}/div ; }整體流程到這里就閉環(huán)了。訂單確認(rèn)頁(yè)提交訂單時(shí)也不需要額外傳地址直接從 localStorage 里根據(jù) addressId 再讀一次即可。5. 避坑要點(diǎn)這套方案整體不難但有幾個(gè)坑容易翻車(chē)提前注意radio 的 onchange 和 label 沖突在渲染時(shí)如果label包裹了input和文本在部分瀏覽器里點(diǎn)擊文本不會(huì)觸發(fā) radio 的 change 事件。建議在容器上用事件委托或者給label加onclick手動(dòng)賦值。刪除當(dāng)前選中地址后沒(méi)有清空 selectedShippingId不刪除的話訂單確認(rèn)頁(yè)會(huì) read 到一個(gè)不存在的地址導(dǎo)致回填缺失。上面的代碼里已經(jīng)處理但如果你用框架Vue/React需要在刪除時(shí)同步setSelectedId(null)。多個(gè)入口共用 localStorage 時(shí)的 key 沖突如果項(xiàng)目里其他地方也用shippingAddresses會(huì)互相覆蓋。建議給 key 加前綴例如mall_shipping_addresses。異步渲染時(shí)序問(wèn)題如果你在 Vue 的mounted里讀取 localStorage注意renderAddressList要等在 DOM 渲染后再執(zhí)行或者用nextTick。否則事件綁定可能找不到元素。訂單確認(rèn)頁(yè)刷新后 addressId 還在但地址已被刪除頁(yè)面 load 時(shí)調(diào)用loadSelectedAddress發(fā)現(xiàn)查不到地址時(shí)要不就自動(dòng)跳到商品詳情頁(yè)重新選要不彈窗提示。避免拿著一個(gè)失效的地址繼續(xù)提交。6. 擴(kuò)展建議如果后續(xù)要對(duì)接真實(shí)接口這套前端方案可以平滑遷移刪除時(shí)調(diào)用后端接口刪除成功后再更新 localStorage保存地址時(shí)把后端返回的數(shù)據(jù)結(jié)構(gòu)如 addressId一并保存訂單確認(rèn)頁(yè)提交時(shí)向后端傳addressId后端再去查地址而不是信任前端傳的完整地址文本。純前端方案先跑通交互后續(xù)接后端只改數(shù)據(jù)源界面和流程不需要大改。希望這套最簡(jiǎn)方案能幫到你有問(wèn)題可以隨時(shí)對(duì)照調(diào)試。本文還有配套的精品資源點(diǎn)擊獲取