feat(payment): rebuild order details and asset records

This commit is contained in:
2026-10-05 16:42:25 +08:00
parent 476e2f961d
commit 7b55cfb190
57 changed files with 2080 additions and 1294 deletions
+95 -6
View File
@@ -74,7 +74,6 @@
var map = {
pending: { icon: "fa-clock", desc: "订单已生成,请在有效期内完成支付" },
paid: { icon: "fa-check-circle", desc: "支付已完成,相关权益已发放至账户" },
refunding: { icon: "fa-hourglass-half", desc: "退款申请已受理,正在处理中" },
refunded: { icon: "fa-undo-alt", desc: "退款已完成,款项将原路退回" },
closed: { icon: "fa-times-circle", desc: "订单已关闭,如有需要可重新下单" },
};
@@ -106,6 +105,26 @@
return "<div><dt>" + escapeHtml(label) + "</dt><dd>" + cell + "</dd></div>";
}
function gatewayResultText(value) {
if (!value) return "";
var result = value;
if (typeof value === "string") {
try { result = JSON.parse(value); } catch (_) { return String(value); }
}
if (!result || typeof result !== "object") return String(result);
var labels = { succeeded: "退款成功", failed: "渠道明确拒绝", pending: "渠道处理中", unknown: "结果待确认" };
var parts = [];
if (result.status) parts.push(labels[String(result.status)] || String(result.status));
if (result.message) parts.push(String(result.message));
if (result.refund_id) parts.push("退款单号 " + String(result.refund_id));
return parts.join(" · ") || "渠道暂未提供可读结果";
}
function afterSaleAmount(value, order) {
var amount = String(value == null ? "" : value);
return order && order.is_points ? amount + " " + String(order.currency_symbol || "积分") : "¥" + amount;
}
function renderDetail(state, order, session) {
var allowedActions = order.allowed_actions || {};
var reconciliation = order.reconciliation || {};
@@ -145,15 +164,29 @@
html += '<h4 class="aksrt-order-detail__group-title">交易信息</h4>';
html += '<dl class="aksrt-order-detail__list">';
[
["流水号", order.order_number],
["订单号", order.order_number],
["支付方式", paymentMethodText(order)],
["创建时间", order.created_at],
["支付时间", order.paid_at],
].forEach(function (row) {
html += detailRow(row[0], row[1], row[0] === "流水号");
html += detailRow(row[0], row[1], row[0] === "订单号");
});
html += "</dl></section>";
var afterSales = Array.isArray(order.after_sales) ? order.after_sales : [];
if (afterSales.length) {
html += '<section class="aksrt-order-detail__group"><h4 class="aksrt-order-detail__group-title">售后记录</h4><ul class="aksrt-order-detail__after-sales">';
afterSales.forEach(function (item) {
var labels = { pending: "待审核", processing: "处理中", approved: "已通过", rejected: "已拒绝", cancelled: "已取消", completed: "已完成" };
html += "<li><strong>" + escapeHtml(labels[item.status] || item.status || "") + "</strong><span>" + escapeHtml(item.reason || "") + "</span><small>退款金额:" + escapeHtml(afterSaleAmount(item.refund_amount, order)) + "</small>";
if (item.admin_note) html += "<small>处理备注:" + escapeHtml(item.admin_note) + "</small>";
if (item.gateway_result) html += "<small>渠道结果:" + escapeHtml(gatewayResultText(item.gateway_result)) + "</small>";
if (item.status === "pending") html += '<button class="btn-ghost" type="button" data-aksrt-after-sale-cancel="' + escapeHtml(order.order_number) + '">取消申请</button>';
html += "</li>";
});
html += "</ul></section>";
}
if (reconciliation.state === "refresh_available") {
html += '<div class="aksrt-order-detail__refresh-state" role="status">';
html += '<i class="fa fas fa-info-circle" aria-hidden="true"></i>';
@@ -166,6 +199,7 @@
html += '<button class="btn-outline" type="button" data-aksrt-modal-close>关闭</button>';
if (allowedActions.can_cancel) html += '<button class="btn-ghost" type="button" data-aksrt-order-cancel="' + escapeHtml(order.order_number) + '">关闭订单</button>';
if (allowedActions.can_pay) html += '<button class="btn-primary" type="button" data-aksrt-order-pay="' + escapeHtml(order.order_number) + '" data-aksrt-order-method="' + escapeHtml(paymentMethod.value || "") + '">继续支付</button>';
if (allowedActions.can_after_sale) html += '<form class="aksrt-order-detail__after-sale-form" data-aksrt-after-sale-form="' + escapeHtml(order.order_number) + '"><label>申请售后原因<textarea name="reason" required maxlength="2000" rows="3"></textarea></label><button class="btn-ghost" type="submit">提交售后申请</button></form>';
html += "</footer>";
session.content.innerHTML = html;
@@ -244,6 +278,7 @@
var html = '<button class="btn-outline" type="button" data-aksrt-order-detail="' + escapeHtml(order.order_number) + '">订单详情</button>';
if (allowedActions.can_cancel) html += '<button class="btn-ghost" type="button" data-aksrt-order-cancel="' + escapeHtml(order.order_number) + '">关闭订单</button>';
if (allowedActions.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 (allowedActions.can_after_sale) html += '<button class="btn-outline" type="button" data-aksrt-order-detail="' + escapeHtml(order.order_number) + '">申请售后</button>';
return html;
}
@@ -256,7 +291,7 @@
: "";
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>" +
'<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>";
}
@@ -265,7 +300,10 @@
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-paid">' + Number(stats.paid || 0) + "</strong></div>";
'<div class="aksrt-orders-page__stat"><span>已支付</span><strong class="is-paid">' + Number(stats.paid || 0) + "</strong></div>" +
'<div class="aksrt-orders-page__stat"><span>已退款</span><strong>' + Number(stats.refunded || 0) + "</strong></div>" +
'<div class="aksrt-orders-page__stat"><span>已关闭</span><strong>' + Number(stats.closed || 0) + "</strong></div>" +
'<div class="aksrt-orders-page__stat"><span>售后待处理</span><strong>' + Number(stats.after_sale_pending || 0) + "</strong></div>";
var pendingCount = state.root.querySelector('[data-aksrt-order-filter="status"][data-aksrt-order-filter-value="pending"] .aksrt-orders-page__filter-count');
if (pendingCount) {
pendingCount.textContent = String(Number(stats.pending || 0));
@@ -428,7 +466,7 @@
if (!value) return;
var icon = button.querySelector("i");
var settle = function (ok) {
notyf(ok ? "流水号已复制" : "复制失败,请手动选择复制", ok ? "success" : "danger");
notyf(ok ? "订单号已复制" : "复制失败,请手动选择复制", ok ? "success" : "danger");
if (!ok) return;
button.classList.add("is-copied");
if (icon) icon.className = "fa fas fa-check";
@@ -517,12 +555,42 @@
if (cancel && root.contains(cancel)) { event.preventDefault(); closeOrder(state, cancel); }
};
state.onRootSubmit = function (event) {
var afterSaleForm = closest(event.target, "[data-aksrt-after-sale-form]");
if (afterSaleForm) {
event.preventDefault();
var number = afterSaleForm.getAttribute("data-aksrt-after-sale-form") || "";
var reason = afterSaleForm.querySelector("[name=reason]");
if (!number || !reason || !String(reason.value || "").trim()) return;
request(orderPath(number, "/after-sale"), { reason: String(reason.value).trim() }, "POST", window.AksrtRequest.createIdempotencyKey()).then(function () {
notyf("售后申请已提交", "success");
invalidateOrder(number);
openDetail(state, number);
refreshView(state);
}).catch(function (error) { notyf(errorMessage(error, "售后申请提交失败"), "danger"); });
return;
}
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.onDocumentSubmit = function (event) {
var afterSaleForm = closest(event.target, "[data-aksrt-after-sale-form]");
if (!afterSaleForm || root.contains(afterSaleForm) || event.__aksrtAfterSaleHandled) return;
var number = afterSaleForm.getAttribute("data-aksrt-after-sale-form") || "";
if (!activeDetail(state, number)) return;
event.__aksrtAfterSaleHandled = true;
event.preventDefault();
var reason = afterSaleForm.querySelector("[name=reason]");
if (!number || !reason || !String(reason.value || "").trim()) return;
request(orderPath(number, "/after-sale"), { reason: String(reason.value).trim() }, "POST", window.AksrtRequest.createIdempotencyKey()).then(function () {
notyf("售后申请已提交", "success");
invalidateOrder(number);
openDetail(state, number);
refreshView(state);
}).catch(function (error) { notyf(errorMessage(error, "售后申请提交失败"), "danger"); });
};
state.onModalClose = function (event) {
var detail = event && event.detail ? event.detail : {};
if (!state.detailSession || detail.session !== state.detailSession) return;
@@ -533,6 +601,25 @@
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) {
@@ -561,6 +648,7 @@
root.addEventListener("click", state.onRootClick);
root.addEventListener("submit", state.onRootSubmit);
document.addEventListener("submit", state.onDocumentSubmit);
document.addEventListener("aksrt:modal:close", state.onModalClose);
document.addEventListener("click", state.onDocumentClick);
state.countdownTimer = window.setInterval(function () { updateCountdowns(state); }, 1000);
@@ -586,6 +674,7 @@
if (activeDetail(state, state.detailOrderNumber)) window.AksrtModalRuntime.close("replace");
item.removeEventListener("click", state.onRootClick);
item.removeEventListener("submit", state.onRootSubmit);
document.removeEventListener("submit", state.onDocumentSubmit);
document.removeEventListener("aksrt:modal:close", state.onModalClose);
document.removeEventListener("click", state.onDocumentClick);
window.clearInterval(state.countdownTimer);