diff --git a/assets/js/order-after-sale.js b/assets/js/order-after-sale.js
index 95572af3..3c5b24e3 100644
--- a/assets/js/order-after-sale.js
+++ b/assets/js/order-after-sale.js
@@ -1,521 +1,551 @@
-/**
- * AKSRT After-Sale — 前台售后弹窗(申请 / 超时效提示 / 售后详细)
- *
- * 与「禁封/举报」链路同构(report.js / user-ban.js):
- * - [data-aksrt-after-sale-apply] → 申请售后弹窗(超时效则改为超时效提示弹窗)
- * - [data-aksrt-after-sale-detail] → 售后详细弹窗(可再次申请 / 不可再申请提示)
- * 两个入口的 data-* 属性携带订单与售后状态载荷(同 report.js 的 data-aksrt-report),
- * 点击即同步弹窗,不做前置请求(避免首次点击无反馈)。
- * - 提交走 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, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''');
- }
-
- function colorfulHeader(title, icon) {
- return '
';
- }
-
- 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 '' +
- '
商品 ' +
- '' + escapeHtml(order.product_name || '订单') + '
' +
- '
订单号 ' +
- '' + escapeHtml(order.order_number || '') + '
' +
- '
实付 ' +
- '' + orderAmountText(order) + '
' +
- '
';
- }
-
- function buildApplyHtml(order) {
- var cfg = settings();
- var reasons = reasonOptions();
- var materials = materialOptions();
-
- var html = '';
- html += colorfulHeader('申请售后', 'fa-undo-alt');
- html += '
';
- return html;
- }
-
- function detailRow(label, value) {
- if (value === '' || value == null) return '';
- return '' + escapeHtml(label) + ' ' +
- '' + escapeHtml(value) + '
';
- }
-
- function buildDetailHtml(order) {
- var afterSale = (order && order.after_sale) || {};
- var latest = afterSale.latest || {};
- var status = String(latest.status || '');
-
- var html = '';
- html += colorfulHeader('售后详细', 'fa-clipboard-list');
- html += '
';
- html += orderSummaryHtml(order);
-
- html += '
' +
- '售后状态 ' +
- '' + escapeHtml(STATUS_LABELS[status] || status) + ' ' +
- '
';
-
- html += '
';
- 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 += '
';
-
- if (latest.reason) {
- html += '
申请原因
' +
- '
' + escapeHtml(latest.reason).replace(/\r\n|\r|\n/g, ' ') + '
';
- }
-
- var images = Array.isArray(latest.images) ? latest.images : [];
- if (images.length) {
- html += '
举证图片
';
- images.forEach(function (url) {
- html += '
';
- });
- html += '
';
- }
-
- html += '
';
-
- if (afterSale.can_reapply) {
- html += '
' +
- '关闭 ' +
- ' 再次申请售后 ' +
- '
';
- } else {
- // 不可再申请售后(工单未结束 / 已完结 / 超过时效):仅提示 + 我知道了。
- html += '
' +
- '
' + escapeHtml(REAPPLY_BLOCKED_TEXT) + '
' +
- '
我知道了
' +
- '
';
- }
-
- html += '
';
- return html;
- }
-
- function buildExpiredHtml() {
- var text = String(settings().expiredText || '抱歉,您的订单已超过售后申请时效');
- var html = '';
- html += colorfulHeader('售后提示', 'fa-clock');
- // 超过时效提示(后台 after_sale_expired_text,服务端已 wp_kses_post,支持 HTML)。
- html += '
' + text + '
';
- html += '
我知道了
';
- html += '
';
- 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',
- });
- }
-
- /**
- * 解析按钮 data-* 上的售后载荷(与举报链路 data-aksrt-report 同模式):
- * 入口数据随列表一起渲染,点击即可同步弹窗,不做前置请求。
- */
- function parsePayload(raw) {
- if (!raw) return null;
- try {
- var data = JSON.parse(raw);
- return data && typeof data === 'object' ? data : null;
- } catch (error) {
- return null;
- }
- }
-
- // 申请售后入口:超时效 → 超时效提示;已申请 → 售后详细;否则 → 申请表单。
- function openApplyFlow(payload) {
- var afterSale = payload && payload.after_sale ? payload.after_sale : null;
- if (!payload || !afterSale || !afterSale.enabled) {
- notyf('售后申请暂不可用', 'danger');
- return;
- }
- if (afterSale.expired) { openExpired(); return; }
- if (afterSale.has_application) { openDetail(payload); return; }
- openApply(payload);
- }
-
- function openDetailFlow(payload) {
- var afterSale = payload && payload.after_sale ? payload.after_sale : null;
- if (!payload || !afterSale || !afterSale.enabled || !afterSale.has_application) {
- notyf('暂无售后记录', 'danger');
- return;
- }
- openDetail(payload);
- }
-
- 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 = '×';
- 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 payload = parsePayload(reapplyBtn.getAttribute('data-aksrt-after-sale-payload'));
- window.AksrtModalRuntime.close('replace');
- openApplyFlow(payload);
- }
- });
-
- 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(parsePayload(apply.getAttribute('data-aksrt-after-sale-apply')));
- return;
- }
- var detail = event.target.closest('[data-aksrt-after-sale-detail]');
- if (detail) {
- event.preventDefault();
- openDetailFlow(parsePayload(detail.getAttribute('data-aksrt-after-sale-detail')));
- }
- });
- }
-
- jQuery(document).off('auto_fun.aksrt-after-sale').on('auto_fun.aksrt-after-sale', initAfterSaleButtons);
- // 委托监听不依赖 DOM 扫描,脚本加载时立即挂上;auto_fun 钩子保留作幂等重扫。
- initAfterSaleButtons();
-})();
+/**
+ * AKSRT After-Sale — 前台售后弹窗(申请 / 超时效提示 / 售后详细)
+ *
+ * 与「禁封/举报」链路同构(report.js / user-ban.js):
+ * - [data-aksrt-after-sale-apply] → 申请售后弹窗(超时效则改为超时效提示弹窗)
+ * - [data-aksrt-after-sale-detail] → 售后详细弹窗(可再次申请 / 不可再申请提示)
+ * 两个入口的 data-* 属性携带订单与售后状态载荷(同 report.js 的 data-aksrt-report),
+ * 点击即同步弹窗,不做前置请求(避免首次点击无反馈)。
+ * - 提交走 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, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+ function colorfulHeader(title, icon) {
+ return '';
+ }
+
+ 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 '' +
+ '
商品 ' +
+ '' + escapeHtml(order.product_name || '订单') + '
' +
+ '
订单号 ' +
+ '' + escapeHtml(order.order_number || '') + '
' +
+ '
实付 ' +
+ '' + orderAmountText(order) + '
' +
+ '
';
+ }
+
+ function buildApplyHtml(order) {
+ var cfg = settings();
+ var reasons = reasonOptions();
+ var materials = materialOptions();
+
+ var html = '';
+ html += colorfulHeader('申请售后', 'fa-undo-alt');
+ html += '
';
+ // 滚动容器与禁封申诉弹窗一致:no-scrollbar 完全隐藏滚动条(含细滚动条)+ 50vh 内层滚动。
+ html += '';
+ html += '' +
+ '取消 ' +
+ ' 确认提交 ' +
+ '
';
+ html += ' ';
+ return html;
+ }
+
+ function detailRow(label, value) {
+ if (value === '' || value == null) return '';
+ return '' + escapeHtml(label) + ' ' +
+ '' + escapeHtml(value) + '
';
+ }
+
+ function buildDetailHtml(order) {
+ var afterSale = (order && order.after_sale) || {};
+ var latest = afterSale.latest || {};
+ var status = String(latest.status || '');
+
+ var html = '';
+ html += colorfulHeader('售后详细', 'fa-clipboard-list');
+ html += '
';
+ html += orderSummaryHtml(order);
+
+ html += '
' +
+ '售后状态 ' +
+ '' + escapeHtml(STATUS_LABELS[status] || status) + ' ' +
+ '
';
+
+ html += '
';
+ 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 += '
';
+
+ // 申请表单分行展示:预置原因 / 申请资料 / 申请说明各自独立成行,
+ // 与上方「申请时间」等明细行同格式;老数据无 apply_form 时兜底展示融合串。
+ var applyForm = latest.apply_form && typeof latest.apply_form === 'object' ? latest.apply_form : null;
+ if (applyForm) {
+ html += detailRow('原因', String(applyForm.reason_label || ''));
+ (Array.isArray(applyForm.materials) ? applyForm.materials : []).forEach(function (material) {
+ html += detailRow(String((material && material.label) || ''), String((material && material.value) || ''));
+ });
+ html += detailRow('申请说明', String(applyForm.description || ''));
+ } else if (latest.reason) {
+ html += '
申请原因
' +
+ '
' + escapeHtml(latest.reason).replace(/\r\n|\r|\n/g, ' ') + '
';
+ }
+
+ var images = Array.isArray(latest.images) ? latest.images : [];
+ if (images.length) {
+ html += '
举证图片
';
+ images.forEach(function (url) {
+ html += '
';
+ });
+ html += '
';
+ }
+
+ html += '
';
+
+ if (afterSale.can_reapply) {
+ html += '
' +
+ '关闭 ' +
+ ' 再次申请售后 ' +
+ '
';
+ } else {
+ // 不可再申请售后(工单未结束 / 已完结 / 超过时效):仅提示 + 我知道了。
+ html += '
' +
+ '
' + escapeHtml(REAPPLY_BLOCKED_TEXT) + '
' +
+ '
我知道了
' +
+ '
';
+ }
+
+ html += '
';
+ return html;
+ }
+
+ function buildExpiredHtml() {
+ var text = String(settings().expiredText || '抱歉,您的订单已超过售后申请时效');
+ var html = '';
+ html += colorfulHeader('售后提示', 'fa-clock');
+ // 超过时效提示(后台 after_sale_expired_text,服务端已 wp_kses_post,支持 HTML)。
+ html += '
' + text + '
';
+ html += '
我知道了
';
+ html += '
';
+ 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',
+ });
+ }
+
+ /**
+ * 解析按钮 data-* 上的售后载荷(与举报链路 data-aksrt-report 同模式):
+ * 入口数据随列表一起渲染,点击即可同步弹窗,不做前置请求。
+ */
+ function parsePayload(raw) {
+ if (!raw) return null;
+ try {
+ var data = JSON.parse(raw);
+ return data && typeof data === 'object' ? data : null;
+ } catch (error) {
+ return null;
+ }
+ }
+
+ // 申请售后入口:超时效 → 超时效提示;已申请 → 售后详细;否则 → 申请表单。
+ function openApplyFlow(payload) {
+ var afterSale = payload && payload.after_sale ? payload.after_sale : null;
+ if (!payload || !afterSale || !afterSale.enabled) {
+ notyf('售后申请暂不可用', 'danger');
+ return;
+ }
+ if (afterSale.expired) { openExpired(); return; }
+ if (afterSale.has_application) { openDetail(payload); return; }
+ openApply(payload);
+ }
+
+ function openDetailFlow(payload) {
+ var afterSale = payload && payload.after_sale ? payload.after_sale : null;
+ if (!payload || !afterSale || !afterSale.enabled || !afterSale.has_application) {
+ notyf('暂无售后记录', 'danger');
+ return;
+ }
+ openDetail(payload);
+ }
+
+ 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 = '×';
+ remove.addEventListener('click', function () { item.remove(); });
+ item.appendChild(img);
+ item.appendChild(remove);
+ preview.insertBefore(item, preview.querySelector('.add'));
+ })
+ .catch(function () {
+ notyf('图片上传失败,请重试', 'danger');
+ });
+ }
+
+ function selectedReasonLabel(shell) {
+ var active = shell.querySelector('[data-aksrt-after-sale-reason].active');
+ return active ? String(active.getAttribute('data-aksrt-after-sale-reason') || '') : '';
+ }
+
+ function collectMaterials(shell) {
+ 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({
+ label: String(input.getAttribute('data-aksrt-after-sale-material') || ''),
+ value: value,
+ });
+ });
+ return materials;
+ }
+
+ 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),
+ reason_label: selectedReasonLabel(shell),
+ materials: collectMaterials(shell),
+ description: reasonText,
+ 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 payload = parsePayload(reapplyBtn.getAttribute('data-aksrt-after-sale-payload'));
+ window.AksrtModalRuntime.close('replace');
+ openApplyFlow(payload);
+ }
+ });
+
+ 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(parsePayload(apply.getAttribute('data-aksrt-after-sale-apply')));
+ return;
+ }
+ var detail = event.target.closest('[data-aksrt-after-sale-detail]');
+ if (detail) {
+ event.preventDefault();
+ openDetailFlow(parsePayload(detail.getAttribute('data-aksrt-after-sale-detail')));
+ }
+ });
+ }
+
+ jQuery(document).off('auto_fun.aksrt-after-sale').on('auto_fun.aksrt-after-sale', initAfterSaleButtons);
+ // 委托监听不依赖 DOM 扫描,脚本加载时立即挂上;auto_fun 钩子保留作幂等重扫。
+ initAfterSaleButtons();
+})();