subscription/assets/js/yoone-admin.js

733 lines
28 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Yoone Subscriptions 后台管理脚本
*
* 处理混装产品和订阅功能的后台管理交互
*/
(function($) {
'use strict';
// 全局变量
var YooneAdmin = {
// 初始化
init: function() {
this.initBundleManagement();
this.initSubscriptionManagement();
this.initProductDataPanels();
this.initPaymentSettings();
this.bindEvents();
},
// 初始化混装产品管理
initBundleManagement: function() {
this.initBundleItemSearch();
this.initBundleItemActions();
this.initBundleValidation();
},
// 初始化订阅管理
initSubscriptionManagement: function() {
this.initSubscriptionActions();
this.initSubscriptionFilters();
this.initSubscriptionBulkActions();
},
// 初始化产品数据面板
initProductDataPanels: function() {
this.initBundleProductPanel();
this.initSubscriptionProductPanel();
},
// 初始化支付设置
initPaymentSettings: function() {
this.initMonerisSettings();
this.initTestModeToggle();
},
// 绑定事件
bindEvents: function() {
var self = this;
// 混装产品搜索
$(document).on('input', '.bundle-item-search input', function() {
self.searchProducts($(this));
});
// 添加混装产品项目
$(document).on('click', '.bundle-search-result', function() {
self.addBundleItem($(this));
});
// 移除混装产品项目
$(document).on('click', '.bundle-item-remove', function() {
self.removeBundleItem($(this));
});
// 更新混装产品数量
$(document).on('change', '.bundle-item-quantity input', function() {
self.updateBundleItemQuantity($(this));
});
// 订阅周期管理
$(document).on('click', '.subscription-add-period', function() {
self.addSubscriptionPeriod();
});
$(document).on('click', '.subscription-period-remove', function() {
self.removeSubscriptionPeriod($(this));
});
// 订阅操作
$(document).on('click', '.subscription-action', function(e) {
e.preventDefault();
self.handleSubscriptionAction($(this));
});
// 批量操作
$(document).on('change', '#bulk-action-selector-top, #bulk-action-selector-bottom', function() {
self.handleBulkActionChange($(this));
});
// 支付设置
$(document).on('change', '#yoone_moneris_test_mode', function() {
self.toggleTestMode($(this));
});
// 产品类型切换
$(document).on('change', '#product-type', function() {
self.handleProductTypeChange($(this));
});
// 表单验证
$(document).on('submit', '.yoone-admin-form', function(e) {
return self.validateForm($(this));
});
},
// 混装产品项目搜索
initBundleItemSearch: function() {
var searchTimeout;
var $searchContainer = $('.bundle-item-search');
if ($searchContainer.length) {
$searchContainer.append('<div class="bundle-search-results"></div>');
}
},
// 搜索产品
searchProducts: function($input) {
var self = this;
var query = $input.val().trim();
var $results = $input.closest('.bundle-item-search').find('.bundle-search-results');
// 清除之前的搜索超时
clearTimeout(this.searchTimeout);
if (query.length < 2) {
$results.hide().empty();
return;
}
// 设置新的搜索超时
this.searchTimeout = setTimeout(function() {
self.performProductSearch(query, $results);
}, 300);
},
// 执行产品搜索
performProductSearch: function(query, $results) {
var self = this;
$results.html('<div class="yoone-loading">搜索中...</div>').show();
$.ajax({
url: yoone_admin_ajax.ajax_url,
type: 'POST',
data: {
action: 'yoone_search_products',
query: query,
nonce: yoone_admin_ajax.nonce
},
success: function(response) {
if (response.success && response.data.length > 0) {
self.displaySearchResults(response.data, $results);
} else {
$results.html('<div class="no-results">未找到相关产品</div>');
}
},
error: function() {
$results.html('<div class="search-error">搜索失败,请重试</div>');
}
});
},
// 显示搜索结果
displaySearchResults: function(products, $results) {
var html = '';
$.each(products, function(index, product) {
html += '<div class="bundle-search-result" data-product-id="' + product.id + '">';
html += '<img src="' + product.image + '" alt="' + product.name + '">';
html += '<div class="bundle-search-result-details">';
html += '<div class="bundle-search-result-name">' + product.name + '</div>';
html += '<div class="bundle-search-result-price">' + product.price + '</div>';
html += '</div>';
html += '</div>';
});
$results.html(html);
},
// 添加混装产品项目
addBundleItem: function($element) {
var productId = $element.data('product-id');
var productName = $element.find('.bundle-search-result-name').text();
var productPrice = $element.find('.bundle-search-result-price').text();
var productImage = $element.find('img').attr('src');
// 检查是否已存在
if ($('.bundle-item-row[data-product-id="' + productId + '"]').length > 0) {
this.showNotice('该产品已添加到混装中', 'warning');
return;
}
var html = '<div class="bundle-item-row" data-product-id="' + productId + '">';
html += '<div class="bundle-item-image"><img src="' + productImage + '" alt="' + productName + '"></div>';
html += '<div class="bundle-item-details">';
html += '<div class="bundle-item-name">' + productName + '</div>';
html += '<div class="bundle-item-price">' + productPrice + '</div>';
html += '</div>';
html += '<div class="bundle-item-quantity">';
html += '<input type="number" name="bundle_items[' + productId + '][quantity]" value="1" min="1" max="999">';
html += '</div>';
html += '<div class="bundle-item-actions">';
html += '<button type="button" class="bundle-item-remove">移除</button>';
html += '</div>';
html += '<input type="hidden" name="bundle_items[' + productId + '][product_id]" value="' + productId + '">';
html += '</div>';
$('.bundle-items-list').append(html);
// 清空搜索
$('.bundle-item-search input').val('');
$('.bundle-search-results').hide().empty();
this.showNotice('产品已添加到混装中', 'success');
},
// 移除混装产品项目
removeBundleItem: function($button) {
var $row = $button.closest('.bundle-item-row');
var productName = $row.find('.bundle-item-name').text();
if (confirm('确定要移除 "' + productName + '" 吗?')) {
$row.fadeOut(300, function() {
$(this).remove();
});
this.showNotice('产品已从混装中移除', 'success');
}
},
// 更新混装产品数量
updateBundleItemQuantity: function($input) {
var quantity = parseInt($input.val());
var min = parseInt($input.attr('min')) || 1;
var max = parseInt($input.attr('max')) || 999;
if (quantity < min) {
$input.val(min);
this.showNotice('数量不能小于 ' + min, 'warning');
} else if (quantity > max) {
$input.val(max);
this.showNotice('数量不能大于 ' + max, 'warning');
}
},
// 初始化混装产品操作
initBundleItemActions: function() {
// 拖拽排序
if ($.fn.sortable) {
$('.bundle-items-list').sortable({
handle: '.bundle-item-image',
placeholder: 'bundle-item-placeholder',
update: function(event, ui) {
// 更新排序
}
});
}
},
// 初始化混装验证
initBundleValidation: function() {
// 实时验证混装设置
},
// 添加订阅周期
addSubscriptionPeriod: function() {
var html = '<div class="subscription-period-row">';
html += '<input type="number" name="subscription_periods[][interval]" value="1" min="1" max="365" placeholder="间隔">';
html += '<select name="subscription_periods[][period]">';
html += '<option value="day">天</option>';
html += '<option value="week">周</option>';
html += '<option value="month" selected>月</option>';
html += '<option value="year">年</option>';
html += '</select>';
html += '<button type="button" class="subscription-period-remove">移除</button>';
html += '</div>';
$('.subscription-periods-list').append(html);
},
// 移除订阅周期
removeSubscriptionPeriod: function($button) {
var $row = $button.closest('.subscription-period-row');
if ($('.subscription-period-row').length > 1) {
$row.fadeOut(300, function() {
$(this).remove();
});
} else {
this.showNotice('至少需要保留一个订阅周期', 'warning');
}
},
// 初始化订阅操作
initSubscriptionActions: function() {
// 订阅状态切换
$('.subscription-status-toggle').on('change', function() {
var $this = $(this);
var subscriptionId = $this.data('subscription-id');
var newStatus = $this.is(':checked') ? 'active' : 'paused';
// 发送AJAX请求更新状态
// ...
});
},
// 处理订阅操作
handleSubscriptionAction: function($button) {
var action = $button.data('action');
var subscriptionId = $button.data('subscription-id');
var confirmMessage = $button.data('confirm');
if (confirmMessage && !confirm(confirmMessage)) {
return;
}
this.performSubscriptionAction(action, subscriptionId, $button);
},
// 执行订阅操作
performSubscriptionAction: function(action, subscriptionId, $button) {
var self = this;
var originalText = $button.text();
$button.text('处理中...').prop('disabled', true);
$.ajax({
url: yoone_admin_ajax.ajax_url,
type: 'POST',
data: {
action: 'yoone_subscription_action',
subscription_action: action,
subscription_id: subscriptionId,
nonce: yoone_admin_ajax.nonce
},
success: function(response) {
if (response.success) {
self.showNotice(response.data.message, 'success');
// 刷新页面或更新状态
if (response.data.reload) {
location.reload();
} else {
self.updateSubscriptionStatus(subscriptionId, response.data.status);
}
} else {
self.showNotice(response.data.message || '操作失败', 'error');
}
},
error: function() {
self.showNotice('网络错误,请重试', 'error');
},
complete: function() {
$button.text(originalText).prop('disabled', false);
}
});
},
// 更新订阅状态
updateSubscriptionStatus: function(subscriptionId, newStatus) {
var $row = $('tr[data-subscription-id="' + subscriptionId + '"]');
var $statusBadge = $row.find('.subscription-status-badge');
// 更新状态徽章
$statusBadge.removeClass().addClass('subscription-status-badge status-' + newStatus);
$statusBadge.text(this.getStatusText(newStatus));
// 更新操作按钮
this.updateActionButtons($row, newStatus);
},
// 获取状态文本
getStatusText: function(status) {
var statusTexts = {
'active': '活跃',
'paused': '暂停',
'cancelled': '已取消',
'expired': '已过期'
};
return statusTexts[status] || status;
},
// 更新操作按钮
updateActionButtons: function($row, status) {
var $actions = $row.find('.subscription-actions');
var subscriptionId = $row.data('subscription-id');
var html = '';
switch (status) {
case 'active':
html += '<a href="#" class="button subscription-action" data-action="pause" data-subscription-id="' + subscriptionId + '">暂停</a>';
html += '<a href="#" class="button subscription-action" data-action="cancel" data-subscription-id="' + subscriptionId + '" data-confirm="确定要取消这个订阅吗?">取消</a>';
break;
case 'paused':
html += '<a href="#" class="button subscription-action" data-action="resume" data-subscription-id="' + subscriptionId + '">恢复</a>';
html += '<a href="#" class="button subscription-action" data-action="cancel" data-subscription-id="' + subscriptionId + '" data-confirm="确定要取消这个订阅吗?">取消</a>';
break;
case 'cancelled':
case 'expired':
html += '<a href="#" class="button subscription-action" data-action="reactivate" data-subscription-id="' + subscriptionId + '">重新激活</a>';
break;
}
html += '<a href="' + yoone_admin_ajax.subscription_edit_url.replace('%d', subscriptionId) + '" class="button">编辑</a>';
$actions.html(html);
},
// 初始化订阅过滤器
initSubscriptionFilters: function() {
// 状态过滤器
$('.subscription-status-filter').on('change', function() {
var status = $(this).val();
var url = new URL(window.location);
if (status) {
url.searchParams.set('status', status);
} else {
url.searchParams.delete('status');
}
window.location = url.toString();
});
// 日期范围过滤器
if ($.fn.datepicker) {
$('.date-filter').datepicker({
dateFormat: 'yy-mm-dd',
changeMonth: true,
changeYear: true
});
}
},
// 初始化批量操作
initSubscriptionBulkActions: function() {
// 全选/取消全选
$('#cb-select-all-1, #cb-select-all-2').on('change', function() {
var checked = $(this).is(':checked');
$('.subscription-checkbox').prop('checked', checked);
});
// 单个复选框
$(document).on('change', '.subscription-checkbox', function() {
var totalCheckboxes = $('.subscription-checkbox').length;
var checkedCheckboxes = $('.subscription-checkbox:checked').length;
$('#cb-select-all-1, #cb-select-all-2').prop('checked', totalCheckboxes === checkedCheckboxes);
});
},
// 处理批量操作变化
handleBulkActionChange: function($select) {
var action = $select.val();
var $form = $select.closest('form');
if (action === 'delete') {
$form.attr('onsubmit', 'return confirm("确定要删除选中的订阅吗?此操作不可撤销。");');
} else {
$form.removeAttr('onsubmit');
}
},
// 初始化混装产品面板
initBundleProductPanel: function() {
var $panel = $('#yoone_bundle_product_data');
if ($panel.length) {
// 折扣类型切换
$panel.find('select[name="_bundle_discount_type"]').on('change', function() {
var type = $(this).val();
var $valueField = $panel.find('input[name="_bundle_discount_value"]');
if (type === 'percentage') {
$valueField.attr('max', '100').attr('placeholder', '例如10 (表示10%)');
} else {
$valueField.removeAttr('max').attr('placeholder', '例如50.00');
}
});
// 数量限制切换
$panel.find('input[name="_bundle_enable_quantity_limits"]').on('change', function() {
var $limits = $panel.find('.bundle-quantity-limits');
if ($(this).is(':checked')) {
$limits.show();
} else {
$limits.hide();
}
});
}
},
// 初始化订阅产品面板
initSubscriptionProductPanel: function() {
var $panel = $('#yoone_subscription_product_data');
if ($panel.length) {
// 订阅启用切换
$panel.find('input[name="_subscription_enabled"]').on('change', function() {
var $options = $panel.find('.subscription-options');
if ($(this).is(':checked')) {
$options.show();
} else {
$options.hide();
}
});
// 折扣类型切换
$panel.find('select[name="_subscription_discount_type"]').on('change', function() {
var type = $(this).val();
var $valueField = $panel.find('input[name="_subscription_discount_value"]');
if (type === 'percentage') {
$valueField.attr('max', '100').attr('placeholder', '例如15 (表示15%)');
} else {
$valueField.removeAttr('max').attr('placeholder', '例如25.00');
}
});
// 试用期切换
$panel.find('input[name="_subscription_trial_enabled"]').on('change', function() {
var $trialOptions = $panel.find('.subscription-trial-options');
if ($(this).is(':checked')) {
$trialOptions.show();
} else {
$trialOptions.hide();
}
});
}
},
// 处理产品类型变化
handleProductTypeChange: function($select) {
var productType = $select.val();
// 显示/隐藏相关面板
$('.yoone-product-data-panel').hide();
if (productType === 'yoone_bundle') {
$('#yoone_bundle_product_data').show();
} else if (productType === 'yoone_subscription') {
$('#yoone_subscription_product_data').show();
}
},
// 初始化Moneris设置
initMonerisSettings: function() {
var $form = $('.yoone-payment-settings');
if ($form.length) {
// 测试连接
$form.find('.test-connection').on('click', function(e) {
e.preventDefault();
var $button = $(this);
$button.text('测试中...').prop('disabled', true);
$.ajax({
url: yoone_admin_ajax.ajax_url,
type: 'POST',
data: {
action: 'yoone_test_moneris_connection',
store_id: $form.find('input[name="yoone_moneris_store_id"]').val(),
api_token: $form.find('input[name="yoone_moneris_api_token"]').val(),
test_mode: $form.find('input[name="yoone_moneris_test_mode"]').is(':checked'),
nonce: yoone_admin_ajax.nonce
},
success: function(response) {
if (response.success) {
alert('连接测试成功!');
} else {
alert('连接测试失败:' + (response.data.message || '未知错误'));
}
},
error: function() {
alert('网络错误,请检查网络连接后重试');
},
complete: function() {
$button.text('测试连接').prop('disabled', false);
}
});
});
}
},
// 切换测试模式
toggleTestMode: function($checkbox) {
var $form = $checkbox.closest('form');
var $testNotice = $form.find('.payment-test-mode');
if ($checkbox.is(':checked')) {
if ($testNotice.length === 0) {
var html = '<div class="payment-test-mode">';
html += '<h4>测试模式已启用</h4>';
html += '<p>当前处于测试模式,所有交易都是模拟的,不会产生实际费用。</p>';
html += '</div>';
$form.prepend(html);
}
} else {
$testNotice.remove();
}
},
// 表单验证
validateForm: function($form) {
var isValid = true;
var errors = [];
// 混装产品验证
if ($form.hasClass('bundle-form')) {
var bundleItems = $form.find('.bundle-item-row').length;
if (bundleItems < 2) {
errors.push('混装产品至少需要包含2个产品');
isValid = false;
}
var discountValue = parseFloat($form.find('input[name="_bundle_discount_value"]').val());
var discountType = $form.find('select[name="_bundle_discount_type"]').val();
if (discountType === 'percentage' && (discountValue < 0 || discountValue > 100)) {
errors.push('折扣百分比必须在0-100之间');
isValid = false;
}
}
// 订阅产品验证
if ($form.hasClass('subscription-form')) {
var subscriptionEnabled = $form.find('input[name="_subscription_enabled"]').is(':checked');
if (subscriptionEnabled) {
var periods = $form.find('.subscription-period-row').length;
if (periods === 0) {
errors.push('启用订阅功能时至少需要设置一个订阅周期');
isValid = false;
}
}
}
// 支付设置验证
if ($form.hasClass('payment-settings-form')) {
var storeId = $form.find('input[name="yoone_moneris_store_id"]').val().trim();
var apiToken = $form.find('input[name="yoone_moneris_api_token"]').val().trim();
if (!storeId) {
errors.push('请输入Moneris商店ID');
isValid = false;
}
if (!apiToken) {
errors.push('请输入Moneris API令牌');
isValid = false;
}
}
// 显示错误信息
if (!isValid) {
var errorMessage = '请修正以下错误:\n\n' + errors.join('\n');
alert(errorMessage);
}
return isValid;
},
// 显示通知
showNotice: function(message, type) {
type = type || 'info';
var $notice = $('<div class="yoone-notice notice-' + type + '">' + message + '</div>');
// 移除现有通知
$('.yoone-notice').remove();
// 添加新通知
$('.wrap').prepend($notice);
// 自动隐藏
setTimeout(function() {
$notice.fadeOut(300, function() {
$(this).remove();
});
}, 5000);
},
// 工具函数:格式化价格
formatPrice: function(price) {
return parseFloat(price).toFixed(2);
},
// 工具函数:格式化日期
formatDate: function(date) {
if (typeof date === 'string') {
date = new Date(date);
}
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
},
// 工具函数:防抖
debounce: function(func, wait) {
var timeout;
return function executedFunction() {
var context = this;
var args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
};
// 文档就绪时初始化
$(document).ready(function() {
YooneAdmin.init();
});
// 导出到全局作用域
window.YooneAdmin = YooneAdmin;
})(jQuery);