/** * Knowledge-payment order center interactions. * * @package AKSRT */ (function () { "use strict"; var instances = new WeakMap(); function request(path, body, method) { var options = { method: method || "POST" }; if (body !== undefined) options.body = JSON.stringify(body || {}); return window.AksrtRequest.request(path, options); } function orderPath(orderNumber, suffix) { return "/orders/" + encodeURIComponent(orderNumber || "") + (suffix || ""); } function errorMessage(error, fallback) { if (error && error.message) return String(error.message); return fallback || "请求失败"; } 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 || 0); return order && order.is_points ? escapeHtml(order.currency_symbol || "积分") + " " + Math.round(amount) : "¥" + amount.toFixed(2); } function orderTag(orderNumber) { return "order:" + String(orderNumber || "").toLowerCase(); } function invalidateOrder(orderNumber, order) { var tags = order && Array.isArray(order.invalidation_tags) ? order.invalidation_tags : [ "orders", orderTag(orderNumber), ]; window.AksrtModalRuntime.invalidate(tags); } function activeDetail(state, number) { return state.detailOrderNumber === number && !!state.detailSession && !state.detailSession.closed && state.detailSession.options && state.detailSession.options.kind === "orders" && window.AksrtModalRuntime.getActive() === state.detailSession.shell; } function detailSkeleton() { return '
' + '' + '' + '' + '
'; } function renderDetail(state, order, session) { var allowedActions = order.allowed_actions || {}; var reconciliation = order.reconciliation || {}; var image = order.image || ""; var title = order.product_name || "订单"; var status = order.status_group || "closed"; var html = '
'; html += '' + escapeHtml(order.status_label || "") + ""; html += '' + formatMoney(order) + "
"; html += '
'; html += image ? '' : ''; html += '
' + escapeHtml(title) + ""; html += '' + escapeHtml(order.product_type_label || "") + "
"; html += '
'; [["订单编号", order.order_number], ["创建时间", order.created_at], ["支付时间", order.paid_at || "—"]].forEach(function (row) { html += "
" + escapeHtml(row[0]) + "
" + escapeHtml(row[1] || "—") + "
"; }); html += "
"; if (reconciliation.state === "refresh_available") { html += '
支付状态可能仍在同步,请刷新后查看。'; html += '
'; } html += '"; session.content.innerHTML = html; } function detailOptions(state, orderNumber, refresh) { return { url: orderPath(orderNumber, refresh ? "/refresh" : ""), method: refresh ? "POST" : "GET", kind: "orders", title: "订单详情", responseType: "json", cache: !refresh, cacheKey: "orders:detail:" + orderNumber, cacheTags: ["orders", orderTag(orderNumber)], skeleton: detailSkeleton(), onState: function (_, session) { if (orderNumber === state.detailOrderNumber) state.detailSession = session; }, renderData: function (data, session) { if (orderNumber !== state.detailOrderNumber || !instances.has(state.root)) return false; state.detailSession = session; if (!data || typeof data !== "object") { session.content.innerHTML = '

订单不存在

'; return; } renderDetail(state, data, session); }, onData: function (data) { if (refresh) invalidateOrder(orderNumber, data); }, }; } function openDetail(state, orderNumber) { if (!orderNumber) return; state.detailSession = null; state.detailReplacement = false; state.detailReplacementSession = null; state.detailOrderNumber = orderNumber; window.AksrtModalRuntime.openRemote(detailOptions(state, orderNumber, false)); } function refreshDetail(state, orderNumber) { if (!orderNumber || !activeDetail(state, orderNumber)) return; state.detailReplacement = true; state.detailReplacementSession = state.detailSession; var refreshPromise = window.AksrtModalRuntime.openRemote(detailOptions(state, orderNumber, true)); refreshPromise.then(function () { state.detailReplacement = false; state.detailReplacementSession = null; }, function () { state.detailReplacement = false; state.detailReplacementSession = null; }); } 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 allowedActions = order.allowed_actions || {}; var html = ''; if (allowedActions.can_cancel) html += ''; if (allowedActions.can_pay) html += ''; if (allowedActions.can_open && order.permalink) html += '' + escapeHtml(order.entitlement_label || "打开已购内容") + ""; return html; } function cardHtml(order) { var image = order.image ? '' : ''; var countdown = Number(order.time_left || 0) > 0 ? '' : ""; return '
' + '
' + escapeHtml(order.product_type_label || "") + '' + escapeHtml(order.status_label || "") + "
" + '
' + image + '

' + escapeHtml(order.product_name || "订单") + "

订单号 " + escapeHtml(order.order_number) + "

" + formatMoney(order) + "实付
" + '
' + countdown + actionHtml(order) + "
"; } function renderStats(state, stats) { var root = state.root.querySelector("[data-aksrt-order-stats]"); if (!root) return; root.innerHTML = '
全部' + Number(stats.total || 0) + "
" + '
待支付' + Number(stats.pending || 0) + "
" + '
已生效' + Number(stats.completed || 0) + "
"; } function renderPagination(state, view) { var existing = state.root.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 = state.root.querySelector("[data-aksrt-order-list]"); if (list) list.after(root); } function updateCountdowns(state) { state.root.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 + " 秒" : "订单即将关闭"; }); } function renderView(state, view) { renderStats(state, view.stats || {}); var list = state.root.querySelector("[data-aksrt-order-list]"); if (!list) return; var items = Array.isArray(view.items) ? view.items : []; if (!items.length) { list.innerHTML = '

没有匹配的订单

换个条件试试,或清除当前筛选。

'; list.classList.add("is-empty"); } else { list.classList.remove("is-empty"); list.innerHTML = items.map(cardHtml).join(""); } renderPagination(state, view); updateCountdowns(state); } function refreshView(state) { return request(currentViewPath(), undefined, "GET").then(function (result) { if (!result || !result.data) throw new Error("刷新失败"); renderView(state, result.data); return result.data; }).catch(function (error) { notyf(errorMessage(error, "订单状态已变更,请刷新后查看"), "danger"); return null; }); } function startPayment(state, button) { var number = button.getAttribute("data-aksrt-order-pay") || ""; var method = button.getAttribute("data-aksrt-order-method") || ""; if (!number || button.disabled) return; button.disabled = true; request(orderPath(number, "/pay"), method ? { payment_method: method } : {}, "POST").then(function (result) { var data = result.data || {}; invalidateOrder(number, data.order); if (data.redirect && data.payment_url) { window.location.assign(data.payment_url); return; } notyf(data.message || "支付成功", "success"); refreshView(state); if (activeDetail(state, number)) openDetail(state, number); }).catch(function (error) { button.disabled = false; notyf(errorMessage(error, "网络错误,请稍后重试"), "danger"); }); } function closeOrder(state, button) { var number = button.getAttribute("data-aksrt-order-cancel") || ""; if (!number || button.disabled) return; if (button.getAttribute("data-aksrt-confirm") !== "true") { button.setAttribute("data-aksrt-confirm", "true"); button.textContent = "确认关闭"; return; } button.disabled = true; request(orderPath(number, "/cancel"), {}, "POST").then(function (result) { var data = result.data || {}; invalidateOrder(number, data.order); notyf("订单已关闭", "success"); refreshView(state); if (activeDetail(state, number)) openDetail(state, number); }).catch(function (error) { button.disabled = false; button.removeAttribute("data-aksrt-confirm"); button.textContent = "关闭订单"; notyf(errorMessage(error, "网络错误,请稍后重试"), "danger"); }); } function closest(target, selector) { return target && target.closest ? target.closest(selector) : null; } function initRoot(root) { if (instances.has(root) || !window.AksrtModalRuntime.isRolloutEnabled("order")) return; var state = { root: root, detailOrderNumber: "", detailSession: null, detailReplacement: false, detailReplacementSession: null, countdownTimer: null, }; state.onRootClick = function (event) { var filterToggle = closest(event.target, "[data-aksrt-orders-filter-toggle]"); if (filterToggle && root.contains(filterToggle)) { var panel = root.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 = closest(event.target, "[data-aksrt-order-detail]"); if (detail && root.contains(detail)) { openDetail(state, detail.getAttribute("data-aksrt-order-detail")); return; } var pay = closest(event.target, "[data-aksrt-order-pay]"); if (pay && root.contains(pay)) { event.preventDefault(); startPayment(state, pay); return; } var cancel = closest(event.target, "[data-aksrt-order-cancel]"); if (cancel && root.contains(cancel)) { event.preventDefault(); closeOrder(state, cancel); } }; state.onModalClose = function (event) { var detail = event && event.detail ? event.detail : {}; if (!state.detailSession || detail.session !== state.detailSession) return; if (detail.reason === "replace" && state.detailReplacement && detail.session === state.detailReplacementSession) return; state.detailSession = null; state.detailOrderNumber = ""; state.detailReplacement = false; state.detailReplacementSession = null; }; state.onDocumentClick = function (event) { if (!state.detailSession || window.AksrtModalRuntime.getActive() !== state.detailSession.shell) return; var refresh = closest(event.target, "[data-aksrt-order-refresh]"); if (refresh) { event.preventDefault(); refreshDetail(state, refresh.getAttribute("data-aksrt-order-refresh") || ""); return; } var pay = closest(event.target, "[data-aksrt-order-pay]"); if (pay && !root.contains(pay)) { event.preventDefault(); startPayment(state, pay); return; } var cancel = closest(event.target, "[data-aksrt-order-cancel]"); if (cancel && !root.contains(cancel)) { event.preventDefault(); closeOrder(state, cancel); } }; root.addEventListener("click", state.onRootClick); document.addEventListener("aksrt:modal:close", state.onModalClose); document.addEventListener("click", state.onDocumentClick); state.countdownTimer = window.setInterval(function () { updateCountdowns(state); }, 1000); instances.set(root, state); } function rootsIn(root) { if (!root) return []; var roots = []; if (root.matches && root.matches("[data-aksrt-order-center]")) roots.push(root); if (root.querySelectorAll) root.querySelectorAll("[data-aksrt-order-center]").forEach(function (item) { roots.push(item); }); return roots; } function init(root) { rootsIn(root || document).forEach(initRoot); } function destroy(root) { rootsIn(root).forEach(function (item) { var state = instances.get(item); if (!state) return; if (activeDetail(state, state.detailOrderNumber)) window.AksrtModalRuntime.close("replace"); item.removeEventListener("click", state.onRootClick); document.removeEventListener("aksrt:modal:close", state.onModalClose); document.removeEventListener("click", state.onDocumentClick); window.clearInterval(state.countdownTimer); instances.delete(item); }); } window.AKSRTOrderCenter = { init: init, destroy: destroy }; if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") { window.AksrtFrontend.register("aksrt-order-center", init); } init(document); }());