feat(admin): pending shipment board with immediate ship actions
Add paid-but-unshipped order queue on the shipping page (zibll-style), repository query via NOT EXISTS on order_shipping, overview pending card, and create-shipping redirect back to the pending tab.
This commit is contained in:
@@ -259,9 +259,14 @@ class AKSRT_Admin_Pay_Menus {
|
||||
echo '<p class="description">' . esc_html__('运营数据入口(对齐 zibll 商城中心)。配置项仍在 AKSRT 主题设置 SPA。', 'aksrt') . '</p>';
|
||||
|
||||
echo '<div class="aksrt-pay-stats" style="display:flex;flex-wrap:wrap;gap:16px;margin:20px 0;">';
|
||||
$pending_ship = 0;
|
||||
if (class_exists('AKSRT_Order_Repository')) {
|
||||
$pending_ship = (int) (AKSRT_Order_Repository::get_instance()->get_pending_shipment_orders(['limit' => 1])['total'] ?? 0);
|
||||
}
|
||||
$this->stat_card(__('订单总数', 'aksrt'), (string) (int) ($order_stats['total'] ?? 0), $this->page_url('aksrt_orders'));
|
||||
$this->stat_card(__('待支付', 'aksrt'), (string) (int) ($order_stats['pending'] ?? 0), $this->page_url('aksrt_orders', ['status' => 'pending']));
|
||||
$this->stat_card(__('已支付', 'aksrt'), (string) (int) ($order_stats['paid'] ?? 0), $this->page_url('aksrt_orders', ['status' => 'paid']));
|
||||
$this->stat_card(__('待发货', 'aksrt'), (string) $pending_ship, $this->page_url('aksrt_shipping', ['view' => 'pending']));
|
||||
$this->stat_card(__('营收', 'aksrt'), number_format((float) ($order_stats['revenue'] ?? 0), 2), $this->page_url('aksrt_orders'));
|
||||
$this->stat_card(__('待审提现', 'aksrt'), (string) (int) ($withdraw_stats['pending'] ?? 0), $this->page_url('aksrt_withdraw', ['status' => 'pending']));
|
||||
$this->stat_card(__('可用卡密', 'aksrt'), (string) (int) ($card_stats['active'] ?? 0), $this->page_url('aksrt_charge_cards', ['status' => 'active']));
|
||||
@@ -1055,6 +1060,10 @@ class AKSRT_Admin_Pay_Menus {
|
||||
return;
|
||||
}
|
||||
|
||||
$view = isset($_GET['view']) ? sanitize_key((string) wp_unslash($_GET['view'])) : 'records';
|
||||
if (!in_array($view, ['records', 'pending'], true)) {
|
||||
$view = 'records';
|
||||
}
|
||||
$status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
|
||||
$search = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
|
||||
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
@@ -1063,12 +1072,39 @@ class AKSRT_Admin_Pay_Menus {
|
||||
$stats = $svc->get_stats();
|
||||
$status_options = $this->shipping_status_options(true);
|
||||
|
||||
$pending_total = 0;
|
||||
if (class_exists('AKSRT_Order_Repository')) {
|
||||
$pending_total = (int) (AKSRT_Order_Repository::get_instance()->get_pending_shipment_orders(['limit' => 1, 'search' => ''])['total'] ?? 0);
|
||||
}
|
||||
|
||||
echo '<p>' . esc_html(sprintf(
|
||||
/* translators: 1: total */
|
||||
__('物流记录共 %d 条', 'aksrt'),
|
||||
(int) ($stats['total'] ?? 0)
|
||||
/* translators: 1: shipping records 2: pending shipment orders */
|
||||
__('物流记录 %1$d 条 · 待发货订单 %2$d', 'aksrt'),
|
||||
(int) ($stats['total'] ?? 0),
|
||||
$pending_total
|
||||
)) . '</p>';
|
||||
|
||||
echo '<h2 class="nav-tab-wrapper" style="margin:12px 0 16px;">';
|
||||
printf(
|
||||
'<a href="%s" class="nav-tab%s">%s</a>',
|
||||
esc_url($this->page_url('aksrt_shipping', ['view' => 'pending'])),
|
||||
$view === 'pending' ? ' nav-tab-active' : '',
|
||||
esc_html(sprintf(__('待发货 (%d)', 'aksrt'), $pending_total))
|
||||
);
|
||||
printf(
|
||||
'<a href="%s" class="nav-tab%s">%s</a>',
|
||||
esc_url($this->page_url('aksrt_shipping', ['view' => 'records'])),
|
||||
$view === 'records' ? ' nav-tab-active' : '',
|
||||
esc_html__('物流记录', 'aksrt')
|
||||
);
|
||||
echo '</h2>';
|
||||
|
||||
if ($view === 'pending') {
|
||||
$this->render_pending_shipment_board($search, $page_num, $per_page);
|
||||
$this->render_footer();
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<h2>' . esc_html__('新建发货', 'aksrt') . '</h2>';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="margin:8px 0 20px;display:flex;flex-wrap:wrap;gap:8px;align-items:end;">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_create_shipping" />';
|
||||
@@ -1085,6 +1121,7 @@ class AKSRT_Admin_Pay_Menus {
|
||||
|
||||
echo '<form method="get" style="margin:12px 0;">';
|
||||
echo '<input type="hidden" name="page" value="aksrt_shipping" />';
|
||||
echo '<input type="hidden" name="view" value="records" />';
|
||||
echo '<select name="status">';
|
||||
foreach ($status_options as $key => $label) {
|
||||
printf('<option value="%s"%s>%s</option>', esc_attr($key), selected($status, $key, false), esc_html($label));
|
||||
@@ -1147,6 +1184,75 @@ class AKSRT_Admin_Pay_Menus {
|
||||
$this->render_footer();
|
||||
}
|
||||
|
||||
private function render_pending_shipment_board(string $search, int $page_num, int $per_page): void {
|
||||
if (!class_exists('AKSRT_Order_Repository')) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'aksrt') . '</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<form method="get" style="margin:12px 0;">';
|
||||
echo '<input type="hidden" name="page" value="aksrt_shipping" />';
|
||||
echo '<input type="hidden" name="view" value="pending" />';
|
||||
echo '<input type="search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('订单号 / 类型', 'aksrt') . '" /> ';
|
||||
submit_button(__('筛选', 'aksrt'), 'secondary', '', false);
|
||||
echo '</form>';
|
||||
|
||||
$result = AKSRT_Order_Repository::get_instance()->get_pending_shipment_orders([
|
||||
'search' => $search,
|
||||
'limit' => $per_page,
|
||||
'offset' => ($page_num - 1) * $per_page,
|
||||
]);
|
||||
$items = $result['items'] ?? [];
|
||||
$total = (int) ($result['total'] ?? 0);
|
||||
$pages = max(1, (int) ($result['max_pages'] ?? 1));
|
||||
$labels = class_exists('AKSRT_Domain_Model')
|
||||
? AKSRT_Domain_Model::order_status_labels()
|
||||
: [];
|
||||
|
||||
echo '<table class="wp-list-table widefat fixed striped"><thead><tr>';
|
||||
echo '<th>' . esc_html__('订单号', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('用户', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('类型', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('金额', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('状态', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('支付时间', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('立即发货', 'aksrt') . '</th>';
|
||||
echo '</tr></thead><tbody>';
|
||||
|
||||
if (!$items) {
|
||||
echo '<tr><td colspan="7">' . esc_html__('暂无待发货订单', 'aksrt') . '</td></tr>';
|
||||
}
|
||||
|
||||
foreach ($items as $row) {
|
||||
$order_number = (string) ($row['order_number'] ?? '');
|
||||
$user_id = (int) ($row['user_id'] ?? 0);
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : null;
|
||||
$status_key = (string) ($row['status'] ?? '');
|
||||
echo '<tr>';
|
||||
echo '<td><a href="' . esc_url($this->page_url('aksrt_orders', ['order' => $order_number])) . '"><code>' . esc_html($order_number) . '</code></a></td>';
|
||||
echo '<td>' . ($user ? esc_html($user->display_name . ' (#' . $user_id . ')') : esc_html((string) $user_id)) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['product_type'] ?? '')) . '</td>';
|
||||
echo '<td>' . esc_html(number_format((float) ($row['amount'] ?? 0), 2)) . ' ' . esc_html((string) ($row['currency'] ?? '')) . '</td>';
|
||||
echo '<td>' . esc_html($labels[$status_key] ?? $status_key) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['paid_at'] ?? $row['created_at'] ?? '')) . '</td>';
|
||||
echo '<td>';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="display:flex;flex-wrap:wrap;gap:4px;align-items:center;">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_create_shipping" />';
|
||||
echo '<input type="hidden" name="order_number" value="' . esc_attr($order_number) . '" />';
|
||||
echo '<input type="hidden" name="redirect_page" value="pending" />';
|
||||
wp_nonce_field('aksrt_pay_create_shipping_' . $order_number);
|
||||
$this->render_carrier_field('');
|
||||
echo '<input type="text" name="tracking_no" required placeholder="' . esc_attr__('运单号', 'aksrt') . '" />';
|
||||
echo '<input type="text" name="note" placeholder="' . esc_attr__('备注', 'aksrt') . '" />';
|
||||
submit_button(__('发货', 'aksrt'), 'primary small', '', false);
|
||||
echo '</form>';
|
||||
echo '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
$this->render_pagination($pages, $page_num);
|
||||
}
|
||||
|
||||
public function handle_update_shipping(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
@@ -1207,7 +1313,18 @@ class AKSRT_Admin_Pay_Menus {
|
||||
private function redirect_shipping_create_result(bool $ok, string $order_number, string $redirect_page): void {
|
||||
$msg = $ok ? 'shipping_created' : 'shipping_create_failed';
|
||||
if ($redirect_page === 'shipping') {
|
||||
wp_safe_redirect($this->page_url('aksrt_shipping', ['aksrt_pay_msg' => $msg, 's' => $order_number]));
|
||||
wp_safe_redirect($this->page_url('aksrt_shipping', [
|
||||
'view' => 'records',
|
||||
'aksrt_pay_msg' => $msg,
|
||||
's' => $order_number,
|
||||
]));
|
||||
exit;
|
||||
}
|
||||
if ($redirect_page === 'pending') {
|
||||
wp_safe_redirect($this->page_url('aksrt_shipping', [
|
||||
'view' => 'pending',
|
||||
'aksrt_pay_msg' => $msg,
|
||||
]));
|
||||
exit;
|
||||
}
|
||||
wp_safe_redirect($this->page_url('aksrt_orders', [
|
||||
|
||||
Reference in New Issue
Block a user