feat(admin): order shipping/after-sale create and plate list columns
Allow creating shipping records and after-sale tickets from the native order detail page, and show plate moderator/stats columns on the forum plate list table. Source only; no SPA build artifacts.
This commit is contained in:
@@ -315,6 +315,11 @@ if (is_admin() && class_exists('AKSRT_Topic_Admin_List')) {
|
||||
if (is_admin() && class_exists('AKSRT_Topic_Meta_Box')) {
|
||||
AKSRT_Topic_Meta_Box::get_instance()->init();
|
||||
}
|
||||
// 论坛版块列表列
|
||||
if (is_admin() && class_exists('AKSRT_Plate_Admin_List')) {
|
||||
AKSRT_Plate_Admin_List::get_instance()->init();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -325,6 +325,7 @@ require_once AKSRT_DIR . '/inc/classes/shop/class-aksrt-product-variants-meta-bo
|
||||
require_once AKSRT_DIR . '/inc/classes/forum/class-aksrt-plate-meta-box.php';
|
||||
require_once AKSRT_DIR . '/inc/classes/forum/class-aksrt-topic-admin-list.php';
|
||||
require_once AKSRT_DIR . '/inc/classes/forum/class-aksrt-topic-meta-box.php';
|
||||
require_once AKSRT_DIR . '/inc/classes/forum/class-aksrt-plate-admin-list.php';
|
||||
require_once AKSRT_DIR . '/inc/classes/shop/shop-traits/trait-aksrt-shop-part-1.php';
|
||||
require_once AKSRT_DIR . '/inc/classes/shop/shop-traits/trait-aksrt-shop-part-2.php';
|
||||
require_once AKSRT_DIR . '/inc/classes/shop/shop-traits/trait-aksrt-shop-part-3.php';
|
||||
|
||||
@@ -35,7 +35,9 @@ class AKSRT_Admin_Pay_Menus {
|
||||
add_action('admin_post_aksrt_pay_disable_charge_card', [$this, 'handle_disable_charge_card']);
|
||||
add_action('admin_post_aksrt_pay_batch_charge_cards', [$this, 'handle_batch_charge_cards']);
|
||||
add_action('admin_post_aksrt_pay_update_shipping', [$this, 'handle_update_shipping']);
|
||||
add_action('admin_post_aksrt_pay_create_shipping', [$this, 'handle_create_shipping']);
|
||||
add_action('admin_post_aksrt_pay_update_after_sale', [$this, 'handle_update_after_sale']);
|
||||
add_action('admin_post_aksrt_pay_create_after_sale', [$this, 'handle_create_after_sale']);
|
||||
add_action('admin_post_aksrt_pay_create_coupon', [$this, 'handle_create_coupon']);
|
||||
add_action('admin_post_aksrt_pay_disable_coupon', [$this, 'handle_disable_coupon']);
|
||||
add_action('admin_post_aksrt_pay_settle_income', [$this, 'handle_settle_income']);
|
||||
@@ -202,8 +204,12 @@ class AKSRT_Admin_Pay_Menus {
|
||||
'card_batch_failed' => __('卡密批量生成失败。', 'aksrt'),
|
||||
'shipping_updated' => __('物流状态已更新。', 'aksrt'),
|
||||
'shipping_failed' => __('物流状态更新失败。', 'aksrt'),
|
||||
'shipping_created' => __('发货记录已创建。', 'aksrt'),
|
||||
'shipping_create_failed' => __('发货记录创建失败。', 'aksrt'),
|
||||
'after_sale_updated' => __('售后状态已更新。', 'aksrt'),
|
||||
'after_sale_failed' => __('售后状态更新失败。', 'aksrt'),
|
||||
'after_sale_created' => __('售后工单已创建。', 'aksrt'),
|
||||
'after_sale_create_failed' => __('售后工单创建失败。', 'aksrt'),
|
||||
'coupon_created' => __('优惠码已创建。', 'aksrt'),
|
||||
'coupon_create_failed'=> __('优惠码创建失败(可能重复)。', 'aksrt'),
|
||||
'coupon_disabled' => __('优惠码已禁用。', 'aksrt'),
|
||||
@@ -463,8 +469,18 @@ class AKSRT_Admin_Pay_Menus {
|
||||
echo '</form>';
|
||||
|
||||
echo '<h2 style="margin-top:24px;">' . esc_html__('物流记录', 'aksrt') . '</h2>';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="margin:8px 0 16px;display:flex;flex-wrap:wrap;gap:8px;align-items:end;">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_create_shipping" />';
|
||||
echo '<input type="hidden" name="order_number" value="' . esc_attr($order_number) . '" />';
|
||||
wp_nonce_field('aksrt_pay_create_shipping_' . $order_number);
|
||||
echo '<p><label>' . esc_html__('承运商', 'aksrt') . '<br><input type="text" name="carrier" required placeholder="SF/YTO/..." /></label></p>';
|
||||
echo '<p><label>' . esc_html__('运单号', 'aksrt') . '<br><input type="text" name="tracking_no" required /></label></p>';
|
||||
echo '<p><label>' . esc_html__('备注', 'aksrt') . '<br><input type="text" name="note" /></label></p>';
|
||||
submit_button(__('创建发货', 'aksrt'), 'secondary', '', false);
|
||||
echo ' <a class="button" href="' . esc_url($this->page_url('aksrt_shipping')) . '">' . esc_html__('物流管理', 'aksrt') . '</a>';
|
||||
echo '</form>';
|
||||
if (!$shipping_items) {
|
||||
echo '<p>' . esc_html__('暂无物流记录。', 'aksrt') . ' <a href="' . esc_url($this->page_url('aksrt_shipping')) . '">' . esc_html__('去物流管理', 'aksrt') . '</a></p>';
|
||||
echo '<p>' . esc_html__('暂无物流记录。', 'aksrt') . '</p>';
|
||||
} else {
|
||||
echo '<table class="widefat striped"><thead><tr><th>ID</th><th>' . esc_html__('承运商', 'aksrt') . '</th><th>' . esc_html__('运单号', 'aksrt') . '</th><th>' . esc_html__('状态', 'aksrt') . '</th><th>' . esc_html__('时间', 'aksrt') . '</th></tr></thead><tbody>';
|
||||
foreach ($shipping_items as $item) {
|
||||
@@ -480,8 +496,29 @@ class AKSRT_Admin_Pay_Menus {
|
||||
}
|
||||
|
||||
echo '<h2 style="margin-top:24px;">' . esc_html__('售后工单', 'aksrt') . '</h2>';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="margin:8px 0 16px;display:flex;flex-wrap:wrap;gap:8px;align-items:end;">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_create_after_sale" />';
|
||||
echo '<input type="hidden" name="order_number" value="' . esc_attr($order_number) . '" />';
|
||||
echo '<input type="hidden" name="user_id" value="' . esc_attr((string) $user_id) . '" />';
|
||||
wp_nonce_field('aksrt_pay_create_after_sale_' . $order_number);
|
||||
echo '<p><label>' . esc_html__('类型', 'aksrt') . '<br><select name="type">';
|
||||
foreach ([
|
||||
'refund' => __('仅退款', 'aksrt'),
|
||||
'refund_return' => __('退货退款', 'aksrt'),
|
||||
'exchange' => __('换货', 'aksrt'),
|
||||
'repair' => __('维修', 'aksrt'),
|
||||
] as $k => $label) {
|
||||
printf('<option value="%s">%s</option>', esc_attr($k), esc_html($label));
|
||||
}
|
||||
echo '</select></label></p>';
|
||||
echo '<p><label>' . esc_html__('金额', 'aksrt') . '<br><input type="number" step="0.01" min="0" name="amount" value="' . esc_attr((string) ($order['amount'] ?? 0)) . '" /></label></p>';
|
||||
echo '<p><label>' . esc_html__('原因', 'aksrt') . '<br><input type="text" name="reason" required class="regular-text" /></label></p>';
|
||||
echo '<p><label>' . esc_html__('备注', 'aksrt') . '<br><input type="text" name="note" /></label></p>';
|
||||
submit_button(__('创建售后', 'aksrt'), 'secondary', '', false);
|
||||
echo ' <a class="button" href="' . esc_url($this->page_url('aksrt_after_sale', ['s' => $order_number])) . '">' . esc_html__('售后管理', 'aksrt') . '</a>';
|
||||
echo '</form>';
|
||||
if (!$after_sale) {
|
||||
echo '<p>' . esc_html__('暂无售后工单。', 'aksrt') . ' <a href="' . esc_url($this->page_url('aksrt_after_sale')) . '">' . esc_html__('去售后管理', 'aksrt') . '</a></p>';
|
||||
echo '<p>' . esc_html__('暂无售后工单。', 'aksrt') . '</p>';
|
||||
} else {
|
||||
echo '<table class="widefat striped"><thead><tr><th>ID</th><th>' . esc_html__('类型', 'aksrt') . '</th><th>' . esc_html__('状态', 'aksrt') . '</th><th>' . esc_html__('时间', 'aksrt') . '</th></tr></thead><tbody>';
|
||||
foreach ($after_sale as $item) {
|
||||
@@ -1019,6 +1056,64 @@ class AKSRT_Admin_Pay_Menus {
|
||||
exit;
|
||||
}
|
||||
|
||||
public function handle_create_shipping(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
}
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
check_admin_referer('aksrt_pay_create_shipping_' . $order_number);
|
||||
|
||||
$ok = false;
|
||||
if ($order_number !== '' && class_exists('AKSRT_Shipping')) {
|
||||
$id = AKSRT_Shipping::get_instance()->create([
|
||||
'order_number' => $order_number,
|
||||
'carrier' => sanitize_text_field((string) wp_unslash($_POST['carrier'] ?? '')),
|
||||
'tracking_no' => sanitize_text_field((string) wp_unslash($_POST['tracking_no'] ?? '')),
|
||||
'note' => sanitize_textarea_field((string) wp_unslash($_POST['note'] ?? '')),
|
||||
'operator_id' => get_current_user_id(),
|
||||
]);
|
||||
$ok = $id > 0;
|
||||
}
|
||||
|
||||
wp_safe_redirect($this->page_url('aksrt_orders', [
|
||||
'order' => $order_number,
|
||||
'aksrt_pay_msg' => $ok ? 'shipping_created' : 'shipping_create_failed',
|
||||
]));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function handle_create_after_sale(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
}
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
check_admin_referer('aksrt_pay_create_after_sale_' . $order_number);
|
||||
|
||||
$ok = false;
|
||||
if ($order_number !== '' && class_exists('AKSRT_After_Sale')) {
|
||||
$user_id = isset($_POST['user_id']) ? (int) $_POST['user_id'] : 0;
|
||||
if ($user_id <= 0 && class_exists('AKSRT_Order_Repository')) {
|
||||
$order = AKSRT_Order_Repository::get_instance()->get($order_number);
|
||||
$user_id = (int) ($order['user_id'] ?? 0);
|
||||
}
|
||||
$id = AKSRT_After_Sale::get_instance()->create([
|
||||
'order_number' => $order_number,
|
||||
'user_id' => $user_id,
|
||||
'type' => sanitize_key((string) wp_unslash($_POST['type'] ?? 'refund')),
|
||||
'reason' => sanitize_textarea_field((string) wp_unslash($_POST['reason'] ?? '')),
|
||||
'amount' => (float) ($_POST['amount'] ?? 0),
|
||||
'note' => sanitize_textarea_field((string) wp_unslash($_POST['note'] ?? '')),
|
||||
]);
|
||||
$ok = $id > 0;
|
||||
}
|
||||
|
||||
wp_safe_redirect($this->page_url('aksrt_orders', [
|
||||
'order' => $order_number,
|
||||
'aksrt_pay_msg' => $ok ? 'after_sale_created' : 'after_sale_create_failed',
|
||||
]));
|
||||
exit;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
* After-sale
|
||||
* ----------------------------------------------------------------*/
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* Native list-table columns for aksrt_forum_plate.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class AKSRT_Plate_Admin_List {
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function init(): void {
|
||||
if (!is_admin()) {
|
||||
return;
|
||||
}
|
||||
add_filter('manage_edit-aksrt_forum_plate_columns', [$this, 'columns']);
|
||||
add_action('manage_aksrt_forum_plate_posts_custom_column', [$this, 'render_column'], 10, 2);
|
||||
add_filter('manage_edit-aksrt_forum_plate_sortable_columns', [$this, 'sortable_columns']);
|
||||
add_action('pre_get_posts', [$this, 'apply_orderby']);
|
||||
}
|
||||
|
||||
public function columns(array $columns): array {
|
||||
$new = [];
|
||||
foreach ($columns as $key => $label) {
|
||||
$new[$key] = $label;
|
||||
if ($key === 'title') {
|
||||
$new['aksrt_plate_mods'] = __('版主数', 'aksrt');
|
||||
$new['aksrt_plate_posts'] = __('帖子数', 'aksrt');
|
||||
$new['aksrt_plate_reply'] = __('回复数', 'aksrt');
|
||||
$new['aksrt_plate_views'] = __('浏览', 'aksrt');
|
||||
$new['aksrt_plate_follow'] = __('关注', 'aksrt');
|
||||
$new['aksrt_plate_pending_apps'] = __('待审申请', 'aksrt');
|
||||
}
|
||||
}
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function sortable_columns(array $columns): array {
|
||||
$columns['aksrt_plate_posts'] = 'aksrt_posts_count';
|
||||
$columns['aksrt_plate_views'] = 'aksrt_plate_views';
|
||||
return $columns;
|
||||
}
|
||||
|
||||
public function render_column(string $column, int $post_id): void {
|
||||
switch ($column) {
|
||||
case 'aksrt_plate_mods':
|
||||
$mods = get_post_meta($post_id, '_aksrt_moderators', true);
|
||||
$count = is_array($mods) ? count(array_filter(array_map('intval', $mods))) : 0;
|
||||
echo esc_html((string) $count);
|
||||
break;
|
||||
case 'aksrt_plate_posts':
|
||||
echo esc_html((string) (int) get_post_meta($post_id, '_aksrt_posts_count', true));
|
||||
break;
|
||||
case 'aksrt_plate_reply':
|
||||
echo esc_html((string) (int) get_post_meta($post_id, '_aksrt_reply_count', true));
|
||||
break;
|
||||
case 'aksrt_plate_views':
|
||||
echo esc_html((string) (int) get_post_meta($post_id, '_aksrt_plate_views', true));
|
||||
break;
|
||||
case 'aksrt_plate_follow':
|
||||
echo esc_html((string) (int) get_post_meta($post_id, '_aksrt_follow_count', true));
|
||||
break;
|
||||
case 'aksrt_plate_pending_apps':
|
||||
$apps = get_post_meta($post_id, '_aksrt_moderator_applications', true);
|
||||
$pending = 0;
|
||||
if (is_array($apps)) {
|
||||
foreach ($apps as $app) {
|
||||
if (is_array($app) && (($app['status'] ?? '') === 'pending')) {
|
||||
$pending++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($pending > 0) {
|
||||
echo '<strong style="color:#b32d2e;">' . esc_html((string) $pending) . '</strong>';
|
||||
} else {
|
||||
echo '0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function apply_orderby(WP_Query $query): void {
|
||||
if (!is_admin() || !$query->is_main_query()) {
|
||||
return;
|
||||
}
|
||||
if ($query->get('post_type') !== 'aksrt_forum_plate') {
|
||||
return;
|
||||
}
|
||||
$orderby = $query->get('orderby');
|
||||
if ($orderby === 'aksrt_posts_count') {
|
||||
$query->set('meta_key', '_aksrt_posts_count');
|
||||
$query->set('orderby', 'meta_value_num');
|
||||
} elseif ($orderby === 'aksrt_plate_views') {
|
||||
$query->set('meta_key', '_aksrt_plate_views');
|
||||
$query->set('orderby', 'meta_value_num');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user