Files
wordpress-theme-aksrt/assets/js/order-center.js
T
2026-08-04 23:24:37 +08:00

293 lines
15 KiB
JavaScript

/**
* Knowledge-payment order center interactions.
*
* @package AKSRT
*/
(function () {
"use strict";
var page = document.querySelector("[data-aksrt-order-center]");
if (!page || !window.aksrtData) return;
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 toast(message, type) {
if (typeof window.AksrtToast === "function") {
window.AksrtToast(message, type || "info");
return;
}
if (typeof window.aksrtNotify === "function") {
window.aksrtNotify(message, type || "info");
return;
}
window.alert(message);
}
function request(path, body, method) {
if (!window.AksrtRequest || typeof window.AksrtRequest.request !== "function") {
return Promise.resolve({ success: false, message: "请求适配器未加载" });
}
var options = { method: method || "POST" };
if (body !== undefined) options.body = JSON.stringify(body || {});
return window.AksrtRequest.request(path, options).then(function (response) {
return response.json || {};
}).catch(function (error) {
var response = error && error.response ? error.response : {};
return error && error.json ? error.json : (response.json || {
success: false,
message: error && error.message ? error.message : "请求失败",
});
});
}
function orderPath(orderNumber, suffix) {
return "/orders/" + encodeURIComponent(orderNumber || "") + (suffix || "");
}
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&amp;").replace(/</g, "&lt;")
.replace(/>/g, "&gt;").replace(/\"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function formatMoney(order) {
var amount = Number(order && order.amount || 0);
return order && order.is_points
? escapeHtml(order.currency_symbol || "积分") + " " + Math.round(amount)
: "¥" + amount.toFixed(2);
}
function closeDetail() {
if (detailModal) detailModal.hidden = true;
detailOrderNumber = "";
document.documentElement.classList.remove("aksrt-modal-open");
}
function renderDetail(order) {
if (!detailBody || !detailFooter) return;
var actions = order.actions || {};
var image = order.image || "";
var title = order.product_name || "订单";
var status = order.status_group || "closed";
var html = '<div class="aksrt-order-detail__hero is-' + escapeHtml(status) + '">';
html += '<span class="aksrt-order-detail__status-pill is-' + escapeHtml(status) + '">' + escapeHtml(order.status_label || "") + "</span>";
html += '<strong class="aksrt-order-detail__hero-amount">' + formatMoney(order) + "</strong></div>";
html += '<div class="aksrt-order-detail__product">';
html += image ? '<img class="aksrt-order-detail__thumb" src="' + escapeHtml(image) + '" alt="">' : '<span class="aksrt-order-detail__thumb aksrt-order-detail__thumb--empty"><i class="fas fa-book-open"></i></span>';
html += '<div><strong class="aksrt-order-detail__product-title">' + escapeHtml(title) + "</strong>";
html += '<span class="aksrt-order-detail__product-meta">' + escapeHtml(order.product_type_label || "") + "</span></div></div>";
html += '<dl class="aksrt-order-detail__list">';
[["订单编号", order.order_number], ["创建时间", order.created_at], ["支付时间", order.paid_at || "—"]].forEach(function (row) {
html += "<div><dt>" + escapeHtml(row[0]) + "</dt><dd>" + escapeHtml(row[1] || "—") + "</dd></div>";
});
html += "</dl>";
detailBody.innerHTML = html;
var footer = '<button class="btn-outline" type="button" data-aksrt-order-detail-close>关闭</button>';
if (actions.can_cancel) footer += '<button class="btn-ghost" type="button" data-aksrt-order-cancel="' + escapeHtml(order.order_number) + '">关闭订单</button>';
if (actions.can_pay) footer += '<button class="btn-primary" type="button" data-aksrt-order-pay="' + escapeHtml(order.order_number) + '" data-aksrt-order-method="' + escapeHtml((order.payment_method || {}).value || "") + '">继续支付</button>';
if (actions.can_open && order.permalink) footer += '<a class="btn-primary" href="' + escapeHtml(order.permalink) + '">' + escapeHtml(order.entitlement_label || "打开已购内容") + "</a>";
detailFooter.innerHTML = footer;
}
function openDetail(orderNumber) {
if (!detailModal || !detailBody) return;
detailOrderNumber = orderNumber;
var telemetry = window.AksrtModalTelemetry;
var telemetryId = telemetry && typeof telemetry.start === "function" ? telemetry.start("orders") : "";
function telemetryMark(event) {
if (telemetry && telemetryId && typeof telemetry.mark === "function") telemetry.mark(telemetryId, event);
}
detailBody.innerHTML = '<p class="muted-2-color">加载中…</p>';
detailFooter.innerHTML = "";
detailModal.hidden = false;
document.documentElement.classList.add("aksrt-modal-open");
telemetryMark("shell");
request(orderPath(orderNumber), undefined, "GET").then(function (result) {
if (orderNumber !== detailOrderNumber) return;
if (!result.success || !result.data) {
telemetryMark("error");
detailBody.innerHTML = '<p class="c-red">' + escapeHtml(result.message || "订单不存在") + "</p>";
return;
}
telemetryMark("first-content");
renderDetail(result.data);
telemetryMark("ready");
}).catch(function () {
telemetryMark("error");
detailBody.innerHTML = '<p class="c-red">网络错误,请稍后重试</p>';
});
}
function currentViewPath() {
var params = new URLSearchParams(window.location.search);
var allowed = ["order_status", "order_type", "order_number", "order_page"];
var query = new URLSearchParams();
allowed.forEach(function (key) { if (params.get(key)) query.set(key, params.get(key)); });
return "/orders/view" + (query.toString() ? "?" + query.toString() : "");
}
function actionHtml(order) {
var actions = order.actions || {};
var html = '<button class="btn-outline" type="button" data-aksrt-order-detail="' + escapeHtml(order.order_number) + '">订单详情</button>';
if (actions.can_cancel) html += '<button class="btn-ghost" type="button" data-aksrt-order-cancel="' + escapeHtml(order.order_number) + '">关闭订单</button>';
if (actions.can_pay) html += '<button class="btn-primary" type="button" data-aksrt-order-pay="' + escapeHtml(order.order_number) + '" data-aksrt-order-method="' + escapeHtml((order.payment_method || {}).value || '') + '">继续支付</button>';
if (actions.can_open && order.permalink) html += '<a class="btn-primary is-entitlement" href="' + escapeHtml(order.permalink) + '">' + escapeHtml(order.entitlement_label || '打开已购内容') + '</a>';
return html;
}
function cardHtml(order) {
var image = order.image
? '<img class="aksrt-orders-page__card-img" src="' + escapeHtml(order.image) + '" alt="">'
: '<div class="aksrt-orders-page__card-art" aria-hidden="true"><i class="fas fa-book-open"></i></div>';
var countdown = Number(order.time_left || 0) > 0
? '<span class="aksrt-orders-page__countdown" data-aksrt-order-countdown="' + Number(order.time_left) + '"></span>'
: '';
return '<article class="aksrt-orders-page__card" data-aksrt-order-card="' + escapeHtml(order.order_number) + '" data-order-status="' + escapeHtml(order.status_group || '') + '">' +
'<div class="aksrt-orders-page__card-rail"><span>' + escapeHtml(order.product_type_label || '') + '</span><time>' + escapeHtml(order.created_at || '') + '</time><span class="aksrt-orders-page__card-status is-' + escapeHtml(order.status_group || '') + '">' + escapeHtml(order.status_label || '') + '</span></div>' +
'<div class="aksrt-orders-page__card-main">' + image + '<div class="aksrt-orders-page__card-copy"><h3>' + escapeHtml(order.product_name || '订单') + '</h3><p>订单号 ' + escapeHtml(order.order_number) + '</p></div><div class="aksrt-orders-page__card-value"><strong>' + formatMoney(order) + '</strong><span>实付</span></div></div>' +
'<footer class="aksrt-orders-page__card-actions">' + countdown + actionHtml(order) + '</footer></article>';
}
function renderStats(stats) {
var root = page.querySelector('[data-aksrt-order-stats]');
if (!root) return;
root.innerHTML = '<div class="aksrt-orders-page__stat"><span>全部</span><strong>' + Number(stats.total || 0) + '</strong></div>' +
'<div class="aksrt-orders-page__stat"><span>待支付</span><strong class="is-pending">' + Number(stats.pending || 0) + '</strong></div>' +
'<div class="aksrt-orders-page__stat"><span>已生效</span><strong class="is-completed">' + Number(stats.completed || 0) + '</strong></div>';
}
function renderPagination(view) {
var existing = page.querySelector('.aksrt-orders-page__pagination');
if (existing) existing.remove();
if (Number(view.total_pages || 1) <= 1) return;
var root = document.createElement('nav');
root.className = 'aksrt-orders-page__pagination';
root.setAttribute('aria-label', '订单分页');
var base = new URL(window.location.href);
for (var i = 1; i <= Number(view.total_pages); i++) {
base.searchParams.set('order_page', String(i));
var link = document.createElement('a');
link.className = 'aksrt-orders-page__page-chip' + (i === Number(view.page) ? ' is-active' : '');
link.href = base.toString();
link.textContent = String(i);
root.appendChild(link);
}
var list = page.querySelector('[data-aksrt-order-list]');
if (list) list.after(root);
}
function renderView(view) {
renderStats(view.stats || {});
var list = page.querySelector('[data-aksrt-order-list]');
if (!list) return;
var items = Array.isArray(view.items) ? view.items : [];
if (!items.length) {
list.innerHTML = '<div class="aksrt-orders-page__empty"><i class="fas fa-receipt" aria-hidden="true"></i><h3>没有匹配的订单</h3><p>换个条件试试,或清除当前筛选。</p></div>';
list.classList.add('is-empty');
} else {
list.classList.remove('is-empty');
list.innerHTML = items.map(cardHtml).join('');
}
renderPagination(view);
if (detailOrderNumber) {
var detail = items.filter(function (item) { return item.order_number === detailOrderNumber; })[0];
if (detail) renderDetail(detail); else closeDetail();
}
updateCountdowns();
}
function refreshView() {
return request(currentViewPath(), undefined, "GET").then(function (result) {
if (!result.success || !result.data) throw new Error(result.message || "刷新失败");
renderView(result.data);
return result.data;
}).catch(function (error) {
toast(error.message || "订单状态已变更,请刷新后查看");
});
}
function startPayment(button) {
var number = button.getAttribute("data-aksrt-order-pay") || "";
var method = button.getAttribute("data-aksrt-order-method") || "";
if (!number) return;
button.disabled = true;
request(orderPath(number, "/pay"), method ? { payment_method: method } : {}).then(function (result) {
if (!result.success) throw new Error(result.message || "支付失败");
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === "function") {
window.AksrtModalRuntime.invalidate(["orders", "vip"]);
}
var data = result.data || {};
if (data.redirect && data.payment_url) {
window.location.assign(data.payment_url);
return;
}
toast(data.message || "支付成功");
refreshView();
}).catch(function (error) {
button.disabled = false;
toast(error.message || "网络错误,请稍后重试");
});
}
function closeOrder(button) {
var number = button.getAttribute("data-aksrt-order-cancel") || "";
if (!number) return;
if (button.getAttribute("data-aksrt-confirm") !== "true") {
button.setAttribute("data-aksrt-confirm", "true");
button.textContent = "确认关闭";
return;
}
button.disabled = true;
request(orderPath(number, "/cancel")).then(function (result) {
if (!result.success) throw new Error(result.message || "关闭订单失败");
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === "function") {
window.AksrtModalRuntime.invalidate(["orders", "vip"]);
}
toast("订单已关闭");
refreshView();
}).catch(function (error) {
button.disabled = false;
button.removeAttribute("data-aksrt-confirm");
button.textContent = "关闭订单";
toast(error.message || "网络错误,请稍后重试");
});
}
function updateCountdowns() {
page.querySelectorAll("[data-aksrt-order-countdown]").forEach(function (node) {
var value = Math.max(0, Number(node.getAttribute("data-aksrt-order-countdown") || 0) - 1);
node.setAttribute("data-aksrt-order-countdown", String(value));
node.textContent = value ? "剩余支付时间 " + value + " 秒" : "订单即将关闭";
});
}
page.addEventListener("click", function (event) {
var filterToggle = event.target.closest("[data-aksrt-orders-filter-toggle]");
if (filterToggle) {
var panel = page.querySelector("[data-aksrt-orders-filter-panel]");
var expanded = filterToggle.getAttribute("aria-expanded") === "true";
filterToggle.setAttribute("aria-expanded", String(!expanded));
if (panel) panel.hidden = expanded;
return;
}
var detail = event.target.closest("[data-aksrt-order-detail]");
if (detail) { openDetail(detail.getAttribute("data-aksrt-order-detail")); return; }
if (event.target.closest("[data-aksrt-order-detail-close]")) { closeDetail(); return; }
var pay = event.target.closest("[data-aksrt-order-pay]");
if (pay) { startPayment(pay); return; }
var cancel = event.target.closest("[data-aksrt-order-cancel]");
if (cancel) closeOrder(cancel);
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && detailModal && !detailModal.hidden) closeDetail();
});
window.setInterval(updateCountdowns, 1000);
}());