Wire product cat/tag archives to settings-driven sort and AJAX paging, seller product options/video/params editing, revenue withdraw form and order review submit — finishing remaining shop frontend gaps after buy-path.
415 lines
18 KiB
JavaScript
415 lines
18 KiB
JavaScript
/**
|
|
* AKSRT Order Center — v2.0
|
|
*
|
|
* 前台订单中心交互:
|
|
* - 支付 / 取消 / 售后:操作成功后局部刷新订单卡片,不再整页 reload
|
|
* - 倒计时
|
|
* - Toast 通知替代 window.alert
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
var page = document.querySelector("[data-aksrt-order-center]");
|
|
if (!page || !window.aksrtData) {
|
|
return;
|
|
}
|
|
|
|
/* ── Toast helper ── */
|
|
function showToast(text, duration) {
|
|
duration = duration || 2500;
|
|
var container = document.getElementById("toast-container");
|
|
if (!container) {
|
|
container = document.createElement("div");
|
|
container.id = "toast-container";
|
|
container.style.cssText = "position:fixed;bottom:24px;right:24px;z-index:99999;display:flex;flex-direction:column-reverse;gap:10px;pointer-events:none;";
|
|
document.body.appendChild(container);
|
|
}
|
|
var toast = document.createElement("div");
|
|
toast.style.cssText = "pointer-events:auto;display:flex;align-items:center;gap:8px;padding:10px 20px;background:rgba(0,0,0,0.78);backdrop-filter:blur(12px);color:#fff;font-family:var(--font-ui);font-size:14px;font-weight:500;border-radius:100px;box-shadow:0 4px 20px rgba(0,0,0,0.2);animation:aksrtToastIn 0.35s cubic-bezier(0.22,1,0.36,1) forwards;white-space:nowrap;";
|
|
toast.textContent = text;
|
|
container.appendChild(toast);
|
|
setTimeout(function () {
|
|
toast.style.animation = "aksrtToastOut 0.4s ease forwards";
|
|
setTimeout(function () { if (toast.parentNode) toast.parentNode.removeChild(toast); }, 400);
|
|
}, duration);
|
|
}
|
|
|
|
// Inject toast keyframes if not present
|
|
if (!document.getElementById("aksrt-toast-keyframes")) {
|
|
var style = document.createElement("style");
|
|
style.id = "aksrt-toast-keyframes";
|
|
style.textContent = "@keyframes aksrtToastIn{0%{opacity:0;transform:translateY(16px) scale(0.92)}100%{opacity:1;transform:translateY(0) scale(1)}}@keyframes aksrtToastOut{0%{opacity:1;transform:translateY(0) scale(1)}100%{opacity:0;transform:translateY(8px) scale(0.95)}}";
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
/* ── REST helper ── */
|
|
function restRoot() {
|
|
return ((aksrtData.siteUrl || "").replace(/\/$/, "")) + "/wp-json/aksrt/v1";
|
|
}
|
|
|
|
function request(path, body, method) {
|
|
return fetch(restRoot() + path, {
|
|
method: method || "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"X-WP-Nonce": aksrtData.restNonce
|
|
},
|
|
body: body === undefined ? undefined : JSON.stringify(body || {})
|
|
}).then(function (response) {
|
|
return response.json();
|
|
});
|
|
}
|
|
|
|
function cartUrl() {
|
|
return aksrtData.cartUrl || (aksrtData.routes && aksrtData.routes.cart) || "/";
|
|
}
|
|
|
|
/* ── 扫码支付弹层(微信 Native code_url 等)── */
|
|
function showPaymentQr(content, orderNumber) {
|
|
var existing = document.getElementById("aksrt-pay-qr-overlay");
|
|
if (existing) existing.remove();
|
|
|
|
var overlay = document.createElement("div");
|
|
overlay.id = "aksrt-pay-qr-overlay";
|
|
overlay.style.cssText = "position:fixed;inset:0;z-index:100000;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.5);";
|
|
|
|
var qrImg = "";
|
|
if (window.AksrtShare && typeof window.AksrtShare.buildQrDataUrl === "function") {
|
|
var dataUrl = window.AksrtShare.buildQrDataUrl(content, 220);
|
|
if (dataUrl) {
|
|
qrImg = '<img src="' + dataUrl + '" alt="支付二维码" width="220" height="220" style="display:block;margin:0 auto;">';
|
|
}
|
|
}
|
|
if (!qrImg) {
|
|
qrImg = '<p style="word-break:break-all;font-size:12px;color:#666;">' + String(content).replace(/[<>&]/g, "") + "</p>";
|
|
}
|
|
|
|
var card = document.createElement("div");
|
|
card.style.cssText = "background:#fff;border-radius:12px;padding:24px;max-width:300px;text-align:center;box-shadow:0 10px 40px rgba(0,0,0,0.2);";
|
|
card.innerHTML =
|
|
'<h3 style="margin:0 0 12px;font-size:16px;">请扫码完成支付</h3>' +
|
|
qrImg +
|
|
'<p style="margin:12px 0 0;font-size:13px;color:#888;">支付完成后本页将自动更新</p>' +
|
|
'<button type="button" id="aksrt-pay-qr-close" style="margin-top:14px;padding:8px 20px;border:none;border-radius:8px;background:var(--color-primary,#f04494);color:#fff;cursor:pointer;">完成 / 关闭</button>';
|
|
overlay.appendChild(card);
|
|
document.body.appendChild(overlay);
|
|
|
|
function close() { overlay.remove(); }
|
|
overlay.addEventListener("click", function (e) { if (e.target === overlay) close(); });
|
|
card.querySelector("#aksrt-pay-qr-close").addEventListener("click", function () {
|
|
close();
|
|
// 关闭后查询订单是否已到账
|
|
if (orderNumber) {
|
|
request("/shop/frontend/orders/" + orderNumber, {}).then(function () {
|
|
window.location.reload();
|
|
}).catch(function () {});
|
|
}
|
|
});
|
|
}
|
|
|
|
/* ── 第三方支付返回提示(?payment=success|pending|cancelled)── */
|
|
(function paymentReturnNotice() {
|
|
var params = new URLSearchParams(window.location.search);
|
|
var pay = params.get("payment");
|
|
if (!pay) return;
|
|
var map = { success: "支付成功!", pending: "支付处理中,到账后将自动更新。", cancelled: "支付已取消。", fail: "支付失败,请重试。" };
|
|
showToast(map[pay] || "支付状态:" + pay, 3500);
|
|
})();
|
|
|
|
/* ── Local card refresh helpers ── */
|
|
function findOrderCard(orderNumber) {
|
|
return page.querySelector('[data-aksrt-order-card="' + orderNumber + '"]');
|
|
}
|
|
|
|
function updateCardStatus(card, newStatus, statusLabel) {
|
|
if (!card) return;
|
|
var badge = card.querySelector(".aksrt-orders-page__card-status, .aksrt-orders-page__item-badge");
|
|
if (badge) {
|
|
badge.textContent = statusLabel || newStatus;
|
|
}
|
|
// Hide action buttons that are no longer relevant
|
|
var actions = card.querySelectorAll("[data-aksrt-order-pay], [data-aksrt-order-cancel], [data-aksrt-order-after-sale], [data-aksrt-order-receipt], [data-aksrt-order-rebuy]");
|
|
for (var i = 0; i < actions.length; i++) {
|
|
actions[i].style.display = "none";
|
|
}
|
|
// Hide countdown if present
|
|
var countdown = card.querySelector("[data-aksrt-order-countdown]");
|
|
if (countdown) {
|
|
countdown.style.display = "none";
|
|
}
|
|
}
|
|
|
|
/* ── After-Sale Modal ── */
|
|
var afterSaleModal = page.querySelector("[data-aksrt-aftersale-modal]");
|
|
var afterSaleForm = page.querySelector("[data-aksrt-aftersale-form]");
|
|
var afterSaleOrderInput = afterSaleForm ? afterSaleForm.querySelector('[name="order_number"]') : null;
|
|
|
|
function openAfterSaleModal(orderNumber) {
|
|
if (!afterSaleModal || !afterSaleForm) {
|
|
showToast("售后表单加载失败,请刷新页面后重试。");
|
|
return;
|
|
}
|
|
afterSaleForm.reset();
|
|
if (afterSaleOrderInput) afterSaleOrderInput.value = orderNumber;
|
|
afterSaleModal.hidden = false;
|
|
}
|
|
|
|
function closeAfterSaleModal() {
|
|
if (afterSaleModal) afterSaleModal.hidden = true;
|
|
}
|
|
|
|
if (afterSaleForm) {
|
|
afterSaleForm.addEventListener("submit", function (event) {
|
|
event.preventDefault();
|
|
var formData = new FormData(afterSaleForm);
|
|
var orderNumber = formData.get("order_number");
|
|
var reason = (formData.get("reason") || "").trim();
|
|
if (!reason) {
|
|
showToast("请填写售后原因。");
|
|
return;
|
|
}
|
|
|
|
var submitBtn = afterSaleForm.querySelector('button[type="submit"]');
|
|
if (submitBtn) {
|
|
submitBtn.disabled = true;
|
|
submitBtn.textContent = "提交中...";
|
|
}
|
|
|
|
request("/shop/frontend/orders/" + orderNumber + "/after-sale", {
|
|
type: formData.get("type") || "refund",
|
|
reason: reason,
|
|
note: (formData.get("note") || "").trim()
|
|
}).then(function (result) {
|
|
if (submitBtn) {
|
|
submitBtn.disabled = false;
|
|
submitBtn.textContent = "提交售后";
|
|
}
|
|
if (!result.success) {
|
|
showToast(result.message || "提交售后失败。");
|
|
return;
|
|
}
|
|
closeAfterSaleModal();
|
|
showToast("售后申请已提交。");
|
|
// Local refresh: update card status
|
|
var card = findOrderCard(orderNumber);
|
|
updateCardStatus(card, "refunded", "已退款");
|
|
}).catch(function () {
|
|
if (submitBtn) {
|
|
submitBtn.disabled = false;
|
|
submitBtn.textContent = "提交售后";
|
|
}
|
|
showToast("网络错误,请稍后重试。");
|
|
});
|
|
});
|
|
}
|
|
|
|
/* ── Countdown timer ── */
|
|
function updateCountdowns() {
|
|
var nodes = page.querySelectorAll("[data-aksrt-order-countdown]");
|
|
for (var i = 0; i < nodes.length; i++) {
|
|
var node = nodes[i];
|
|
var value = Number(node.getAttribute("data-aksrt-order-countdown") || 0);
|
|
if (!value) continue;
|
|
|
|
value -= 1;
|
|
node.setAttribute("data-aksrt-order-countdown", String(value));
|
|
node.textContent = value > 0 ? "剩余支付时间 " + value + " 秒" : "待支付已超时,请重新发起。";
|
|
}
|
|
}
|
|
|
|
setInterval(updateCountdowns, 1000);
|
|
|
|
/* ── Event delegation ── */
|
|
page.addEventListener("click", function (event) {
|
|
// Timeline toggle
|
|
var timelineToggle = event.target.closest("[data-aksrt-toggle-timeline]");
|
|
if (timelineToggle) {
|
|
var timeline = timelineToggle.closest("[data-aksrt-timeline]");
|
|
var body = timeline ? timeline.querySelector(".aksrt-orders-page__timeline-body") : null;
|
|
if (body) body.hidden = !body.hidden;
|
|
return;
|
|
}
|
|
|
|
// Confirm receipt
|
|
var receiptBtn = event.target.closest("[data-aksrt-order-receipt]");
|
|
if (receiptBtn) {
|
|
var receiptOrder = receiptBtn.getAttribute("data-aksrt-order-receipt");
|
|
if (!receiptOrder) return;
|
|
if (!window.confirm("确认已收到货物?")) return;
|
|
receiptBtn.disabled = true;
|
|
request("/shop/frontend/orders/" + receiptOrder + "/confirm-receipt", {})
|
|
.then(function (result) {
|
|
receiptBtn.disabled = false;
|
|
if (!result || !result.success) {
|
|
showToast((result && result.message) || "确认收货失败");
|
|
return;
|
|
}
|
|
showToast("已确认收货");
|
|
updateCardStatus(findOrderCard(receiptOrder), "completed", "已完成");
|
|
})
|
|
.catch(function () {
|
|
receiptBtn.disabled = false;
|
|
showToast("网络错误");
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Rebuy
|
|
var rebuyBtn = event.target.closest("[data-aksrt-order-rebuy]");
|
|
if (rebuyBtn) {
|
|
var rebuyOrder = rebuyBtn.getAttribute("data-aksrt-order-rebuy");
|
|
if (!rebuyOrder) return;
|
|
rebuyBtn.disabled = true;
|
|
request("/shop/frontend/orders/" + rebuyOrder + "/rebuy", {})
|
|
.then(function (result) {
|
|
rebuyBtn.disabled = false;
|
|
if (!result || !result.success) {
|
|
showToast((result && result.message) || "再次购买失败");
|
|
return;
|
|
}
|
|
showToast("已加入购物车");
|
|
window.location.href = cartUrl();
|
|
})
|
|
.catch(function () {
|
|
rebuyBtn.disabled = false;
|
|
showToast("网络错误");
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Review submit modal
|
|
var reviewBtn = event.target.closest("[data-aksrt-order-review]");
|
|
if (reviewBtn) {
|
|
var reviewOrder = reviewBtn.getAttribute("data-aksrt-order-review");
|
|
var card = reviewBtn.closest("[data-aksrt-order-card]");
|
|
var productId = 0;
|
|
if (card) {
|
|
var firstItem = card.querySelector("[data-product-id]");
|
|
if (firstItem) productId = Number(firstItem.getAttribute("data-product-id") || 0);
|
|
}
|
|
if (!productId) {
|
|
// try parse from item title block dataset if present
|
|
var any = card ? card.querySelector(".aksrt-orders-page__card-item") : null;
|
|
if (any && any.getAttribute("data-product-id")) {
|
|
productId = Number(any.getAttribute("data-product-id") || 0);
|
|
}
|
|
}
|
|
if (!reviewOrder) return;
|
|
var content = window.prompt("请输入评价内容(可选)", "好评!");
|
|
if (content === null) return;
|
|
var rating = window.prompt("商品评分 1-5", "5");
|
|
var r = Math.max(1, Math.min(5, parseInt(rating || "5", 10) || 5));
|
|
reviewBtn.disabled = true;
|
|
request("/shop/frontend/orders/" + reviewOrder + "/review", {
|
|
product_id: productId,
|
|
content: content || "好评!",
|
|
rating_product: r,
|
|
rating_service: r,
|
|
rating_shipping: r
|
|
}).then(function (result) {
|
|
reviewBtn.disabled = false;
|
|
if (!result || !result.success) {
|
|
showToast((result && result.message) || "评价失败");
|
|
return;
|
|
}
|
|
showToast(result.message || "评价已提交");
|
|
updateCardStatus(findOrderCard(reviewOrder), "completed", "已完成");
|
|
}).catch(function () {
|
|
reviewBtn.disabled = false;
|
|
showToast("网络错误");
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Pay
|
|
var payButton = event.target.closest("[data-aksrt-order-pay]");
|
|
if (payButton) {
|
|
var payOrderNumber = payButton.getAttribute("data-aksrt-order-pay");
|
|
// 支付方式取订单创建时选定的方式(按钮 data 属性可覆盖);
|
|
// 留空则由服务端使用订单存储的 payment_method。
|
|
var payMethod = payButton.getAttribute("data-aksrt-order-method") || "";
|
|
payButton.disabled = true;
|
|
payButton.textContent = "处理中...";
|
|
request("/shop/frontend/orders/" + payOrderNumber + "/pay", payMethod ? { payment_method: payMethod } : {})
|
|
.then(function (result) {
|
|
if (!result.success) {
|
|
payButton.disabled = false;
|
|
payButton.textContent = "立即支付";
|
|
showToast(result.message || "支付失败。");
|
|
return;
|
|
}
|
|
|
|
var data = result.data || {};
|
|
|
|
// 跳转/扫码型网关:到第三方支付页或展示二维码,订单仍为待支付,
|
|
// 由回调确认到账。
|
|
if (data.redirect) {
|
|
if (data.payment_url && /^https?:\/\//i.test(data.payment_url)) {
|
|
window.location.href = data.payment_url;
|
|
return;
|
|
}
|
|
if (data.qr_content || data.payment_url) {
|
|
showPaymentQr(data.qr_content || data.payment_url, payOrderNumber);
|
|
payButton.disabled = false;
|
|
payButton.textContent = "已下单,待支付";
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 同步型网关(站内余额):已到账。
|
|
showToast("支付成功!");
|
|
var card = findOrderCard(payOrderNumber);
|
|
updateCardStatus(card, "paid", "已支付");
|
|
})
|
|
.catch(function () {
|
|
payButton.disabled = false;
|
|
payButton.textContent = "立即支付";
|
|
showToast("网络错误,请稍后重试。");
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Cancel
|
|
var cancelButton = event.target.closest("[data-aksrt-order-cancel]");
|
|
if (cancelButton) {
|
|
var cancelOrderNumber = cancelButton.getAttribute("data-aksrt-order-cancel");
|
|
cancelButton.disabled = true;
|
|
cancelButton.textContent = "取消中...";
|
|
request("/shop/frontend/orders/" + cancelOrderNumber + "/cancel")
|
|
.then(function (result) {
|
|
if (!result.success) {
|
|
cancelButton.disabled = false;
|
|
cancelButton.textContent = "取消订单";
|
|
showToast(result.message || "取消失败。");
|
|
return;
|
|
}
|
|
showToast("订单已取消。");
|
|
var card = findOrderCard(cancelOrderNumber);
|
|
updateCardStatus(card, "cancelled", "已取消");
|
|
})
|
|
.catch(function () {
|
|
cancelButton.disabled = false;
|
|
cancelButton.textContent = "取消订单";
|
|
showToast("网络错误,请稍后重试。");
|
|
});
|
|
return;
|
|
}
|
|
|
|
// After-Sale
|
|
var afterSaleButton = event.target.closest("[data-aksrt-order-after-sale]");
|
|
if (afterSaleButton) {
|
|
var afterSaleOrderNumber = afterSaleButton.getAttribute("data-aksrt-order-after-sale");
|
|
openAfterSaleModal(afterSaleOrderNumber);
|
|
return;
|
|
}
|
|
|
|
// Close modal
|
|
if (event.target.closest("[data-aksrt-aftersale-modal-close]") || (event.target.classList && event.target.classList.contains("aksrt-modal__overlay"))) {
|
|
closeAfterSaleModal();
|
|
}
|
|
});
|
|
})();
|