Files
wordpress-theme-aksrt/assets/js/cart-checkout.js
T

457 lines
18 KiB
JavaScript

(function () {
"use strict";
var page = document.querySelector("[data-aksrt-cart-page]");
if (!page || !window.aksrtData) {
return;
}
var CART_KEY = "aksrt_cart";
var currencySymbol = page.getAttribute("data-currency-symbol") || "¥";
var itemsEl = page.querySelector("[data-aksrt-cart-items]");
var subtotalEl = page.querySelector("[data-aksrt-summary-subtotal]");
var shippingEl = page.querySelector("[data-aksrt-summary-shipping]");
var discountEl = page.querySelector("[data-aksrt-summary-discount]");
var totalEl = page.querySelector("[data-aksrt-summary-total]");
var previewMessageEl = page.querySelector("[data-aksrt-preview-message]");
var createBtn = page.querySelector("[data-aksrt-create-order]");
var previewBtn = page.querySelector("[data-aksrt-preview-checkout]");
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 money(value) {
return currencySymbol + Number(value || 0).toFixed(2);
}
function selectedValue(name) {
var checked = page.querySelector('input[name="' + name + '"]:checked');
return checked ? checked.value : "";
}
function getPayload() {
var items = getCart().map(function (item) {
return {
product_id: Number(item.product_id || item.id || 0),
quantity: Number(item.qty || item.quantity || 1)
};
}).filter(function (item) {
return item.product_id > 0;
});
return {
items: items,
coupon_code: (page.querySelector("[data-aksrt-coupon-code]") || {}).value || "",
shipping_method: selectedValue("aksrt_cart_shipping"),
address_id: Number(selectedValue("aksrt_cart_address") || 0),
payment_method: selectedValue("aksrt_cart_payment"),
customer_note: (page.querySelector("[data-aksrt-customer-note]") || {}).value || ""
};
}
function renderCart() {
var items = getCart();
if (!items.length) {
itemsEl.innerHTML = '' +
'<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>' +
"</div>";
subtotalEl.textContent = money(0);
shippingEl.textContent = money(0);
discountEl.textContent = "-" + money(0);
totalEl.textContent = money(0);
createBtn.disabled = true;
previewState = null;
return;
}
var total = 0;
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;
total += lineTotal;
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>') +
'<div class="aksrt-cart-page__item-main">' +
'<h3>' + (item.title || "商品") + "</h3>" +
'<p>' + money(price) + "</p>" +
"</div>" +
'<div class="aksrt-cart-page__item-qty">' +
'<button type="button" data-qty-action="dec">-</button>' +
"<span>" + qty + "</span>" +
'<button type="button" data-qty-action="inc">+</button>' +
"</div>" +
'<strong class="aksrt-cart-page__item-total">' + money(lineTotal) + "</strong>" +
'<button type="button" class="aksrt-cart-page__item-remove" data-qty-action="remove"><i class="fas fa-trash"></i></button>' +
"</article>";
});
itemsEl.innerHTML = html;
subtotalEl.textContent = money(total);
if (!previewState) {
shippingEl.textContent = money(0);
discountEl.textContent = "-" + money(0);
totalEl.textContent = money(total);
}
}
function setSummary(data) {
previewState = data || null;
subtotalEl.textContent = money((data && data.subtotal) || 0);
shippingEl.textContent = money((data && data.shipping_fee) || 0);
discountEl.textContent = "-" + money((data && data.discount) || 0);
totalEl.textContent = money((data && data.total) || 0);
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 previewCheckout() {
var payload = getPayload();
if (!payload.items.length) {
previewMessageEl.textContent = "请先添加商品后再试算。";
return;
}
previewBtn.disabled = true;
previewMessageEl.textContent = "正在试算订单...";
request("/shop/frontend/checkout/preview", payload)
.then(function (result) {
if (!result.success) {
setSummary(null);
previewMessageEl.textContent = result.message || "试算失败。";
return;
}
setSummary(result.data);
previewMessageEl.textContent = "已完成试算,可直接创建订单。";
})
.catch(function () {
setSummary(null);
previewMessageEl.textContent = "网络错误,请稍后重试。";
})
.finally(function () {
previewBtn.disabled = false;
});
}
function createOrder() {
var payload = getPayload();
if (!previewState) {
previewCheckout();
return;
}
createBtn.disabled = true;
request("/shop/frontend/orders", payload)
.then(function (result) {
if (!result.success) {
previewMessageEl.textContent = result.message || "订单创建失败。";
createBtn.disabled = false;
return;
}
localStorage.removeItem(CART_KEY);
var orderNumber = (result.data && result.data.order_number) || "";
var ordersUrl = window.location.pathname.replace("cart", "orders") + "?order_status=pending";
if (!orderNumber) {
window.location.href = ordersUrl;
return;
}
// 立即发起支付:跳转型网关直接前往第三方,余额型同步完成。
request("/shop/frontend/orders/" + orderNumber + "/pay", {})
.then(function (pay) {
var data = (pay && pay.data) || {};
if (pay && pay.success && data.redirect && data.payment_url && /^https?:\/\//i.test(data.payment_url)) {
window.location.href = data.payment_url;
return;
}
// 二维码 / 余额 / 失败:统一回订单中心处理(含 ?payment 提示)。
window.location.href = ordersUrl;
})
.catch(function () {
window.location.href = ordersUrl;
});
})
.catch(function () {
previewMessageEl.textContent = "网络错误,请稍后重试。";
createBtn.disabled = false;
});
}
page.addEventListener("click", function (event) {
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]) {
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();
return;
}
if (event.target.closest("[data-aksrt-clear-cart]")) {
localStorage.removeItem(CART_KEY);
setSummary(null);
renderCart();
return;
}
if (event.target.closest("[data-aksrt-preview-checkout]")) {
previewCheckout();
return;
}
if (event.target.closest("[data-aksrt-create-order]")) {
createOrder();
return;
}
var resumeButton = event.target.closest("[data-aksrt-resume-order]");
if (resumeButton) {
window.location.href = window.location.pathname.replace("cart", "orders") + "?order_status=pending";
}
});
var couponInput = page.querySelector("[data-aksrt-coupon-code]");
if (couponInput && searchParams.has("coupon")) {
couponInput.value = searchParams.get("coupon") || "";
}
/* ── Address Book Management ── */
var addressModal = page.querySelector("[data-aksrt-address-modal]");
var addressForm = page.querySelector("[data-aksrt-address-form]");
var addressList = page.querySelector("[data-aksrt-address-list]");
var addressTitle = page.querySelector("[data-aksrt-address-modal-title]");
function openAddressModal(mode, address) {
if (!addressModal || !addressForm) return;
addressForm.reset();
addressForm.querySelector('[name="address_id"]').value = "";
if (mode === "edit" && address) {
addressTitle.textContent = "编辑地址";
addressForm.querySelector('[name="address_id"]').value = address.id || "";
addressForm.querySelector('[name="name"]').value = address.name || "";
addressForm.querySelector('[name="phone"]').value = address.phone || "";
addressForm.querySelector('[name="province"]').value = address.province || "";
addressForm.querySelector('[name="city"]').value = address.city || "";
addressForm.querySelector('[name="district"]').value = address.district || "";
addressForm.querySelector('[name="address"]').value = address.address || "";
addressForm.querySelector('[name="is_default"]').checked = !!address.is_default;
} else {
addressTitle.textContent = "新增地址";
}
addressModal.hidden = false;
}
function closeAddressModal() {
if (addressModal) addressModal.hidden = true;
}
function refreshAddressList() {
fetch("/wp-json/aksrt/v1/shop/frontend/address", {
headers: { "X-WP-Nonce": aksrtData.restNonce }
})
.then(function (r) { return r.json(); })
.then(function (result) {
if (!result.success || !addressList) return;
var addresses = (result.data && result.data.addresses) || [];
renderAddressList(addresses);
})
.catch(function () {});
}
function renderAddressList(addresses) {
if (!addressList) return;
if (!addresses.length) {
addressList.innerHTML =
'<div class="aksrt-cart-page__address-empty" data-aksrt-address-empty>' +
'<i class="fas fa-map-marker-alt" aria-hidden="true"></i>' +
"<p>暂无收货地址,请先新增。</p>" +
"</div>";
return;
}
var html = "";
addresses.forEach(function (addr, index) {
html +=
'<div class="aksrt-cart-page__option-card aksrt-cart-page__address-card" data-aksrt-address-card="' + addr.id + '">' +
'<label class="aksrt-cart-page__address-label">' +
'<input type="radio" name="aksrt_cart_address" value="' + addr.id + '"' + (index === 0 || addr.is_default ? " checked" : "") + ">" +
'<span class="aksrt-cart-page__option-body">' +
"<strong>" + escHtml(addr.name) + (addr.is_default ? ' <span class="aksrt-cart-page__default-tag">默认</span>' : "") + "</strong>" +
"<span>" + escHtml(addr.phone) + "</span>" +
"<span>" + escHtml(addr.full_text) + "</span>" +
"</span></label>" +
'<div class="aksrt-cart-page__address-actions">' +
'<button type="button" class="btn-ghost btn-xs" data-aksrt-address-edit="' + addr.id + '">编辑</button>' +
'<button type="button" class="btn-ghost btn-xs" data-aksrt-address-delete="' + addr.id + '">删除</button>' +
(addr.is_default ? "" : '<button type="button" class="btn-ghost btn-xs" data-aksrt-address-set-default="' + addr.id + '">设为默认</button>') +
"</div></div>";
});
addressList.innerHTML = html;
}
function escHtml(str) {
var div = document.createElement("div");
div.appendChild(document.createTextNode(str || ""));
return div.innerHTML;
}
function getAddressById(id) {
var cards = addressList ? addressList.querySelectorAll("[data-aksrt-address-card]") : [];
var found = null;
cards.forEach(function (card) {
if (String(card.getAttribute("data-aksrt-address-card")) === String(id)) {
var body = card.querySelector(".aksrt-cart-page__option-body");
if (body) {
var spans = body.querySelectorAll("span");
var strong = body.querySelector("strong");
found = {
id: id,
name: strong ? strong.textContent.replace("默认", "").trim() : "",
phone: spans[0] ? spans[0].textContent : "",
full_text: spans[1] ? spans[1].textContent : ""
};
}
}
});
return found;
}
// Address event delegation
page.addEventListener("click", function (event) {
if (event.target.closest("[data-aksrt-address-add]")) {
openAddressModal("add");
return;
}
var editBtn = event.target.closest("[data-aksrt-address-edit]");
if (editBtn) {
var editId = editBtn.getAttribute("data-aksrt-address-edit");
var addr = getAddressById(editId);
openAddressModal("edit", addr);
return;
}
var deleteBtn = event.target.closest("[data-aksrt-address-delete]");
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 || "删除失败。");
return;
}
refreshAddressList();
}).catch(function () { alert("网络错误。"); });
return;
}
var defaultBtn = event.target.closest("[data-aksrt-address-set-default]");
if (defaultBtn) {
var defaultId = defaultBtn.getAttribute("data-aksrt-address-set-default");
request("/shop/frontend/address/" + defaultId + "/default", {}).then(function (result) {
if (!result.success) {
alert(result.message || "设置失败。");
return;
}
refreshAddressList();
}).catch(function () { alert("网络错误。"); });
return;
}
if (event.target.closest("[data-aksrt-address-modal-close]") || event.target.classList.contains("aksrt-modal__overlay")) {
closeAddressModal();
}
});
if (addressForm) {
addressForm.addEventListener("submit", function (event) {
event.preventDefault();
var formData = new FormData(addressForm);
var addressId = formData.get("address_id");
var payload = {
name: (formData.get("name") || "").trim(),
phone: (formData.get("phone") || "").trim(),
province: (formData.get("province") || "").trim(),
city: (formData.get("city") || "").trim(),
district: (formData.get("district") || "").trim(),
address: (formData.get("address") || "").trim(),
is_default: formData.get("is_default") === "1"
};
if (!payload.name || !payload.phone || !payload.address) {
alert("请填写必填项。");
return;
}
var url = addressId
? "/shop/frontend/address/" + addressId
: "/shop/frontend/address";
request(url, payload).then(function (result) {
if (!result.success) {
alert(result.message || "保存失败。");
return;
}
closeAddressModal();
refreshAddressList();
}).catch(function () { alert("网络错误。"); });
});
}
renderCart();
})();