feat(payment): 新增「售后策略」配置与前台售后弹窗链路

- 后台「支付&付费 → 售后策略」:售后功能开关、申请时效、超时效提示(HTML)、
  预置原因、申请售后说明、所需材料、申请说明占位文案、可上传图片开关;
  PHP schema 与 SPA schema 1:1 对齐,group 字段支持拖拽排序
- PaymentRuntime 新增售后设置解析(缺键回退 schema 默认、HTML 走 kses、
  中文原因稳定 slug)与 after_sale_expired 时效判定(支付时间起算)
- 售后工单支持举证图片(order_aftersale.images 列,最多 3 张,
  复用评论图片上传校验)与上传路由;订单视图模型输出 after_sale 按钮状态
- 前台申请 / 超时效提示 / 售后详细三个独立弹窗(modal-mini + 蓝色炫彩头),
  订单详情弹窗不再内嵌售后表单并清理相关死代码
- 新增 after-sale-policy 契约测试与运行时 harness,527 项契约测试通过
This commit is contained in:
2026-10-06 22:21:47 +08:00
parent b447c41f13
commit 1a9a2762f3
19 changed files with 1835 additions and 86 deletions
+519
View File
@@ -0,0 +1,519 @@
/**
* AKSRT After-Sale — 前台售后弹窗(申请 / 超时效提示 / 售后详细)
*
* 与「禁封/举报」链路同构(report.js / user-ban.js):
* - [data-aksrt-after-sale-apply] → 申请售后弹窗(超时效则改为超时效提示弹窗)
* - [data-aksrt-after-sale-detail] → 售后详细弹窗(可再次申请 / 不可再申请提示)
* - 提交走 REST /aksrt/v1/orders/{order_number}/after-sale
* - 举证图片走 /orders/{order_number}/after-sale/upload-image(复用评论图片上传校验)
*
* 按钮形态由订单视图模型的 after_sale 状态决定(UserOrders::after_sale_state):
* 已申请 → 售后详细;未申请且在时效内 → 申请售后;超时效 → 申请售后(点击弹超时效提示)。
* 弹窗统一经 AksrtModalRuntime.open 直出 HTML:桌面为主题 modal-mini 几何
* (400px)+ 蓝色炫彩标题区(modal-colorful-header colorful-bg jb-blue),
* 移动端由 mobileBottom 底部抽屉接管。
*
* @package AKSRT
*/
(function () {
'use strict';
var API_ROOT = (function () {
var d = window.aksrtData || {};
var raw = d.apiUrl || d.restUrl || d.rest_url || '/index.php?rest_route=/aksrt/v1';
return String(raw).replace(/\/$/, '');
})();
var NONCE = (window.aksrtData && window.aksrtData.restNonce) || '';
var MAX_IMAGES = 3;
// 不可再申请售后(工单未结束 / 已完结 / 超过时效)时的固定提示。
var REAPPLY_BLOCKED_TEXT = '该无法再次申请售后,该订单已售后,暂不支持再次申请售后。';
var STATUS_LABELS = {
pending: '待审核',
processing: '处理中',
approved: '已通过',
rejected: '已拒绝',
cancelled: '已取消',
completed: '已完成',
};
function settings() {
var d = window.aksrtData && window.aksrtData.afterSale;
return d && typeof d === 'object' ? d : {};
}
function reasonOptions() {
var list = settings().reasons;
if (!Array.isArray(list)) return [];
return list
.map(function (item) {
return {
slug: String((item && (item.slug || item.value)) || ''),
name: String((item && (item.name || item.label || item.t)) || ''),
};
})
.filter(function (item) { return item.name !== ''; });
}
function materialOptions() {
var list = settings().materials;
if (!Array.isArray(list)) return [];
return list.map(function (item) { return String(item == null ? '' : item); })
.filter(function (text) { return text !== ''; });
}
function escapeHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function colorfulHeader(title, icon) {
return '<div class="modal-colorful-header colorful-bg jb-blue">' +
'<button type="button" class="close" data-aksrt-modal-close aria-label="关闭"><svg class="ic-close" aria-hidden="true"><use xlink:href="#icon-close"></use></svg></button>' +
'<div class="colorful-make"></div>' +
'<div class="text-center"><div class="em2x"><i class="fa fas ' + escapeHtml(icon) + '" aria-hidden="true"></i></div>' +
'<div class="mt10 em12 padding-w10">' + escapeHtml(title) + '</div></div></div>';
}
function orderAmountText(order) {
var raw = String(order && order.amount != null ? order.amount : '0');
if (order && order.is_points) {
return escapeHtml(String(order.currency_symbol || '积分')) + ' ' + (/^\d+$/.test(raw) ? raw : '0');
}
return '¥' + raw;
}
function aftersaleAmountText(value, order) {
var amount = String(value == null ? '' : value);
return order && order.is_points ? amount + ' ' + String((order && order.currency_symbol) || '积分') : '¥' + amount;
}
function orderSummaryHtml(order) {
if (!order) return '';
return '<div class="muted-box padding-10 mb20 em09 aksrt-after-sale-summary">' +
'<div class="aksrt-after-sale-summary__row"><span class="muted-2-color">商品</span>' +
'<span class="aksrt-after-sale-summary__value">' + escapeHtml(order.product_name || '订单') + '</span></div>' +
'<div class="aksrt-after-sale-summary__row"><span class="muted-2-color">订单号</span>' +
'<span class="aksrt-after-sale-summary__value">' + escapeHtml(order.order_number || '') + '</span></div>' +
'<div class="aksrt-after-sale-summary__row"><span class="muted-2-color">实付</span>' +
'<span class="aksrt-after-sale-summary__value">' + orderAmountText(order) + '</span></div>' +
'</div>';
}
function buildApplyHtml(order) {
var cfg = settings();
var reasons = reasonOptions();
var materials = materialOptions();
var html = '<div class="aksrt-after-sale-apply-modal">';
html += colorfulHeader('申请售后', 'fa-undo-alt');
html += '<form class="aksrt-after-sale-form" data-aksrt-after-sale-order="' + escapeHtml((order && order.order_number) || '') + '" novalidate>';
html += '<div class="scroll-y mini-scrollbar max-vh5">';
html += orderSummaryHtml(order);
if (reasons.length) {
html += '<div class="mb20">';
html += '<div class="em09 muted-2-color mb6">售后原因</div>';
html += '<div class="form-radio form-but-radio">';
reasons.forEach(function (reason) {
html += '<span class="mr6 mb6 badg p2-10 pointer" data-aksrt-after-sale-reason="' + escapeHtml(reason.name) + '" data-reason-slug="' + escapeHtml(reason.slug) + '">' + escapeHtml(reason.name) + '</span>';
});
html += '</div></div>';
}
materials.forEach(function (label) {
html += '<div class="mb10"><div class="muted-2-color mb6">' + escapeHtml(label) + '</div>' +
'<input class="form-control" type="text" maxlength="120" data-aksrt-after-sale-material="' + escapeHtml(label) + '" placeholder="请输入' + escapeHtml(label) + '"></div>';
});
var placeholder = String(cfg.reasonPlaceholder || '必填,请您详细填写申请原因');
html += '<div class="mb10"><div class="muted-2-color mb6">申请说明</div>' +
'<textarea class="form-control" name="reason" rows="3" maxlength="2000" required placeholder="' + escapeHtml(placeholder) + '"></textarea></div>';
if (cfg.imgEnabled) {
html += '<div class="form-upload mb10">' +
'<div class="em09 muted-2-color mb6">上传图片</div>' +
'<div class="preview"><div class="add" data-aksrt-after-sale-add role="button" tabindex="0" aria-label="上传图片"></div></div>' +
'<input class="hide" type="file" accept="image/gif,image/jpeg,image/jpg,image/png,image/webp" data-aksrt-after-sale-file>' +
'<div class="muted-2-color em09 mt6">最多上传 ' + MAX_IMAGES + ' 张图片</div>' +
'</div>';
}
// 申请售后说明(后台 after_sale_desc,服务端已 wp_kses_post)。
var desc = String(cfg.desc || '');
if (desc) {
html += '<div class="em09 padding-h10 muted-box mb10">' + desc + '</div>';
}
html += '</div>';
html += '<div class="but-average modal-buts">' +
'<button type="button" class="but" data-aksrt-modal-close>取消</button>' +
'<button type="button" class="but c-blue" data-aksrt-after-sale-submit><i class="fa fas fa-check" aria-hidden="true"></i>确认提交</button>' +
'</div>';
html += '</form></div>';
return html;
}
function detailRow(label, value) {
if (value === '' || value == null) return '';
return '<div class="aksrt-after-sale-detail__row"><span class="muted-2-color">' + escapeHtml(label) + '</span>' +
'<span class="aksrt-after-sale-detail__value">' + escapeHtml(value) + '</span></div>';
}
function buildDetailHtml(order) {
var afterSale = (order && order.after_sale) || {};
var latest = afterSale.latest || {};
var status = String(latest.status || '');
var html = '<div class="aksrt-after-sale-detail-modal">';
html += colorfulHeader('售后详细', 'fa-clipboard-list');
html += '<div class="scroll-y mini-scrollbar max-vh5 aksrt-after-sale-detail__body">';
html += orderSummaryHtml(order);
html += '<div class="aksrt-after-sale-detail__statusRow">' +
'<span class="muted-2-color em09">售后状态</span>' +
'<span class="badg p2-10 aksrt-after-sale-status is-' + escapeHtml(status) + '">' + escapeHtml(STATUS_LABELS[status] || status) + '</span>' +
'</div>';
html += '<div class="aksrt-after-sale-detail__rows">';
html += detailRow('申请时间', String(latest.created_at || ''));
html += detailRow('退款金额', aftersaleAmountText(latest.refund_amount, order));
html += detailRow('处理备注', String(latest.admin_note || ''));
html += detailRow('完成时间', String(latest.completed_at || ''));
html += '</div>';
if (latest.reason) {
html += '<div class="mb10"><div class="muted-2-color em09 mb6">申请原因</div>' +
'<div class="em09 aksrt-after-sale-reason-text">' + escapeHtml(latest.reason).replace(/\r\n|\r|\n/g, '<br>') + '</div></div>';
}
var images = Array.isArray(latest.images) ? latest.images : [];
if (images.length) {
html += '<div class="mb10"><div class="muted-2-color em09 mb6">举证图片</div><div class="aksrt-after-sale-images">';
images.forEach(function (url) {
html += '<a href="' + escapeHtml(url) + '" target="_blank" rel="noopener noreferrer"><img src="' + escapeHtml(url) + '" alt=""></a>';
});
html += '</div></div>';
}
html += '</div>';
if (afterSale.can_reapply) {
html += '<div class="but-average modal-buts">' +
'<button type="button" class="but" data-aksrt-modal-close>关闭</button>' +
'<button type="button" class="but c-blue" data-aksrt-after-sale-reapply="' + escapeHtml((order && order.order_number) || '') + '"><i class="fa fas fa-undo-alt" aria-hidden="true"></i>再次申请售后</button>' +
'</div>';
} else {
// 不可再申请售后(工单未结束 / 已完结 / 超过时效):仅提示 + 我知道了。
html += '<div class="aksrt-after-sale-detail__blocked">' +
'<div class="c-yellow box-body">' + escapeHtml(REAPPLY_BLOCKED_TEXT) + '</div>' +
'<div class="but-average modal-buts mt10"><button type="button" class="but c-blue" data-aksrt-modal-close>我知道了</button></div>' +
'</div>';
}
html += '</div>';
return html;
}
function buildExpiredHtml() {
var text = String(settings().expiredText || '抱歉,您的订单已超过售后申请时效');
var html = '<div class="aksrt-after-sale-expired-modal">';
html += colorfulHeader('售后提示', 'fa-clock');
// 超过时效提示(后台 after_sale_expired_text,服务端已 wp_kses_post,支持 HTML)。
html += '<div class="aksrt-after-sale-expired__body em09">' + text + '</div>';
html += '<div class="but-average modal-buts"><button type="button" class="but c-blue" data-aksrt-modal-close>我知道了</button></div>';
html += '</div>';
return html;
}
function openModal(options) {
if (!window.AksrtModalRuntime || typeof window.AksrtModalRuntime.open !== 'function') {
notyf('售后组件加载失败,请刷新页面后重试', 'danger');
return null;
}
var shell = window.AksrtModalRuntime.open({
html: options.html,
className: options.className,
kind: options.kind,
mobileBottom: true,
});
bindShell(shell);
return shell;
}
function openApply(order) {
return openModal({
html: buildApplyHtml(order),
className: 'aksrt-after-sale-apply-modal',
kind: 'after-sale-apply',
});
}
function openDetail(order) {
return openModal({
html: buildDetailHtml(order),
className: 'aksrt-after-sale-detail-modal',
kind: 'after-sale-detail',
});
}
function openExpired() {
return openModal({
html: buildExpiredHtml(),
className: 'aksrt-after-sale-expired-modal',
kind: 'after-sale-expired',
});
}
function loadOrder(number) {
if (!window.AksrtRequest || typeof window.AksrtRequest.request !== 'function') {
return Promise.reject(new Error('请求组件加载失败,请刷新页面后重试'));
}
return window.AksrtRequest.request('/orders/' + encodeURIComponent(number), { method: 'GET' })
.then(function (result) { return (result && result.data) || null; });
}
function openApplyFlow(number) {
if (!number) return;
loadOrder(number).then(function (order) {
var afterSale = order && order.after_sale ? order.after_sale : null;
if (!order || !afterSale || !afterSale.enabled) {
notyf('售后申请暂不可用', 'danger');
return;
}
if (afterSale.expired) { openExpired(); return; }
if (afterSale.has_application) { openDetail(order); return; }
openApply(order);
}).catch(function (error) {
notyf((error && error.message) || '订单加载失败,请刷新后重试', 'danger');
});
}
function openDetailFlow(number) {
if (!number) return;
loadOrder(number).then(function (order) {
var afterSale = order && order.after_sale ? order.after_sale : null;
if (!order || !afterSale || !afterSale.enabled || !afterSale.has_application) {
notyf('暂无售后记录', 'danger');
return;
}
openDetail(order);
}).catch(function (error) {
notyf((error && error.message) || '订单加载失败,请刷新后重试', 'danger');
});
}
function collectImages(shell) {
return Array.prototype.map.call(
shell.querySelectorAll('.preview .preview-item img'),
function (img) { return img.getAttribute('src') || ''; }
).filter(function (url) { return url !== ''; });
}
function uploadImage(shell, file) {
var form = shell.querySelector('[data-aksrt-after-sale-order]');
var preview = shell.querySelector('.form-upload .preview');
var orderNumber = form ? form.getAttribute('data-aksrt-after-sale-order') : '';
if (!preview || !orderNumber) return Promise.resolve();
if (collectImages(shell).length >= MAX_IMAGES) {
notyf('最多上传 ' + MAX_IMAGES + ' 张图片', 'danger');
return Promise.resolve();
}
var body = new FormData();
body.append('image', file);
return window.fetch(API_ROOT + '/orders/' + encodeURIComponent(orderNumber) + '/after-sale/upload-image', {
method: 'POST',
headers: { 'X-WP-Nonce': NONCE },
body: body,
})
.then(function (res) { return res.json(); })
.then(function (result) {
var url = result && result.success && result.data && result.data.url;
if (!url) {
notyf((result && result.message) || '图片上传失败', 'danger');
return;
}
if (collectImages(shell).length >= MAX_IMAGES) {
notyf('最多上传 ' + MAX_IMAGES + ' 张图片', 'danger');
return;
}
var item = document.createElement('div');
item.className = 'preview-item';
var img = document.createElement('img');
img.src = url;
img.alt = '';
var remove = document.createElement('button');
remove.type = 'button';
remove.className = 'remove';
remove.setAttribute('data-aksrt-after-sale-image-remove', '');
remove.setAttribute('aria-label', '删除图片');
remove.innerHTML = '&times;';
remove.addEventListener('click', function () { item.remove(); });
item.appendChild(img);
item.appendChild(remove);
preview.insertBefore(item, preview.querySelector('.add'));
})
.catch(function () {
notyf('图片上传失败,请重试', 'danger');
});
}
function composeReason(shell) {
var parts = [];
var active = shell.querySelector('[data-aksrt-after-sale-reason].active');
if (active) {
var label = active.getAttribute('data-aksrt-after-sale-reason') || '';
if (label) parts.push(label);
}
var materials = [];
Array.prototype.forEach.call(shell.querySelectorAll('[data-aksrt-after-sale-material]'), function (input) {
var value = String(input.value || '').trim();
if (value === '') return;
materials.push(String(input.getAttribute('data-aksrt-after-sale-material') || '') + ':' + value);
});
if (materials.length) parts.push(materials.join('\n'));
var reasonInput = shell.querySelector('[name="reason"]');
var reasonText = reasonInput ? String(reasonInput.value || '').trim() : '';
if (reasonText) parts.push(reasonText);
return parts.join('\n');
}
function submitApply(shell) {
var form = shell.querySelector('[data-aksrt-after-sale-order]');
var orderNumber = form ? form.getAttribute('data-aksrt-after-sale-order') : '';
var reasonInput = shell.querySelector('[name="reason"]');
var reasonText = reasonInput ? String(reasonInput.value || '').trim() : '';
if (!reasonText) {
notyf('请填写申请说明', 'danger');
if (reasonInput) reasonInput.focus();
return;
}
// 预置原因被后台清空时不强制选择;有配置则必须选中一项(与举报弹窗一致)。
if (shell.querySelector('[data-aksrt-after-sale-reason]') && !shell.querySelector('[data-aksrt-after-sale-reason].active')) {
notyf('请选择售后原因', 'danger');
return;
}
var submitBtn = shell.querySelector('[data-aksrt-after-sale-submit]');
if (submitBtn) submitBtn.disabled = true;
notyf('正在提交售后申请…', 'load', 3000, 'aksrt-after-sale-submit');
window.fetch(API_ROOT + '/orders/' + encodeURIComponent(orderNumber) + '/after-sale', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': NONCE,
},
body: JSON.stringify({
reason: composeReason(shell),
images: collectImages(shell),
}),
})
.then(function (res) { return res.json(); })
.then(function (result) {
if (result && result.success) {
window.AksrtModalRuntime.close('submit');
notyf('售后申请已提交', 'success');
if (window.AKSRTOrderCenter && typeof window.AKSRTOrderCenter.refresh === 'function') {
window.AKSRTOrderCenter.refresh(orderNumber);
}
} else {
if (submitBtn) submitBtn.disabled = false;
notyf((result && result.message) || '售后申请提交失败', 'danger');
}
})
.catch(function () {
if (submitBtn) submitBtn.disabled = false;
notyf('网络错误,请重试', 'danger');
})
.then(function () {
var toast = document.getElementById('aksrt-after-sale-submit');
if (toast) toast.remove();
});
}
function bindShell(shell) {
if (!shell) return;
shell.addEventListener('click', function (event) {
// 原因徽章:单选 active(与举报弹窗同语义,重复点击已选徽章保持选中)。
var chip = event.target.closest('[data-aksrt-after-sale-reason]');
if (chip) {
event.preventDefault();
Array.prototype.forEach.call(shell.querySelectorAll('[data-aksrt-after-sale-reason].active'), function (el) {
el.classList.remove('active');
});
chip.classList.add('active');
return;
}
var addBtn = event.target.closest('[data-aksrt-after-sale-add]');
if (addBtn) {
event.preventDefault();
var fileInput = shell.querySelector('[data-aksrt-after-sale-file]');
if (fileInput) fileInput.click();
return;
}
var removeBtn = event.target.closest('[data-aksrt-after-sale-image-remove]');
if (removeBtn) {
event.preventDefault();
var item = removeBtn.closest('.preview-item');
if (item) item.remove();
return;
}
var submitBtn = event.target.closest('[data-aksrt-after-sale-submit]');
if (submitBtn) {
event.preventDefault();
submitApply(shell);
return;
}
// 再次申请售后:重新拉取订单状态,时效/工单口径以最新为准。
var reapplyBtn = event.target.closest('[data-aksrt-after-sale-reapply]');
if (reapplyBtn) {
event.preventDefault();
var number = reapplyBtn.getAttribute('data-aksrt-after-sale-reapply') || '';
window.AksrtModalRuntime.close('replace');
openApplyFlow(number);
}
});
shell.addEventListener('change', function (event) {
var fileInput = event.target.closest('[data-aksrt-after-sale-file]');
if (!fileInput || !fileInput.files || !fileInput.files.length) return;
Array.prototype.forEach.call(fileInput.files, function (file) {
uploadImage(shell, file);
});
fileInput.value = '';
});
}
function initAfterSaleButtons() {
if (document.documentElement.dataset.aksrtAfterSaleInitialized === '1') return;
document.documentElement.dataset.aksrtAfterSaleInitialized = '1';
document.addEventListener('click', function (event) {
var apply = event.target.closest('[data-aksrt-after-sale-apply]');
if (apply) {
event.preventDefault();
openApplyFlow(apply.getAttribute('data-aksrt-after-sale-apply') || '');
return;
}
var detail = event.target.closest('[data-aksrt-after-sale-detail]');
if (detail) {
event.preventDefault();
openDetailFlow(detail.getAttribute('data-aksrt-after-sale-detail') || '');
}
});
}
jQuery(document).off('auto_fun.aksrt-after-sale').on('auto_fun.aksrt-after-sale', initAfterSaleButtons);
})();
+29 -35
View File
@@ -7,6 +7,7 @@
"use strict";
var instances = new WeakMap();
var liveStates = [];
function request(path, body, method, operationKey) {
var options = { method: method || "POST" };
@@ -199,7 +200,8 @@
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>';
// 售后申请为独立弹窗,不再占用订单详情(order-after-sale.js)。
html += afterSaleButtons(order.order_number, order.after_sale || {});
html += "</footer>";
session.content.innerHTML = html;
@@ -275,10 +277,24 @@
function actionHtml(order) {
var allowedActions = order.allowed_actions || {};
var afterSale = order.after_sale || {};
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>';
// 售后入口:申请售后 / 售后详细走 order-after-sale.js 的独立弹窗。
if (afterSale.enabled) html += afterSaleButtons(order.order_number, afterSale);
return html;
}
// 售后按钮(列表卡片与订单详情弹窗共用):已申请 → 售后详细,否则 → 申请售后。
function afterSaleButtons(orderNumber, afterSale) {
var html = "";
if (!afterSale || !afterSale.enabled) return html;
if (afterSale.has_application) {
html += '<button class="btn-outline" type="button" data-aksrt-after-sale-detail="' + escapeHtml(orderNumber) + '">售后详细</button>';
return html;
}
html += '<button class="btn-outline" type="button" data-aksrt-after-sale-apply="' + escapeHtml(orderNumber) + '">申请售后</button>';
return html;
}
@@ -555,42 +571,12 @@
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;
@@ -648,11 +634,11 @@
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);
instances.set(root, state);
liveStates.push(state);
}
function rootsIn(root) {
@@ -674,14 +660,22 @@
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);
instances.delete(item);
liveStates = liveStates.filter(function (entry) { return entry !== state; });
});
}
window.AKSRTOrderCenter = { init: init, destroy: destroy };
// 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); });
}());