功能:增加订单搜索与状态筛选
This commit is contained in:
@@ -89,9 +89,9 @@ func (h *MerchantHandler) UpdateProductStatus(c *gin.Context) {
|
||||
func (h *MerchantHandler) ListOrders(c *gin.Context) {
|
||||
page, size := pageParams(c)
|
||||
orderSource := strings.TrimSpace(c.Query("order_source"))
|
||||
list, total, err := h.fulfillmentSvc.ListOrders(middleware.GetMerchantID(c), page, size, c.Query("order_status"), orderSource)
|
||||
list, total, err := h.fulfillmentSvc.ListOrders(middleware.GetMerchantID(c), page, size, c.Query("order_status"), orderSource, c.Query("keyword"))
|
||||
if err != nil {
|
||||
if orderSource != "" && err.Error() == "无效的订单来源" {
|
||||
if err.Error() == "无效的订单来源" || err.Error() == "无效的订单状态" {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"affiliate_dash/internal/model"
|
||||
@@ -23,10 +24,15 @@ func (s *FulfillmentService) GetOrder(merchantID uint, orderNo string) (*model.F
|
||||
return &order, nil
|
||||
}
|
||||
|
||||
func (s *FulfillmentService) ListOrders(merchantID uint, page, size int, orderStatus, orderSource string) ([]model.FulfillmentOrder, int64, error) {
|
||||
func (s *FulfillmentService) ListOrders(merchantID uint, page, size int, orderStatus, orderSource, keyword string) ([]model.FulfillmentOrder, int64, error) {
|
||||
page, size = normalizePage(page, size)
|
||||
tx := s.db.Model(&model.FulfillmentOrder{}).Where("merchant_id = ?", merchantID)
|
||||
if orderStatus != "" {
|
||||
switch orderStatus {
|
||||
case model.OrderStatusPaid, model.OrderStatusDelivering, model.OrderStatusDelivered, model.OrderStatusShipFailed, model.OrderStatusCancelled:
|
||||
default:
|
||||
return nil, 0, errors.New("无效的订单状态")
|
||||
}
|
||||
tx = tx.Where("order_status = ?", orderStatus)
|
||||
}
|
||||
if orderSource != "" {
|
||||
@@ -37,6 +43,10 @@ func (s *FulfillmentService) ListOrders(merchantID uint, page, size int, orderSt
|
||||
return nil, 0, errors.New("无效的订单来源")
|
||||
}
|
||||
}
|
||||
if keyword = strings.TrimSpace(keyword); keyword != "" {
|
||||
like := "%" + keyword + "%"
|
||||
tx = tx.Where("order_no LIKE ? OR client_order_no LIKE ?", like, like)
|
||||
}
|
||||
var total int64
|
||||
if err := tx.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
|
||||
@@ -65,6 +65,7 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
||||
const [products, setProducts] = useState<PageResult<MerchantProduct>>({ list: [], total: 0, page: 1, size: 10 })
|
||||
const [orders, setOrders] = useState<PageResult<FulfillmentOrder>>({ list: [], total: 0, page: 1, size: 10 })
|
||||
const [orderSource, setOrderSource] = useState<'api' | 'manual'>('api')
|
||||
const [orderFilters, setOrderFilters] = useState<{ keyword: string; order_status: string }>({ keyword: '', order_status: '' })
|
||||
const [ledger, setLedger] = useState<PageResult<WalletLedgerEntry>>({ list: [], total: 0, page: 1, size: 10 })
|
||||
const [wallet, setWallet] = useState<WalletAccount | null>(null)
|
||||
const [apiClients, setApiClients] = useState<ApiClient[]>([])
|
||||
@@ -163,10 +164,15 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
||||
setProducts(data)
|
||||
}, [products.page, products.size])
|
||||
|
||||
const loadOrders = useCallback(async (page = orders.page, size = orders.size, source = orderSource) => {
|
||||
const data = await merchantApi.orders({ page, size, order_source: source })
|
||||
const loadOrders = useCallback(async (
|
||||
page = orders.page,
|
||||
size = orders.size,
|
||||
source = orderSource,
|
||||
filters = orderFilters,
|
||||
) => {
|
||||
const data = await merchantApi.orders({ page, size, order_source: source, ...filters })
|
||||
setOrders(data)
|
||||
}, [orderSource, orders.page, orders.size])
|
||||
}, [orderFilters, orderSource, orders.page, orders.size])
|
||||
|
||||
const loadWallet = useCallback(async (
|
||||
page = ledger.page,
|
||||
@@ -341,7 +347,12 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
||||
|
||||
const changeOrderSource = (source: 'api' | 'manual') => {
|
||||
setOrderSource(source)
|
||||
loadOrders(1, orders.size, source).catch((e) => message.error(e instanceof Error ? e.message : '订单加载失败'))
|
||||
loadOrders(1, orders.size, source, orderFilters).catch((e) => message.error(e instanceof Error ? e.message : '订单加载失败'))
|
||||
}
|
||||
|
||||
const changeOrderFilters = (filters: { keyword: string; order_status: string }) => {
|
||||
setOrderFilters(filters)
|
||||
loadOrders(1, orders.size, orderSource, filters).catch((e) => message.error(e instanceof Error ? e.message : '订单加载失败'))
|
||||
}
|
||||
|
||||
const submitAPIClient = async () => {
|
||||
@@ -499,7 +510,7 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
||||
<ProductsTab loading={loading} products={products} canManage={canManage} onToggleProduct={openProductToggle} onLoadProducts={loadProducts} />
|
||||
) },
|
||||
{ key: 'orders', label: '发货订单', disabled: !hasFeature('orders'), children: (
|
||||
<OrdersTab loading={loading} orders={orders} orderSource={orderSource} canManage={canOrderManage} onCreateManualOrder={openManualOrderCreate} onOrderSourceChange={changeOrderSource} onLoadOrders={loadOrders} onCopyLink={copyDeliveryLink} onOpenLink={openDeliveryLink} onRevokeLink={revokeDeliveryLink} onRestoreLink={restoreDeliveryLink} />
|
||||
<OrdersTab loading={loading} orders={orders} orderSource={orderSource} filters={orderFilters} canManage={canOrderManage} onCreateManualOrder={openManualOrderCreate} onOrderSourceChange={changeOrderSource} onFilterChange={changeOrderFilters} onLoadOrders={loadOrders} onCopyLink={copyDeliveryLink} onOpenLink={openDeliveryLink} onRevokeLink={revokeDeliveryLink} onRestoreLink={restoreDeliveryLink} />
|
||||
) },
|
||||
{ key: 'wallet', label: '钱包', disabled: !hasFeature('wallet'), children: (
|
||||
<WalletTab loading={loading} wallet={wallet} canFinance={canFinance} ledger={ledger} form={walletFilterForm} onFilter={handleWalletFilter} onResetFilter={handleWalletFilterReset} onLoadLedger={handleLoadLedger} />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Button, Card, Col, Form, Input, Popconfirm, Row, Select, Space, Table, Tabs, Tag, Typography, message } from 'antd'
|
||||
import type { FormInstance } from 'antd'
|
||||
import {
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
defaultCallbackFormValues,
|
||||
eventOptions,
|
||||
money,
|
||||
orderStatusOptions,
|
||||
orderStatusMap,
|
||||
pageConfig,
|
||||
rolePermissionText,
|
||||
@@ -171,9 +173,11 @@ export function OrdersTab({
|
||||
loading,
|
||||
orders,
|
||||
orderSource,
|
||||
filters,
|
||||
canManage,
|
||||
onCreateManualOrder,
|
||||
onOrderSourceChange,
|
||||
onFilterChange,
|
||||
onLoadOrders,
|
||||
onCopyLink,
|
||||
onOpenLink,
|
||||
@@ -183,15 +187,25 @@ export function OrdersTab({
|
||||
loading: boolean
|
||||
orders: PageResult<FulfillmentOrder>
|
||||
orderSource: 'api' | 'manual'
|
||||
filters: { keyword: string; order_status: string }
|
||||
canManage: boolean
|
||||
onCreateManualOrder: () => void
|
||||
onOrderSourceChange: (source: 'api' | 'manual') => void
|
||||
onFilterChange: (filters: { keyword: string; order_status: string }) => void
|
||||
onLoadOrders: (page?: number, size?: number) => void
|
||||
onCopyLink: (orderNo: string) => void
|
||||
onOpenLink: (orderNo: string) => void
|
||||
onRevokeLink: (orderNo: string) => void
|
||||
onRestoreLink: (orderNo: string) => void
|
||||
}) {
|
||||
const [keyword, setKeyword] = useState(filters.keyword)
|
||||
|
||||
useEffect(() => {
|
||||
setKeyword(filters.keyword)
|
||||
}, [filters.keyword])
|
||||
|
||||
const submitKeyword = () => onFilterChange({ ...filters, keyword: keyword.trim() })
|
||||
|
||||
const orderColumns: ColumnsType<FulfillmentOrder> = [
|
||||
{
|
||||
title: '平台订单号',
|
||||
@@ -253,6 +267,33 @@ export function OrdersTab({
|
||||
</Space>
|
||||
)}
|
||||
</Space>
|
||||
<Card size="small" style={{ borderRadius: 8 }}>
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Space wrap>
|
||||
<Input.Search
|
||||
value={keyword}
|
||||
onChange={(event) => setKeyword(event.target.value)}
|
||||
onSearch={submitKeyword}
|
||||
placeholder="搜索平台订单号或商户单号"
|
||||
allowClear
|
||||
onClear={() => onFilterChange({ ...filters, keyword: '' })}
|
||||
style={{ width: 300, maxWidth: '100%' }}
|
||||
/>
|
||||
</Space>
|
||||
<Space wrap size={[8, 8]}>
|
||||
{orderStatusOptions.map((option) => (
|
||||
<Button
|
||||
key={option.value || 'all'}
|
||||
type={filters.order_status === option.value ? 'primary' : 'text'}
|
||||
onClick={() => onFilterChange({ ...filters, order_status: option.value })}
|
||||
style={{ minWidth: 76 }}
|
||||
>
|
||||
{option.label}
|
||||
</Button>
|
||||
))}
|
||||
</Space>
|
||||
</Space>
|
||||
</Card>
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
|
||||
@@ -8,6 +8,15 @@ export const orderStatusMap: Record<string, { color: string; text: string }> = {
|
||||
cancelled: { color: 'default', text: '已取消' },
|
||||
}
|
||||
|
||||
export const orderStatusOptions = [
|
||||
{ value: '', label: '全部' },
|
||||
{ value: 'paid', label: '待发货' },
|
||||
{ value: 'delivering', label: '发货中' },
|
||||
{ value: 'delivered', label: '已交付' },
|
||||
{ value: 'ship_failed', label: '发货失败' },
|
||||
{ value: 'cancelled', label: '已取消' },
|
||||
]
|
||||
|
||||
export const memberRoleOptions = [
|
||||
{ value: 'owner', label: '负责人' },
|
||||
{ value: 'operator', label: '运营' },
|
||||
|
||||
Reference in New Issue
Block a user