From fed8ff3d3adb7a225b554bf50e27fc1bdf07d2f6 Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Tue, 25 Aug 2026 14:19:49 +0800 Subject: [PATCH] =?UTF-8?q?fix(admin):=20=E5=88=97=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E9=9D=A0=E5=8F=B3=E5=B9=B6=E6=94=AF=E6=8C=81=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HQ 列表主操作居右、列设置贴最右侧;订单筛选状态可多选,导出同步过滤。 Co-authored-by: Cursor --- apps/admin-web/src/admin.css | 44 ++++++++++++ .../src/components/AdminListHeader.tsx | 36 ++++++++++ .../admin-web/src/lib/useAdminListColumns.tsx | 8 ++- .../src/pages/BenefitCouponsPage.tsx | 15 ++-- .../src/pages/BenefitLedgersPage.tsx | 4 +- apps/admin-web/src/pages/CitiesPage.tsx | 11 +-- apps/admin-web/src/pages/CityPartnersPage.tsx | 47 ++++++------- .../src/pages/CityWarehousesPage.tsx | 46 +++++++------ apps/admin-web/src/pages/DeliveriesPage.tsx | 15 +++- apps/admin-web/src/pages/DevPlanTasksPage.tsx | 69 ++++++++++--------- .../src/pages/DevPlanVersionsPage.tsx | 20 +++--- .../src/pages/FulfillmentProvidersPage.tsx | 26 +++---- apps/admin-web/src/pages/HqAccountsPage.tsx | 35 +++++----- apps/admin-web/src/pages/HqLogsPage.tsx | 11 +-- apps/admin-web/src/pages/InvoicesPage.tsx | 47 ++++++------- .../src/pages/KnowledgeBasesPage.tsx | 15 ++-- apps/admin-web/src/pages/LlmConfigsPage.tsx | 15 ++-- .../src/pages/LogisticsBillsPage.tsx | 15 ++-- apps/admin-web/src/pages/OrdersPage.tsx | 44 +++++++----- .../src/pages/PartnerAccountsPage.tsx | 15 ++-- apps/admin-web/src/pages/PartnerBillsPage.tsx | 15 ++-- apps/admin-web/src/pages/PartnerLogsPage.tsx | 6 +- apps/admin-web/src/pages/PartnersPage.tsx | 17 +++-- .../admin-web/src/pages/PendingRedeemPage.tsx | 4 +- apps/admin-web/src/pages/ProductsPage.tsx | 11 +-- apps/admin-web/src/pages/PromoCodesPage.tsx | 11 +-- .../admin-web/src/pages/RedeemRecordsPage.tsx | 4 +- apps/admin-web/src/pages/ResourcesPage.tsx | 15 ++-- .../admin-web/src/pages/StoreAccountsPage.tsx | 35 +++++----- apps/admin-web/src/pages/StoreBillsPage.tsx | 33 +++++---- .../src/pages/StoreCategoriesPage.tsx | 43 ++++++------ apps/admin-web/src/pages/StoreLogsPage.tsx | 11 +-- apps/admin-web/src/pages/StoreMediaPage.tsx | 11 +-- .../src/pages/StorePackageAuditsPage.tsx | 4 +- apps/admin-web/src/pages/StoreRatingsPage.tsx | 6 +- .../src/pages/StoreWithdrawalsPage.tsx | 6 +- apps/admin-web/src/pages/StoresPage.tsx | 17 +++-- .../admin-web/src/pages/TestWhitelistPage.tsx | 6 +- .../src/pages/ThirdPartyLogsPage.tsx | 4 +- apps/admin-web/src/pages/UserLogsPage.tsx | 4 +- apps/admin-web/src/pages/UsersPage.tsx | 31 +++++---- .../src/pages/WechatBindingsPage.tsx | 11 +-- apps/admin-web/src/pages/WecomBotLogsPage.tsx | 11 +-- .../src/pages/WecomMessagePushesPage.tsx | 17 +++-- apps/admin-web/src/pages/WineryBillsPage.tsx | 38 ++++------ .../src/pages/promo/PromoCodeUsersPage.tsx | 35 ++++++---- docs/杜康好客-v3-PRD.md | 2 +- docs/杜康好客-v3-现状对照.md | 2 +- docs/杜康好客-v3.5.9-开发文档.md | 7 ++ packages/shared-types/src/ops.ts | 15 ++++ .../src/modules/ops/admin-orders.service.ts | 4 +- .../src/modules/ops/dto/admin-query.dto.ts | 29 ++++++-- 52 files changed, 566 insertions(+), 437 deletions(-) create mode 100644 apps/admin-web/src/components/AdminListHeader.tsx diff --git a/apps/admin-web/src/admin.css b/apps/admin-web/src/admin.css index b8bc382..f312640 100644 --- a/apps/admin-web/src/admin.css +++ b/apps/admin-web/src/admin.css @@ -74,6 +74,50 @@ body.admin-col-resizing * { user-select: none !important; } +/* 列表页顶栏:标题左、主操作右、列设置最右 */ +.admin-list-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + margin-bottom: 16px; +} + +.admin-list-header-left { + min-width: 0; +} + +.admin-list-header-desc { + margin-top: 4px; + color: rgba(0, 0, 0, 0.45); + line-height: 1.5; +} + +.admin-list-header-right { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; + flex-shrink: 0; + margin-left: auto; +} + +.admin-list-settings-slot { + display: inline-flex; + margin-left: auto; + order: 99; + flex-shrink: 0; +} + +.admin-list-settings-btn.ant-btn { + color: rgba(0, 0, 0, 0.55); + padding-inline: 8px; +} + +.admin-list-settings-btn.ant-btn:hover { + color: rgba(0, 0, 0, 0.88); +} + /* 操作列保持可见 */ .admin-layout .ant-table-cell:has(.ant-btn), .ant-drawer .ant-table-cell:has(.ant-btn), diff --git a/apps/admin-web/src/components/AdminListHeader.tsx b/apps/admin-web/src/components/AdminListHeader.tsx new file mode 100644 index 0000000..983143a --- /dev/null +++ b/apps/admin-web/src/components/AdminListHeader.tsx @@ -0,0 +1,36 @@ +import type { ReactNode } from 'react'; +import { Space, Typography } from 'antd'; + +/** 列表页顶栏:标题在左,主操作在右,列设置永远最右 */ +export function AdminListHeader({ + title, + description, + actions, + settings, +}: { + title?: ReactNode; + description?: ReactNode; + actions?: ReactNode; + settings?: ReactNode; +}) { + return ( +
+
+ {title == null || title === '' ? null : typeof title === 'string' || typeof title === 'number' ? ( + + {title} + + ) : ( + title + )} + {description ? ( +
{description}
+ ) : null} +
+
+ {actions ? {actions} : null} + {settings} +
+
+ ); +} diff --git a/apps/admin-web/src/lib/useAdminListColumns.tsx b/apps/admin-web/src/lib/useAdminListColumns.tsx index 46e3f1c..ac0ebb3 100644 --- a/apps/admin-web/src/lib/useAdminListColumns.tsx +++ b/apps/admin-web/src/lib/useAdminListColumns.tsx @@ -153,9 +153,11 @@ export function useAdminListColumns( }, [configured, serialCol, pref?.widths, localWidths, persistWidth]); const settingsButton = ( - + + + ); const settingsModal = ( diff --git a/apps/admin-web/src/pages/BenefitCouponsPage.tsx b/apps/admin-web/src/pages/BenefitCouponsPage.tsx index 4ed800e..19e4b31 100644 --- a/apps/admin-web/src/pages/BenefitCouponsPage.tsx +++ b/apps/admin-web/src/pages/BenefitCouponsPage.tsx @@ -23,6 +23,7 @@ import { COUPON_STATUS_LABELS, DELIVERY_TYPE_LABELS, fmtTime } from '../lib/cons import RedeemRecordDetailDescriptions from '../components/RedeemRecordDetailDescriptions'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; type CouponOrder = { @@ -248,17 +249,15 @@ export default function BenefitCouponsPage() { return (
{settingsModal} -
- - 好客权益券 - - - {settingsButton} + setGrantOpen(true)}> 手动发放 - -
+ } + />
{ setFilters(v); setPage(1); }}> diff --git a/apps/admin-web/src/pages/BenefitLedgersPage.tsx b/apps/admin-web/src/pages/BenefitLedgersPage.tsx index 588434f..028335f 100644 --- a/apps/admin-web/src/pages/BenefitLedgersPage.tsx +++ b/apps/admin-web/src/pages/BenefitLedgersPage.tsx @@ -4,6 +4,7 @@ import type { ColumnsType } from 'antd/es/table'; import { LEDGER_TYPE_LABELS, fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; type Row = { @@ -41,8 +42,7 @@ export default function BenefitLedgersPage() { return (
{settingsModal} - 权益流水 - {settingsButton} + { setFilters(v); setPage(1); }}> diff --git a/apps/admin-web/src/pages/CitiesPage.tsx b/apps/admin-web/src/pages/CitiesPage.tsx index efbe032..b1dc8ff 100644 --- a/apps/admin-web/src/pages/CitiesPage.tsx +++ b/apps/admin-web/src/pages/CitiesPage.tsx @@ -25,6 +25,7 @@ import { useAdminList } from '../lib/useAdminList'; import CityPartnersPanel from '../components/CityPartnersPanel'; import ChinaProvinceCityCascader from '../components/ChinaProvinceCityCascader'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -326,11 +327,11 @@ export default function CitiesPage() { return (
{settingsModal} - - 城市 - {settingsButton} - - + 新建城市} + /> { setFilters(v); setPage(1); }}> diff --git a/apps/admin-web/src/pages/CityPartnersPage.tsx b/apps/admin-web/src/pages/CityPartnersPage.tsx index 24b10b7..d97ea96 100644 --- a/apps/admin-web/src/pages/CityPartnersPage.tsx +++ b/apps/admin-web/src/pages/CityPartnersPage.tsx @@ -37,6 +37,7 @@ import { useAdminList } from '../lib/useAdminList'; import CityDistrictMultiSelect from '../components/CityDistrictMultiSelect'; import PartnerSubAccountList, { type PartnerSubAccountRow } from '../components/PartnerSubAccountList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -350,29 +351,29 @@ export default function CityPartnersPage() { return (
{settingsModal} - - - 城市合伙人 - - {settingsButton} - - + { + createForm.resetFields(); + createForm.setFieldsValue({ + orderCommissionRate: 0, + redeemCommissionRate: 3, + scopeType: CityPartnerScopeType.CITY_WIDE, + }); + setCreateScopeType(CityPartnerScopeType.CITY_WIDE); + setCreateMaxRate(0.05); + setCreateCityCode(undefined); + setCreateOpen(true); + }} + > + 新建合伙人 + + } + /> {settingsModal} - - 仓库 - {settingsButton} - - + { + createForm.resetFields(); + setCreateManagerType(WarehouseManagerType.HQ); + setCreateCityId(undefined); + setCreateFulfillmentMode(WarehouseFulfillmentMode.MANUAL); + createForm.setFieldsValue({ + managerType: WarehouseManagerType.HQ, + status: WarehouseStatus.ACTIVE, + fulfillmentMode: WarehouseFulfillmentMode.MANUAL, + }); + setCreateOpen(true); + }} + > + 新增仓库 + + } + /> v || '—' }, + { + title: '运费', + dataIndex: 'logisticsFee', + width: 90, + render: (v: number | null | undefined) => (v == null ? '—' : `¥${Number(v).toFixed(2)}`), + }, { title: '第三方单号', dataIndex: 'providerOrderNo', width: 140, render: (v) => v || '—' }, { title: '订单状态', dataIndex: ['order', 'status'], width: 100, render: (s) => ORDER_STATUS_LABELS[s] || s }, { title: '收货人', dataIndex: ['order', 'receiverName'], width: 90 }, @@ -107,8 +116,7 @@ export default function DeliveriesPage() { return (
{settingsModal} - 快递/配送单 - {settingsButton} + { setFilters(v); setPage(1); }}> @@ -136,6 +144,9 @@ export default function DeliveriesPage() { {detail.order?.orderNo} {detail.order?.receiverName} {detail.order?.receiverPhone} + + {detail.logisticsFee == null ? '—' : `¥${Number(detail.logisticsFee).toFixed(2)}`} + diff --git a/apps/admin-web/src/pages/DevPlanTasksPage.tsx b/apps/admin-web/src/pages/DevPlanTasksPage.tsx index 498de0e..809aaf4 100644 --- a/apps/admin-web/src/pages/DevPlanTasksPage.tsx +++ b/apps/admin-web/src/pages/DevPlanTasksPage.tsx @@ -31,6 +31,7 @@ import { downloadBase64File } from '../lib/exportExcel'; import { useAdminList } from '../lib/useAdminList'; import OssUpload from '../components/OssUpload'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -309,40 +310,40 @@ export default function DevPlanTasksPage() { return (
{settingsModal} -
- - 开发计划 · 任务列表 - - {settingsButton} - - + + + + + + + } + /> {settingsModal} -
- - 开发计划 · 版本列表 - - {settingsButton} - -
+ + 新建版本 + + } + /> {settingsModal} - -
- - 仓配管理 - - {settingsButton} - - 注册承运商接口凭证,并配置物流对账用的银行账户、结算方式与计价标准 - -
- -
+ + 注册承运商 + + } + /> {settingsModal} - - HQ 账户 - {settingsButton} - {isSuperAdmin && ( - - )} - + { + createForm.resetFields(); + createForm.setFieldsValue({ credentialType: 'phone', adminRole: 'OPS', cityIds: [] }); + setCreateOpen(true); + }} + > + 新建账户 + + ) : null + } + /> { setFilters(v); setPage(1); }}> diff --git a/apps/admin-web/src/pages/HqLogsPage.tsx b/apps/admin-web/src/pages/HqLogsPage.tsx index 2e7c3a9..eb7110b 100644 --- a/apps/admin-web/src/pages/HqLogsPage.tsx +++ b/apps/admin-web/src/pages/HqLogsPage.tsx @@ -10,6 +10,7 @@ import { fmtTime } from '../lib/constants'; import { HQ_OPERATION_ACTION_OPTIONS, resolveHqOperationLabel } from '../lib/hq-log'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -109,11 +110,11 @@ export default function HqLogsPage() { return (
{settingsModal} - HQ 操作日志 - {settingsButton} - - 记录总部后台写操作(开城、订单、用户、权限、合伙人等),仅追加不删除。 - + {settingsModal} -
- - 发票管理 - - {settingsButton} - -
+ { + createForm.setFieldsValue({ + titleType: 'PERSONAL', + invoiceKind: 'NORMAL', + invoiceCategory: 'LIQUOR', + }); + setCreateOpen(true); + }} + > + 创建发票申请 + + } + /> {settingsModal} - - - 知识库 - - {settingsButton} - - 支持粘贴文本或上传 .txt/.md;可绑定到企微机器人供 AI 检索 - - + {settingsModal} - - - 语言模型配置 - - {settingsButton} - - 非超管仅可见自己创建的配置,且创建后只能改是否生效 - - + {settingsModal} - - - 物流对账 - - {settingsButton} - - 按快递承运商汇总月度物流费;计价与银行账户在「仓配管理」配置。前期充值扣款,后期可切挂账月结。 - - + Boolean(s)); + if (typeof status === 'string' && status) return [status]; + return []; +} + function buildExportPayload( scope: OrderExportScope, format: OrderExportFormat, @@ -231,7 +238,8 @@ function buildExportPayload( return payload; } if (filters.orderNo) payload.orderNo = filters.orderNo; - if (filters.status) payload.status = filters.status; + const statuses = selectedStatuses(filters.status); + if (statuses.length) payload.status = statuses; if (filters.orderType) payload.orderType = filters.orderType; if (filters.cityId) payload.cityId = filters.cityId; if (filters.receiverPhone) payload.receiverPhone = filters.receiverPhone; @@ -357,7 +365,7 @@ export default function OrdersPage() { const qs = new URLSearchParams({ page: String(page), pageSize: String(pageSize) }); if (initialOrderNo) qs.set('orderNo', initialOrderNo); if (values.orderNo) qs.set('orderNo', values.orderNo); - if (values.status) qs.set('status', values.status); + for (const status of selectedStatuses(values.status)) qs.append('status', status); if (values.orderType) qs.set('orderType', values.orderType); if (values.cityId) qs.set('cityId', values.cityId); if (values.receiverPhone) qs.set('receiverPhone', values.receiverPhone); @@ -733,18 +741,20 @@ export default function OrdersPage() { return (
{settingsModal} - - 订单监控 - - {settingsButton} - - {canProxyOrder ? ( - - ) : null} - - + + + {canProxyOrder ? ( + + ) : null} + + } + /> {!canDeleteOrders && profile ? ( - + diff --git a/apps/admin-web/src/pages/PendingRedeemPage.tsx b/apps/admin-web/src/pages/PendingRedeemPage.tsx index 15b9b71..c0ac245 100644 --- a/apps/admin-web/src/pages/PendingRedeemPage.tsx +++ b/apps/admin-web/src/pages/PendingRedeemPage.tsx @@ -24,6 +24,7 @@ import { request } from '../lib/api'; import { fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -146,8 +147,7 @@ export default function PendingRedeemPage() { return (
{settingsModal} - 待处理核销(弱网兜底) - {settingsButton} + {settingsModal} - - 商品管理 - {settingsButton} - - + setCreateOpen(true)}>新建商品} + /> { setFilters(v); setPage(1); }}> diff --git a/apps/admin-web/src/pages/PromoCodesPage.tsx b/apps/admin-web/src/pages/PromoCodesPage.tsx index f43281c..b5ea5ea 100644 --- a/apps/admin-web/src/pages/PromoCodesPage.tsx +++ b/apps/admin-web/src/pages/PromoCodesPage.tsx @@ -15,6 +15,7 @@ import { request } from '../lib/api'; import { fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -175,11 +176,11 @@ export default function PromoCodesPage() { return (
{settingsModal} -
- 推广码管理 - {settingsButton} - -
+ setCreateOpen(true)}>创建推广码} + /> {settingsModal} - 核销记录 - {settingsButton} + {settingsModal} - - - OSS 资源库 - - {settingsButton} - - + setCreateOpen(true)}>登记资源} + /> {settingsModal} - - - 门店账户 - {settingsButton} - - 主账号可绑定多家门店;收款信息挂在主账号;「新建账户」用于补录无主账号门店 - - - - + { + void loadStores(); + setCreateOpen(true); + }} + > + 新建账户 + + } + /> {settingsModal} - - - 门店账单 - - {settingsButton} - - 含 T+1 自动出账与门店手动提现;按类型筛选,详情与审核动作沿用原接口。 - 「已打款」= T+1 出账终态,「已结算」= 手动提现终态。 - - {overdueSummary && overdueSummary.overdueCount > 0 ? ( - - 手动提现超时未审 {overdueSummary.overdueCount} 笔(待审共 {overdueSummary.pendingCount}) - - ) : null} - + + 含 T+1 自动出账与门店手动提现;按类型筛选,详情与审核动作沿用原接口。 + 「已打款」= T+1 出账终态,「已结算」= 手动提现终态。 + {overdueSummary && overdueSummary.overdueCount > 0 ? ( +
+ + 手动提现超时未审 {overdueSummary.overdueCount} 笔(待审共 {overdueSummary.pendingCount}) + +
+ ) : null} + + } + /> {summary && ( diff --git a/apps/admin-web/src/pages/StoreCategoriesPage.tsx b/apps/admin-web/src/pages/StoreCategoriesPage.tsx index cc43073..b037bec 100644 --- a/apps/admin-web/src/pages/StoreCategoriesPage.tsx +++ b/apps/admin-web/src/pages/StoreCategoriesPage.tsx @@ -5,6 +5,7 @@ import { import type { ColumnsType } from 'antd/es/table'; import { request, type HqProfile } from '../lib/api'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -179,27 +180,27 @@ export default function StoreCategoriesPage() { return (
{settingsModal} - - - 门店分类 - {settingsButton} - 两级分类:一级(餐饮/住宿/娱乐)→ 二级业态,供合伙人开店选择 - - - {canDelete ? ( - - ) : null} - - - + + {canDelete ? ( + + ) : null} + + + } + /> {settingsModal} - 商户日志 - {settingsButton} - - 门店登录、微信授权、核销、打款与营业状态等操作记录;历史核销/打款数据来自业务表归档。 - + ({ value: o.value, label: o.label }))} diff --git a/apps/admin-web/src/pages/StoreMediaPage.tsx b/apps/admin-web/src/pages/StoreMediaPage.tsx index 2ba93d8..505a7fc 100644 --- a/apps/admin-web/src/pages/StoreMediaPage.tsx +++ b/apps/admin-web/src/pages/StoreMediaPage.tsx @@ -8,6 +8,7 @@ import { ADMIN_OPTIONS_PAGE_SIZE, MEDIA_TYPE_LABELS, fmtTime } from '../lib/cons import { useAdminList } from '../lib/useAdminList'; import OssUpload from '../components/OssUpload'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -96,11 +97,11 @@ export default function StoreMediaPage() { return (
{settingsModal} - - 门店资源 - {settingsButton} - - + { void loadStores(); setCreateOpen(true); }}>新增资源} + /> { setFilters(v); setPage(1); }}> diff --git a/apps/admin-web/src/pages/StorePackageAuditsPage.tsx b/apps/admin-web/src/pages/StorePackageAuditsPage.tsx index 68cfb15..961146a 100644 --- a/apps/admin-web/src/pages/StorePackageAuditsPage.tsx +++ b/apps/admin-web/src/pages/StorePackageAuditsPage.tsx @@ -33,6 +33,7 @@ import { notifyPackageAuditChanged } from '../lib/admin-events'; import { fmtTime } from '../lib/constants'; import StorePackageAuditPanel from '../components/StorePackageAuditPanel'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; const HQ_PACKAGE_STATUS_LABELS: Record = { @@ -545,8 +546,7 @@ export default function StorePackageAuditsPage() { return (
{settingsModal} - 审核通知 - {settingsButton} + {settingsModal} - - 门店评价 - - {settingsButton} + {settingsModal} - - 门店提现审 - - {settingsButton} + {overdueSummary ? ( 待审 {overdueSummary.pendingCount} 笔 diff --git a/apps/admin-web/src/pages/StoresPage.tsx b/apps/admin-web/src/pages/StoresPage.tsx index 25952c2..f24a4f8 100644 --- a/apps/admin-web/src/pages/StoresPage.tsx +++ b/apps/admin-web/src/pages/StoresPage.tsx @@ -40,6 +40,7 @@ import { } from '../lib/storeCreate'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; import { resolveRegionBinding } from '../lib/china-region'; import ChinaRegionCascader from '../components/ChinaRegionCascader'; @@ -873,16 +874,14 @@ export default function StoresPage() { return (
{settingsModal} - - - 门店 - 共 {data?.total ?? 0} 家门店(含合伙人录入) - - - {settingsButton} + 新建门店 - - + } + /> { setFilters(v); setPage(1); }}> diff --git a/apps/admin-web/src/pages/TestWhitelistPage.tsx b/apps/admin-web/src/pages/TestWhitelistPage.tsx index 346b102..2cdcc10 100644 --- a/apps/admin-web/src/pages/TestWhitelistPage.tsx +++ b/apps/admin-web/src/pages/TestWhitelistPage.tsx @@ -21,6 +21,7 @@ import type { ColumnsType } from 'antd/es/table'; import { request, type Paginated } from '../lib/api'; import { fmtTime } from '../lib/constants'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; const MOCK_KEYS = ['MOCK_SMS', 'MOCK_WECHAT', 'MOCK_PAY'] as const; @@ -410,10 +411,7 @@ export default function TestWhitelistPage() { return (
{settingsModal} - - 白名单管理 - - {settingsButton} + diff --git a/apps/admin-web/src/pages/ThirdPartyLogsPage.tsx b/apps/admin-web/src/pages/ThirdPartyLogsPage.tsx index a24f618..b3c82be 100644 --- a/apps/admin-web/src/pages/ThirdPartyLogsPage.tsx +++ b/apps/admin-web/src/pages/ThirdPartyLogsPage.tsx @@ -5,6 +5,7 @@ import { request } from '../lib/api'; import { fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -137,8 +138,7 @@ export default function ThirdPartyLogsPage() { return (
{settingsModal} - 第三方日志 - {settingsButton} + {settingsModal} - 用户日志 - {settingsButton} + ({ value: o.value, label: o.label }))} diff --git a/apps/admin-web/src/pages/UsersPage.tsx b/apps/admin-web/src/pages/UsersPage.tsx index 9085cb5..26741ca 100644 --- a/apps/admin-web/src/pages/UsersPage.tsx +++ b/apps/admin-web/src/pages/UsersPage.tsx @@ -20,6 +20,7 @@ import type { ColumnsType } from 'antd/es/table'; import { USER_SOURCE_TYPE_LABELS, resolveUserLogCategory, type UserSourceType } from '@dukang/shared-types'; import { request, type AdminUserRow, type HqProfile, type Paginated } from '../lib/api'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; import { ORDER_STATUS_LABELS, fmtTime, maskPhone } from '../lib/constants'; @@ -481,21 +482,21 @@ export default function UsersPage() { return (
{settingsModal} - - 用户监控 - - {settingsButton} - {canDeleteUsers ? ( - - ) : null} - - + void openBatchDeleteModal()} + > + 批量删除{selectedRowKeys.length ? ` (${selectedRowKeys.length})` : ''} + + ) : null + } + /> { setPage(1); void load(); }}> diff --git a/apps/admin-web/src/pages/WechatBindingsPage.tsx b/apps/admin-web/src/pages/WechatBindingsPage.tsx index e64b506..4098e60 100644 --- a/apps/admin-web/src/pages/WechatBindingsPage.tsx +++ b/apps/admin-web/src/pages/WechatBindingsPage.tsx @@ -8,6 +8,7 @@ import { AdminCellLine } from '../components/AdminCellLine'; import { fmtTime, maskPhone } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -210,11 +211,11 @@ export default function WechatBindingsPage() { return (
{settingsModal} - 微信绑定总览 - {settingsButton} - - 按 unionId 聚合展示已绑定微信的 C 端用户、门店账号、合伙人账号与 HQ 账号;无 unionId 时按单账号分组。 - + {settingsModal} - 企微机器人日志 - {settingsButton} - - 记录机器人在企微内的查询与审批操作,含权限点、耗时与成败。 - + {settingsModal} - - 配置群机器人 Webhook:按推送条件订阅业务通知 / 告警。运行时不再读取 .env 中的 Webhook URL。 - + + 新建推送 + + } + /> 重置 - - {settingsButton} diff --git a/apps/admin-web/src/pages/WineryBillsPage.tsx b/apps/admin-web/src/pages/WineryBillsPage.tsx index 77a2ec5..c5dec89 100644 --- a/apps/admin-web/src/pages/WineryBillsPage.tsx +++ b/apps/admin-web/src/pages/WineryBillsPage.tsx @@ -27,6 +27,7 @@ import { downloadExcelCsv } from '../lib/exportExcel'; import { request, type HqProfile } from '../lib/api'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; +import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; @@ -305,31 +306,18 @@ export default function WineryBillsPage() { return (
{settingsModal} -
- - - 酒厂对账单 - - {settingsButton} - - T+3:每日 8:00 汇总 3 天前(自然日)已完成的同城/跨城订单(实付 × {ratePct}%);未打款红色、已打款绿色、应付为 0 - 无需打款(灰),可展开订单明细 - - - {canEditWineryBank ? ( - - ) : null} -
+ void openBankModal()}> + 酒厂银行账户信息配置 + + ) : null + } + /> {summary && ( diff --git a/apps/admin-web/src/pages/promo/PromoCodeUsersPage.tsx b/apps/admin-web/src/pages/promo/PromoCodeUsersPage.tsx index cccf480..a4b761b 100644 --- a/apps/admin-web/src/pages/promo/PromoCodeUsersPage.tsx +++ b/apps/admin-web/src/pages/promo/PromoCodeUsersPage.tsx @@ -11,6 +11,7 @@ import { useAdminList } from '../../lib/useAdminList'; import type { PromoCodeDetailContext } from './PromoCodeDetailLayout'; import { useAdminListColumns } from '../../lib/useAdminListColumns'; import { AdminPrimaryLink } from '../../components/AdminPrimaryLink'; +import { AdminListHeader } from '../../components/AdminListHeader'; export default function PromoCodeUsersPage() { @@ -84,20 +85,24 @@ export default function PromoCodeUsersPage() { const { columns, settingsButton, settingsModal } = useAdminListColumns('promo-code-users', baseColumns, { page, pageSize }); return ( -
{ setPage(p); setPageSize(ps); }, - }} - /> + <> + {settingsModal} + +
{ setPage(p); setPageSize(ps); }, + }} + /> + ); } diff --git a/docs/杜康好客-v3-PRD.md b/docs/杜康好客-v3-PRD.md index 85b2b07..c2fcaf1 100644 --- a/docs/杜康好客-v3-PRD.md +++ b/docs/杜康好客-v3-PRD.md @@ -84,7 +84,7 @@ **HQ 订单导出**(`admin-web` 订单监控): -- 支持按筛选(下单日期、状态、类型、城市、配送、收货手机等)或勾选订单导出 +- 支持按筛选(下单日期、状态可多选、类型、城市、配送、收货手机等)或勾选订单导出 - 格式:Excel(`.xlsx`)/ PDF(`.pdf`) - 单次上限 5000 条;按筛选且未指定日期时默认近 30 天 - 权限:`orders`;操作审计 `ORDER_EXPORT` diff --git a/docs/杜康好客-v3-现状对照.md b/docs/杜康好客-v3-现状对照.md index 8402bc2..dfd01be 100644 --- a/docs/杜康好客-v3-现状对照.md +++ b/docs/杜康好客-v3-现状对照.md @@ -67,7 +67,7 @@ | 日期 | 说明 | |------|------| -| 2026-08-25 | v3.5.9:门店列表累计核销好客权益;HQ 表格去省略号;主列表序号 + 列设置/列宽存 HQ 账号;用户列表备注(不改昵称)、手机号不脱敏 | +| 2026-08-25 | v3.5.9:门店列表累计核销好客权益;HQ 表格去省略号;主列表序号 + 列设置/列宽存 HQ 账号;用户列表备注(不改昵称)、手机号不脱敏;订单列表状态可多选 | | 2026-08-24 | v3.5.8:HQ 单账号追加/撤销权限;运营改客服;城市门店服务与城市范围;分类删除权限与概览按权限/城市裁剪 | | 2026-08-23 | v3.5.7:门店核销记录时间显示秒;首页「今日到账金额」→「今日核销金额」 | | 2026-08-23 | v3.5.6:开发计划任务导出;核销/财务银行信息与打款凭证;工单图片入任务;运营去调试;门店审核全屏 | diff --git a/docs/杜康好客-v3.5.9-开发文档.md b/docs/杜康好客-v3.5.9-开发文档.md index dfd20b9..2687f09 100644 --- a/docs/杜康好客-v3.5.9-开发文档.md +++ b/docs/杜康好客-v3.5.9-开发文档.md @@ -120,6 +120,7 @@ - [ ] 用户列表昵称只读;双击「备注」可改,离开编辑后 `user_user.hq_remark` 已更新;C 端看不到该字段 - [ ] 用户列表手机号完整可见(详情仍脱敏) - [ ] 权益券列表:用户编号、订单号可点进对应用户/订单详情;来源含商品名、规格、数量、配送方式、实付金额 +- [ ] 订单列表「状态」可多选;导出按所选状态过滤;不选即全部 --- @@ -128,3 +129,9 @@ - **用户**、**订单**列(及券详情)用主列下划线,分别打开用户详情抽屉、订单详情抽屉。 - **来源**:关联订单时拼 `商品名 / 规格 / 数量(瓶或箱) / 配送方式 / ¥实付`;无订单仍用 `sourceProduct`(如总部手动发放)。 - 列表接口 `order` 增补 `productName`、`productSpec`、`quantity`、`saleUnit`、`deliveryType`、`payAmount`。 + +--- + +## 11. 订单列表状态多选 + +`GET /admin/orders` 与 `POST /admin/orders/export` 的 `status` 支持多值(重复 query、逗号串、JSON 数组均可)。列表筛选为多选;空 = 全部。单值旧链接仍可用。 diff --git a/packages/shared-types/src/ops.ts b/packages/shared-types/src/ops.ts index 7d00f46..2303ab1 100644 --- a/packages/shared-types/src/ops.ts +++ b/packages/shared-types/src/ops.ts @@ -3,6 +3,21 @@ export interface AdminListQuery { pageSize?: number; } +/** HQ 订单列表 / 导出筛选。`status` 可多选(单值仍兼容) */ +export interface AdminOrdersListQuery extends AdminListQuery { + orderNo?: string; + status?: string | string[]; + orderType?: string; + userId?: string; + cityId?: string; + receiverPhone?: string; + fulfillmentHold?: string | boolean; + createdFrom?: string; + createdTo?: string; + excludeTest?: boolean; + deliveryType?: string; +} + export interface UpdateAdminUserRequest { hqRemark: string; } diff --git a/server/dukang-api/src/modules/ops/admin-orders.service.ts b/server/dukang-api/src/modules/ops/admin-orders.service.ts index ea1b62f..1658208 100644 --- a/server/dukang-api/src/modules/ops/admin-orders.service.ts +++ b/server/dukang-api/src/modules/ops/admin-orders.service.ts @@ -155,7 +155,9 @@ export class AdminOrdersService { const where: Prisma.OrderWhereInput = {}; if (query.orderNo) where.orderNo = { contains: query.orderNo }; - if (query.status) where.status = query.status as Prisma.EnumOrderStatusFilter['equals']; + if (query.status?.length) { + where.status = { in: query.status as Prisma.EnumOrderStatusFilter['in'] }; + } if (query.orderType) where.orderType = query.orderType as Prisma.EnumOrderTypeFilter['equals']; if (query.userId) where.userId = BigInt(query.userId); if (query.cityId) where.cityId = BigInt(query.cityId); diff --git a/server/dukang-api/src/modules/ops/dto/admin-query.dto.ts b/server/dukang-api/src/modules/ops/dto/admin-query.dto.ts index d6f3639..f1d4ec8 100644 --- a/server/dukang-api/src/modules/ops/dto/admin-query.dto.ts +++ b/server/dukang-api/src/modules/ops/dto/admin-query.dto.ts @@ -1,6 +1,9 @@ +import { OrderStatus } from '@dukang/shared-types'; import { Type, Transform } from 'class-transformer'; import { IsArray, IsBoolean, IsIn, IsInt, IsOptional, IsString, Max, Min } from 'class-validator'; +const ORDER_STATUS_VALUES = Object.values(OrderStatus); + function toOptionalBoolean(value: unknown): boolean | undefined { if (value === undefined || value === null || value === '') return undefined; if (value === true || value === 'true' || value === '1' || value === 1) return true; @@ -8,6 +11,19 @@ function toOptionalBoolean(value: unknown): boolean | undefined { return undefined; } +/** Query/body:单值、逗号串、重复 key、数组均可 → string[] */ +function toOptionalStringList(value: unknown): string[] | undefined { + if (value === undefined || value === null || value === '') return undefined; + const raw = Array.isArray(value) ? value : [value]; + const items = [...new Set( + raw + .flatMap((v) => String(v).split(',')) + .map((s) => s.trim()) + .filter(Boolean), + )]; + return items.length ? items : undefined; +} + export class PaginationQueryDto { @IsOptional() @Type(() => Number) @@ -57,9 +73,12 @@ export class AdminOrdersQueryDto extends PaginationQueryDto { @IsString() orderNo?: string; + /** 单值或多项:`?status=PENDING_PAY` / `?status=A&status=B` / `?status=A,B` */ @IsOptional() - @IsString() - status?: string; + @Transform(({ value }) => toOptionalStringList(value)) + @IsArray() + @IsIn(ORDER_STATUS_VALUES, { each: true }) + status?: string[]; @IsOptional() @IsIn(['NORMAL', 'PROXY']) @@ -117,8 +136,10 @@ export class AdminOrdersExportDto { orderNo?: string; @IsOptional() - @IsString() - status?: string; + @Transform(({ value }) => toOptionalStringList(value)) + @IsArray() + @IsIn(ORDER_STATUS_VALUES, { each: true }) + status?: string[]; @IsOptional() @IsIn(['NORMAL', 'PROXY', 'RESHIPMENT'])