@@ -74,6 +74,50 @@ body.admin-col-resizing * {
|
|||||||
user-select: none !important;
|
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),
|
.admin-layout .ant-table-cell:has(.ant-btn),
|
||||||
.ant-drawer .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]);
|
}, [configured, serialCol, pref?.widths, localWidths, persistWidth]);
|
||||||
|
|
||||||
const settingsButton = (
|
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>
|
</Button>
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
const settingsModal = (
|
const settingsModal = (
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { COUPON_STATUS_LABELS, DELIVERY_TYPE_LABELS, fmtTime } from '../lib/cons
|
|||||||
import RedeemRecordDetailDescriptions from '../components/RedeemRecordDetailDescriptions';
|
import RedeemRecordDetailDescriptions from '../components/RedeemRecordDetailDescriptions';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
type CouponOrder = {
|
type CouponOrder = {
|
||||||
@@ -248,17 +249,15 @@ export default function BenefitCouponsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="好客权益券"
|
||||||
好客权益券
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
actions={
|
||||||
<Space>
|
|
||||||
{settingsButton}
|
|
||||||
<Button type="primary" onClick={() => setGrantOpen(true)}>
|
<Button type="primary" onClick={() => setGrantOpen(true)}>
|
||||||
手动发放
|
手动发放
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
}
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||||
<Form.Item name="couponNo" label="券号">
|
<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 { LEDGER_TYPE_LABELS, fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
|
|
||||||
|
|
||||||
type Row = {
|
type Row = {
|
||||||
@@ -41,8 +42,7 @@ export default function BenefitLedgersPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>权益流水</Typography.Title>
|
<AdminListHeader title="权益流水" settings={settingsButton} />
|
||||||
{settingsButton}
|
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<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="userId" label="用户ID"><Input allowClear /></Form.Item>
|
||||||
<Form.Item name="couponId" 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 CityPartnersPanel from '../components/CityPartnersPanel';
|
||||||
import ChinaProvinceCityCascader from '../components/ChinaProvinceCityCascader';
|
import ChinaProvinceCityCascader from '../components/ChinaProvinceCityCascader';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -326,11 +327,11 @@ export default function CitiesPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>城市</Typography.Title>
|
title="城市"
|
||||||
{settingsButton}
|
settings={settingsButton}
|
||||||
<Button type="primary" onClick={openCreateModal}>新建城市</Button>
|
actions={<Button type="primary" onClick={openCreateModal}>新建城市</Button>}
|
||||||
</Space>
|
/>
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<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="name" label="城市"><Input allowClear /></Form.Item>
|
||||||
<Form.Item name="code" 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 CityDistrictMultiSelect from '../components/CityDistrictMultiSelect';
|
||||||
import PartnerSubAccountList, { type PartnerSubAccountRow } from '../components/PartnerSubAccountList';
|
import PartnerSubAccountList, { type PartnerSubAccountRow } from '../components/PartnerSubAccountList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -350,11 +351,10 @@ export default function CityPartnersPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="城市合伙人"
|
||||||
城市合伙人
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
actions={
|
||||||
{settingsButton}
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -372,7 +372,8 @@ export default function CityPartnersPage() {
|
|||||||
>
|
>
|
||||||
新建合伙人
|
新建合伙人
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Alert
|
<Alert
|
||||||
type="info"
|
type="info"
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
Typography,
|
|
||||||
message,
|
message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
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 { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -316,9 +316,10 @@ export default function CityWarehousesPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>仓库</Typography.Title>
|
title="仓库"
|
||||||
{settingsButton}
|
settings={settingsButton}
|
||||||
|
actions={
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -336,7 +337,8 @@ export default function CityWarehousesPage() {
|
|||||||
>
|
>
|
||||||
新增仓库
|
新增仓库
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Alert
|
<Alert
|
||||||
type="info"
|
type="info"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { request } from '../lib/api';
|
|||||||
import { ORDER_STATUS_LABELS, fmtTime } from '../lib/constants';
|
import { ORDER_STATUS_LABELS, fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -18,6 +19,8 @@ type Row = {
|
|||||||
trackingNo: string | null;
|
trackingNo: string | null;
|
||||||
providerOrderNo: string | null;
|
providerOrderNo: string | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
/** 当次应付物流费 */
|
||||||
|
logisticsFee?: number | null;
|
||||||
order?: {
|
order?: {
|
||||||
id: string;
|
id: string;
|
||||||
orderNo: string;
|
orderNo: string;
|
||||||
@@ -82,6 +85,12 @@ export default function DeliveriesPage() {
|
|||||||
},
|
},
|
||||||
{ title: 'provider', dataIndex: 'provider', width: 90 },
|
{ title: 'provider', dataIndex: 'provider', width: 90 },
|
||||||
{ title: '运单号', dataIndex: 'trackingNo', width: 140, render: (v) => v || '—' },
|
{ 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: 'providerOrderNo', width: 140, render: (v) => v || '—' },
|
||||||
{ title: '订单状态', dataIndex: ['order', 'status'], width: 100, render: (s) => ORDER_STATUS_LABELS[s] || s },
|
{ title: '订单状态', dataIndex: ['order', 'status'], width: 100, render: (s) => ORDER_STATUS_LABELS[s] || s },
|
||||||
{ title: '收货人', dataIndex: ['order', 'receiverName'], width: 90 },
|
{ title: '收货人', dataIndex: ['order', 'receiverName'], width: 90 },
|
||||||
@@ -107,8 +116,7 @@ export default function DeliveriesPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>快递/配送单</Typography.Title>
|
<AdminListHeader title="快递/配送单" settings={settingsButton} />
|
||||||
{settingsButton}
|
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<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="orderNo" label="订单号"><Input allowClear /></Form.Item>
|
||||||
<Form.Item name="provider" label="provider"><Input allowClear placeholder="MOCK" /></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 column={1} bordered size="small" style={{ marginBottom: 16 }}>
|
||||||
<Descriptions.Item label="订单">{detail.order?.orderNo}</Descriptions.Item>
|
<Descriptions.Item label="订单">{detail.order?.orderNo}</Descriptions.Item>
|
||||||
<Descriptions.Item label="收货">{detail.order?.receiverName} {detail.order?.receiverPhone}</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>
|
</Descriptions>
|
||||||
<Form form={editForm} layout="vertical">
|
<Form form={editForm} layout="vertical">
|
||||||
<Form.Item name="provider" label="provider" rules={[{ required: true }]}><Input /></Form.Item>
|
<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 { useAdminList } from '../lib/useAdminList';
|
||||||
import OssUpload from '../components/OssUpload';
|
import OssUpload from '../components/OssUpload';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -309,12 +310,11 @@ export default function DevPlanTasksPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="开发计划 · 任务列表"
|
||||||
开发计划 · 任务列表
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
actions={
|
||||||
{settingsButton}
|
<>
|
||||||
<Space wrap>
|
|
||||||
<Select
|
<Select
|
||||||
value={exportFormat}
|
value={exportFormat}
|
||||||
style={{ width: 120 }}
|
style={{ width: 120 }}
|
||||||
@@ -341,8 +341,9 @@ export default function DevPlanTasksPage() {
|
|||||||
<Button type="primary" onClick={openCreate}>
|
<Button type="primary" onClick={openCreate}>
|
||||||
新建任务
|
新建任务
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</>
|
||||||
</div>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
layout="inline"
|
layout="inline"
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
Typography,
|
|
||||||
message,
|
message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
@@ -23,6 +22,7 @@ import { request } from '../lib/api';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -168,15 +168,15 @@ export default function DevPlanVersionsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="开发计划 · 版本列表"
|
||||||
开发计划 · 版本列表
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
actions={
|
||||||
{settingsButton}
|
|
||||||
<Button type="primary" onClick={openCreate}>
|
<Button type="primary" onClick={openCreate}>
|
||||||
新建版本
|
新建版本
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
layout="inline"
|
layout="inline"
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
Typography,
|
|
||||||
message,
|
message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
@@ -29,6 +28,7 @@ import {
|
|||||||
import { request } from '../lib/api';
|
import { request } from '../lib/api';
|
||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -247,20 +247,16 @@ export default function FulfillmentProvidersPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<div>
|
title="仓配管理"
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
description="注册承运商接口凭证,并配置物流对账用的银行账户、结算方式与计价标准"
|
||||||
仓配管理
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
actions={
|
||||||
{settingsButton}
|
|
||||||
<Typography.Text type="secondary">
|
|
||||||
注册承运商接口凭证,并配置物流对账用的银行账户、结算方式与计价标准
|
|
||||||
</Typography.Text>
|
|
||||||
</div>
|
|
||||||
<Button type="primary" onClick={openCreate}>
|
<Button type="primary" onClick={openCreate}>
|
||||||
注册承运商
|
注册承运商
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Alert
|
<Alert
|
||||||
type="info"
|
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 { ACCOUNT_STATUS_LABELS, ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -145,10 +146,11 @@ export default function HqAccountsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>HQ 账户</Typography.Title>
|
title="HQ 账户"
|
||||||
{settingsButton}
|
settings={settingsButton}
|
||||||
{isSuperAdmin && (
|
actions={
|
||||||
|
isSuperAdmin ? (
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -159,8 +161,9 @@ export default function HqAccountsPage() {
|
|||||||
>
|
>
|
||||||
新建账户
|
新建账户
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
) : null
|
||||||
</Space>
|
}
|
||||||
|
/>
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<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="phone" label="手机"><Input allowClear /></Form.Item>
|
||||||
<Form.Item name="adminRole" label="角色">
|
<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 { HQ_OPERATION_ACTION_OPTIONS, resolveHqOperationLabel } from '../lib/hq-log';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -109,11 +110,11 @@ export default function HqLogsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>HQ 操作日志</Typography.Title>
|
<AdminListHeader
|
||||||
{settingsButton}
|
title="HQ 操作日志"
|
||||||
<Typography.Paragraph type="secondary">
|
settings={settingsButton}
|
||||||
记录总部后台写操作(开城、订单、用户、权限、合伙人等),仅追加不删除。
|
description="记录总部后台写操作(开城、订单、用户、权限、合伙人等),仅追加不删除。"
|
||||||
</Typography.Paragraph>
|
/>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
Typography,
|
|
||||||
Upload,
|
Upload,
|
||||||
message,
|
message,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
@@ -31,6 +30,7 @@ import { fmtTime } from '../lib/constants';
|
|||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { uploadFileToOss } from '../lib/upload';
|
import { uploadFileToOss } from '../lib/upload';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -241,18 +241,10 @@ export default function InvoicesPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<div
|
<AdminListHeader
|
||||||
style={{
|
title="发票管理"
|
||||||
display: 'flex',
|
settings={settingsButton}
|
||||||
justifyContent: 'space-between',
|
actions={
|
||||||
alignItems: 'center',
|
|
||||||
marginBottom: 16,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
|
||||||
发票管理
|
|
||||||
</Typography.Title>
|
|
||||||
{settingsButton}
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -266,7 +258,8 @@ export default function InvoicesPage() {
|
|||||||
>
|
>
|
||||||
创建发票申请
|
创建发票申请
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
}
|
||||||
|
/>
|
||||||
<Form
|
<Form
|
||||||
form={filterForm}
|
form={filterForm}
|
||||||
layout="inline"
|
layout="inline"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { fmtTime } from '../lib/constants';
|
|||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { uploadFileToOss } from '../lib/upload';
|
import { uploadFileToOss } from '../lib/upload';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -264,15 +265,11 @@ export default function KnowledgeBasesPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16 }} wrap>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="知识库"
|
||||||
知识库
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
description="支持粘贴文本或上传 .txt/.md;可绑定到企微机器人供 AI 检索"
|
||||||
{settingsButton}
|
/>
|
||||||
<Typography.Text type="secondary">
|
|
||||||
支持粘贴文本或上传 .txt/.md;可绑定到企微机器人供 AI 检索
|
|
||||||
</Typography.Text>
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
form={filterForm}
|
form={filterForm}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { request } from '../lib/api';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -271,15 +272,11 @@ export default function LlmConfigsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16 }} wrap>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="语言模型配置"
|
||||||
语言模型配置
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
description="非超管仅可见自己创建的配置,且创建后只能改是否生效"
|
||||||
{settingsButton}
|
/>
|
||||||
<Typography.Text type="secondary">
|
|
||||||
非超管仅可见自己创建的配置,且创建后只能改是否生效
|
|
||||||
</Typography.Text>
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
form={filterForm}
|
form={filterForm}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { downloadExcelCsv } from '../lib/exportExcel';
|
|||||||
import { request } from '../lib/api';
|
import { request } from '../lib/api';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
type BillRow = {
|
type BillRow = {
|
||||||
@@ -405,15 +406,11 @@ export default function LogisticsBillsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space direction="vertical" size={0} style={{ marginBottom: 16 }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="物流对账"
|
||||||
物流对账
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
description="按快递承运商汇总月度物流费;计价与银行账户在「仓配管理」配置。前期充值扣款,后期可切挂账月结。"
|
||||||
{settingsButton}
|
/>
|
||||||
<Typography.Text type="secondary">
|
|
||||||
按快递承运商汇总月度物流费;计价与银行账户在「仓配管理」配置。前期充值扣款,后期可切挂账月结。
|
|
||||||
</Typography.Text>
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
items={[
|
items={[
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import dayjs, { type Dayjs } from 'dayjs';
|
|||||||
import { ORDER_TYPE_LABELS } from '@dukang/shared-types';
|
import { ORDER_TYPE_LABELS } from '@dukang/shared-types';
|
||||||
import { request, type AdminOrderItem, type AdminOrderRow, type HqProfile, type Paginated } from '../lib/api';
|
import { request, type AdminOrderItem, type AdminOrderRow, type HqProfile, type Paginated } from '../lib/api';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
import { downloadBase64File } from '../lib/exportExcel';
|
import { downloadBase64File } from '../lib/exportExcel';
|
||||||
import {
|
import {
|
||||||
@@ -144,7 +145,7 @@ type OrderExportFormat = 'xlsx' | 'pdf';
|
|||||||
|
|
||||||
type OrderExportFilters = {
|
type OrderExportFilters = {
|
||||||
orderNo?: string;
|
orderNo?: string;
|
||||||
status?: string;
|
status?: string | string[];
|
||||||
orderType?: string;
|
orderType?: string;
|
||||||
cityId?: string;
|
cityId?: string;
|
||||||
receiverPhone?: string;
|
receiverPhone?: string;
|
||||||
@@ -219,6 +220,12 @@ function formatBenefitBrief(row: AdminOrderRow) {
|
|||||||
return '—';
|
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(
|
function buildExportPayload(
|
||||||
scope: OrderExportScope,
|
scope: OrderExportScope,
|
||||||
format: OrderExportFormat,
|
format: OrderExportFormat,
|
||||||
@@ -231,7 +238,8 @@ function buildExportPayload(
|
|||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
if (filters.orderNo) payload.orderNo = filters.orderNo;
|
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.orderType) payload.orderType = filters.orderType;
|
||||||
if (filters.cityId) payload.cityId = filters.cityId;
|
if (filters.cityId) payload.cityId = filters.cityId;
|
||||||
if (filters.receiverPhone) payload.receiverPhone = filters.receiverPhone;
|
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) });
|
const qs = new URLSearchParams({ page: String(page), pageSize: String(pageSize) });
|
||||||
if (initialOrderNo) qs.set('orderNo', initialOrderNo);
|
if (initialOrderNo) qs.set('orderNo', initialOrderNo);
|
||||||
if (values.orderNo) qs.set('orderNo', values.orderNo);
|
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.orderType) qs.set('orderType', values.orderType);
|
||||||
if (values.cityId) qs.set('cityId', values.cityId);
|
if (values.cityId) qs.set('cityId', values.cityId);
|
||||||
if (values.receiverPhone) qs.set('receiverPhone', values.receiverPhone);
|
if (values.receiverPhone) qs.set('receiverPhone', values.receiverPhone);
|
||||||
@@ -733,18 +741,20 @@ export default function OrdersPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 20, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>订单监控</Typography.Title>
|
title="订单监控"
|
||||||
<Space size={12}>
|
settings={settingsButton}
|
||||||
{settingsButton}
|
actions={
|
||||||
|
<>
|
||||||
<Button onClick={() => window.open('/orders/big-screen', 'dukang-big-screen')}>大屏</Button>
|
<Button onClick={() => window.open('/orders/big-screen', 'dukang-big-screen')}>大屏</Button>
|
||||||
{canProxyOrder ? (
|
{canProxyOrder ? (
|
||||||
<Button type="primary" onClick={() => setProxyOpen(true)}>
|
<Button type="primary" onClick={() => setProxyOpen(true)}>
|
||||||
代下单
|
代下单
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
</Space>
|
</>
|
||||||
</Space>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{!canDeleteOrders && profile ? (
|
{!canDeleteOrders && profile ? (
|
||||||
<Alert
|
<Alert
|
||||||
@@ -782,10 +792,12 @@ export default function OrdersPage() {
|
|||||||
</Space>
|
</Space>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</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 }}>
|
<Form.Item name="status" label="状态" style={{ marginBottom: 12 }}>
|
||||||
<Select
|
<Select
|
||||||
|
mode="multiple"
|
||||||
allowClear
|
allowClear
|
||||||
|
maxTagCount="responsive"
|
||||||
placeholder="全部"
|
placeholder="全部"
|
||||||
options={Object.entries(ORDER_STATUS_LABELS).map(([value, label]) => ({ value, label }))}
|
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 { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
import { ACCOUNT_STATUS_LABELS, ADMIN_OPTIONS_PAGE_SIZE, ORDER_STATUS_LABELS, PARTNER_BILL_STATUS_LABELS, fmtTime } from '../lib/constants';
|
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 { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
|
|
||||||
|
|
||||||
type PartnerOption = { id: string; companyName: string };
|
type PartnerOption = { id: string; companyName: string };
|
||||||
@@ -261,15 +262,11 @@ export default function PartnerAccountsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<div>
|
title="合伙人子账号"
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>合伙人子账号</Typography.Title>
|
settings={settingsButton}
|
||||||
{settingsButton}
|
description="主账号在「开城合伙人」创建;此处仅管理子账号树与权限"
|
||||||
<Typography.Text type="secondary">
|
/>
|
||||||
主账号在「开城合伙人」创建;此处仅管理子账号树与权限
|
|
||||||
</Typography.Text>
|
|
||||||
</div>
|
|
||||||
</Space>
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="inline"
|
layout="inline"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
|||||||
import { downloadExcelCsv } from '../lib/exportExcel';
|
import { downloadExcelCsv } from '../lib/exportExcel';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -349,15 +350,11 @@ export default function PartnerBillsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space direction="vertical" size={0} style={{ marginBottom: 16 }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="合伙人账单"
|
||||||
合伙人账单
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
description="每月 1 日 8:00 自动生成上月账单(待审核)→ 发送合伙人确认 → 未打款 → 已打款"
|
||||||
{settingsButton}
|
/>
|
||||||
<Typography.Text type="secondary">
|
|
||||||
每月 1 日 8:00 自动生成上月账单(待审核)→ 发送合伙人确认 → 未打款 → 已打款
|
|
||||||
</Typography.Text>
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
{summary && (
|
{summary && (
|
||||||
<Card size="small" style={{ marginBottom: 16 }}>
|
<Card size="small" style={{ marginBottom: 16 }}>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { AdminCellLine } from '../components/AdminCellLine';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -156,10 +157,7 @@ export default function PartnerLogsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4} style={{ marginBottom: 16 }}>
|
<AdminListHeader title="合伙人日志" settings={settingsButton} />
|
||||||
合伙人日志
|
|
||||||
</Typography.Title>
|
|
||||||
{settingsButton}
|
|
||||||
<Segmented
|
<Segmented
|
||||||
options={PARTNER_LOG_CATEGORY_OPTIONS.map((o) => ({ value: o.value, label: o.label }))}
|
options={PARTNER_LOG_CATEGORY_OPTIONS.map((o) => ({ value: o.value, label: o.label }))}
|
||||||
value={category}
|
value={category}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
|||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import CityDistrictMultiSelect from '../components/CityDistrictMultiSelect';
|
import CityDistrictMultiSelect from '../components/CityDistrictMultiSelect';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -218,13 +219,15 @@ export default function PartnersPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>开城合伙人</Typography.Title>
|
title="开城合伙人"
|
||||||
{settingsButton}
|
settings={settingsButton}
|
||||||
|
actions={
|
||||||
<Button type="primary" onClick={() => { setCreateOpen(true); createForm.resetFields(); setCreateScopeType(CityPartnerScopeType.CITY_WIDE); }}>
|
<Button type="primary" onClick={() => { setCreateOpen(true); createForm.resetFields(); setCreateScopeType(CityPartnerScopeType.CITY_WIDE); }}>
|
||||||
新建城市合伙人
|
新建城市合伙人
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
}
|
||||||
|
/>
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<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="companyName" label="公司"><Input allowClear /></Form.Item>
|
||||||
<Form.Item name="contactPhone" 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 { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -146,8 +147,7 @@ export default function PendingRedeemPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>待处理核销(弱网兜底)</Typography.Title>
|
<AdminListHeader title="待处理核销(弱网兜底)" settings={settingsButton} />
|
||||||
{settingsButton}
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="inline"
|
layout="inline"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import ProductDetailTemplatePicker from '../components/ProductDetailTemplatePick
|
|||||||
import ProductSpecsEditor from '../components/ProductSpecsEditor';
|
import ProductSpecsEditor from '../components/ProductSpecsEditor';
|
||||||
import type { FormInstance } from 'antd/es/form';
|
import type { FormInstance } from 'antd/es/form';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -510,11 +511,11 @@ export default function ProductsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>商品管理</Typography.Title>
|
title="商品管理"
|
||||||
{settingsButton}
|
settings={settingsButton}
|
||||||
<Button type="primary" onClick={() => setCreateOpen(true)}>新建商品</Button>
|
actions={<Button type="primary" onClick={() => setCreateOpen(true)}>新建商品</Button>}
|
||||||
</Space>
|
/>
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||||
<Form.Item name="name" label="名称">
|
<Form.Item name="name" label="名称">
|
||||||
<Input allowClear placeholder="名称 / SKU / 69 码" style={{ width: 200 }} />
|
<Input allowClear placeholder="名称 / SKU / 69 码" style={{ width: 200 }} />
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { request } from '../lib/api';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -175,11 +176,11 @@ export default function PromoCodesPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>推广码管理</Typography.Title>
|
title="推广码管理"
|
||||||
{settingsButton}
|
settings={settingsButton}
|
||||||
<Button type="primary" onClick={() => setCreateOpen(true)}>创建推广码</Button>
|
actions={<Button type="primary" onClick={() => setCreateOpen(true)}>创建推广码</Button>}
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
form={filterForm}
|
form={filterForm}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { request } from '../lib/api';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -141,8 +142,7 @@ export default function RedeemRecordsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>核销记录</Typography.Title>
|
<AdminListHeader title="核销记录" settings={settingsButton} />
|
||||||
{settingsButton}
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="inline"
|
layout="inline"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from '../lib/constants';
|
} from '../lib/constants';
|
||||||
import OssUpload from '../components/OssUpload';
|
import OssUpload from '../components/OssUpload';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
|
|
||||||
|
|
||||||
type Row = {
|
type Row = {
|
||||||
@@ -121,15 +122,11 @@ export default function ResourcesPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="OSS 资源库"
|
||||||
OSS 资源库
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
actions={<Button type="primary" onClick={() => setCreateOpen(true)}>登记资源</Button>}
|
||||||
{settingsButton}
|
/>
|
||||||
<Button type="primary" onClick={() => setCreateOpen(true)}>
|
|
||||||
登记资源
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="inline"
|
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 { ACCOUNT_STATUS_LABELS, ADMIN_OPTIONS_PAGE_SIZE, STORE_STATUS_LABELS, fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -153,14 +154,11 @@ export default function StoreAccountsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Space direction="vertical" size={0}>
|
title="门店账户"
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>门店账户</Typography.Title>
|
settings={settingsButton}
|
||||||
{settingsButton}
|
description="主账号可绑定多家门店;收款信息挂在主账号;「新建账户」用于补录无主账号门店"
|
||||||
<Typography.Text type="secondary">
|
actions={
|
||||||
主账号可绑定多家门店;收款信息挂在主账号;「新建账户」用于补录无主账号门店
|
|
||||||
</Typography.Text>
|
|
||||||
</Space>
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -170,7 +168,8 @@ export default function StoreAccountsPage() {
|
|||||||
>
|
>
|
||||||
新建账户
|
新建账户
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
}
|
||||||
|
/>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="inline"
|
layout="inline"
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
|||||||
import { downloadExcelCsv } from '../lib/exportExcel';
|
import { downloadExcelCsv } from '../lib/exportExcel';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -381,21 +382,23 @@ export default function StoreBillsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space direction="vertical" size={0} style={{ marginBottom: 16 }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
title="门店账单"
|
||||||
门店账单
|
settings={settingsButton}
|
||||||
</Typography.Title>
|
description={
|
||||||
{settingsButton}
|
<>
|
||||||
<Typography.Text type="secondary">
|
|
||||||
含 T+1 自动出账与门店手动提现;按类型筛选,详情与审核动作沿用原接口。
|
含 T+1 自动出账与门店手动提现;按类型筛选,详情与审核动作沿用原接口。
|
||||||
「已打款」= T+1 出账终态,「已结算」= 手动提现终态。
|
「已打款」= T+1 出账终态,「已结算」= 手动提现终态。
|
||||||
</Typography.Text>
|
|
||||||
{overdueSummary && overdueSummary.overdueCount > 0 ? (
|
{overdueSummary && overdueSummary.overdueCount > 0 ? (
|
||||||
|
<div>
|
||||||
<Typography.Text type="danger">
|
<Typography.Text type="danger">
|
||||||
手动提现超时未审 {overdueSummary.overdueCount} 笔(待审共 {overdueSummary.pendingCount})
|
手动提现超时未审 {overdueSummary.overdueCount} 笔(待审共 {overdueSummary.pendingCount})
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</Space>
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{summary && (
|
{summary && (
|
||||||
<Card size="small" style={{ marginBottom: 16 }}>
|
<Card size="small" style={{ marginBottom: 16 }}>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
import { request, type HqProfile } from '../lib/api';
|
import { request, type HqProfile } from '../lib/api';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -179,13 +180,12 @@ export default function StoreCategoriesPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Space direction="vertical" size={0}>
|
title="门店分类"
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>门店分类</Typography.Title>
|
settings={settingsButton}
|
||||||
{settingsButton}
|
description="两级分类:一级(餐饮/住宿/娱乐)→ 二级业态,供合伙人开店选择"
|
||||||
<Typography.Text type="secondary">两级分类:一级(餐饮/住宿/娱乐)→ 二级业态,供合伙人开店选择</Typography.Text>
|
actions={
|
||||||
</Space>
|
<>
|
||||||
<Space>
|
|
||||||
{canDelete ? (
|
{canDelete ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -198,8 +198,9 @@ export default function StoreCategoriesPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
<Button type="primary" onClick={() => openCreate()}>新增一级</Button>
|
<Button type="primary" onClick={() => openCreate()}>新增一级</Button>
|
||||||
</Space>
|
</>
|
||||||
</Space>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { AdminCellLine } from '../components/AdminCellLine';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -151,11 +152,11 @@ export default function StoreLogsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>商户日志</Typography.Title>
|
<AdminListHeader
|
||||||
{settingsButton}
|
title="商户日志"
|
||||||
<Typography.Paragraph type="secondary" style={{ marginTop: -8 }}>
|
settings={settingsButton}
|
||||||
门店登录、微信授权、核销、打款与营业状态等操作记录;历史核销/打款数据来自业务表归档。
|
description="门店登录、微信授权、核销、打款与营业状态等操作记录;历史核销/打款数据来自业务表归档。"
|
||||||
</Typography.Paragraph>
|
/>
|
||||||
<Segmented
|
<Segmented
|
||||||
style={{ marginBottom: 16 }}
|
style={{ marginBottom: 16 }}
|
||||||
options={STORE_LOG_CATEGORY_OPTIONS.map((o) => ({ value: o.value, label: o.label }))}
|
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 { useAdminList } from '../lib/useAdminList';
|
||||||
import OssUpload from '../components/OssUpload';
|
import OssUpload from '../components/OssUpload';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -96,11 +97,11 @@ export default function StoreMediaPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>门店资源</Typography.Title>
|
title="门店资源"
|
||||||
{settingsButton}
|
settings={settingsButton}
|
||||||
<Button type="primary" onClick={() => { void loadStores(); setCreateOpen(true); }}>新增资源</Button>
|
actions={<Button type="primary" onClick={() => { void loadStores(); setCreateOpen(true); }}>新增资源</Button>}
|
||||||
</Space>
|
/>
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<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="storeId" label="门店ID"><Input allowClear /></Form.Item>
|
||||||
<Form.Item name="mediaType" label="类型">
|
<Form.Item name="mediaType" label="类型">
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { notifyPackageAuditChanged } from '../lib/admin-events';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import StorePackageAuditPanel from '../components/StorePackageAuditPanel';
|
import StorePackageAuditPanel from '../components/StorePackageAuditPanel';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
const HQ_PACKAGE_STATUS_LABELS: Record<StorePackageChangeStatus, string> = {
|
const HQ_PACKAGE_STATUS_LABELS: Record<StorePackageChangeStatus, string> = {
|
||||||
@@ -545,8 +546,7 @@ export default function StorePackageAuditsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>审核通知</Typography.Title>
|
<AdminListHeader title="审核通知" settings={settingsButton} />
|
||||||
{settingsButton}
|
|
||||||
<Tabs
|
<Tabs
|
||||||
activeKey={activeTab}
|
activeKey={activeTab}
|
||||||
onChange={setActiveTab}
|
onChange={setActiveTab}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { fmtTime, ADMIN_OPTIONS_PAGE_SIZE } from '../lib/constants';
|
|||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { request, type Paginated } from '../lib/api';
|
import { request, type Paginated } from '../lib/api';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
|
|
||||||
|
|
||||||
type Row = {
|
type Row = {
|
||||||
@@ -69,10 +70,7 @@ export default function StoreRatingsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
<AdminListHeader title="门店评价" settings={settingsButton} />
|
||||||
门店评价
|
|
||||||
</Typography.Title>
|
|
||||||
{settingsButton}
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="inline"
|
layout="inline"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { request, type Paginated } from '../lib/api';
|
|||||||
import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -237,10 +238,7 @@ export default function StoreWithdrawalsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
<AdminListHeader title="门店提现审" settings={settingsButton} />
|
||||||
门店提现审
|
|
||||||
</Typography.Title>
|
|
||||||
{settingsButton}
|
|
||||||
{overdueSummary ? (
|
{overdueSummary ? (
|
||||||
<Typography.Paragraph type="secondary">
|
<Typography.Paragraph type="secondary">
|
||||||
待审 {overdueSummary.pendingCount} 笔
|
待审 {overdueSummary.pendingCount} 笔
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import {
|
|||||||
} from '../lib/storeCreate';
|
} from '../lib/storeCreate';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
import { resolveRegionBinding } from '../lib/china-region';
|
import { resolveRegionBinding } from '../lib/china-region';
|
||||||
import ChinaRegionCascader from '../components/ChinaRegionCascader';
|
import ChinaRegionCascader from '../components/ChinaRegionCascader';
|
||||||
@@ -873,16 +874,14 @@ export default function StoresPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Space direction="vertical" size={0}>
|
title="门店"
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>门店</Typography.Title>
|
settings={settingsButton}
|
||||||
<Typography.Text type="secondary">共 {data?.total ?? 0} 家门店(含合伙人录入)</Typography.Text>
|
description={`共 ${data?.total ?? 0} 家门店(含合伙人录入)`}
|
||||||
</Space>
|
actions={
|
||||||
<Space>
|
|
||||||
{settingsButton}
|
|
||||||
<Button type="primary" onClick={openCreateModal}>新建门店</Button>
|
<Button type="primary" onClick={openCreateModal}>新建门店</Button>
|
||||||
</Space>
|
}
|
||||||
</Space>
|
/>
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
<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="name" label="名称"><Input allowClear /></Form.Item>
|
||||||
<Form.Item name="phone" 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 { request, type Paginated } from '../lib/api';
|
||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
const MOCK_KEYS = ['MOCK_SMS', 'MOCK_WECHAT', 'MOCK_PAY'] as const;
|
const MOCK_KEYS = ['MOCK_SMS', 'MOCK_WECHAT', 'MOCK_PAY'] as const;
|
||||||
@@ -410,10 +411,7 @@ export default function TestWhitelistPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
<AdminListHeader title="白名单管理" settings={settingsButton} />
|
||||||
白名单管理
|
|
||||||
</Typography.Title>
|
|
||||||
{settingsButton}
|
|
||||||
|
|
||||||
<Card size="small" loading={mockLoading} style={{ marginBottom: 16 }}>
|
<Card size="small" loading={mockLoading} style={{ marginBottom: 16 }}>
|
||||||
<Typography.Text type="secondary" style={{ display: 'block', marginBottom: 12 }}>
|
<Typography.Text type="secondary" style={{ display: 'block', marginBottom: 12 }}>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { request } from '../lib/api';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -137,8 +138,7 @@ export default function ThirdPartyLogsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>第三方日志</Typography.Title>
|
<AdminListHeader title="第三方日志" settings={settingsButton} />
|
||||||
{settingsButton}
|
|
||||||
<Form
|
<Form
|
||||||
layout="inline"
|
layout="inline"
|
||||||
style={{ marginBottom: 16 }}
|
style={{ marginBottom: 16 }}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { AdminCellLine } from '../components/AdminCellLine';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -120,8 +121,7 @@ export default function UserLogsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>用户日志</Typography.Title>
|
<AdminListHeader title="用户日志" settings={settingsButton} />
|
||||||
{settingsButton}
|
|
||||||
<Segmented
|
<Segmented
|
||||||
style={{ marginBottom: 16 }}
|
style={{ marginBottom: 16 }}
|
||||||
options={USER_LOG_CATEGORY_OPTIONS.map((o) => ({ value: o.value, label: o.label }))}
|
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 { USER_SOURCE_TYPE_LABELS, resolveUserLogCategory, type UserSourceType } from '@dukang/shared-types';
|
||||||
import { request, type AdminUserRow, type HqProfile, type Paginated } from '../lib/api';
|
import { request, type AdminUserRow, type HqProfile, type Paginated } from '../lib/api';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
import { ORDER_STATUS_LABELS, fmtTime, maskPhone } from '../lib/constants';
|
import { ORDER_STATUS_LABELS, fmtTime, maskPhone } from '../lib/constants';
|
||||||
|
|
||||||
@@ -481,11 +482,11 @@ export default function UsersPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}>
|
<AdminListHeader
|
||||||
<Typography.Title level={4} style={{ margin: 0 }}>用户监控</Typography.Title>
|
title="用户监控"
|
||||||
<Space>
|
settings={settingsButton}
|
||||||
{settingsButton}
|
actions={
|
||||||
{canDeleteUsers ? (
|
canDeleteUsers ? (
|
||||||
<Button
|
<Button
|
||||||
danger
|
danger
|
||||||
disabled={!selectedRowKeys.length}
|
disabled={!selectedRowKeys.length}
|
||||||
@@ -493,9 +494,9 @@ export default function UsersPage() {
|
|||||||
>
|
>
|
||||||
批量删除{selectedRowKeys.length ? ` (${selectedRowKeys.length})` : ''}
|
批量删除{selectedRowKeys.length ? ` (${selectedRowKeys.length})` : ''}
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null
|
||||||
</Space>
|
}
|
||||||
</Space>
|
/>
|
||||||
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={() => { setPage(1); void load(); }}>
|
<Form form={form} layout="inline" style={{ marginBottom: 16 }} onFinish={() => { setPage(1); void load(); }}>
|
||||||
<Form.Item name="phone" label="手机号">
|
<Form.Item name="phone" label="手机号">
|
||||||
<Input placeholder="模糊搜索" allowClear />
|
<Input placeholder="模糊搜索" allowClear />
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { AdminCellLine } from '../components/AdminCellLine';
|
|||||||
import { fmtTime, maskPhone } from '../lib/constants';
|
import { fmtTime, maskPhone } from '../lib/constants';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -210,11 +211,11 @@ export default function WechatBindingsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>微信绑定总览</Typography.Title>
|
<AdminListHeader
|
||||||
{settingsButton}
|
title="微信绑定总览"
|
||||||
<Typography.Paragraph type="secondary">
|
settings={settingsButton}
|
||||||
按 unionId 聚合展示已绑定微信的 C 端用户、门店账号、合伙人账号与 HQ 账号;无 unionId 时按单账号分组。
|
description="按 unionId 聚合展示已绑定微信的 C 端用户、门店账号、合伙人账号与 HQ 账号;无 unionId 时按单账号分组。"
|
||||||
</Typography.Paragraph>
|
/>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { request } from '../lib/api';
|
|||||||
import { fmtTime } from '../lib/constants';
|
import { fmtTime } from '../lib/constants';
|
||||||
import type { WecomBotLogDto } from '@dukang/shared-types';
|
import type { WecomBotLogDto } from '@dukang/shared-types';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -64,11 +65,11 @@ export default function WecomBotLogsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Title level={4}>企微机器人日志</Typography.Title>
|
<AdminListHeader
|
||||||
{settingsButton}
|
title="企微机器人日志"
|
||||||
<Typography.Paragraph type="secondary">
|
settings={settingsButton}
|
||||||
记录机器人在企微内的查询与审批操作,含权限点、耗时与成败。
|
description="记录机器人在企微内的查询与审批操作,含权限点、耗时与成败。"
|
||||||
</Typography.Paragraph>
|
/>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { fmtTime } from '../lib/constants';
|
|||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import OssUpload from '../components/OssUpload';
|
import OssUpload from '../components/OssUpload';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -272,9 +273,15 @@ function PushRoutesTab() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<Typography.Paragraph type="secondary">
|
<AdminListHeader
|
||||||
配置群机器人 Webhook:按推送条件订阅业务通知 / 告警。运行时不再读取 .env 中的 Webhook URL。
|
settings={settingsButton}
|
||||||
</Typography.Paragraph>
|
description="配置群机器人 Webhook:按推送条件订阅业务通知 / 告警。运行时不再读取 .env 中的 Webhook URL。"
|
||||||
|
actions={
|
||||||
|
<Button type="primary" onClick={openCreate}>
|
||||||
|
新建推送
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
form={filterForm}
|
form={filterForm}
|
||||||
@@ -309,10 +316,6 @@ function PushRoutesTab() {
|
|||||||
>
|
>
|
||||||
重置
|
重置
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="primary" onClick={openCreate}>
|
|
||||||
新建推送
|
|
||||||
</Button>
|
|
||||||
{settingsButton}
|
|
||||||
</Space>
|
</Space>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { downloadExcelCsv } from '../lib/exportExcel';
|
|||||||
import { request, type HqProfile } from '../lib/api';
|
import { request, type HqProfile } from '../lib/api';
|
||||||
import { useAdminList } from '../lib/useAdminList';
|
import { useAdminList } from '../lib/useAdminList';
|
||||||
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../lib/useAdminListColumns';
|
||||||
|
import { AdminListHeader } from '../components/AdminListHeader';
|
||||||
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../components/AdminPrimaryLink';
|
||||||
|
|
||||||
|
|
||||||
@@ -305,31 +306,18 @@ export default function WineryBillsPage() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{settingsModal}
|
{settingsModal}
|
||||||
<div
|
<AdminListHeader
|
||||||
style={{
|
title="酒厂对账单"
|
||||||
display: 'flex',
|
settings={settingsButton}
|
||||||
justifyContent: 'space-between',
|
description={`T+3:每日 8:00 汇总 3 天前(自然日)已完成的同城/跨城订单(实付 × ${ratePct}%);未打款红色、已打款绿色、应付为 0 无需打款(灰),可展开订单明细`}
|
||||||
alignItems: 'flex-start',
|
actions={
|
||||||
marginBottom: 16,
|
canEditWineryBank ? (
|
||||||
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 ? (
|
|
||||||
<Button type="default" onClick={() => void openBankModal()}>
|
<Button type="default" onClick={() => void openBankModal()}>
|
||||||
酒厂银行账户信息配置
|
酒厂银行账户信息配置
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null
|
||||||
</div>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{summary && (
|
{summary && (
|
||||||
<Card size="small" style={{ marginBottom: 16 }}>
|
<Card size="small" style={{ marginBottom: 16 }}>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { useAdminList } from '../../lib/useAdminList';
|
|||||||
import type { PromoCodeDetailContext } from './PromoCodeDetailLayout';
|
import type { PromoCodeDetailContext } from './PromoCodeDetailLayout';
|
||||||
import { useAdminListColumns } from '../../lib/useAdminListColumns';
|
import { useAdminListColumns } from '../../lib/useAdminListColumns';
|
||||||
import { AdminPrimaryLink } from '../../components/AdminPrimaryLink';
|
import { AdminPrimaryLink } from '../../components/AdminPrimaryLink';
|
||||||
|
import { AdminListHeader } from '../../components/AdminListHeader';
|
||||||
|
|
||||||
|
|
||||||
export default function PromoCodeUsersPage() {
|
export default function PromoCodeUsersPage() {
|
||||||
@@ -84,6 +85,9 @@ export default function PromoCodeUsersPage() {
|
|||||||
const { columns, settingsButton, settingsModal } = useAdminListColumns('promo-code-users', baseColumns, { page, pageSize });
|
const { columns, settingsButton, settingsModal } = useAdminListColumns('promo-code-users', baseColumns, { page, pageSize });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{settingsModal}
|
||||||
|
<AdminListHeader settings={settingsButton} />
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
className="admin-table-nowrap"
|
className="admin-table-nowrap"
|
||||||
@@ -99,5 +103,6 @@ export default function PromoCodeUsersPage() {
|
|||||||
onChange: (p, ps) => { setPage(p); setPageSize(ps); },
|
onChange: (p, ps) => { setPage(p); setPageSize(ps); },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -84,7 +84,7 @@
|
|||||||
|
|
||||||
**HQ 订单导出**(`admin-web` 订单监控):
|
**HQ 订单导出**(`admin-web` 订单监控):
|
||||||
|
|
||||||
- 支持按筛选(下单日期、状态、类型、城市、配送、收货手机等)或勾选订单导出
|
- 支持按筛选(下单日期、状态可多选、类型、城市、配送、收货手机等)或勾选订单导出
|
||||||
- 格式:Excel(`.xlsx`)/ PDF(`.pdf`)
|
- 格式:Excel(`.xlsx`)/ PDF(`.pdf`)
|
||||||
- 单次上限 5000 条;按筛选且未指定日期时默认近 30 天
|
- 单次上限 5000 条;按筛选且未指定日期时默认近 30 天
|
||||||
- 权限:`orders`;操作审计 `ORDER_EXPORT`
|
- 权限:`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-24 | v3.5.8:HQ 单账号追加/撤销权限;运营改客服;城市门店服务与城市范围;分类删除权限与概览按权限/城市裁剪 |
|
||||||
| 2026-08-23 | v3.5.7:门店核销记录时间显示秒;首页「今日到账金额」→「今日核销金额」 |
|
| 2026-08-23 | v3.5.7:门店核销记录时间显示秒;首页「今日到账金额」→「今日核销金额」 |
|
||||||
| 2026-08-23 | v3.5.6:开发计划任务导出;核销/财务银行信息与打款凭证;工单图片入任务;运营去调试;门店审核全屏 |
|
| 2026-08-23 | v3.5.6:开发计划任务导出;核销/财务银行信息与打款凭证;工单图片入任务;运营去调试;门店审核全屏 |
|
||||||
|
|||||||
@@ -120,6 +120,7 @@
|
|||||||
- [ ] 用户列表昵称只读;双击「备注」可改,离开编辑后 `user_user.hq_remark` 已更新;C 端看不到该字段
|
- [ ] 用户列表昵称只读;双击「备注」可改,离开编辑后 `user_user.hq_remark` 已更新;C 端看不到该字段
|
||||||
- [ ] 用户列表手机号完整可见(详情仍脱敏)
|
- [ ] 用户列表手机号完整可见(详情仍脱敏)
|
||||||
- [ ] 权益券列表:用户编号、订单号可点进对应用户/订单详情;来源含商品名、规格、数量、配送方式、实付金额
|
- [ ] 权益券列表:用户编号、订单号可点进对应用户/订单详情;来源含商品名、规格、数量、配送方式、实付金额
|
||||||
|
- [ ] 订单列表「状态」可多选;导出按所选状态过滤;不选即全部
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -128,3 +129,9 @@
|
|||||||
- **用户**、**订单**列(及券详情)用主列下划线,分别打开用户详情抽屉、订单详情抽屉。
|
- **用户**、**订单**列(及券详情)用主列下划线,分别打开用户详情抽屉、订单详情抽屉。
|
||||||
- **来源**:关联订单时拼 `商品名 / 规格 / 数量(瓶或箱) / 配送方式 / ¥实付`;无订单仍用 `sourceProduct`(如总部手动发放)。
|
- **来源**:关联订单时拼 `商品名 / 规格 / 数量(瓶或箱) / 配送方式 / ¥实付`;无订单仍用 `sourceProduct`(如总部手动发放)。
|
||||||
- 列表接口 `order` 增补 `productName`、`productSpec`、`quantity`、`saleUnit`、`deliveryType`、`payAmount`。
|
- 列表接口 `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;
|
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 {
|
export interface UpdateAdminUserRequest {
|
||||||
hqRemark: string;
|
hqRemark: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,9 @@ export class AdminOrdersService {
|
|||||||
const where: Prisma.OrderWhereInput = {};
|
const where: Prisma.OrderWhereInput = {};
|
||||||
|
|
||||||
if (query.orderNo) where.orderNo = { contains: query.orderNo };
|
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.orderType) where.orderType = query.orderType as Prisma.EnumOrderTypeFilter['equals'];
|
||||||
if (query.userId) where.userId = BigInt(query.userId);
|
if (query.userId) where.userId = BigInt(query.userId);
|
||||||
if (query.cityId) where.cityId = BigInt(query.cityId);
|
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 { Type, Transform } from 'class-transformer';
|
||||||
import { IsArray, IsBoolean, IsIn, IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
|
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 {
|
function toOptionalBoolean(value: unknown): boolean | undefined {
|
||||||
if (value === undefined || value === null || value === '') return undefined;
|
if (value === undefined || value === null || value === '') return undefined;
|
||||||
if (value === true || value === 'true' || value === '1' || value === 1) return true;
|
if (value === true || value === 'true' || value === '1' || value === 1) return true;
|
||||||
@@ -8,6 +11,19 @@ function toOptionalBoolean(value: unknown): boolean | undefined {
|
|||||||
return 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 {
|
export class PaginationQueryDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@@ -57,9 +73,12 @@ export class AdminOrdersQueryDto extends PaginationQueryDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
orderNo?: string;
|
orderNo?: string;
|
||||||
|
|
||||||
|
/** 单值或多项:`?status=PENDING_PAY` / `?status=A&status=B` / `?status=A,B` */
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@Transform(({ value }) => toOptionalStringList(value))
|
||||||
status?: string;
|
@IsArray()
|
||||||
|
@IsIn(ORDER_STATUS_VALUES, { each: true })
|
||||||
|
status?: string[];
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsIn(['NORMAL', 'PROXY'])
|
@IsIn(['NORMAL', 'PROXY'])
|
||||||
@@ -117,8 +136,10 @@ export class AdminOrdersExportDto {
|
|||||||
orderNo?: string;
|
orderNo?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@Transform(({ value }) => toOptionalStringList(value))
|
||||||
status?: string;
|
@IsArray()
|
||||||
|
@IsIn(ORDER_STATUS_VALUES, { each: true })
|
||||||
|
status?: string[];
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsIn(['NORMAL', 'PROXY', 'RESHIPMENT'])
|
@IsIn(['NORMAL', 'PROXY', 'RESHIPMENT'])
|
||||||
|
|||||||
Reference in New Issue
Block a user