' + escapeHtml(order.product_name || "订单") + "
订单号 " + escapeHtml(order.order_number) + "
diff --git a/assets/js/order-center.js b/assets/js/order-center.js index 1684d5cc..cd93dd2b 100644 --- a/assets/js/order-center.js +++ b/assets/js/order-center.js @@ -1,692 +1,701 @@ -/** - * Knowledge-payment order center interactions. - * - * @package AKSRT - */ -(function () { - "use strict"; - - var instances = new WeakMap(); - var liveStates = []; - - function request(path, body, method, operationKey) { - var options = { method: method || "POST" }; - if (body !== undefined) options.body = JSON.stringify(body || {}); - if (operationKey) options.headers = { "Idempotency-Key": operationKey }; - 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 raw = String(order && order.amount != null ? order.amount : "0"); - if (order && order.is_points) { - return escapeHtml(order.currency_symbol || "积分") + " " + (/^\d+$/.test(raw) ? raw : "0"); - } - var match = /^(0|[1-9]\d*)(?:\.(\d{1,2}))?$/.exec(raw); - var amount = match ? match[1] + "." + (match[2] || "").padEnd(2, "0") : "0.00"; - return "¥" + amount; - } - - 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 '
' + escapeHtml(meta.desc) + "
"; - if (status === "pending" && timeLeft > 0) { - html += '剩余支付时间 ' + timeLeft + " 秒"; - } - html += "支付状态可能仍在同步,请刷新后查看。
"; - html += ''; - html += "订单不存在
'; - return; - } - renderDetail(state, data, session); - }, - onData: function (data) { - if (refresh) { - delete state.operationKeys[actionKey]; - 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(state) { - var query = new URLSearchParams(); - if (state.query.status && state.query.status !== "all") query.set("order_status", state.query.status); - if (state.query.type) query.set("order_type", state.query.type); - if (state.query.orderNumber) query.set("order_number", state.query.orderNumber); - if (Number(state.query.page) > 1) query.set("order_page", String(state.query.page)); - return "/orders/view" + (query.toString() ? "?" + query.toString() : ""); - } - - function actionHtml(order) { - var allowedActions = order.allowed_actions || {}; - var afterSale = order.after_sale || {}; - var html = ''; - if (allowedActions.can_cancel) html += ''; - if (allowedActions.can_pay) html += ''; - // 售后入口:申请售后 / 售后详细走 order-after-sale.js 的独立弹窗。 - if (afterSale.enabled) html += afterSaleButtons(order); - return html; - } - - // 售后按钮(列表卡片与订单详情弹窗共用):已申请 → 售后详细,否则 → 申请售后。 - // 载荷与 PHP 模板同构(order-after-sale.js 同步消费),点击即弹窗、无前置请求。 - function afterSaleButtons(order) { - var afterSale = order.after_sale || {}; - var html = ""; - if (!afterSale.enabled) return html; - var payload = escapeHtml(JSON.stringify({ - order_number: order.order_number || "", - product_name: order.product_name || "", - amount: order.amount || "0", - is_points: !!order.is_points, - currency_symbol: order.currency_symbol || "", - status_label: order.status_label || "", - after_sale: afterSale, - })); - if (afterSale.has_application) { - html += ''; - return html; - } - html += ''; - return html; - } - - function cardHtml(order) { - var image = order.image - ? '订单号 " + escapeHtml(order.order_number) + "
换个条件试试,或清除当前筛选。
'; - list.classList.remove("aksrt-orders-page__list"); - list.classList.add("aksrt-orders-page__empty"); - } else { - list.classList.remove("aksrt-orders-page__empty"); - list.classList.add("aksrt-orders-page__list"); - list.innerHTML = items.map(cardHtml).join(""); - } - renderPagination(state, view); - syncControls(state); - updateCountdowns(state); - } - - function syncControls(state) { - state.root.querySelectorAll("[data-aksrt-order-filter]").forEach(function (button) { - var field = button.getAttribute("data-aksrt-order-filter"); - var expected = field === "status" ? state.query.status : state.query.type; - var active = button.getAttribute("data-aksrt-order-filter-value") === expected; - button.classList.toggle("is-active", active); - button.setAttribute("aria-pressed", active ? "true" : "false"); - }); - var input = state.root.querySelector("#aksrt-order-number"); - if (input && input.value !== state.query.orderNumber) input.value = state.query.orderNumber; - } - - function setViewLoading(state, loading) { - state.root.classList.toggle("is-loading", loading); - state.root.setAttribute("aria-busy", loading ? "true" : "false"); - } - - function loadView(state, patch) { - Object.keys(patch || {}).forEach(function (key) { state.query[key] = patch[key]; }); - var requestId = ++state.viewRequest; - setViewLoading(state, true); - return request(currentViewPath(state), undefined, "GET").then(function (result) { - if (requestId !== state.viewRequest) return null; - if (!result || !result.data) throw new Error("刷新失败"); - renderView(state, result.data); - return result.data; - }).catch(function (error) { - if (requestId === state.viewRequest) notyf(errorMessage(error, "订单加载失败,请稍后重试"), "danger"); - return null; - }).finally(function () { - if (requestId === state.viewRequest) setViewLoading(state, false); - }); - } - - function refreshView(state) { - return loadView(state); - } - - 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; - var actionKey = "pay:" + number; - if (!state.operationKeys[actionKey]) state.operationKeys[actionKey] = window.AksrtRequest.createIdempotencyKey(); - button.disabled = true; - request(orderPath(number, "/pay"), method ? { payment_method: method } : {}, "POST", state.operationKeys[actionKey]).then(function (result) { - delete state.operationKeys[actionKey]; - 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; - var actionKey = "cancel:" + number; - if (!state.operationKeys[actionKey]) state.operationKeys[actionKey] = window.AksrtRequest.createIdempotencyKey(); - request(orderPath(number, "/cancel"), {}, "POST", state.operationKeys[actionKey]).then(function (result) { - delete state.operationKeys[actionKey]; - 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 copyOrderValue(button) { - var value = button.getAttribute("data-aksrt-order-copy") || ""; - if (!value) return; - var icon = button.querySelector("i"); - var settle = function (ok) { - notyf(ok ? "订单号已复制" : "复制失败,请手动选择复制", ok ? "success" : "danger"); - if (!ok) return; - button.classList.add("is-copied"); - if (icon) icon.className = "fa fas fa-check"; - window.setTimeout(function () { - button.classList.remove("is-copied"); - if (icon) icon.className = "fa fas fa-copy"; - }, 1600); - }; - var fallback = function () { - try { - var field = document.createElement("textarea"); - field.value = value; - field.setAttribute("readonly", "readonly"); - field.style.position = "fixed"; - field.style.top = "-1000px"; - field.style.opacity = "0"; - document.body.appendChild(field); - field.select(); - document.execCommand("copy"); - document.body.removeChild(field); - settle(true); - } catch (error) { - settle(false); - } - }; - if (navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText(value).then(function () { settle(true); }, fallback); - return; - } - fallback(); - } - - function closest(target, selector) { - return target && target.closest ? target.closest(selector) : null; - } - - function initRoot(root) { - if (instances.has(root)) return; - var state = { - root: root, - detailOrderNumber: "", - detailSession: null, - detailReplacement: false, - detailReplacementSession: null, - countdownTimer: null, - operationKeys: {}, - query: { status: "all", type: "", orderNumber: "", page: 1 }, - viewRequest: 0, - }; - - 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 filter = closest(event.target, "[data-aksrt-order-filter]"); - if (filter && root.contains(filter)) { - event.preventDefault(); - var field = filter.getAttribute("data-aksrt-order-filter"); - var patch = { page: 1 }; - patch[field] = filter.getAttribute("data-aksrt-order-filter-value") || ""; - loadView(state, patch); - return; - } - var page = closest(event.target, "[data-aksrt-order-page]"); - if (page && root.contains(page)) { - event.preventDefault(); - loadView(state, { page: Math.max(1, Number(page.getAttribute("data-aksrt-order-page") || 1)) }); - return; - } - var clear = closest(event.target, "[data-aksrt-orders-clear]"); - if (clear && root.contains(clear)) { - event.preventDefault(); - loadView(state, { status: "all", type: "", orderNumber: "", page: 1 }); - 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.onRootSubmit = function (event) { - var form = closest(event.target, "[data-aksrt-orders-search]"); - if (!form || !root.contains(form)) return; - event.preventDefault(); - var input = form.querySelector("#aksrt-order-number"); - loadView(state, { orderNumber: input ? String(input.value || "").trim() : "", page: 1 }); - }; - 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) { - var afterSaleCancel = closest(event.target, "[data-aksrt-after-sale-cancel]"); - if (afterSaleCancel && !event.__aksrtAfterSaleCancelHandled) { - var afterSaleNumber = afterSaleCancel.getAttribute("data-aksrt-after-sale-cancel") || ""; - if (activeDetail(state, afterSaleNumber)) { - event.__aksrtAfterSaleCancelHandled = true; - event.preventDefault(); - afterSaleCancel.disabled = true; - request(orderPath(afterSaleNumber, "/after-sale"), undefined, "DELETE", window.AksrtRequest.createIdempotencyKey()).then(function () { - notyf("售后申请已取消", "success"); - invalidateOrder(afterSaleNumber); - openDetail(state, afterSaleNumber); - refreshView(state); - }).catch(function (error) { - afterSaleCancel.disabled = false; - notyf(errorMessage(error, "取消申请失败"), "danger"); - }); - return; - } - } - if (!state.detailSession || window.AksrtModalRuntime.getActive() !== state.detailSession.shell) return; - var copy = closest(event.target, "[data-aksrt-order-copy]"); - if (copy) { - event.preventDefault(); - copyOrderValue(copy); - 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); - root.addEventListener("submit", state.onRootSubmit); - document.addEventListener("aksrt:modal:close", state.onModalClose); - document.addEventListener("click", state.onDocumentClick); - state.countdownTimer = window.setInterval(function () { updateCountdowns(state); }, 1000); - instances.set(root, state); - liveStates.push(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); - item.removeEventListener("submit", state.onRootSubmit); - document.removeEventListener("aksrt:modal:close", state.onModalClose); - document.removeEventListener("click", state.onDocumentClick); - window.clearInterval(state.countdownTimer); - instances.delete(item); - liveStates = liveStates.filter(function (entry) { return entry !== state; }); - }); - } - - // refresh 供售后弹窗(order-after-sale.js)提交/取消后刷新列表与统计。 - function refresh(orderNumber) { - invalidateOrder(orderNumber); - liveStates.forEach(function (state) { - refreshView(state); - }); - } - - window.AKSRTOrderCenter = { init: init, destroy: destroy, refresh: refresh }; - jQuery(document).off("auto_fun.aksrt-order-center").on("auto_fun.aksrt-order-center", function () { init(document); }); -}()); +/** + * Knowledge-payment order center interactions. + * + * @package AKSRT + */ +(function () { + "use strict"; + + var instances = new WeakMap(); + var liveStates = []; + + function request(path, body, method, operationKey) { + var options = { method: method || "POST" }; + if (body !== undefined) options.body = JSON.stringify(body || {}); + if (operationKey) options.headers = { "Idempotency-Key": operationKey }; + 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 raw = String(order && order.amount != null ? order.amount : "0"); + if (order && order.is_points) { + return escapeHtml(order.currency_symbol || "积分") + " " + (/^\d+$/.test(raw) ? raw : "0"); + } + var match = /^(0|[1-9]\d*)(?:\.(\d{1,2}))?$/.exec(raw); + var amount = match ? match[1] + "." + (match[2] || "").padEnd(2, "0") : "0.00"; + return "¥" + amount; + } + + 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 '' + escapeHtml(meta.desc) + "
"; + if (status === "pending" && timeLeft > 0) { + html += '剩余支付时间 ' + timeLeft + " 秒"; + } + html += "' + escapeHtml(item.reason).replace(/\r\n|\r|\n/g, "
") + "
支付状态可能仍在同步,请刷新后查看。
"; + html += ''; + html += "订单不存在
'; + return; + } + renderDetail(state, data, session); + }, + onData: function (data) { + if (refresh) { + delete state.operationKeys[actionKey]; + 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(state) { + var query = new URLSearchParams(); + if (state.query.status && state.query.status !== "all") query.set("order_status", state.query.status); + if (state.query.type) query.set("order_type", state.query.type); + if (state.query.orderNumber) query.set("order_number", state.query.orderNumber); + if (Number(state.query.page) > 1) query.set("order_page", String(state.query.page)); + return "/orders/view" + (query.toString() ? "?" + query.toString() : ""); + } + + function actionHtml(order) { + var allowedActions = order.allowed_actions || {}; + var afterSale = order.after_sale || {}; + var html = ''; + if (allowedActions.can_cancel) html += ''; + if (allowedActions.can_pay) html += ''; + // 售后入口:申请售后 / 售后详细走 order-after-sale.js 的独立弹窗。 + if (afterSale.enabled) html += afterSaleButtons(order); + return html; + } + + // 售后按钮(列表卡片与订单详情弹窗共用):已申请 → 售后详细,否则 → 申请售后。 + // 载荷与 PHP 模板同构(order-after-sale.js 同步消费),点击即弹窗、无前置请求。 + function afterSaleButtons(order) { + var afterSale = order.after_sale || {}; + var html = ""; + if (!afterSale.enabled) return html; + var payload = escapeHtml(JSON.stringify({ + order_number: order.order_number || "", + product_name: order.product_name || "", + amount: order.amount || "0", + is_points: !!order.is_points, + currency_symbol: order.currency_symbol || "", + status_label: order.status_label || "", + after_sale: afterSale, + })); + if (afterSale.has_application) { + html += ''; + return html; + } + html += ''; + return html; + } + + function cardHtml(order) { + var image = order.image + ? '订单号 " + escapeHtml(order.order_number) + "
换个条件试试,或清除当前筛选。
'; + list.classList.remove("aksrt-orders-page__list"); + list.classList.add("aksrt-orders-page__empty"); + } else { + list.classList.remove("aksrt-orders-page__empty"); + list.classList.add("aksrt-orders-page__list"); + list.innerHTML = items.map(cardHtml).join(""); + } + renderPagination(state, view); + syncControls(state); + updateCountdowns(state); + } + + function syncControls(state) { + state.root.querySelectorAll("[data-aksrt-order-filter]").forEach(function (button) { + var field = button.getAttribute("data-aksrt-order-filter"); + var expected = field === "status" ? state.query.status : state.query.type; + var active = button.getAttribute("data-aksrt-order-filter-value") === expected; + button.classList.toggle("is-active", active); + button.setAttribute("aria-pressed", active ? "true" : "false"); + }); + var input = state.root.querySelector("#aksrt-order-number"); + if (input && input.value !== state.query.orderNumber) input.value = state.query.orderNumber; + } + + function setViewLoading(state, loading) { + state.root.classList.toggle("is-loading", loading); + state.root.setAttribute("aria-busy", loading ? "true" : "false"); + } + + function loadView(state, patch) { + Object.keys(patch || {}).forEach(function (key) { state.query[key] = patch[key]; }); + var requestId = ++state.viewRequest; + setViewLoading(state, true); + return request(currentViewPath(state), undefined, "GET").then(function (result) { + if (requestId !== state.viewRequest) return null; + if (!result || !result.data) throw new Error("刷新失败"); + renderView(state, result.data); + return result.data; + }).catch(function (error) { + if (requestId === state.viewRequest) notyf(errorMessage(error, "订单加载失败,请稍后重试"), "danger"); + return null; + }).finally(function () { + if (requestId === state.viewRequest) setViewLoading(state, false); + }); + } + + function refreshView(state) { + return loadView(state); + } + + 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; + var actionKey = "pay:" + number; + if (!state.operationKeys[actionKey]) state.operationKeys[actionKey] = window.AksrtRequest.createIdempotencyKey(); + button.disabled = true; + request(orderPath(number, "/pay"), method ? { payment_method: method } : {}, "POST", state.operationKeys[actionKey]).then(function (result) { + delete state.operationKeys[actionKey]; + 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; + var actionKey = "cancel:" + number; + if (!state.operationKeys[actionKey]) state.operationKeys[actionKey] = window.AksrtRequest.createIdempotencyKey(); + request(orderPath(number, "/cancel"), {}, "POST", state.operationKeys[actionKey]).then(function (result) { + delete state.operationKeys[actionKey]; + 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 copyOrderValue(button) { + var value = button.getAttribute("data-aksrt-order-copy") || ""; + if (!value) return; + var icon = button.querySelector("i"); + var settle = function (ok) { + notyf(ok ? "订单号已复制" : "复制失败,请手动选择复制", ok ? "success" : "danger"); + if (!ok) return; + button.classList.add("is-copied"); + if (icon) icon.className = "fa fas fa-check"; + window.setTimeout(function () { + button.classList.remove("is-copied"); + if (icon) icon.className = "fa fas fa-copy"; + }, 1600); + }; + var fallback = function () { + try { + var field = document.createElement("textarea"); + field.value = value; + field.setAttribute("readonly", "readonly"); + field.style.position = "fixed"; + field.style.top = "-1000px"; + field.style.opacity = "0"; + document.body.appendChild(field); + field.select(); + document.execCommand("copy"); + document.body.removeChild(field); + settle(true); + } catch (error) { + settle(false); + } + }; + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(value).then(function () { settle(true); }, fallback); + return; + } + fallback(); + } + + function closest(target, selector) { + return target && target.closest ? target.closest(selector) : null; + } + + function initRoot(root) { + if (instances.has(root)) return; + var state = { + root: root, + detailOrderNumber: "", + detailSession: null, + detailReplacement: false, + detailReplacementSession: null, + countdownTimer: null, + operationKeys: {}, + query: { status: "all", type: "", orderNumber: "", page: 1 }, + viewRequest: 0, + }; + + 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 filter = closest(event.target, "[data-aksrt-order-filter]"); + if (filter && root.contains(filter)) { + event.preventDefault(); + var field = filter.getAttribute("data-aksrt-order-filter"); + var patch = { page: 1 }; + patch[field] = filter.getAttribute("data-aksrt-order-filter-value") || ""; + loadView(state, patch); + return; + } + var page = closest(event.target, "[data-aksrt-order-page]"); + if (page && root.contains(page)) { + event.preventDefault(); + loadView(state, { page: Math.max(1, Number(page.getAttribute("data-aksrt-order-page") || 1)) }); + return; + } + var clear = closest(event.target, "[data-aksrt-orders-clear]"); + if (clear && root.contains(clear)) { + event.preventDefault(); + loadView(state, { status: "all", type: "", orderNumber: "", page: 1 }); + 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.onRootSubmit = function (event) { + var form = closest(event.target, "[data-aksrt-orders-search]"); + if (!form || !root.contains(form)) return; + event.preventDefault(); + var input = form.querySelector("#aksrt-order-number"); + loadView(state, { orderNumber: input ? String(input.value || "").trim() : "", page: 1 }); + }; + 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) { + var afterSaleCancel = closest(event.target, "[data-aksrt-after-sale-cancel]"); + if (afterSaleCancel && !event.__aksrtAfterSaleCancelHandled) { + var afterSaleNumber = afterSaleCancel.getAttribute("data-aksrt-after-sale-cancel") || ""; + if (activeDetail(state, afterSaleNumber)) { + event.__aksrtAfterSaleCancelHandled = true; + event.preventDefault(); + afterSaleCancel.disabled = true; + request(orderPath(afterSaleNumber, "/after-sale"), undefined, "DELETE", window.AksrtRequest.createIdempotencyKey()).then(function () { + notyf("售后申请已取消", "success"); + invalidateOrder(afterSaleNumber); + openDetail(state, afterSaleNumber); + refreshView(state); + }).catch(function (error) { + afterSaleCancel.disabled = false; + notyf(errorMessage(error, "取消申请失败"), "danger"); + }); + return; + } + } + if (!state.detailSession || window.AksrtModalRuntime.getActive() !== state.detailSession.shell) return; + var copy = closest(event.target, "[data-aksrt-order-copy]"); + if (copy) { + event.preventDefault(); + copyOrderValue(copy); + 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); + root.addEventListener("submit", state.onRootSubmit); + document.addEventListener("aksrt:modal:close", state.onModalClose); + document.addEventListener("click", state.onDocumentClick); + state.countdownTimer = window.setInterval(function () { updateCountdowns(state); }, 1000); + instances.set(root, state); + liveStates.push(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); + item.removeEventListener("submit", state.onRootSubmit); + document.removeEventListener("aksrt:modal:close", state.onModalClose); + document.removeEventListener("click", state.onDocumentClick); + window.clearInterval(state.countdownTimer); + instances.delete(item); + liveStates = liveStates.filter(function (entry) { return entry !== state; }); + }); + } + + // refresh 供售后弹窗(order-after-sale.js)提交/取消后刷新列表与统计。 + function refresh(orderNumber) { + invalidateOrder(orderNumber); + liveStates.forEach(function (state) { + refreshView(state); + }); + } + + window.AKSRTOrderCenter = { init: init, destroy: destroy, refresh: refresh }; + jQuery(document).off("auto_fun.aksrt-order-center").on("auto_fun.aksrt-order-center", function () { init(document); }); +}());