feat(shop): wire storefront buy path to server cart

Connect product options/add-cart, server cart checkout payloads,
favorite/rebuy/receipt REST, and order-center actions without
localStorage dual-read or hardcoded cart/orders URL hacks.
This commit is contained in:
kate522 committed 2026-07-12 09:15:45 +08:00
1 parent 2ed3ffe317
commit 50e3db85bb
6 files changed
+621 -304

No files matched your search

+89 -62
View File
@@ -6,7 +6,28 @@
return;
}
var CART_KEY = "aksrt_cart";
function restBase() {
return ((aksrtData.siteUrl || "").replace(/\/$/, "")) + "/wp-json/aksrt/v1";
}
function rest(path, method, body) {
var opts = {
method: method || "GET",
credentials: "same-origin",
headers: { "X-WP-Nonce": aksrtData.restNonce || "" }
};
if (body !== undefined) {
opts.headers["Content-Type"] = "application/json";
opts.body = JSON.stringify(body);
}
return fetch(restBase() + path, opts).then(function (r) { return r.json(); });
}
function ordersUrl() {
return aksrtData.ordersUrl || (aksrtData.routes && aksrtData.routes.orders) || "/";
}
function shopHomeUrl() {
return (aksrtData.routes && aksrtData.routes.home) || aksrtData.siteUrl || "/";
}
var cartItems = [];
var currencySymbol = page.getAttribute("data-currency-symbol") || "¥";
var itemsEl = page.querySelector("[data-aksrt-cart-items]");
var subtotalEl = page.querySelector("[data-aksrt-summary-subtotal]");
@@ -20,18 +41,8 @@
var previewState = null;
var searchParams = new URLSearchParams(window.location.search);
function getCart() {
try {
var parsed = JSON.parse(localStorage.getItem(CART_KEY) || "[]");
return Array.isArray(parsed) ? parsed : [];
} catch (error) {
return [];
}
}
function setCart(items) {
localStorage.setItem(CART_KEY, JSON.stringify(items));
}
function getCart() { return cartItems; }
function setCart(items) { cartItems = Array.isArray(items) ? items : []; }
function money(value) {
return currencySymbol + Number(value || 0).toFixed(2);
@@ -46,7 +57,8 @@
var items = getCart().map(function (item) {
return {
product_id: Number(item.product_id || item.id || 0),
quantity: Number(item.qty || item.quantity || 1)
quantity: Number(item.qty || item.quantity || 1),
options_string: item.options_string || ""
};
}).filter(function (item) {
return item.product_id > 0;
@@ -69,7 +81,7 @@
'<div class="aksrt-cart-page__empty">' +
'<i class="fas fa-shopping-cart" aria-hidden="true"></i>' +
"<p>购物车为空,去商品页添加想购买的内容。</p>" +
'<a href="/" class="btn-outline btn-sm">继续浏览</a>' +
'<a href="' + shopHomeUrl() + '" class="btn-outline btn-sm">继续浏览</a>' +
"</div>";
subtotalEl.textContent = money(0);
shippingEl.textContent = money(0);
@@ -84,15 +96,19 @@
var html = "";
items.forEach(function (item, index) {
var qty = Number(item.qty || item.quantity || 1);
var price = Number(item.price || 0);
var lineTotal = qty * price;
var price = Number(item.unit_price != null ? item.unit_price : (item.price != null ? item.price : (item.product && item.product.price) || 0));
var lineTotal = Number(item.line_total != null ? item.line_total : qty * price);
total += lineTotal;
var title = item.title || (item.product && item.product.title) || "商品";
var image = item.image || (item.product && item.product.image) || "";
var opts = item.options_label || item.options_string || "";
html += '' +
'<article class="aksrt-cart-page__item" data-cart-index="' + index + '">' +
(item.image ? '<img class="aksrt-cart-page__item-image" src="' + item.image + '" alt="">' : '<div class="aksrt-cart-page__item-image aksrt-cart-page__item-image--placeholder"><i class="fas fa-box"></i></div>') +
'<article class="aksrt-cart-page__item" data-cart-index="' + index + '" data-product-id="' + Number(item.product_id || item.id || 0) + '" data-options-string="' + String(item.options_string || "").replace(/"/g, "&quot;") + '">' +
(image ? '<img class="aksrt-cart-page__item-image" src="' + image + '" alt="">' : '<div class="aksrt-cart-page__item-image aksrt-cart-page__item-image--placeholder"><i class="fas fa-box"></i></div>') +
'<div class="aksrt-cart-page__item-main">' +
'<h3>' + (item.title || "商品") + "</h3>" +
'<h3>' + title + "</h3>" +
(opts ? "<small>" + opts + "</small>" : "") +
'<p>' + money(price) + "</p>" +
"</div>" +
'<div class="aksrt-cart-page__item-qty">' +
@@ -124,18 +140,7 @@
createBtn.disabled = !data;
}
function request(path, body) {
return fetch("/wp-json/aksrt/v1" + path, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-WP-Nonce": aksrtData.restNonce
},
body: JSON.stringify(body || {})
}).then(function (response) {
return response.json();
});
}
function request(path, body) { return rest(path, "POST", body || {}); }
function previewCheckout() {
var payload = getPayload();
@@ -183,12 +188,11 @@
return;
}
localStorage.removeItem(CART_KEY);
var orderNumber = (result.data && result.data.order_number) || "";
var ordersUrl = window.location.pathname.replace("cart", "orders") + "?order_status=pending";
var nextOrdersUrl = ordersUrl() + (String(ordersUrl()).indexOf("?") >= 0 ? "&" : "?") + "order_status=pending";
if (!orderNumber) {
window.location.href = ordersUrl;
window.location.href = nextOrdersUrl;
return;
}
@@ -201,10 +205,10 @@
return;
}
// 二维码 / 余额 / 失败:统一回订单中心处理(含 ?payment 提示)。
window.location.href = ordersUrl;
window.location.href = nextOrdersUrl;
})
.catch(function () {
window.location.href = ordersUrl;
window.location.href = nextOrdersUrl;
});
})
.catch(function () {
@@ -217,31 +221,36 @@
var actionButton = event.target.closest("[data-qty-action]");
if (actionButton) {
var itemEl = actionButton.closest("[data-cart-index]");
var index = Number(itemEl.getAttribute("data-cart-index"));
var items = getCart();
if (!items[index]) {
if (!itemEl) return;
var productId = Number(itemEl.getAttribute("data-product-id") || 0);
var optionsString = itemEl.getAttribute("data-options-string") || "";
var action = actionButton.getAttribute("data-qty-action");
var path = "/shop/frontend/cart/add";
var body = { product_id: productId, options_string: optionsString, count: 1 };
if (action === "dec") {
path = "/shop/frontend/cart/reduce";
} else if (action === "remove") {
path = "/shop/frontend/cart/remove";
body = { product_id: productId, options_string: optionsString };
} else if (action !== "inc") {
return;
}
var action = actionButton.getAttribute("data-qty-action");
if (action === "inc") {
items[index].qty = Number(items[index].qty || 1) + 1;
} else if (action === "dec") {
items[index].qty = Math.max(1, Number(items[index].qty || 1) - 1);
} else if (action === "remove") {
items.splice(index, 1);
}
setCart(items);
setSummary(null);
renderCart();
rest(path, "POST", body).then(function (result) {
if (!result || !result.success) {
window.alert((result && result.message) || "操作失败");
return;
}
setSummary(null);
loadServerCart();
});
return;
}
if (event.target.closest("[data-aksrt-clear-cart]")) {
localStorage.removeItem(CART_KEY);
setSummary(null);
renderCart();
rest("/shop/frontend/cart/empty", "POST", {}).then(function () {
setSummary(null);
loadServerCart();
});
return;
}
@@ -257,10 +266,28 @@
var resumeButton = event.target.closest("[data-aksrt-resume-order]");
if (resumeButton) {
window.location.href = window.location.pathname.replace("cart", "orders") + "?order_status=pending";
window.location.href = ordersUrl() + (String(ordersUrl()).indexOf("?") >= 0 ? "&" : "?") + "order_status=pending";
}
});
function loadServerCart() {
return rest("/shop/frontend/cart", "GET").then(function (result) {
var items = (result && result.success && result.data && result.data.items) ? result.data.items : [];
items = items.map(function (it) {
it.qty = it.quantity || it.qty || 1;
it.price = it.unit_price != null ? it.unit_price : (it.product && it.product.price) || 0;
it.title = it.title || (it.product && it.product.title) || "商品";
it.image = it.image || (it.product && it.product.image) || "";
return it;
});
setCart(items);
renderCart();
}).catch(function () {
setCart([]);
renderCart();
});
}
var couponInput = page.querySelector("[data-aksrt-coupon-code]");
if (couponInput && searchParams.has("coupon")) {
couponInput.value = searchParams.get("coupon") || "";
@@ -300,7 +327,7 @@
}
function refreshAddressList() {
fetch("/wp-json/aksrt/v1/shop/frontend/address", {
fetch(restBase() + "/shop/frontend/address", {
headers: { "X-WP-Nonce": aksrtData.restNonce }
})
.then(function (r) { return r.json(); })
@@ -389,9 +416,9 @@
if (deleteBtn) {
var deleteId = deleteBtn.getAttribute("data-aksrt-address-delete");
if (!confirm("确定删除该地址吗?")) return;
request("/shop/frontend/address/" + deleteId, {}).then(function (result) {
if (!result.success) {
alert(result.message || "删除失败。");
rest("/shop/frontend/address/" + deleteId, "DELETE").then(function (result) {
if (!result || !result.success) {
alert((result && result.message) || "删除失败。");
return;
}
refreshAddressList();
@@ -452,5 +479,5 @@
});
}
renderCart();
loadServerCart();
})();