fix(admin): 列设置靠右并支持订单状态多选
HQ 列表主操作居右、列设置贴最右侧;订单筛选状态可多选,导出同步过滤。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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 (
|
||||
<div className="admin-list-header">
|
||||
<div className="admin-list-header-left">
|
||||
{title == null || title === '' ? null : typeof title === 'string' || typeof title === 'number' ? (
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
{title}
|
||||
</Typography.Title>
|
||||
) : (
|
||||
title
|
||||
)}
|
||||
{description ? (
|
||||
<div className="admin-list-header-desc">{description}</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="admin-list-header-right">
|
||||
{actions ? <Space wrap>{actions}</Space> : null}
|
||||
{settings}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -153,9 +153,11 @@ export function useAdminListColumns<T>(
|
||||
}, [configured, serialCol, pref?.widths, localWidths, persistWidth]);
|
||||
|
||||
const settingsButton = (
|
||||
<Button icon={<SettingOutlined />} onClick={() => setOpen(true)}>
|
||||
<span className="admin-list-settings-slot">
|
||||
<Button type="text" icon={<SettingOutlined />} className="admin-list-settings-btn" onClick={() => setOpen(true)}>
|
||||
列设置
|
||||
</Button>
|
||||
</span>
|
||||
);
|
||||
|
||||
const settingsModal = (
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
好客权益券
|
||||
</Typography.Title>
|
||||
<Space>
|
||||
{settingsButton}
|
||||
<AdminListHeader
|
||||
title="好客权益券"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
<Button type="primary" onClick={() => setGrantOpen(true)}>
|
||||
手动发放
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="couponNo" label="券号">
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>权益流水</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="权益流水" settings={settingsButton} />
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="userId" label="用户ID"><Input allowClear /></Form.Item>
|
||||
<Form.Item name="couponId" label="券ID"><Input allowClear /></Form.Item>
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>城市</Typography.Title>
|
||||
{settingsButton}
|
||||
<Button type="primary" onClick={openCreateModal}>新建城市</Button>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="城市"
|
||||
settings={settingsButton}
|
||||
actions={<Button type="primary" onClick={openCreateModal}>新建城市</Button>}
|
||||
/>
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="name" label="城市"><Input allowClear /></Form.Item>
|
||||
<Form.Item name="code" label="编码"><Input allowClear /></Form.Item>
|
||||
|
||||
@@ -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,11 +351,10 @@ export default function CityPartnersPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
城市合伙人
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader
|
||||
title="城市合伙人"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
@@ -372,7 +372,8 @@ export default function CityPartnersPage() {
|
||||
>
|
||||
新建合伙人
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
<Alert
|
||||
type="info"
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
Typography,
|
||||
message,
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
@@ -31,6 +30,7 @@ import { request, type Paginated } from '../lib/api';
|
||||
import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -316,9 +316,10 @@ export default function CityWarehousesPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>仓库</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader
|
||||
title="仓库"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
@@ -336,7 +337,8 @@ export default function CityWarehousesPage() {
|
||||
>
|
||||
新增仓库
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
<Alert
|
||||
type="info"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { request } from '../lib/api';
|
||||
import { ORDER_STATUS_LABELS, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -18,6 +19,8 @@ type Row = {
|
||||
trackingNo: string | null;
|
||||
providerOrderNo: string | null;
|
||||
updatedAt: string;
|
||||
/** 当次应付物流费 */
|
||||
logisticsFee?: number | null;
|
||||
order?: {
|
||||
id: string;
|
||||
orderNo: string;
|
||||
@@ -82,6 +85,12 @@ export default function DeliveriesPage() {
|
||||
},
|
||||
{ title: 'provider', dataIndex: 'provider', width: 90 },
|
||||
{ title: '运单号', dataIndex: 'trackingNo', width: 140, render: (v) => 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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>快递/配送单</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="快递/配送单" settings={settingsButton} />
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="orderNo" label="订单号"><Input allowClear /></Form.Item>
|
||||
<Form.Item name="provider" label="provider"><Input allowClear placeholder="MOCK" /></Form.Item>
|
||||
@@ -136,6 +144,9 @@ export default function DeliveriesPage() {
|
||||
<Descriptions column={1} bordered size="small" style={{ marginBottom: 16 }}>
|
||||
<Descriptions.Item label="订单">{detail.order?.orderNo}</Descriptions.Item>
|
||||
<Descriptions.Item label="收货">{detail.order?.receiverName} {detail.order?.receiverPhone}</Descriptions.Item>
|
||||
<Descriptions.Item label="运费">
|
||||
{detail.logisticsFee == null ? '—' : `¥${Number(detail.logisticsFee).toFixed(2)}`}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Form form={editForm} layout="vertical">
|
||||
<Form.Item name="provider" label="provider" rules={[{ required: true }]}><Input /></Form.Item>
|
||||
|
||||
@@ -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,12 +310,11 @@ export default function DevPlanTasksPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
开发计划 · 任务列表
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<Space wrap>
|
||||
<AdminListHeader
|
||||
title="开发计划 · 任务列表"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
<>
|
||||
<Select
|
||||
value={exportFormat}
|
||||
style={{ width: 120 }}
|
||||
@@ -341,8 +341,9 @@ export default function DevPlanTasksPage() {
|
||||
<Button type="primary" onClick={openCreate}>
|
||||
新建任务
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
<Form
|
||||
layout="inline"
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
Typography,
|
||||
message,
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
@@ -23,6 +22,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';
|
||||
|
||||
|
||||
@@ -168,15 +168,15 @@ export default function DevPlanVersionsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
开发计划 · 版本列表
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader
|
||||
title="开发计划 · 版本列表"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
<Button type="primary" onClick={openCreate}>
|
||||
新建版本
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<Form
|
||||
layout="inline"
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
Typography,
|
||||
message,
|
||||
} from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
@@ -29,6 +28,7 @@ import {
|
||||
import { request } from '../lib/api';
|
||||
import { fmtTime } from '../lib/constants';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -247,20 +247,16 @@ export default function FulfillmentProvidersPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<div>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
仓配管理
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">
|
||||
注册承运商接口凭证,并配置物流对账用的银行账户、结算方式与计价标准
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<AdminListHeader
|
||||
title="仓配管理"
|
||||
description="注册承运商接口凭证,并配置物流对账用的银行账户、结算方式与计价标准"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
<Button type="primary" onClick={openCreate}>
|
||||
注册承运商
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
<Alert
|
||||
type="info"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { request, type HqProfile, type Paginated } from '../lib/api';
|
||||
import { ACCOUNT_STATUS_LABELS, ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -145,10 +146,11 @@ export default function HqAccountsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>HQ 账户</Typography.Title>
|
||||
{settingsButton}
|
||||
{isSuperAdmin && (
|
||||
<AdminListHeader
|
||||
title="HQ 账户"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
isSuperAdmin ? (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
@@ -159,8 +161,9 @@ export default function HqAccountsPage() {
|
||||
>
|
||||
新建账户
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="phone" label="手机"><Input allowClear /></Form.Item>
|
||||
<Form.Item name="adminRole" label="角色">
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>HQ 操作日志</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Paragraph type="secondary">
|
||||
记录总部后台写操作(开城、订单、用户、权限、合伙人等),仅追加不删除。
|
||||
</Typography.Paragraph>
|
||||
<AdminListHeader
|
||||
title="HQ 操作日志"
|
||||
settings={settingsButton}
|
||||
description="记录总部后台写操作(开城、订单、用户、权限、合伙人等),仅追加不删除。"
|
||||
/>
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
Typography,
|
||||
Upload,
|
||||
message,
|
||||
} from 'antd';
|
||||
@@ -31,6 +30,7 @@ import { fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { uploadFileToOss } from '../lib/upload';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -241,18 +241,10 @@ export default function InvoicesPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 16,
|
||||
}}
|
||||
>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
发票管理
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader
|
||||
title="发票管理"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
@@ -266,7 +258,8 @@ export default function InvoicesPage() {
|
||||
>
|
||||
创建发票申请
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<Form
|
||||
form={filterForm}
|
||||
layout="inline"
|
||||
|
||||
@@ -26,6 +26,7 @@ import { fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { uploadFileToOss } from '../lib/upload';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -264,15 +265,11 @@ export default function KnowledgeBasesPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16 }} wrap>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
知识库
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">
|
||||
支持粘贴文本或上传 .txt/.md;可绑定到企微机器人供 AI 检索
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="知识库"
|
||||
settings={settingsButton}
|
||||
description="支持粘贴文本或上传 .txt/.md;可绑定到企微机器人供 AI 检索"
|
||||
/>
|
||||
|
||||
<Form
|
||||
form={filterForm}
|
||||
|
||||
@@ -26,6 +26,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';
|
||||
|
||||
|
||||
@@ -271,15 +272,11 @@ export default function LlmConfigsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16 }} wrap>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
语言模型配置
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">
|
||||
非超管仅可见自己创建的配置,且创建后只能改是否生效
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="语言模型配置"
|
||||
settings={settingsButton}
|
||||
description="非超管仅可见自己创建的配置,且创建后只能改是否生效"
|
||||
/>
|
||||
|
||||
<Form
|
||||
form={filterForm}
|
||||
|
||||
@@ -30,6 +30,7 @@ import { downloadExcelCsv } from '../lib/exportExcel';
|
||||
import { request } from '../lib/api';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
type BillRow = {
|
||||
@@ -405,15 +406,11 @@ export default function LogisticsBillsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space direction="vertical" size={0} style={{ marginBottom: 16 }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
物流对账
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">
|
||||
按快递承运商汇总月度物流费;计价与银行账户在「仓配管理」配置。前期充值扣款,后期可切挂账月结。
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="物流对账"
|
||||
settings={settingsButton}
|
||||
description="按快递承运商汇总月度物流费;计价与银行账户在「仓配管理」配置。前期充值扣款,后期可切挂账月结。"
|
||||
/>
|
||||
|
||||
<Tabs
|
||||
items={[
|
||||
|
||||
@@ -27,6 +27,7 @@ import dayjs, { type Dayjs } from 'dayjs';
|
||||
import { ORDER_TYPE_LABELS } from '@dukang/shared-types';
|
||||
import { request, type AdminOrderItem, type AdminOrderRow, type HqProfile, type Paginated } from '../lib/api';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
import { downloadBase64File } from '../lib/exportExcel';
|
||||
import {
|
||||
@@ -144,7 +145,7 @@ type OrderExportFormat = 'xlsx' | 'pdf';
|
||||
|
||||
type OrderExportFilters = {
|
||||
orderNo?: string;
|
||||
status?: string;
|
||||
status?: string | string[];
|
||||
orderType?: string;
|
||||
cityId?: string;
|
||||
receiverPhone?: string;
|
||||
@@ -219,6 +220,12 @@ function formatBenefitBrief(row: AdminOrderRow) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
function selectedStatuses(status: unknown): string[] {
|
||||
if (Array.isArray(status)) return status.filter((s): s is string => 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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 20, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>订单监控</Typography.Title>
|
||||
<Space size={12}>
|
||||
{settingsButton}
|
||||
<AdminListHeader
|
||||
title="订单监控"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
<>
|
||||
<Button onClick={() => window.open('/orders/big-screen', 'dukang-big-screen')}>大屏</Button>
|
||||
{canProxyOrder ? (
|
||||
<Button type="primary" onClick={() => setProxyOpen(true)}>
|
||||
代下单
|
||||
</Button>
|
||||
) : null}
|
||||
</Space>
|
||||
</Space>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
{!canDeleteOrders && profile ? (
|
||||
<Alert
|
||||
@@ -782,10 +792,12 @@ export default function OrdersPage() {
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={12} sm={6} md={5} lg={3}>
|
||||
<Col xs={24} sm={12} md={8} lg={6}>
|
||||
<Form.Item name="status" label="状态" style={{ marginBottom: 12 }}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
maxTagCount="responsive"
|
||||
placeholder="全部"
|
||||
options={Object.entries(ORDER_STATUS_LABELS).map(([value, label]) => ({ value, label }))}
|
||||
/>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { request, type Paginated } from '../lib/api';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
import { ACCOUNT_STATUS_LABELS, ADMIN_OPTIONS_PAGE_SIZE, ORDER_STATUS_LABELS, PARTNER_BILL_STATUS_LABELS, fmtTime } from '../lib/constants';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
|
||||
|
||||
type PartnerOption = { id: string; companyName: string };
|
||||
@@ -261,15 +262,11 @@ export default function PartnerAccountsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<div>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>合伙人子账号</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">
|
||||
主账号在「开城合伙人」创建;此处仅管理子账号树与权限
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="合伙人子账号"
|
||||
settings={settingsButton}
|
||||
description="主账号在「开城合伙人」创建;此处仅管理子账号树与权限"
|
||||
/>
|
||||
<Form
|
||||
form={form}
|
||||
layout="inline"
|
||||
|
||||
@@ -23,6 +23,7 @@ import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||
import { downloadExcelCsv } from '../lib/exportExcel';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -349,15 +350,11 @@ export default function PartnerBillsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space direction="vertical" size={0} style={{ marginBottom: 16 }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
合伙人账单
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">
|
||||
每月 1 日 8:00 自动生成上月账单(待审核)→ 发送合伙人确认 → 未打款 → 已打款
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="合伙人账单"
|
||||
settings={settingsButton}
|
||||
description="每月 1 日 8:00 自动生成上月账单(待审核)→ 发送合伙人确认 → 未打款 → 已打款"
|
||||
/>
|
||||
|
||||
{summary && (
|
||||
<Card size="small" style={{ marginBottom: 16 }}>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { AdminCellLine } from '../components/AdminCellLine';
|
||||
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';
|
||||
|
||||
|
||||
@@ -156,10 +157,7 @@ export default function PartnerLogsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4} style={{ marginBottom: 16 }}>
|
||||
合伙人日志
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="合伙人日志" settings={settingsButton} />
|
||||
<Segmented
|
||||
options={PARTNER_LOG_CATEGORY_OPTIONS.map((o) => ({ value: o.value, label: o.label }))}
|
||||
value={category}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import CityDistrictMultiSelect from '../components/CityDistrictMultiSelect';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -218,13 +219,15 @@ export default function PartnersPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>开城合伙人</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader
|
||||
title="开城合伙人"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
<Button type="primary" onClick={() => { setCreateOpen(true); createForm.resetFields(); setCreateScopeType(CityPartnerScopeType.CITY_WIDE); }}>
|
||||
新建城市合伙人
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="companyName" label="公司"><Input allowClear /></Form.Item>
|
||||
<Form.Item name="contactPhone" label="电话"><Input allowClear /></Form.Item>
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>待处理核销(弱网兜底)</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="待处理核销(弱网兜底)" settings={settingsButton} />
|
||||
<Form
|
||||
form={form}
|
||||
layout="inline"
|
||||
|
||||
@@ -15,6 +15,7 @@ import ProductDetailTemplatePicker from '../components/ProductDetailTemplatePick
|
||||
import ProductSpecsEditor from '../components/ProductSpecsEditor';
|
||||
import type { FormInstance } from 'antd/es/form';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -510,11 +511,11 @@ export default function ProductsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>商品管理</Typography.Title>
|
||||
{settingsButton}
|
||||
<Button type="primary" onClick={() => setCreateOpen(true)}>新建商品</Button>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="商品管理"
|
||||
settings={settingsButton}
|
||||
actions={<Button type="primary" onClick={() => setCreateOpen(true)}>新建商品</Button>}
|
||||
/>
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="name" label="名称">
|
||||
<Input allowClear placeholder="名称 / SKU / 69 码" style={{ width: 200 }} />
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>推广码管理</Typography.Title>
|
||||
{settingsButton}
|
||||
<Button type="primary" onClick={() => setCreateOpen(true)}>创建推广码</Button>
|
||||
</div>
|
||||
<AdminListHeader
|
||||
title="推广码管理"
|
||||
settings={settingsButton}
|
||||
actions={<Button type="primary" onClick={() => setCreateOpen(true)}>创建推广码</Button>}
|
||||
/>
|
||||
|
||||
<Form
|
||||
form={filterForm}
|
||||
|
||||
@@ -8,6 +8,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';
|
||||
|
||||
|
||||
@@ -141,8 +142,7 @@ export default function RedeemRecordsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>核销记录</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="核销记录" settings={settingsButton} />
|
||||
<Form
|
||||
form={form}
|
||||
layout="inline"
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from '../lib/constants';
|
||||
import OssUpload from '../components/OssUpload';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
|
||||
|
||||
type Row = {
|
||||
@@ -121,15 +122,11 @@ export default function ResourcesPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
OSS 资源库
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<Button type="primary" onClick={() => setCreateOpen(true)}>
|
||||
登记资源
|
||||
</Button>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="OSS 资源库"
|
||||
settings={settingsButton}
|
||||
actions={<Button type="primary" onClick={() => setCreateOpen(true)}>登记资源</Button>}
|
||||
/>
|
||||
<Form
|
||||
form={form}
|
||||
layout="inline"
|
||||
|
||||
@@ -7,6 +7,7 @@ import { request, type Paginated } from '../lib/api';
|
||||
import { ACCOUNT_STATUS_LABELS, ADMIN_OPTIONS_PAGE_SIZE, STORE_STATUS_LABELS, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -153,14 +154,11 @@ export default function StoreAccountsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Space direction="vertical" size={0}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>门店账户</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">
|
||||
主账号可绑定多家门店;收款信息挂在主账号;「新建账户」用于补录无主账号门店
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="门店账户"
|
||||
settings={settingsButton}
|
||||
description="主账号可绑定多家门店;收款信息挂在主账号;「新建账户」用于补录无主账号门店"
|
||||
actions={
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
@@ -170,7 +168,8 @@ export default function StoreAccountsPage() {
|
||||
>
|
||||
新建账户
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
<Form
|
||||
form={form}
|
||||
layout="inline"
|
||||
|
||||
@@ -31,6 +31,7 @@ import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||
import { downloadExcelCsv } from '../lib/exportExcel';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -381,21 +382,23 @@ export default function StoreBillsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space direction="vertical" size={0} style={{ marginBottom: 16 }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
门店账单
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">
|
||||
<AdminListHeader
|
||||
title="门店账单"
|
||||
settings={settingsButton}
|
||||
description={
|
||||
<>
|
||||
含 T+1 自动出账与门店手动提现;按类型筛选,详情与审核动作沿用原接口。
|
||||
「已打款」= T+1 出账终态,「已结算」= 手动提现终态。
|
||||
</Typography.Text>
|
||||
{overdueSummary && overdueSummary.overdueCount > 0 ? (
|
||||
<div>
|
||||
<Typography.Text type="danger">
|
||||
手动提现超时未审 {overdueSummary.overdueCount} 笔(待审共 {overdueSummary.pendingCount})
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : null}
|
||||
</Space>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
{summary && (
|
||||
<Card size="small" style={{ marginBottom: 16 }}>
|
||||
|
||||
@@ -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,13 +180,12 @@ export default function StoreCategoriesPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Space direction="vertical" size={0}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>门店分类</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">两级分类:一级(餐饮/住宿/娱乐)→ 二级业态,供合伙人开店选择</Typography.Text>
|
||||
</Space>
|
||||
<Space>
|
||||
<AdminListHeader
|
||||
title="门店分类"
|
||||
settings={settingsButton}
|
||||
description="两级分类:一级(餐饮/住宿/娱乐)→ 二级业态,供合伙人开店选择"
|
||||
actions={
|
||||
<>
|
||||
{canDelete ? (
|
||||
<Button
|
||||
onClick={async () => {
|
||||
@@ -198,8 +198,9 @@ export default function StoreCategoriesPage() {
|
||||
</Button>
|
||||
) : null}
|
||||
<Button type="primary" onClick={() => openCreate()}>新增一级</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
<Table
|
||||
rowKey="id"
|
||||
|
||||
@@ -13,6 +13,7 @@ import { AdminCellLine } from '../components/AdminCellLine';
|
||||
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';
|
||||
|
||||
|
||||
@@ -151,11 +152,11 @@ export default function StoreLogsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>商户日志</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Paragraph type="secondary" style={{ marginTop: -8 }}>
|
||||
门店登录、微信授权、核销、打款与营业状态等操作记录;历史核销/打款数据来自业务表归档。
|
||||
</Typography.Paragraph>
|
||||
<AdminListHeader
|
||||
title="商户日志"
|
||||
settings={settingsButton}
|
||||
description="门店登录、微信授权、核销、打款与营业状态等操作记录;历史核销/打款数据来自业务表归档。"
|
||||
/>
|
||||
<Segmented
|
||||
style={{ marginBottom: 16 }}
|
||||
options={STORE_LOG_CATEGORY_OPTIONS.map((o) => ({ value: o.value, label: o.label }))}
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>门店资源</Typography.Title>
|
||||
{settingsButton}
|
||||
<Button type="primary" onClick={() => { void loadStores(); setCreateOpen(true); }}>新增资源</Button>
|
||||
</Space>
|
||||
<AdminListHeader
|
||||
title="门店资源"
|
||||
settings={settingsButton}
|
||||
actions={<Button type="primary" onClick={() => { void loadStores(); setCreateOpen(true); }}>新增资源</Button>}
|
||||
/>
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="storeId" label="门店ID"><Input allowClear /></Form.Item>
|
||||
<Form.Item name="mediaType" label="类型">
|
||||
|
||||
@@ -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<StorePackageChangeStatus, string> = {
|
||||
@@ -545,8 +546,7 @@ export default function StorePackageAuditsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>审核通知</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="审核通知" settings={settingsButton} />
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onChange={setActiveTab}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { fmtTime, ADMIN_OPTIONS_PAGE_SIZE } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { request, type Paginated } from '../lib/api';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
|
||||
|
||||
type Row = {
|
||||
@@ -69,10 +70,7 @@ export default function StoreRatingsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
||||
门店评价
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="门店评价" settings={settingsButton} />
|
||||
<Form
|
||||
form={form}
|
||||
layout="inline"
|
||||
|
||||
@@ -21,6 +21,7 @@ import { request, type Paginated } from '../lib/api';
|
||||
import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -237,10 +238,7 @@ export default function StoreWithdrawalsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
||||
门店提现审
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="门店提现审" settings={settingsButton} />
|
||||
{overdueSummary ? (
|
||||
<Typography.Paragraph type="secondary">
|
||||
待审 {overdueSummary.pendingCount} 笔
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Space direction="vertical" size={0}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>门店</Typography.Title>
|
||||
<Typography.Text type="secondary">共 {data?.total ?? 0} 家门店(含合伙人录入)</Typography.Text>
|
||||
</Space>
|
||||
<Space>
|
||||
{settingsButton}
|
||||
<AdminListHeader
|
||||
title="门店"
|
||||
settings={settingsButton}
|
||||
description={`共 ${data?.total ?? 0} 家门店(含合伙人录入)`}
|
||||
actions={
|
||||
<Button type="primary" onClick={openCreateModal}>新建门店</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="name" label="名称"><Input allowClear /></Form.Item>
|
||||
<Form.Item name="phone" label="电话"><Input allowClear /></Form.Item>
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
||||
白名单管理
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="白名单管理" settings={settingsButton} />
|
||||
|
||||
<Card size="small" loading={mockLoading} style={{ marginBottom: 16 }}>
|
||||
<Typography.Text type="secondary" style={{ display: 'block', marginBottom: 12 }}>
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>第三方日志</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="第三方日志" settings={settingsButton} />
|
||||
<Form
|
||||
layout="inline"
|
||||
style={{ marginBottom: 16 }}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { AdminCellLine } from '../components/AdminCellLine';
|
||||
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';
|
||||
|
||||
|
||||
@@ -120,8 +121,7 @@ export default function UserLogsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>用户日志</Typography.Title>
|
||||
{settingsButton}
|
||||
<AdminListHeader title="用户日志" settings={settingsButton} />
|
||||
<Segmented
|
||||
style={{ marginBottom: 16 }}
|
||||
options={USER_LOG_CATEGORY_OPTIONS.map((o) => ({ value: o.value, label: o.label }))}
|
||||
|
||||
@@ -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,11 +482,11 @@ export default function UsersPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>用户监控</Typography.Title>
|
||||
<Space>
|
||||
{settingsButton}
|
||||
{canDeleteUsers ? (
|
||||
<AdminListHeader
|
||||
title="用户监控"
|
||||
settings={settingsButton}
|
||||
actions={
|
||||
canDeleteUsers ? (
|
||||
<Button
|
||||
danger
|
||||
disabled={!selectedRowKeys.length}
|
||||
@@ -493,9 +494,9 @@ export default function UsersPage() {
|
||||
>
|
||||
批量删除{selectedRowKeys.length ? ` (${selectedRowKeys.length})` : ''}
|
||||
</Button>
|
||||
) : null}
|
||||
</Space>
|
||||
</Space>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={() => { setPage(1); void load(); }}>
|
||||
<Form.Item name="phone" label="手机号">
|
||||
<Input placeholder="模糊搜索" allowClear />
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>微信绑定总览</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Paragraph type="secondary">
|
||||
按 unionId 聚合展示已绑定微信的 C 端用户、门店账号、合伙人账号与 HQ 账号;无 unionId 时按单账号分组。
|
||||
</Typography.Paragraph>
|
||||
<AdminListHeader
|
||||
title="微信绑定总览"
|
||||
settings={settingsButton}
|
||||
description="按 unionId 聚合展示已绑定微信的 C 端用户、门店账号、合伙人账号与 HQ 账号;无 unionId 时按单账号分组。"
|
||||
/>
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { request } from '../lib/api';
|
||||
import { fmtTime } from '../lib/constants';
|
||||
import type { WecomBotLogDto } from '@dukang/shared-types';
|
||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||
import { AdminListHeader } from '../components/AdminListHeader';
|
||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||
|
||||
|
||||
@@ -64,11 +65,11 @@ export default function WecomBotLogsPage() {
|
||||
return (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<Typography.Title level={4}>企微机器人日志</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Paragraph type="secondary">
|
||||
记录机器人在企微内的查询与审批操作,含权限点、耗时与成败。
|
||||
</Typography.Paragraph>
|
||||
<AdminListHeader
|
||||
title="企微机器人日志"
|
||||
settings={settingsButton}
|
||||
description="记录机器人在企微内的查询与审批操作,含权限点、耗时与成败。"
|
||||
/>
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
|
||||
@@ -35,6 +35,7 @@ import { fmtTime } from '../lib/constants';
|
||||
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';
|
||||
|
||||
|
||||
@@ -272,9 +273,15 @@ function PushRoutesTab() {
|
||||
return (
|
||||
<>
|
||||
{settingsModal}
|
||||
<Typography.Paragraph type="secondary">
|
||||
配置群机器人 Webhook:按推送条件订阅业务通知 / 告警。运行时不再读取 .env 中的 Webhook URL。
|
||||
</Typography.Paragraph>
|
||||
<AdminListHeader
|
||||
settings={settingsButton}
|
||||
description="配置群机器人 Webhook:按推送条件订阅业务通知 / 告警。运行时不再读取 .env 中的 Webhook URL。"
|
||||
actions={
|
||||
<Button type="primary" onClick={openCreate}>
|
||||
新建推送
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
<Form
|
||||
form={filterForm}
|
||||
@@ -309,10 +316,6 @@ function PushRoutesTab() {
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
<Button type="primary" onClick={openCreate}>
|
||||
新建推送
|
||||
</Button>
|
||||
{settingsButton}
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{settingsModal}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
marginBottom: 16,
|
||||
gap: 16,
|
||||
}}
|
||||
>
|
||||
<Space direction="vertical" size={0}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
酒厂对账单
|
||||
</Typography.Title>
|
||||
{settingsButton}
|
||||
<Typography.Text type="secondary">
|
||||
T+3:每日 8:00 汇总 3 天前(自然日)已完成的同城/跨城订单(实付 × {ratePct}%);未打款红色、已打款绿色、应付为 0
|
||||
无需打款(灰),可展开订单明细
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
{canEditWineryBank ? (
|
||||
<AdminListHeader
|
||||
title="酒厂对账单"
|
||||
settings={settingsButton}
|
||||
description={`T+3:每日 8:00 汇总 3 天前(自然日)已完成的同城/跨城订单(实付 × ${ratePct}%);未打款红色、已打款绿色、应付为 0 无需打款(灰),可展开订单明细`}
|
||||
actions={
|
||||
canEditWineryBank ? (
|
||||
<Button type="default" onClick={() => void openBankModal()}>
|
||||
酒厂银行账户信息配置
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
|
||||
{summary && (
|
||||
<Card size="small" style={{ marginBottom: 16 }}>
|
||||
|
||||
@@ -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,6 +85,9 @@ export default function PromoCodeUsersPage() {
|
||||
const { columns, settingsButton, settingsModal } = useAdminListColumns('promo-code-users', baseColumns, { page, pageSize });
|
||||
|
||||
return (
|
||||
<>
|
||||
{settingsModal}
|
||||
<AdminListHeader settings={settingsButton} />
|
||||
<Table
|
||||
rowKey="id"
|
||||
className="admin-table-nowrap"
|
||||
@@ -99,5 +103,6 @@ export default function PromoCodeUsersPage() {
|
||||
onChange: (p, ps) => { setPage(p); setPageSize(ps); },
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@
|
||||
|
||||
**HQ 订单导出**(`admin-web` 订单监控):
|
||||
|
||||
- 支持按筛选(下单日期、状态、类型、城市、配送、收货手机等)或勾选订单导出
|
||||
- 支持按筛选(下单日期、状态可多选、类型、城市、配送、收货手机等)或勾选订单导出
|
||||
- 格式:Excel(`.xlsx`)/ PDF(`.pdf`)
|
||||
- 单次上限 5000 条;按筛选且未指定日期时默认近 30 天
|
||||
- 权限:`orders`;操作审计 `ORDER_EXPORT`
|
||||
|
||||
@@ -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:开发计划任务导出;核销/财务银行信息与打款凭证;工单图片入任务;运营去调试;门店审核全屏 |
|
||||
|
||||
@@ -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 数组均可)。列表筛选为多选;空 = 全部。单值旧链接仍可用。
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'])
|
||||
|
||||
Reference in New Issue
Block a user