feat(api): 新增站点API路由配置和接口参数处理
feat(产品属性): 完善字典项删除逻辑和状态验证 feat(物流管理): 添加批量删除确认弹窗和状态处理 feat(产品排列): 实现导出CSV功能 feat(订阅管理): 支持排序过滤和批量导出选中项 feat(产品编辑): 重构站点SKU编辑表单 feat(站点产品): 添加ID列和库存状态筛选 feat(媒体管理): 优化批量操作和表格滚动 refactor(站点API): 统一接口参数处理逻辑 feat(产品列表): 增强同步到站点功能 feat(订单管理): 完善状态统计和批量导出 feat(客户管理): 添加订单查询和扩展字段 docs(类型定义): 更新接口参数和返回类型
This commit is contained in:
parent
37b266410a
commit
2e9c7fafce
|
|
@ -310,6 +310,11 @@ export default defineConfig({
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: { '^/api': '' },
|
pathRewrite: { '^/api': '' },
|
||||||
},
|
},
|
||||||
|
'/site-api': {
|
||||||
|
target: UMI_APP_API_URL,
|
||||||
|
changeOrigin: true,
|
||||||
|
pathRewrite: { '^/site-api': '/site-api' },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
npmClient: 'pnpm',
|
npmClient: 'pnpm',
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -102,12 +102,33 @@ const AttributePage: React.FC = () => {
|
||||||
// 删除字典项
|
// 删除字典项
|
||||||
const handleDeleteDictItem = async (itemId: number) => {
|
const handleDeleteDictItem = async (itemId: number) => {
|
||||||
try {
|
try {
|
||||||
const success = await request(`/dict/item/${itemId}`, { method: 'DELETE' });
|
const res = await request(`/dict/item/${itemId}`, { method: 'DELETE' });
|
||||||
if (success) {
|
const isOk =
|
||||||
message.success('删除成功');
|
typeof res === 'boolean'
|
||||||
actionRef.current?.reload(); // 刷新 ProTable
|
? res
|
||||||
} else {
|
: res && res.code === 0
|
||||||
|
? res.data === true || res.data === null
|
||||||
|
: false;
|
||||||
|
if (!isOk) {
|
||||||
message.error('删除失败');
|
message.error('删除失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selectedDict?.id) {
|
||||||
|
const list = await request('/dict/items', {
|
||||||
|
params: {
|
||||||
|
dictId: selectedDict.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const exists = Array.isArray(list) && list.some((it: any) => it.id === itemId);
|
||||||
|
if (exists) {
|
||||||
|
message.error('删除失败');
|
||||||
|
} else {
|
||||||
|
message.success('删除成功');
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.success('删除成功');
|
||||||
|
actionRef.current?.reload();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('删除失败');
|
message.error('删除失败');
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ import {
|
||||||
productcontrollerGetcategoriesall,
|
productcontrollerGetcategoriesall,
|
||||||
productcontrollerGetcategoryattributes,
|
productcontrollerGetcategoryattributes,
|
||||||
productcontrollerGetproductcomponents,
|
productcontrollerGetproductcomponents,
|
||||||
|
productcontrollerGetproductsiteskus,
|
||||||
productcontrollerUpdateproduct,
|
productcontrollerUpdateproduct,
|
||||||
} from '@/servers/api/product';
|
} from '@/servers/api/product';
|
||||||
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
import { stockcontrollerGetstocks as getStocks } from '@/servers/api/stock';
|
import { stockcontrollerGetstocks as getStocks } from '@/servers/api/stock';
|
||||||
import {
|
import {
|
||||||
ActionType,
|
ActionType,
|
||||||
|
|
@ -33,6 +35,8 @@ const EditForm: React.FC<{
|
||||||
const [stockStatus, setStockStatus] = useState<
|
const [stockStatus, setStockStatus] = useState<
|
||||||
'in-stock' | 'out-of-stock' | null
|
'in-stock' | 'out-of-stock' | null
|
||||||
>(null);
|
>(null);
|
||||||
|
const [siteSkuEntries, setSiteSkuEntries] = useState<any[]>([]);
|
||||||
|
const [sites, setSites] = useState<any[]>([]);
|
||||||
|
|
||||||
const [categories, setCategories] = useState<any[]>([]);
|
const [categories, setCategories] = useState<any[]>([]);
|
||||||
const [activeAttributes, setActiveAttributes] = useState<any[]>([]);
|
const [activeAttributes, setActiveAttributes] = useState<any[]>([]);
|
||||||
|
|
@ -41,6 +45,10 @@ const EditForm: React.FC<{
|
||||||
productcontrollerGetcategoriesall().then((res: any) => {
|
productcontrollerGetcategoriesall().then((res: any) => {
|
||||||
setCategories(res?.data || []);
|
setCategories(res?.data || []);
|
||||||
});
|
});
|
||||||
|
// 获取站点列表用于站点SKU选择
|
||||||
|
sitecontrollerAll().then((res: any) => {
|
||||||
|
setSites(res?.data || []);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -86,6 +94,9 @@ const EditForm: React.FC<{
|
||||||
const { data: componentsData } =
|
const { data: componentsData } =
|
||||||
await productcontrollerGetproductcomponents({ id: record.id });
|
await productcontrollerGetproductcomponents({ id: record.id });
|
||||||
setComponents(componentsData || []);
|
setComponents(componentsData || []);
|
||||||
|
// 获取站点SKU详细信息
|
||||||
|
const { data: siteSkusData } = await productcontrollerGetproductsiteskus({ id: record.id });
|
||||||
|
setSiteSkuEntries(siteSkusData || []);
|
||||||
})();
|
})();
|
||||||
}, [record]);
|
}, [record]);
|
||||||
|
|
||||||
|
|
@ -106,9 +117,10 @@ const EditForm: React.FC<{
|
||||||
components: components,
|
components: components,
|
||||||
type: type,
|
type: type,
|
||||||
categoryId: (record as any).categoryId || (record as any).category?.id,
|
categoryId: (record as any).categoryId || (record as any).category?.id,
|
||||||
siteSkus: (record as any).siteSkus?.map((s: any) => s.code) || [],
|
// 初始化站点SKU列表为对象形式
|
||||||
|
siteSkus: siteSkuEntries && siteSkuEntries.length ? siteSkuEntries : [],
|
||||||
};
|
};
|
||||||
}, [record, components, type]);
|
}, [record, components, type, siteSkuEntries]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DrawerForm<any>
|
<DrawerForm<any>
|
||||||
|
|
@ -211,13 +223,41 @@ const EditForm: React.FC<{
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
</ProForm.Group>
|
</ProForm.Group>
|
||||||
<ProFormSelect
|
<ProFormList
|
||||||
name="siteSkus"
|
name="siteSkus"
|
||||||
label="站点 SKU 列表"
|
label="站点SKU"
|
||||||
|
creatorButtonProps={{ position: 'bottom', creatorButtonText: '新增站点SKU' }}
|
||||||
|
itemRender={({ listDom, action }) => (
|
||||||
|
<div style={{ marginBottom: 8, display: 'flex', flexDirection: 'row', alignItems: 'end' }}>
|
||||||
|
{listDom}
|
||||||
|
{action}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ProForm.Group>
|
||||||
|
<ProFormSelect
|
||||||
|
name="siteId"
|
||||||
|
label="站点"
|
||||||
width="md"
|
width="md"
|
||||||
mode="tags"
|
options={sites.map((site) => ({ label: site.name, value: site.id }))}
|
||||||
placeholder="输入站点 SKU,回车添加"
|
placeholder="请选择站点"
|
||||||
|
rules={[{ required: true, message: '请选择站点' }]}
|
||||||
/>
|
/>
|
||||||
|
<ProFormText
|
||||||
|
name="code"
|
||||||
|
label="站点SKU"
|
||||||
|
width="md"
|
||||||
|
placeholder="请输入站点SKU"
|
||||||
|
rules={[{ required: true, message: '请输入站点SKU' }]}
|
||||||
|
/>
|
||||||
|
<ProFormText
|
||||||
|
name="quantity"
|
||||||
|
label="数量"
|
||||||
|
width="md"
|
||||||
|
placeholder="请输入数量"
|
||||||
|
/>
|
||||||
|
</ProForm.Group>
|
||||||
|
</ProFormList>
|
||||||
<ProForm.Group>
|
<ProForm.Group>
|
||||||
|
|
||||||
<ProFormText
|
<ProFormText
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {
|
||||||
productcontrollerGetproductcomponents,
|
productcontrollerGetproductcomponents,
|
||||||
productcontrollerGetproductlist,
|
productcontrollerGetproductlist,
|
||||||
productcontrollerUpdatenamecn,
|
productcontrollerUpdatenamecn,
|
||||||
|
productcontrollerBindproductsiteskus,
|
||||||
} from '@/servers/api/product';
|
} from '@/servers/api/product';
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
import {
|
import {
|
||||||
|
|
@ -13,6 +14,7 @@ import {
|
||||||
wpproductcontrollerGetwpproducts,
|
wpproductcontrollerGetwpproducts,
|
||||||
wpproductcontrollerSynctoproduct,
|
wpproductcontrollerSynctoproduct,
|
||||||
} from '@/servers/api/wpProduct';
|
} from '@/servers/api/wpProduct';
|
||||||
|
import { siteapicontrollerGetproducts } from '@/servers/api/siteApi';
|
||||||
import { ActionType, ModalForm, PageContainer, ProColumns, ProFormSelect, ProFormText, ProTable } from '@ant-design/pro-components';
|
import { ActionType, ModalForm, PageContainer, ProColumns, ProFormSelect, ProFormText, ProTable } from '@ant-design/pro-components';
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
import { App, Button, Modal, Popconfirm, Tag, Upload } from 'antd';
|
import { App, Button, Modal, Popconfirm, Tag, Upload } from 'antd';
|
||||||
|
|
@ -167,10 +169,12 @@ const SyncToSiteModal: React.FC<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
productIds: number[];
|
productIds: number[];
|
||||||
|
productRows: API.Product[];
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
}> = ({ visible, onClose, productIds, onSuccess }) => {
|
}> = ({ visible, onClose, productIds, productRows, onSuccess }) => {
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
const [sites, setSites] = useState<any[]>([]);
|
const [sites, setSites] = useState<any[]>([]);
|
||||||
|
const formRef = useRef<any>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
|
@ -186,6 +190,22 @@ const SyncToSiteModal: React.FC<{
|
||||||
open={visible}
|
open={visible}
|
||||||
onOpenChange={(open) => !open && onClose()}
|
onOpenChange={(open) => !open && onClose()}
|
||||||
modalProps={{ destroyOnClose: true }}
|
modalProps={{ destroyOnClose: true }}
|
||||||
|
formRef={formRef}
|
||||||
|
onValuesChange={(changedValues) => {
|
||||||
|
if ('siteId' in changedValues && changedValues.siteId) {
|
||||||
|
const siteId = changedValues.siteId;
|
||||||
|
const site = sites.find((s: any) => s.id === siteId) || {};
|
||||||
|
const prefix = site.skuPrefix || '';
|
||||||
|
const map: Record<string, any> = {};
|
||||||
|
productRows.forEach((p) => {
|
||||||
|
map[p.id] = {
|
||||||
|
code: `${prefix}${p.sku || ''}`,
|
||||||
|
quantity: undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
formRef.current?.setFieldsValue({ productSiteSkus: map });
|
||||||
|
}
|
||||||
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
if (!values.siteId) return false;
|
if (!values.siteId) return false;
|
||||||
try {
|
try {
|
||||||
|
|
@ -193,6 +213,16 @@ const SyncToSiteModal: React.FC<{
|
||||||
{ siteId: values.siteId },
|
{ siteId: values.siteId },
|
||||||
{ productIds }
|
{ productIds }
|
||||||
);
|
);
|
||||||
|
const map = values.productSiteSkus || {};
|
||||||
|
for (const currentProductId of productIds) {
|
||||||
|
const entry = map?.[currentProductId];
|
||||||
|
if (entry && entry.code) {
|
||||||
|
await productcontrollerBindproductsiteskus(
|
||||||
|
{ id: currentProductId },
|
||||||
|
{ siteSkus: [{ siteId: values.siteId, code: entry.code, quantity: entry.quantity }] }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
message.success('同步任务已提交');
|
message.success('同步任务已提交');
|
||||||
onSuccess();
|
onSuccess();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -208,6 +238,21 @@ const SyncToSiteModal: React.FC<{
|
||||||
options={sites.map((site) => ({ label: site.name, value: site.id }))}
|
options={sites.map((site) => ({ label: site.name, value: site.id }))}
|
||||||
rules={[{ required: true, message: '请选择站点' }]}
|
rules={[{ required: true, message: '请选择站点' }]}
|
||||||
/>
|
/>
|
||||||
|
{productRows.map((row) => (
|
||||||
|
<div key={row.id} style={{ display: 'flex', gap: 12, alignItems: 'flex-end' }}>
|
||||||
|
<div style={{ minWidth: 220 }}>原始SKU: {row.sku || '-'}</div>
|
||||||
|
<ProFormText
|
||||||
|
name={['productSiteSkus', row.id, 'code']}
|
||||||
|
label={`商品 ${row.id} 站点SKU`}
|
||||||
|
placeholder="请输入站点SKU"
|
||||||
|
/>
|
||||||
|
<ProFormText
|
||||||
|
name={['productSiteSkus', row.id, 'quantity']}
|
||||||
|
label="数量"
|
||||||
|
placeholder="请输入数量"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -237,37 +282,46 @@ const WpProductInfo: React.FC<{ skus: string[]; record: API.Product; parentTable
|
||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
request={async () => {
|
request={async () => {
|
||||||
|
// 判断是否存在站点SKU列表
|
||||||
if (!skus || skus.length === 0) return { data: [] };
|
if (!skus || skus.length === 0) return { data: [] };
|
||||||
const { data } = await wpproductcontrollerGetwpproducts(
|
try {
|
||||||
{
|
// 获取所有站点列表用于遍历查询
|
||||||
skus,
|
const { data: siteResponse } = await sitecontrollerAll();
|
||||||
pageSize: 100,
|
const siteList = siteResponse || [];
|
||||||
current: 1,
|
// 聚合所有站点的产品数据
|
||||||
},
|
const aggregatedProducts: any[] = [];
|
||||||
{
|
// 遍历每一个站点
|
||||||
paramsSerializer: (params: any) => {
|
for (const siteItem of siteList) {
|
||||||
const searchParams = new URLSearchParams();
|
// 遍历每一个SKU在当前站点进行搜索
|
||||||
Object.keys(params).forEach((key) => {
|
for (const skuCode of skus) {
|
||||||
const value = params[key];
|
// 直接调用站点API根据搜索关键字获取产品列表
|
||||||
if (Array.isArray(value)) {
|
const { data: productPage } = await siteapicontrollerGetproducts({
|
||||||
value.forEach((v) => searchParams.append(key, v));
|
siteId: siteItem.id,
|
||||||
} else if (value !== undefined && value !== null) {
|
per_page: 100,
|
||||||
searchParams.append(key, value);
|
search: skuCode,
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return searchParams.toString();
|
const siteProducts = productPage?.items || [];
|
||||||
},
|
// 将站点信息附加到产品数据中便于展示
|
||||||
},
|
siteProducts.forEach((p: any) => {
|
||||||
);
|
aggregatedProducts.push({
|
||||||
return {
|
...p,
|
||||||
data: data?.items || [],
|
siteId: siteItem.id,
|
||||||
success: true,
|
siteName: siteItem.name,
|
||||||
};
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { data: aggregatedProducts, success: true };
|
||||||
|
} catch (error: any) {
|
||||||
|
// 请求失败进行错误提示
|
||||||
|
message.error(error?.message || '获取站点产品失败');
|
||||||
|
return { data: [], success: false };
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
title: '站点',
|
title: '站点',
|
||||||
dataIndex: ['site', 'name'],
|
dataIndex: 'siteName',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'SKU',
|
title: 'SKU',
|
||||||
|
|
@ -699,6 +753,7 @@ const List: React.FC = () => {
|
||||||
visible={syncModalVisible}
|
visible={syncModalVisible}
|
||||||
onClose={() => setSyncModalVisible(false)}
|
onClose={() => setSyncModalVisible(false)}
|
||||||
productIds={syncProductIds}
|
productIds={syncProductIds}
|
||||||
|
productRows={selectedRows}
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
setSyncModalVisible(false);
|
setSyncModalVisible(false);
|
||||||
setSelectedRows([]);
|
setSelectedRows([]);
|
||||||
|
|
|
||||||
|
|
@ -296,7 +296,45 @@ const PermutationPage: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
scroll={{ x: 'max-content' }}
|
scroll={{ x: 'max-content' }}
|
||||||
search={false}
|
search={false}
|
||||||
toolBarRender={false}
|
toolBarRender={() => [
|
||||||
|
<Button key="export" onClick={() => {
|
||||||
|
const exportColumns = columns.filter((c: any) => c.key !== 'action');
|
||||||
|
const headers = exportColumns.map((c: any) => String(c.title ?? ''));
|
||||||
|
const escapeCsv = (val: any) => {
|
||||||
|
const s = val === undefined || val === null ? '' : String(val);
|
||||||
|
if (/[",\n]/.test(s)) {
|
||||||
|
return '"' + s.replace(/"/g, '""') + '"';
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
const rows: string[][] = permutations.map((record: any) => {
|
||||||
|
return exportColumns.map((c: any) => {
|
||||||
|
if (c.key === 'sku') {
|
||||||
|
const key = generateKeyFromPermutation(record);
|
||||||
|
const product = existingProducts.get(key);
|
||||||
|
const value = product?.sku || '';
|
||||||
|
return escapeCsv(value);
|
||||||
|
}
|
||||||
|
const valueItem = c.dataIndex ? record[c.dataIndex] : undefined;
|
||||||
|
const value = valueItem?.name || '';
|
||||||
|
return escapeCsv(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const csvContent = [headers, ...rows].map(row => row.join(',')).join('\n');
|
||||||
|
const blob = new Blob(['\ufeff' + csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
const categoryName = categories.find(c => c.id === categoryId)?.name || 'Export';
|
||||||
|
link.href = url;
|
||||||
|
link.download = categoryName + '-permutations.csv';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}}>
|
||||||
|
导出CSV
|
||||||
|
</Button>
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ProCard>
|
</ProCard>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { ActionType, DrawerForm, ModalForm, PageContainer, ProColumns, ProFormText, ProFormTextArea, ProTable } from '@ant-design/pro-components';
|
import { ActionType, DrawerForm, ModalForm, PageContainer, ProColumns, ProFormText, ProFormTextArea, ProTable } from '@ant-design/pro-components';
|
||||||
import { request, useParams } from '@umijs/max';
|
import { request, useParams } from '@umijs/max';
|
||||||
import { App, Avatar, Button, Popconfirm, Space, Tag } from 'antd';
|
import { App, Avatar, Button, Modal, Popconfirm, Space, Tag } from 'antd';
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { DeleteFilled, EditOutlined, PlusOutlined, UserOutlined } from '@ant-design/icons';
|
import { DeleteFilled, EditOutlined, PlusOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
|
@ -62,6 +62,8 @@ const CustomerPage: React.FC = () => {
|
||||||
const [editing, setEditing] = useState<any>(null);
|
const [editing, setEditing] = useState<any>(null);
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
|
const [ordersVisible, setOrdersVisible] = useState<boolean>(false);
|
||||||
|
const [ordersCustomer, setOrdersCustomer] = useState<any>(null);
|
||||||
|
|
||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
if (!siteId) return;
|
if (!siteId) return;
|
||||||
|
|
@ -90,9 +92,25 @@ const CustomerPage: React.FC = () => {
|
||||||
return <Avatar src={avatarUrl} icon={<UserOutlined />} size="large" />;
|
return <Avatar src={avatarUrl} icon={<UserOutlined />} size="large" />;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '姓名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
hideInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 120,
|
||||||
|
copyable: true,
|
||||||
|
render: (_, record) => {
|
||||||
|
return record?.id ?? '-';
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '姓名',
|
title: '姓名',
|
||||||
dataIndex: 'username',
|
dataIndex: 'username',
|
||||||
|
hideInSearch: true,
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
// DTO中有first_name和last_name字段,username可能从raw数据中获取
|
// DTO中有first_name和last_name字段,username可能从raw数据中获取
|
||||||
const username = record.username || record.raw?.username || 'N/A';
|
const username = record.username || record.raw?.username || 'N/A';
|
||||||
|
|
@ -126,6 +144,18 @@ const CustomerPage: React.FC = () => {
|
||||||
return <Tag color="blue">{role}</Tag>;
|
return <Tag color="blue">{role}</Tag>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '订单数',
|
||||||
|
dataIndex: 'orders',
|
||||||
|
sorter: true,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '总花费',
|
||||||
|
dataIndex: 'total_spend',
|
||||||
|
sorter: true,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '账单地址',
|
title: '账单地址',
|
||||||
dataIndex: 'billing',
|
dataIndex: 'billing',
|
||||||
|
|
@ -143,6 +173,23 @@ const CustomerPage: React.FC = () => {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '收货地址',
|
||||||
|
dataIndex: 'shipping',
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (_, record) => {
|
||||||
|
const { shipping } = record;
|
||||||
|
if (!shipping) return '-';
|
||||||
|
return (
|
||||||
|
<div style={{ fontSize: 12 }}>
|
||||||
|
<div>{shipping.address_1} {shipping.address_2}</div>
|
||||||
|
<div>{shipping.city}, {shipping.state}, {shipping.postcode}</div>
|
||||||
|
<div>{shipping.country}</div>
|
||||||
|
<div>{shipping.phone}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '注册时间',
|
title: '注册时间',
|
||||||
dataIndex: 'date_created',
|
dataIndex: 'date_created',
|
||||||
|
|
@ -153,12 +200,16 @@ const CustomerPage: React.FC = () => {
|
||||||
title: '操作',
|
title: '操作',
|
||||||
valueType: 'option',
|
valueType: 'option',
|
||||||
width: 120,
|
width: 120,
|
||||||
|
fixed:"right",
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space>
|
<Space>
|
||||||
<Button type="link" title="编辑" icon={<EditOutlined />} onClick={() => setEditing(record)} />
|
<Button type="link" title="编辑" icon={<EditOutlined />} onClick={() => setEditing(record)} />
|
||||||
<Popconfirm title="确定删除?" onConfirm={() => handleDelete(record.id)}>
|
<Popconfirm title="确定删除?" onConfirm={() => handleDelete(record.id)}>
|
||||||
<Button type="link" danger title="删除" icon={<DeleteFilled />} />
|
<Button type="link" danger title="删除" icon={<DeleteFilled />} />
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
|
<Button type="link" title="查询订单" onClick={() => { setOrdersCustomer(record); setOrdersVisible(true); }}>
|
||||||
|
查询订单
|
||||||
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
@ -175,17 +226,36 @@ const CustomerPage: React.FC = () => {
|
||||||
<ProTable
|
<ProTable
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
search={false}
|
search={{ labelWidth: 'auto' }}
|
||||||
options={false}
|
options={{ reload: true }}
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
|
scroll={{ x: 'max-content' }}
|
||||||
rowSelection={{
|
rowSelection={{
|
||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
onChange: setSelectedRowKeys,
|
onChange: setSelectedRowKeys,
|
||||||
}}
|
}}
|
||||||
request={async (params) => {
|
request={async (params, sort, filter) => {
|
||||||
if (!siteId) return { data: [], total: 0, success: true };
|
if (!siteId) return { data: [], total: 0, success: true };
|
||||||
|
const { current, pageSize, name, email, ...rest } = params || {};
|
||||||
|
const where = { ...rest, ...(filter || {}) };
|
||||||
|
if (email) {
|
||||||
|
(where as any).email = email;
|
||||||
|
}
|
||||||
|
let orderObj: Record<string, 'asc' | 'desc'> | undefined = undefined;
|
||||||
|
if (sort && typeof sort === 'object') {
|
||||||
|
const [field, dir] = Object.entries(sort)[0] || [];
|
||||||
|
if (field && dir) {
|
||||||
|
orderObj = { [field]: dir === 'descend' ? 'desc' : 'asc' };
|
||||||
|
}
|
||||||
|
}
|
||||||
const response = await request(`/site-api/${siteId}/customers`, {
|
const response = await request(`/site-api/${siteId}/customers`, {
|
||||||
params: { page: params.current, per_page: params.pageSize },
|
params: {
|
||||||
|
page: current,
|
||||||
|
page_size: pageSize,
|
||||||
|
where,
|
||||||
|
...(orderObj ? { order: orderObj } : {}),
|
||||||
|
...((name || email) ? { search: name || email } : {}),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
|
@ -198,9 +268,21 @@ const CustomerPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
|
let items = (data?.items || []) as any[];
|
||||||
|
if (sort && typeof sort === 'object') {
|
||||||
|
const [field, dir] = Object.entries(sort)[0] || [];
|
||||||
|
if (field === 'orders' || field === 'total_spend') {
|
||||||
|
const isDesc = dir === 'descend';
|
||||||
|
items = items.slice().sort((a, b) => {
|
||||||
|
const av = Number(a?.[field] ?? 0);
|
||||||
|
const bv = Number(b?.[field] ?? 0);
|
||||||
|
return isDesc ? bv - av : av - bv;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
total: data?.total || 0,
|
total: data?.total || 0,
|
||||||
data: data?.items || [],
|
data: items,
|
||||||
success: true,
|
success: true,
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
|
|
@ -236,7 +318,8 @@ const CustomerPage: React.FC = () => {
|
||||||
title="批量导出"
|
title="批量导出"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!siteId) return;
|
if (!siteId) return;
|
||||||
const res = await request(`/site-api/${siteId}/customers/export`, { params: {} });
|
const idsParam = selectedRowKeys.length ? (selectedRowKeys as any[]).join(',') : undefined;
|
||||||
|
const res = await request(`/site-api/${siteId}/customers/export`, { params: { ids: idsParam } });
|
||||||
if (res?.success && res?.data?.csv) {
|
if (res?.success && res?.data?.csv) {
|
||||||
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
@ -315,6 +398,47 @@ const CustomerPage: React.FC = () => {
|
||||||
<ProFormText name="username" label="用户名" />
|
<ProFormText name="username" label="用户名" />
|
||||||
<ProFormText name="phone" label="电话" />
|
<ProFormText name="phone" label="电话" />
|
||||||
</DrawerForm>
|
</DrawerForm>
|
||||||
|
<Modal
|
||||||
|
open={ordersVisible}
|
||||||
|
onCancel={() => { setOrdersVisible(false); setOrdersCustomer(null); }}
|
||||||
|
footer={null}
|
||||||
|
width={1000}
|
||||||
|
title="客户订单"
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<ProTable
|
||||||
|
rowKey="id"
|
||||||
|
search={false}
|
||||||
|
pagination={{ pageSize: 20 }}
|
||||||
|
columns={[
|
||||||
|
{ title: '订单ID', dataIndex: 'id' },
|
||||||
|
{ title: '订单号', dataIndex: 'number' },
|
||||||
|
{ title: '状态', dataIndex: 'status' },
|
||||||
|
{ title: '币种', dataIndex: 'currency' },
|
||||||
|
{ title: '金额', dataIndex: 'total' },
|
||||||
|
{ title: '创建时间', dataIndex: 'date_created', valueType: 'dateTime' },
|
||||||
|
]}
|
||||||
|
request={async (params) => {
|
||||||
|
if (!siteId || !ordersCustomer?.id) return { data: [], total: 0, success: true };
|
||||||
|
const res = await request(`/site-api/${siteId}/customers/${ordersCustomer.id}/orders`, {
|
||||||
|
params: {
|
||||||
|
page: params.current,
|
||||||
|
page_size: params.pageSize,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!res?.success) {
|
||||||
|
message.error(res?.message || '获取订单失败');
|
||||||
|
return { data: [], total: 0, success: false };
|
||||||
|
}
|
||||||
|
const data = res.data || {};
|
||||||
|
return {
|
||||||
|
data: data.items || [],
|
||||||
|
total: data.total || 0,
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -209,28 +209,41 @@ const LogisticsPage: React.FC = () => {
|
||||||
<Button onClick={handleBatchPrint} type="primary">
|
<Button onClick={handleBatchPrint} type="primary">
|
||||||
批量打印
|
批量打印
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Popconfirm
|
||||||
danger
|
title="确定批量删除选中项吗?"
|
||||||
type="primary"
|
okText="确定"
|
||||||
onClick={async () => {
|
cancelText="取消"
|
||||||
|
onConfirm={async () => {
|
||||||
|
// 条件判断 如果当前未选择任何行则直接返回
|
||||||
|
if (!selectedRows || selectedRows.length === 0) return;
|
||||||
try {
|
try {
|
||||||
|
// 进入批量删除处理流程
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
let ok = 0;
|
let successCount = 0;
|
||||||
for (const row of selectedRows) {
|
for (const row of selectedRows) {
|
||||||
|
// 逐条删除 每次调用服务端删除接口
|
||||||
const { success } = await logisticscontrollerDeleteshipment({ id: row.id });
|
const { success } = await logisticscontrollerDeleteshipment({ id: row.id });
|
||||||
if (success) ok++;
|
// 条件判断 累计成功次数
|
||||||
|
if (success) successCount++;
|
||||||
}
|
}
|
||||||
message.success(`成功删除 ${ok} 条`);
|
// 删除完成后提示成功条数
|
||||||
|
message.success(`成功删除 ${successCount} 条`);
|
||||||
|
// 结束加载状态
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
// 刷新表格数据
|
||||||
actionRef.current?.reload();
|
actionRef.current?.reload();
|
||||||
|
// 清空选择列表
|
||||||
setSelectedRows([]);
|
setSelectedRows([]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// 异常处理 结束加载状态
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Button danger type="primary">
|
||||||
批量删除
|
批量删除
|
||||||
</Button>
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,16 @@ const MediaPage: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns: ProColumns<any>[] = [
|
const columns: ProColumns<any>[] = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 120,
|
||||||
|
copyable: true,
|
||||||
|
render: (_, record) => {
|
||||||
|
return record?.id ?? '-';
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '展示',
|
title: '展示',
|
||||||
dataIndex: 'source_url',
|
dataIndex: 'source_url',
|
||||||
|
|
@ -136,13 +146,22 @@ const MediaPage: React.FC = () => {
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowSelection={{ selectedRowKeys, onChange: setSelectedRowKeys }}
|
rowSelection={{ selectedRowKeys, onChange: setSelectedRowKeys }}
|
||||||
request={async (params) => {
|
scroll={{ x: 'max-content' }}
|
||||||
|
request={async (params, sort) => {
|
||||||
if (!siteId) return { data: [], total: 0 };
|
if (!siteId) return { data: [], total: 0 };
|
||||||
|
const { current, pageSize } = params || {};
|
||||||
|
let orderObj: Record<string, 'asc' | 'desc'> | undefined = undefined;
|
||||||
|
if (sort && typeof sort === 'object') {
|
||||||
|
const [field, dir] = Object.entries(sort)[0] || [];
|
||||||
|
if (field && dir) {
|
||||||
|
orderObj = { [field]: dir === 'descend' ? 'desc' : 'asc' };
|
||||||
|
}
|
||||||
|
}
|
||||||
const response = await request(`/site-api/${siteId}/media`, {
|
const response = await request(`/site-api/${siteId}/media`, {
|
||||||
params: {
|
params: {
|
||||||
page: params.current,
|
page: current,
|
||||||
per_page: params.pageSize,
|
page_size: pageSize,
|
||||||
|
...(orderObj ? { order: orderObj } : {}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -164,7 +183,7 @@ const MediaPage: React.FC = () => {
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
search={false}
|
search={false}
|
||||||
options={false}
|
options={{ reload: true }}
|
||||||
toolBarRender={() => [
|
toolBarRender={() => [
|
||||||
<ModalForm
|
<ModalForm
|
||||||
title="上传媒体"
|
title="上传媒体"
|
||||||
|
|
@ -219,7 +238,8 @@ const MediaPage: React.FC = () => {
|
||||||
title="批量导出"
|
title="批量导出"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!siteId) return;
|
if (!siteId) return;
|
||||||
const res = await request(`/site-api/${siteId}/media/export`, { params: {} });
|
const idsParam = selectedRowKeys.length ? (selectedRowKeys as any[]).join(',') : undefined;
|
||||||
|
const res = await request(`/site-api/${siteId}/media/export`, { params: { ids: idsParam } });
|
||||||
if (res?.success && res?.data?.csv) {
|
if (res?.success && res?.data?.csv) {
|
||||||
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
@ -233,23 +253,35 @@ const MediaPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>,
|
/>,
|
||||||
|
<Popconfirm
|
||||||
|
title="确定批量删除选中项吗?"
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
disabled={!selectedRowKeys.length}
|
||||||
|
onConfirm={async () => {
|
||||||
|
// 条件判断 如果站点編號不存在則直接返回
|
||||||
|
if (!siteId) return;
|
||||||
|
// 发起批量删除请求
|
||||||
|
const response = await request(`/site-api/${siteId}/media/batch`, { method: 'POST', data: { delete: selectedRowKeys } });
|
||||||
|
// 条件判断 根据接口返回结果进行提示
|
||||||
|
if (response.success) {
|
||||||
|
message.success('批量删除成功');
|
||||||
|
} else {
|
||||||
|
message.warning(response.message || '部分删除失败');
|
||||||
|
}
|
||||||
|
// 清空已选择的行鍵值
|
||||||
|
setSelectedRowKeys([]);
|
||||||
|
// 刷新列表数据
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
title="批量删除"
|
title="批量删除"
|
||||||
danger
|
danger
|
||||||
icon={<DeleteOutlined />}
|
icon={<DeleteOutlined />}
|
||||||
disabled={!selectedRowKeys.length}
|
disabled={!selectedRowKeys.length}
|
||||||
onClick={async () => {
|
|
||||||
if (!siteId) return;
|
|
||||||
const res = await request(`/site-api/${siteId}/media/batch`, { method: 'POST', data: { delete: selectedRowKeys } });
|
|
||||||
if (res.success) {
|
|
||||||
message.success('批量删除成功');
|
|
||||||
} else {
|
|
||||||
message.warning(res.message || '部分删除失败');
|
|
||||||
}
|
|
||||||
setSelectedRowKeys([]);
|
|
||||||
actionRef.current?.reload();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
</Popconfirm>
|
||||||
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import {
|
||||||
} from '@/servers/api/order';
|
} from '@/servers/api/order';
|
||||||
import { formatShipmentState, formatSource } from '@/utils/format';
|
import { formatShipmentState, formatSource } from '@/utils/format';
|
||||||
import {
|
import {
|
||||||
DownOutlined,
|
EllipsisOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
ActionType,
|
ActionType,
|
||||||
|
|
@ -46,6 +46,7 @@ const OrdersPage: React.FC = () => {
|
||||||
}, [siteId]);
|
}, [siteId]);
|
||||||
|
|
||||||
const tabs: TabsProps['items'] = useMemo(() => {
|
const tabs: TabsProps['items'] = useMemo(() => {
|
||||||
|
// 统计全部数量,依赖状态统计数组
|
||||||
const total = count.reduce((acc, cur) => acc + Number(cur.count), 0);
|
const total = count.reduce((acc, cur) => acc + Number(cur.count), 0);
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ key: 'pending', label: '待确认' },
|
{ key: 'pending', label: '待确认' },
|
||||||
|
|
@ -56,10 +57,12 @@ const OrdersPage: React.FC = () => {
|
||||||
{ key: 'failed', label: '失败' },
|
{ key: 'failed', label: '失败' },
|
||||||
{ key: 'after_sale_pending', label: '售后处理中' },
|
{ key: 'after_sale_pending', label: '售后处理中' },
|
||||||
{ key: 'pending_reshipment', label: '待补发' },
|
{ key: 'pending_reshipment', label: '待补发' },
|
||||||
|
// 退款相关状态
|
||||||
{ key: 'refund_requested', label: '已申请退款' },
|
{ key: 'refund_requested', label: '已申请退款' },
|
||||||
{ key: 'refund_approved', label: '已退款' },
|
{ key: 'refund_approved', label: '退款申请已通过' },
|
||||||
{ key: 'refund_cancelled', label: '已完成' },
|
{ key: 'refund_cancelled', label: '已取消退款' },
|
||||||
].map((v) => {
|
].map((v) => {
|
||||||
|
// 根据状态键匹配统计数量
|
||||||
const number = count.find((el) => el.status === v.key)?.count || '0';
|
const number = count.find((el) => el.status === v.key)?.count || '0';
|
||||||
return {
|
return {
|
||||||
label: `${v.label}(${number})`,
|
label: `${v.label}(${number})`,
|
||||||
|
|
@ -176,10 +179,7 @@ const OrdersPage: React.FC = () => {
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<a onClick={(e) => e.preventDefault()}>
|
<Button type="text" icon={<EllipsisOutlined />} />
|
||||||
<Button type="link" icon={<DownOutlined />}>
|
|
||||||
</Button>
|
|
||||||
</a>
|
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定删除订单?"
|
title="确定删除订单?"
|
||||||
|
|
@ -256,7 +256,8 @@ const OrdersPage: React.FC = () => {
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!siteId) return;
|
if (!siteId) return;
|
||||||
const res = await request(`/site-api/${siteId}/orders/export`, { params: {} });
|
const idsParam = selectedRowKeys.length ? (selectedRowKeys as any[]).join(',') : undefined;
|
||||||
|
const res = await request(`/site-api/${siteId}/orders/export`, { params: { ids: idsParam } });
|
||||||
if (res?.success && res?.data?.csv) {
|
if (res?.success && res?.data?.csv) {
|
||||||
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
@ -293,25 +294,36 @@ const OrdersPage: React.FC = () => {
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
|
|
||||||
]}
|
]}
|
||||||
request={async ({ date, ...param }: any) => {
|
request={async (params, sort, filter) => {
|
||||||
if (param.status === 'all') {
|
const p: any = params || {};
|
||||||
delete param.status;
|
const current = p.current;
|
||||||
|
const pageSize = p.pageSize;
|
||||||
|
const date = p.date;
|
||||||
|
const status = p.status;
|
||||||
|
const { current: _c, pageSize: _ps, date: _d, status: _s, ...rest } = p;
|
||||||
|
const where: Record<string, any> = { ...(filter || {}), ...rest };
|
||||||
|
if (status && status !== 'all') {
|
||||||
|
where.status = status;
|
||||||
}
|
}
|
||||||
if (date) {
|
if (date) {
|
||||||
const [startDate, endDate] = date;
|
const [startDate, endDate] = date;
|
||||||
param.startDate = `${startDate} 00:00:00`;
|
// 将日期范围转为后端筛选参数
|
||||||
param.endDate = `${endDate} 23:59:59`;
|
where.startDate = `${startDate} 00:00:00`;
|
||||||
|
where.endDate = `${endDate} 23:59:59`;
|
||||||
|
}
|
||||||
|
let orderObj: Record<string, 'asc' | 'desc'> | undefined = undefined;
|
||||||
|
if (sort && typeof sort === 'object') {
|
||||||
|
const [field, dir] = Object.entries(sort)[0] || [];
|
||||||
|
if (field && dir) {
|
||||||
|
orderObj = { [field]: dir === 'descend' ? 'desc' : 'asc' };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Inject siteId
|
|
||||||
// if (siteId) {
|
|
||||||
// param.siteId = Number(siteId);
|
|
||||||
// }
|
|
||||||
|
|
||||||
const response = await request(`/site-api/${siteId}/orders`, {
|
const response = await request(`/site-api/${siteId}/orders`, {
|
||||||
params: {
|
params: {
|
||||||
...param,
|
page: current,
|
||||||
page: param.current,
|
page_size: pageSize,
|
||||||
per_page: param.pageSize,
|
where,
|
||||||
|
...(orderObj ? { order: orderObj } : {}),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -324,21 +336,64 @@ const OrdersPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = response;
|
const { data } = response;
|
||||||
// Assuming data has items and count (if backend returns count for tabs)
|
// 计算顶部状态数量,通过按状态并发查询站点接口
|
||||||
// My unified API currently returns items, total, etc.
|
if (siteId) {
|
||||||
// It DOES NOT return 'count' for tabs (order counts by status).
|
try {
|
||||||
// The frontend relies on `data.count` to populate tabs.
|
// 定义需要统计的状态键集合
|
||||||
// If I don't provide it, tabs will show 0.
|
const statusKeys: string[] = [
|
||||||
// I should update backend to return counts or just accept 0 for now.
|
'pending',
|
||||||
// The user asked to "get all data via site api".
|
'processing',
|
||||||
// WC API has `/reports/orders/totals`? Or I have to count manually?
|
'completed',
|
||||||
// WC `orders` endpoint doesn't return counts by status.
|
'cancelled',
|
||||||
// So I might lose this feature or need to implement it in backend `getOrders` by making parallel requests or using a report endpoint.
|
'refunded',
|
||||||
// For now I'll just set count to empty or basic.
|
'failed',
|
||||||
// I'll set setCount([]) to avoid crash.
|
// 站点接口不支持的扩展状态,默认统计为0
|
||||||
|
'after_sale_pending',
|
||||||
|
'pending_reshipment',
|
||||||
|
'refund_requested',
|
||||||
|
'refund_approved',
|
||||||
|
'refund_cancelled',
|
||||||
|
];
|
||||||
|
// 构造基础筛选参数,移除当前状态避免重复过滤
|
||||||
|
const { status: _status, ...baseWhere } = where;
|
||||||
|
// 并发请求各状态的总数,对站点接口不支持的状态使用0
|
||||||
|
const results = await Promise.all(
|
||||||
|
statusKeys.map(async key => {
|
||||||
|
// 将前端退款状态映射为站点接口可能识别的原始状态
|
||||||
|
const mapToRawStatus: Record<string, string> = {
|
||||||
|
refund_requested: 'return-requested',
|
||||||
|
refund_approved: 'return-approved',
|
||||||
|
refund_cancelled: 'return-cancelled',
|
||||||
|
};
|
||||||
|
const rawStatus = mapToRawStatus[key] || key;
|
||||||
|
// 对扩展状态直接返回0,减少不必要的请求
|
||||||
|
const unsupported = ['after_sale_pending', 'pending_reshipment'];
|
||||||
|
if (unsupported.includes(key)) {
|
||||||
|
return { status: key, count: 0 };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await request(`/site-api/${siteId}/orders`, {
|
||||||
|
params: {
|
||||||
|
page: 1,
|
||||||
|
page_size: 1,
|
||||||
|
where: { ...baseWhere, status: rawStatus },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const totalCount = Number(res?.data?.total || 0);
|
||||||
|
return { status: key, count: totalCount };
|
||||||
|
} catch (err) {
|
||||||
|
// 请求失败时该状态数量记为0
|
||||||
|
return { status: key, count: 0 };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setCount(results);
|
||||||
|
} catch (e) {
|
||||||
|
// 统计失败时不影响列表展示
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
// setCount(data?.count || []); // My API doesn't return count yet.
|
|
||||||
return {
|
return {
|
||||||
total: data?.total || 0,
|
total: data?.total || 0,
|
||||||
data: data?.items || [],
|
data: data?.items || [],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { PRODUCT_STATUS_ENUM } from '@/constants';
|
import { PRODUCT_STATUS_ENUM, PRODUCT_STOCK_STATUS_ENUM } from '@/constants';
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
import { useParams } from '@umijs/max';
|
import { useParams } from '@umijs/max';
|
||||||
import {
|
import {
|
||||||
|
|
@ -87,6 +87,16 @@ const ProductsPage: React.FC = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const columns: ProColumns<any>[] = [
|
const columns: ProColumns<any>[] = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 120,
|
||||||
|
copyable: true,
|
||||||
|
render: (_, record) => {
|
||||||
|
return record?.id ?? '-';
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'sku',
|
title: 'sku',
|
||||||
dataIndex: 'sku',
|
dataIndex: 'sku',
|
||||||
|
|
@ -106,6 +116,12 @@ const ProductsPage: React.FC = () => {
|
||||||
title: '产品类型',
|
title: '产品类型',
|
||||||
dataIndex: 'type',
|
dataIndex: 'type',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '库存状态',
|
||||||
|
dataIndex: 'stock_status',
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: PRODUCT_STOCK_STATUS_ENUM,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '库存',
|
title: '库存',
|
||||||
dataIndex: 'stock_quantity',
|
dataIndex: 'stock_quantity',
|
||||||
|
|
@ -192,12 +208,22 @@ const ProductsPage: React.FC = () => {
|
||||||
setSelectedRows(rows);
|
setSelectedRows(rows);
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
request={async (params) => {
|
request={async (params, sort, filter) => {
|
||||||
|
const { current, pageSize, ...rest } = params || {};
|
||||||
|
const where = { ...rest, ...(filter || {}) };
|
||||||
|
let orderObj: Record<string, 'asc' | 'desc'> | undefined = undefined;
|
||||||
|
if (sort && typeof sort === 'object') {
|
||||||
|
const [field, dir] = Object.entries(sort)[0] || [];
|
||||||
|
if (field && dir) {
|
||||||
|
orderObj = { [field]: dir === 'descend' ? 'desc' : 'asc' };
|
||||||
|
}
|
||||||
|
}
|
||||||
const response = await request(`/site-api/${siteId}/products`, {
|
const response = await request(`/site-api/${siteId}/products`, {
|
||||||
params: {
|
params: {
|
||||||
...params,
|
page: current,
|
||||||
page: params.current,
|
page_size: pageSize,
|
||||||
per_page: params.pageSize,
|
where,
|
||||||
|
...(orderObj ? { order: orderObj } : {}),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -227,7 +253,6 @@ const ProductsPage: React.FC = () => {
|
||||||
selectedRowKeys={selectedRowKeys}
|
selectedRowKeys={selectedRowKeys}
|
||||||
setSelectedRowKeys={setSelectedRowKeys}
|
setSelectedRowKeys={setSelectedRowKeys}
|
||||||
selectedRows={selectedRows}
|
selectedRows={selectedRows}
|
||||||
config={config}
|
|
||||||
siteId={siteId}
|
siteId={siteId}
|
||||||
/>,
|
/>,
|
||||||
<BatchDeleteProducts
|
<BatchDeleteProducts
|
||||||
|
|
@ -238,7 +263,8 @@ const ProductsPage: React.FC = () => {
|
||||||
/>,
|
/>,
|
||||||
<ImportCsv tableRef={actionRef} siteId={siteId} />,
|
<ImportCsv tableRef={actionRef} siteId={siteId} />,
|
||||||
<Button onClick={async () => {
|
<Button onClick={async () => {
|
||||||
const res = await request(`/site-api/${siteId}/products/export`);
|
const idsParam = selectedRowKeys.length ? (selectedRowKeys as any[]).join(',') : undefined;
|
||||||
|
const res = await request(`/site-api/${siteId}/products/export`, { params: { ids: idsParam } });
|
||||||
if (res?.success && res?.data?.csv) {
|
if (res?.success && res?.data?.csv) {
|
||||||
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
@ -255,6 +281,15 @@ const ProductsPage: React.FC = () => {
|
||||||
expandedRowRender: (record) => {
|
expandedRowRender: (record) => {
|
||||||
const productExternalId = record.externalProductId || record.external_product_id || record.id;
|
const productExternalId = record.externalProductId || record.external_product_id || record.id;
|
||||||
const innerColumns: ProColumns<any>[] = [
|
const innerColumns: ProColumns<any>[] = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 120,
|
||||||
|
render: (_, row) => {
|
||||||
|
return row?.id ?? '-';
|
||||||
|
}
|
||||||
|
},
|
||||||
{ title: '变体名', dataIndex: 'name' },
|
{ title: '变体名', dataIndex: 'name' },
|
||||||
{ title: 'sku', dataIndex: 'sku' },
|
{ title: 'sku', dataIndex: 'sku' },
|
||||||
{ title: '常规价格', dataIndex: 'regular_price', hideInSearch: true },
|
{ title: '常规价格', dataIndex: 'regular_price', hideInSearch: true },
|
||||||
|
|
|
||||||
|
|
@ -127,13 +127,23 @@ const SubscriptionsPage: React.FC = () => {
|
||||||
* 列表数据请求;保持与后端分页参数一致
|
* 列表数据请求;保持与后端分页参数一致
|
||||||
* 兼容后端 data.items 或 data.list 返回字段
|
* 兼容后端 data.items 或 data.list 返回字段
|
||||||
*/
|
*/
|
||||||
request={async (params) => {
|
request={async (params, sort, filter) => {
|
||||||
if (!siteId) return { data: [], success: true };
|
if (!siteId) return { data: [], success: true };
|
||||||
|
const { current, pageSize, ...rest } = params || {};
|
||||||
|
const where = { ...rest, ...(filter || {}) };
|
||||||
|
let orderObj: Record<string, 'asc' | 'desc'> | undefined = undefined;
|
||||||
|
if (sort && typeof sort === 'object') {
|
||||||
|
const [field, dir] = Object.entries(sort)[0] || [];
|
||||||
|
if (field && dir) {
|
||||||
|
orderObj = { [field]: dir === 'descend' ? 'desc' : 'asc' };
|
||||||
|
}
|
||||||
|
}
|
||||||
const response = await request(`/site-api/${siteId}/subscriptions`, {
|
const response = await request(`/site-api/${siteId}/subscriptions`, {
|
||||||
params: {
|
params: {
|
||||||
...params,
|
page: current,
|
||||||
page: params.current,
|
page_size: pageSize,
|
||||||
per_page: params.pageSize,
|
where,
|
||||||
|
...(orderObj ? { order: orderObj } : {}),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -163,7 +173,8 @@ const SubscriptionsPage: React.FC = () => {
|
||||||
title="批量导出"
|
title="批量导出"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!siteId) return;
|
if (!siteId) return;
|
||||||
const res = await request(`/site-api/${siteId}/subscriptions/export`, { params: {} });
|
const idsParam = selectedRowKeys.length ? (selectedRowKeys as any[]).join(',') : undefined;
|
||||||
|
const res = await request(`/site-api/${siteId}/subscriptions/export`, { params: { ids: idsParam } });
|
||||||
if (res?.success && res?.data?.csv) {
|
if (res?.success && res?.data?.csv) {
|
||||||
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
@ -177,8 +188,23 @@ const SubscriptionsPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>,
|
/>,
|
||||||
<Button title="批量删除" danger icon={<DeleteFilled />} onClick={() => message.info('订阅删除未实现')} />
|
<Popconfirm
|
||||||
]}n />
|
title="确定批量删除选中项吗?"
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
onConfirm={() => {
|
||||||
|
// 条件判断 如果当前未选择任何行则直接返回
|
||||||
|
if (!selectedRowKeys || selectedRowKeys.length === 0) {
|
||||||
|
message.warning('请选择要删除的订阅');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 暂未实现批量删除接口 进行用户提示
|
||||||
|
message.info('订阅删除未实现');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button title="批量删除" danger icon={<DeleteFilled />} />
|
||||||
|
</Popconfirm>
|
||||||
|
]} />
|
||||||
<Drawer
|
<Drawer
|
||||||
open={drawerOpen}
|
open={drawerOpen}
|
||||||
title={drawerTitle}
|
title={drawerTitle}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ export async function siteapicontrollerGetcustomers(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
},
|
},
|
||||||
|
|
@ -70,6 +74,10 @@ export async function siteapicontrollerExportcustomers(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -105,6 +113,10 @@ export async function siteapicontrollerGetmedia(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -140,6 +152,10 @@ export async function siteapicontrollerExportmedia(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -156,6 +172,10 @@ export async function siteapicontrollerGetorders(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -210,6 +230,10 @@ export async function siteapicontrollerExportorders(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -247,6 +271,10 @@ export async function siteapicontrollerGetproducts(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
},
|
},
|
||||||
|
|
@ -302,6 +330,10 @@ export async function siteapicontrollerExportproducts(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -318,6 +350,10 @@ export async function siteapicontrollerExportproductsspecial(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -377,6 +413,10 @@ export async function siteapicontrollerGetsubscriptions(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
},
|
},
|
||||||
|
|
@ -394,6 +434,10 @@ export async function siteapicontrollerExportsubscriptions(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -455,6 +499,29 @@ export async function siteapicontrollerDeletecustomer(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 GET /site-api/${param1}/customers/${param0}/orders */
|
||||||
|
export async function siteapicontrollerGetcustomerorders(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.siteapicontrollerGetcustomerordersParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { customerId: param0, siteId: param1, ...queryParams } = params;
|
||||||
|
return request<API.UnifiedOrderPaginationDTO>(
|
||||||
|
`/site-api/${param1}/customers/${param0}/orders`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
|
},
|
||||||
|
...(options || {}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 此处后端没有提供注释 PUT /site-api/${param1}/media/${param0} */
|
/** 此处后端没有提供注释 PUT /site-api/${param1}/media/${param0} */
|
||||||
export async function siteapicontrollerUpdatemedia(
|
export async function siteapicontrollerUpdatemedia(
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
|
|
||||||
|
|
@ -1463,14 +1463,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1479,14 +1489,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1495,14 +1515,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1511,14 +1541,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1527,14 +1567,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1543,14 +1593,51 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
|
siteId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type siteapicontrollerGetcustomerordersParams = {
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 每页数量 */
|
||||||
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
|
/** 搜索关键词 */
|
||||||
|
search?: string;
|
||||||
|
/** 状态 */
|
||||||
|
status?: string;
|
||||||
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
|
orderby?: string;
|
||||||
|
/** 排序方式(兼容旧入参) */
|
||||||
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
|
customerId: number;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1564,14 +1651,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1580,14 +1677,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1606,14 +1713,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1627,14 +1744,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1643,14 +1770,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2055,8 +2192,18 @@ declare namespace API {
|
||||||
type UnifiedCustomerDTO = {
|
type UnifiedCustomerDTO = {
|
||||||
/** 客户ID */
|
/** 客户ID */
|
||||||
id?: Record<string, any>;
|
id?: Record<string, any>;
|
||||||
|
/** 头像URL */
|
||||||
|
avatar?: string;
|
||||||
/** 邮箱 */
|
/** 邮箱 */
|
||||||
email?: string;
|
email?: string;
|
||||||
|
/** 订单总数 */
|
||||||
|
orders?: number;
|
||||||
|
/** 总花费 */
|
||||||
|
total_spend?: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
date_created?: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
date_modified?: string;
|
||||||
/** 名 */
|
/** 名 */
|
||||||
first_name?: string;
|
first_name?: string;
|
||||||
/** 姓 */
|
/** 姓 */
|
||||||
|
|
@ -2084,6 +2231,8 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 总页数 */
|
/** 总页数 */
|
||||||
totalPages?: number;
|
totalPages?: number;
|
||||||
};
|
};
|
||||||
|
|
@ -2112,6 +2261,8 @@ declare namespace API {
|
||||||
source_url?: string;
|
source_url?: string;
|
||||||
/** 创建时间 */
|
/** 创建时间 */
|
||||||
date_created?: string;
|
date_created?: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
date_modified?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UnifiedMediaPaginationDTO = {
|
type UnifiedMediaPaginationDTO = {
|
||||||
|
|
@ -2123,6 +2274,8 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 总页数 */
|
/** 总页数 */
|
||||||
totalPages?: number;
|
totalPages?: number;
|
||||||
};
|
};
|
||||||
|
|
@ -2160,6 +2313,8 @@ declare namespace API {
|
||||||
payment_method?: string;
|
payment_method?: string;
|
||||||
/** 创建时间 */
|
/** 创建时间 */
|
||||||
date_created?: string;
|
date_created?: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
date_modified?: string;
|
||||||
/** 原始数据 */
|
/** 原始数据 */
|
||||||
raw?: any;
|
raw?: any;
|
||||||
};
|
};
|
||||||
|
|
@ -2173,6 +2328,8 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 总页数 */
|
/** 总页数 */
|
||||||
totalPages?: number;
|
totalPages?: number;
|
||||||
};
|
};
|
||||||
|
|
@ -2223,6 +2380,8 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 总页数 */
|
/** 总页数 */
|
||||||
totalPages?: number;
|
totalPages?: number;
|
||||||
};
|
};
|
||||||
|
|
@ -2232,14 +2391,24 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 搜索关键词 */
|
/** 搜索关键词 */
|
||||||
search?: string;
|
search?: string;
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string;
|
||||||
/** 排序字段 */
|
/** 客户ID,用于筛选订单 */
|
||||||
|
customer_id?: number;
|
||||||
|
/** 过滤条件对象 */
|
||||||
|
where?: Record<string, any>;
|
||||||
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式 */
|
/** 排序方式(兼容旧入参) */
|
||||||
order?: string;
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UnifiedSubscriptionDTO = {
|
type UnifiedSubscriptionDTO = {
|
||||||
|
|
@ -2253,6 +2422,10 @@ declare namespace API {
|
||||||
billing_period?: string;
|
billing_period?: string;
|
||||||
/** 计费间隔 */
|
/** 计费间隔 */
|
||||||
billing_interval?: number;
|
billing_interval?: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
date_created?: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
date_modified?: string;
|
||||||
/** 开始时间 */
|
/** 开始时间 */
|
||||||
start_date?: string;
|
start_date?: string;
|
||||||
/** 下次支付时间 */
|
/** 下次支付时间 */
|
||||||
|
|
@ -2272,6 +2445,8 @@ declare namespace API {
|
||||||
page?: number;
|
page?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
per_page?: number;
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
/** 总页数 */
|
/** 总页数 */
|
||||||
totalPages?: number;
|
totalPages?: number;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue