/**
* 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) {
var opts = {
method: method || "POST",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
"X-WP-Nonce": (aksrtData && aksrtData.restNonce) || ""
}
};
if (body !== undefined) {
opts.body = JSON.stringify(body || {});
}
return fetch(restRoot() + path, opts).then(function (response) {
return response.text().then(function (text) {
var data = {};
try {
data = text ? JSON.parse(text) : {};
} catch (e) {
data = {
success: false,
message: response.status === 404
? "支付接口未找到(404),请刷新页面或联系管理员。"
: ("请求失败(HTTP " + response.status + ")")
};
}
if (typeof data.success === "undefined" && response.ok === false) {
data.success = false;
data.message = data.message || data.code || ("HTTP " + response.status);
}
return data;
});
});
}
function orderPath(orderNumber, suffix) {
var base = "/shop/frontend/orders/" + encodeURIComponent(orderNumber || "");
return suffix ? base + suffix : base;
}
function cartUrl() {
return aksrtData.cartUrl || (aksrtData.routes && aksrtData.routes.cart) || "/";
}
/* ── Order review modal (zibll 1:1) ── */
var STAR_SVG =
'';
var SCORE_LABELS = ["", "非常差", "较差", "一般", "较好", "非常好"];
function formatRemain(sec) {
sec = Math.max(0, sec | 0);
var d = Math.floor(sec / 86400);
var h = Math.floor((sec % 86400) / 3600);
var m = Math.floor((sec % 3600) / 60);
var s = sec % 60;
function z(n) { return n < 10 ? "0" + n : String(n); }
if (d > 0) return d + "天" + z(h) + ":" + z(m) + ":" + z(s);
return z(h) + ":" + z(m) + ":" + z(s);
}
function bindShopScore(root) {
if (!root) return;
root.querySelectorAll("[data-aksrt-score-box], .shop-score-box").forEach(function (box) {
if (box.dataset.scoreBound === "1") return;
box.dataset.scoreBound = "1";
var iconBox = box.querySelector(".score-icon");
var textBox = box.querySelector(".score-text");
var input = box.querySelector(".score-input");
if (!iconBox) return;
var value = parseInt(box.getAttribute("score-value") || (input && input.value) || "0", 10) || 0;
var html = "";
for (var i = 1; i <= 5; i++) {
html += '' + STAR_SVG + "";
}
iconBox.innerHTML = html;
function paint(score, hover) {
iconBox.querySelectorAll(".icon-star").forEach(function (star) {
var s = parseInt(star.getAttribute("data-score"), 10) || 0;
star.classList.toggle("active", !hover && s <= score);
star.classList.toggle("hover", !!hover && s <= score);
});
if (textBox) textBox.textContent = SCORE_LABELS[score] || "";
}
paint(value, false);
iconBox.addEventListener("mouseover", function (e) {
var star = e.target.closest(".icon-star");
if (!star || !iconBox.contains(star)) return;
paint(parseInt(star.getAttribute("data-score"), 10) || 0, true);
});
iconBox.addEventListener("mouseleave", function () {
var cur = parseInt(box.getAttribute("score-value") || "0", 10) || 0;
paint(cur, false);
});
iconBox.addEventListener("click", function (e) {
var star = e.target.closest(".icon-star");
if (!star || !iconBox.contains(star)) return;
var score = parseInt(star.getAttribute("data-score"), 10) || 0;
box.setAttribute("score-value", String(score));
if (input) input.value = String(score);
paint(score, false);
});
});
}
function bindReviewImages(form) {
var preview = form.querySelector("[data-aksrt-review-preview]");
var fileInput = form.querySelector("[data-aksrt-review-file]");
var addBtn = form.querySelector("[data-aksrt-review-add]");
if (!preview || !fileInput) return;
var max = parseInt(preview.getAttribute("data-max") || "6", 10) || 6;
var files = [];
function render() {
preview.querySelectorAll(".preview-item").forEach(function (n) { n.remove(); });
files.forEach(function (item, idx) {
var node = document.createElement("div");
node.className = "preview-item";
node.innerHTML = '';
node.querySelector("img").src = item.url;
node.querySelector(".remove").addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
if (item.url && item.url.indexOf("blob:") === 0) {
try { URL.revokeObjectURL(item.url); } catch (err) {}
}
files.splice(idx, 1);
render();
});
preview.insertBefore(node, addBtn);
});
if (addBtn) addBtn.style.display = files.length >= max ? "none" : "";
form._aksrtReviewFiles = files.slice();
}
function onPick() {
if (files.length >= max) return;
fileInput.click();
}
if (addBtn) {
addBtn.addEventListener("click", onPick);
addBtn.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
onPick();
}
});
}
fileInput.addEventListener("change", function () {
var list = Array.prototype.slice.call(fileInput.files || []);
list.forEach(function (file) {
if (files.length >= max) return;
if (!file.type || file.type.indexOf("image/") !== 0) return;
files.push({ file: file, url: URL.createObjectURL(file) });
});
fileInput.value = "";
render();
});
form._aksrtReviewFiles = [];
render();
}
function bindReviewCountdown(root) {
var el = root.querySelector("[data-aksrt-review-countdown]");
if (!el) return;
var remain = parseInt(el.getAttribute("data-aksrt-review-countdown") || "0", 10) || 0;
if (remain <= 0) return;
el.textContent = formatRemain(remain);
var timer = window.setInterval(function () {
remain -= 1;
if (remain <= 0) {
window.clearInterval(timer);
el.textContent = "交易已关闭";
return;
}
el.textContent = formatRemain(remain);
}, 1000);
root._aksrtReviewTimer = timer;
}
function fileToDataUrl(file) {
return new Promise(function (resolve, reject) {
var reader = new FileReader();
reader.onload = function () { resolve(String(reader.result || "")); };
reader.onerror = function () { reject(new Error("read failed")); };
reader.readAsDataURL(file);
});
}
function openOrderReviewModal(orderNumber, triggerBtn) {
if (!window.AksrtRemoteModal || typeof window.AksrtRemoteModal.openRemote !== "function") {
showToast("评价组件未加载");
return;
}
if (triggerBtn) triggerBtn.disabled = true;
var url = restRoot() + orderPath(orderNumber, "/review-modal");
window.AksrtRemoteModal.openRemote({
url: url,
className: "modal-mini full-sm",
height: 380,
mobileBottom: true
}).then(function (shell) {
if (triggerBtn) triggerBtn.disabled = false;
if (!shell) return;
var form = shell.querySelector("[data-aksrt-order-review-form]");
if (!form) return;
bindShopScore(shell);
bindReviewImages(form);
bindReviewCountdown(shell);
form.addEventListener("submit", function (e) {
e.preventDefault();
var errBox = form.querySelector("[data-aksrt-review-error]");
function showErr(msg) {
if (errBox) {
errBox.hidden = false;
errBox.textContent = msg;
} else {
showToast(msg);
}
}
if (errBox) {
errBox.hidden = true;
errBox.textContent = "";
}
var productId = parseInt(form.getAttribute("data-product-id") || "0", 10) || 0;
var needShipping = form.getAttribute("data-need-shipping") === "1";
var scoreProduct = parseInt((form.querySelector('input[name="score[product]"]') || {}).value || "0", 10) || 0;
var scoreService = parseInt((form.querySelector('input[name="score[service]"]') || {}).value || "0", 10) || 0;
var scoreShipping = parseInt((form.querySelector('input[name="score[shipping]"]') || {}).value || "0", 10) || 0;
if (scoreProduct < 1 || scoreService < 1) {
showErr("请对商品进行评分");
return;
}
if (needShipping && scoreShipping < 1) {
showErr("请对物流服务进行评分");
return;
}
var comment = (form.querySelector('textarea[name="comment"]') || {}).value || "";
var submitBtn = form.querySelector("[data-aksrt-review-submit]");
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.textContent = "提交中…";
}
var pending = form._aksrtReviewFiles || [];
Promise.all(pending.map(function (item) {
return item.file ? fileToDataUrl(item.file) : Promise.resolve(item.url || "");
})).then(function (images) {
images = images.filter(Boolean);
return request(orderPath(orderNumber, "/review"), {
product_id: productId,
content: comment,
rating_product: scoreProduct,
rating_service: scoreService,
rating_shipping: needShipping ? scoreShipping : scoreService,
images: images
});
}).then(function (result) {
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.innerHTML = ' 确认提交';
}
if (!result || !result.success) {
showErr((result && result.message) || "评价失败");
return;
}
showToast((result.message) || "评价成功");
if (window.AksrtRemoteModal) window.AksrtRemoteModal.close();
var card = findOrderCard(orderNumber);
updateCardStatus(card, "completed", "已完成");
if (card) {
var btn = card.querySelector("[data-aksrt-order-review]");
if (btn) btn.remove();
}
}).catch(function () {
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.innerHTML = ' 确认提交';
}
showErr("网络错误");
});
});
}).catch(function (err) {
if (triggerBtn) triggerBtn.disabled = false;
showToast((err && err.message) || "无法打开评价");
});
}
/* ── 扫码支付弹层(微信 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 = '
';
}
}
if (!qrImg) {
qrImg = '
' + String(content).replace(/[<>&]/g, "") + "
"; } 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 = '支付完成后本页将自动更新
' + ''; 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(orderPath(orderNumber), {}, "GET").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 escapeHtml(value) { return String(value == null ? "" : value) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function formatMoney(order) { var amount = Number(order && order.amount != null ? order.amount : 0); if (order && order.is_points) { var symbol = order.currency_symbol || "积分"; return escapeHtml(symbol) + " " + Math.round(amount); } return "¥" + amount.toFixed(2); } /** Plain money for table cells (formatMoney already escapes). */ function formatMoneyText(order) { var amount = Number(order && order.amount != null ? order.amount : 0); if (order && order.is_points) { var symbol = order.currency_symbol || "积分"; return String(symbol) + " " + Math.round(amount); } return "¥" + amount.toFixed(2); } function updateCardStatus(card, newStatus, statusLabel) { if (!card) return; if (newStatus) { card.setAttribute("data-order-status", newStatus); } 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"; } } /* ── Order detail modal ── */ var detailModal = page.querySelector("[data-aksrt-order-detail-modal]"); var detailBody = page.querySelector("[data-aksrt-order-detail-body]"); var detailFooter = page.querySelector("[data-aksrt-order-detail-footer]"); var detailOrderNumber = ""; function closeOrderDetailModal() { if (detailModal) detailModal.hidden = true; detailOrderNumber = ""; document.documentElement.classList.remove("aksrt-modal-open"); } document.addEventListener("keydown", function (event) { if (event.key === "Escape" && detailModal && !detailModal.hidden) { closeOrderDetailModal(); } }); function openOrderDetailModal(orderNumber) { if (!detailModal || !detailBody || !detailFooter) { showToast("订单详情组件未加载"); return; } detailOrderNumber = orderNumber || ""; detailBody.innerHTML = '加载中…
'; detailFooter.innerHTML = ""; detailModal.hidden = false; document.documentElement.classList.add("aksrt-modal-open"); request(orderPath(orderNumber), undefined, "GET").then(function (result) { var order = null; if (result && result.data && result.data.order_number) { order = result.data; } else if (result && result.data && result.data.data && result.data.data.order_number) { order = result.data.data; } else if (result && result.order_number) { order = result; } if (result && result.success === false) { detailBody.innerHTML = '' + escapeHtml((result && result.message) || "加载失败") + "
"; return; } if (!order || !order.order_number) { detailBody.innerHTML = '订单不存在
'; return; } renderOrderDetail(order); }).catch(function () { detailBody.innerHTML = '网络错误,请稍后重试
'; }); } function renderOrderDetail(order) { if (!detailBody || !detailFooter) return; var actions = (order && order.actions) || {}; var payment = order.payment_method || {}; var paymentLabel = payment.label || payment.value || "—"; var qty = order.quantity || 1; if ((!qty || qty < 1) && order.items && order.items.length) { qty = 0; order.items.forEach(function (item) { qty += Math.max(1, parseInt(item.quantity || item.qty || 1, 10) || 1); }); } if (!qty || qty < 1) qty = 1; var status = order.status || ""; var statusLabel = order.status_label || status || "—"; var statusClass = "is-" + String(status).replace(/[^a-z0-9_-]/gi, "-"); var rows = [ ["订单名称", order.product_name || (order.product && order.product.title) || "—"], ["订单数量", String(qty)], ["应付金额", formatMoneyText(order)], ["订单编号", order.order_number || "—"], ["创建时间", order.created_at || "—"], ["支付时间", order.paid_at || "—"], ["支付方式", paymentLabel] ]; if (Number(order.shipping_fee || 0) > 0) { rows.push(["运费", "¥" + Number(order.shipping_fee || 0).toFixed(2)]); } if (Number(order.discount || 0) > 0) { rows.push(["优惠", "¥" + Number(order.discount || 0).toFixed(2)]); } if (order.customer_note) { rows.push(["备注", order.customer_note]); } var html = ""; html += '暂无可用优惠券
'; return; } var html = '加载失败
'; }); page.addEventListener("click", function (e) { var b = e.target.closest("[data-aksrt-copy-coupon]"); if (!b) return; var code = b.getAttribute("data-aksrt-copy-coupon") || ""; if (!code) return; if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(code).then(function () { showToast("已复制 " + code); }); } else { showToast(code); } }); } })();