feat: 添加产品工具, 重构产品 #31
|
|
@ -310,11 +310,6 @@ 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',
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ export const layout = (): ProLayoutProps => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const request: RequestConfig = {
|
export const request: RequestConfig = {
|
||||||
baseURL: UMI_APP_API_URL,
|
baseURL: '/api', // baseURL: UMI_APP_API_URL,
|
||||||
requestInterceptors: [
|
requestInterceptors: [
|
||||||
(url: string, options: any) => {
|
(url: string, options: any) => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
|
|
||||||
|
|
@ -296,45 +296,7 @@ const PermutationPage: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
scroll={{ x: 'max-content' }}
|
scroll={{ x: 'max-content' }}
|
||||||
search={false}
|
search={false}
|
||||||
toolBarRender={() => [
|
toolBarRender={false}
|
||||||
<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,17 +1,21 @@
|
||||||
import {
|
import {
|
||||||
ActionType,
|
ActionType,
|
||||||
DrawerForm,
|
|
||||||
ProColumns,
|
ProColumns,
|
||||||
ProFormDependency,
|
|
||||||
ProFormInstance,
|
|
||||||
ProFormSelect,
|
|
||||||
ProFormSwitch,
|
|
||||||
ProFormText,
|
|
||||||
ProTable,
|
ProTable,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
|
import {
|
||||||
|
sitecontrollerCreate,
|
||||||
|
sitecontrollerDisable,
|
||||||
|
sitecontrollerList,
|
||||||
|
sitecontrollerUpdate,
|
||||||
|
} from '@/servers/api/site';
|
||||||
|
import { wpproductcontrollerSyncproducts } from '@/servers/api/wpProduct';
|
||||||
|
import { ordercontrollerSyncorder } from '@/servers/api/order';
|
||||||
|
import { subscriptioncontrollerSync } from '@/servers/api/subscription';
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
import { Button, message, notification, Popconfirm, Space, Tag } from 'antd';
|
import { Button, message, notification, Popconfirm, Space, Tag } from 'antd';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
|
import EditSiteForm from '../Shop/EditSiteForm'; // 引入重构后的表单组件
|
||||||
|
|
||||||
// 区域数据项类型
|
// 区域数据项类型
|
||||||
interface AreaItem {
|
interface AreaItem {
|
||||||
|
|
@ -26,11 +30,12 @@ interface StockPointItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 站点数据项类型(前端不包含密钥字段,后端列表不返回密钥)
|
// 站点数据项类型(前端不包含密钥字段,后端列表不返回密钥)
|
||||||
interface SiteItem {
|
export interface SiteItem {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
apiUrl?: string;
|
apiUrl?: string;
|
||||||
|
websiteUrl?: string; // 网站地址
|
||||||
type?: 'woocommerce' | 'shopyy';
|
type?: 'woocommerce' | 'shopyy';
|
||||||
skuPrefix?: string;
|
skuPrefix?: string;
|
||||||
isDisabled: number;
|
isDisabled: number;
|
||||||
|
|
@ -38,24 +43,8 @@ interface SiteItem {
|
||||||
stockPoints?: StockPointItem[];
|
stockPoints?: StockPointItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建/更新表单的值类型,包含可选的密钥字段
|
|
||||||
interface SiteFormValues {
|
|
||||||
name: string;
|
|
||||||
description?: string;
|
|
||||||
apiUrl?: string;
|
|
||||||
type?: 'woocommerce' | 'shopyy';
|
|
||||||
isDisabled?: boolean;
|
|
||||||
consumerKey?: string; // WooCommerce REST API 的 consumer key
|
|
||||||
consumerSecret?: string; // WooCommerce REST API 的 consumer secret
|
|
||||||
token?: string; // Shopyy token
|
|
||||||
skuPrefix?: string;
|
|
||||||
areas?: string[];
|
|
||||||
stockPointIds?: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const SiteList: React.FC = () => {
|
const SiteList: React.FC = () => {
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const formRef = useRef<ProFormInstance>();
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [editing, setEditing] = useState<SiteItem | null>(null);
|
const [editing, setEditing] = useState<SiteItem | null>(null);
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
|
|
@ -73,24 +62,27 @@ const SiteList: React.FC = () => {
|
||||||
try {
|
try {
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
// 同步产品
|
// 同步产品
|
||||||
const prodRes = await request(`/wp_product/sync/${id}`, { method: 'POST' });
|
const prodRes = await wpproductcontrollerSyncproducts({ siteId: id });
|
||||||
if (prodRes) {
|
if (prodRes.success) {
|
||||||
stats.products.success += prodRes.successCount || 0;
|
stats.products.success += 1;
|
||||||
stats.products.fail += prodRes.failureCount || 0;
|
} else {
|
||||||
|
stats.products.fail += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步订单
|
// 同步订单
|
||||||
const orderRes = await request(`/order/syncOrder/${id}`, { method: 'POST' });
|
const orderRes = await ordercontrollerSyncorder({ siteId: id });
|
||||||
if (orderRes) {
|
if (orderRes.success) {
|
||||||
stats.orders.success += orderRes.successCount || 0;
|
stats.orders.success += 1;
|
||||||
stats.orders.fail += orderRes.failureCount || 0;
|
} else {
|
||||||
|
stats.orders.fail += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步订阅
|
// 同步订阅
|
||||||
const subRes = await request(`/subscription/sync/${id}`, { method: 'POST' });
|
const subRes = await subscriptioncontrollerSync({ siteId: id });
|
||||||
if (subRes) {
|
if (subRes.success) {
|
||||||
stats.subscriptions.success += subRes.successCount || 0;
|
stats.subscriptions.success += 1;
|
||||||
stats.subscriptions.fail += subRes.failureCount || 0;
|
} else {
|
||||||
|
stats.subscriptions.fail += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hide();
|
hide();
|
||||||
|
|
@ -115,39 +107,6 @@ const SiteList: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!open) return;
|
|
||||||
if (editing) {
|
|
||||||
formRef.current?.setFieldsValue({
|
|
||||||
name: editing.name,
|
|
||||||
description: editing.description,
|
|
||||||
apiUrl: editing.apiUrl,
|
|
||||||
type: editing.type,
|
|
||||||
skuPrefix: editing.skuPrefix,
|
|
||||||
isDisabled: !!editing.isDisabled,
|
|
||||||
consumerKey: undefined,
|
|
||||||
consumerSecret: undefined,
|
|
||||||
token: undefined,
|
|
||||||
areas: editing.areas?.map((area) => area.code) ?? [],
|
|
||||||
stockPointIds: editing.stockPoints?.map((sp) => sp.id) ?? [],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
formRef.current?.setFieldsValue({
|
|
||||||
name: undefined,
|
|
||||||
description: undefined,
|
|
||||||
apiUrl: undefined,
|
|
||||||
type: 'woocommerce',
|
|
||||||
skuPrefix: undefined,
|
|
||||||
isDisabled: false,
|
|
||||||
consumerKey: undefined,
|
|
||||||
consumerSecret: undefined,
|
|
||||||
token: undefined,
|
|
||||||
areas: [],
|
|
||||||
stockPointIds: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [open, editing]);
|
|
||||||
|
|
||||||
// 表格列定义
|
// 表格列定义
|
||||||
const columns: ProColumns<SiteItem>[] = [
|
const columns: ProColumns<SiteItem>[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -160,6 +119,13 @@ const SiteList: React.FC = () => {
|
||||||
{ title: '名称', dataIndex: 'name', width: 220 },
|
{ title: '名称', dataIndex: 'name', width: 220 },
|
||||||
{ title: '描述', dataIndex: 'description', width: 220, hideInSearch: true },
|
{ title: '描述', dataIndex: 'description', width: 220, hideInSearch: true },
|
||||||
{ title: 'API 地址', dataIndex: 'apiUrl', width: 280, hideInSearch: true },
|
{ title: 'API 地址', dataIndex: 'apiUrl', width: 280, hideInSearch: true },
|
||||||
|
{
|
||||||
|
title: '网站地址',
|
||||||
|
dataIndex: 'websiteUrl',
|
||||||
|
width: 280,
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (text) => <a href={text as string} target="_blank" rel="noopener noreferrer">{text}</a>
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'SKU 前缀',
|
title: 'SKU 前缀',
|
||||||
dataIndex: 'skuPrefix',
|
dataIndex: 'skuPrefix',
|
||||||
|
|
@ -176,24 +142,6 @@ const SiteList: React.FC = () => {
|
||||||
{ label: 'Shopyy', value: 'shopyy' },
|
{ label: 'Shopyy', value: 'shopyy' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// title: '区域',
|
|
||||||
// dataIndex: 'areas',
|
|
||||||
// width: 200,
|
|
||||||
// hideInSearch: true,
|
|
||||||
// render: (_, row) => {
|
|
||||||
// if (!row.areas || row.areas.length === 0) {
|
|
||||||
// return <Tag color="blue">全球</Tag>;
|
|
||||||
// }
|
|
||||||
// return (
|
|
||||||
// <Space wrap>
|
|
||||||
// {row.areas.map((area) => (
|
|
||||||
// <Tag key={area.code}>{area.name}</Tag>
|
|
||||||
// ))}
|
|
||||||
// </Space>
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
title: '关联仓库',
|
title: '关联仓库',
|
||||||
dataIndex: 'stockPoints',
|
dataIndex: 'stockPoints',
|
||||||
|
|
@ -254,10 +202,7 @@ const SiteList: React.FC = () => {
|
||||||
}
|
}
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
await request(`/site/disable/${row.id}`, {
|
await sitecontrollerDisable({ id: String(row.id) }, { disabled: !row.isDisabled });
|
||||||
method: 'PUT',
|
|
||||||
data: { disabled: !row.isDisabled },
|
|
||||||
});
|
|
||||||
message.success('更新成功');
|
message.success('更新成功');
|
||||||
actionRef.current?.reload();
|
actionRef.current?.reload();
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|
@ -277,21 +222,17 @@ const SiteList: React.FC = () => {
|
||||||
// 表格数据请求
|
// 表格数据请求
|
||||||
const tableRequest = async (params: Record<string, any>) => {
|
const tableRequest = async (params: Record<string, any>) => {
|
||||||
try {
|
try {
|
||||||
const { current = 1, pageSize = 10, name, type } = params;
|
const { current, pageSize, name, type } = params;
|
||||||
const resp = await request('/site/list', {
|
const resp = await sitecontrollerList({
|
||||||
method: 'GET',
|
current,
|
||||||
params: {
|
pageSize,
|
||||||
current,
|
keyword: name || undefined,
|
||||||
pageSize,
|
type: type || undefined,
|
||||||
keyword: name || undefined,
|
|
||||||
type: type || undefined,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const { success, data, message: errMsg } = resp as any;
|
// 假设 resp 直接就是后端返回的结构,包含 items 和 total
|
||||||
if (!success) throw new Error(errMsg || '获取失败');
|
|
||||||
return {
|
return {
|
||||||
data: (data?.items ?? []) as SiteItem[],
|
data: (resp?.data?.items ?? []) as SiteItem[],
|
||||||
total: data?.total ?? 0,
|
total: resp?.data?.total ?? 0,
|
||||||
success: true,
|
success: true,
|
||||||
};
|
};
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|
@ -300,80 +241,20 @@ const SiteList: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 提交创建/更新逻辑;编辑时未填写密钥则不提交(保持原值)
|
const handleFinish = async (values: any) => {
|
||||||
const handleSubmit = async (values: SiteFormValues) => {
|
|
||||||
try {
|
try {
|
||||||
const isShopyy = values.type === 'shopyy';
|
|
||||||
const apiUrl = isShopyy ? 'https://openapi.oemapps.com' : values.apiUrl;
|
|
||||||
|
|
||||||
if (editing) {
|
if (editing) {
|
||||||
const payload: Record<string, any> = {
|
await sitecontrollerUpdate({ id: String(editing.id) }, values);
|
||||||
// 仅提交存在的字段,避免覆盖为 null/空
|
message.success('更新成功');
|
||||||
...(values.name ? { name: values.name } : {}),
|
|
||||||
...(values.description ? { description: values.description } : {}),
|
|
||||||
...(apiUrl ? { apiUrl: apiUrl } : {}),
|
|
||||||
...(values.type ? { type: values.type } : {}),
|
|
||||||
...(typeof values.isDisabled === 'boolean'
|
|
||||||
? { isDisabled: values.isDisabled }
|
|
||||||
: {}),
|
|
||||||
...(values.skuPrefix ? { skuPrefix: values.skuPrefix } : {}),
|
|
||||||
areas: values.areas ?? [],
|
|
||||||
stockPointIds: values.stockPointIds ?? [],
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isShopyy) {
|
|
||||||
if (values.token && values.token.trim()) {
|
|
||||||
payload.token = values.token.trim();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 仅当输入了新密钥时才提交,未输入则保持原本值
|
|
||||||
if (values.consumerKey && values.consumerKey.trim()) {
|
|
||||||
payload.consumerKey = values.consumerKey.trim();
|
|
||||||
}
|
|
||||||
if (values.consumerSecret && values.consumerSecret.trim()) {
|
|
||||||
payload.consumerSecret = values.consumerSecret.trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await request(`/site/update/${editing.id}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
data: payload,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
if (isShopyy) {
|
await sitecontrollerCreate(values);
|
||||||
if (!values.token) {
|
message.success('创建成功');
|
||||||
throw new Error('Token is required for Shopyy');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 新增站点时要求填写 consumerKey 和 consumerSecret
|
|
||||||
if (!values.consumerKey || !values.consumerSecret) {
|
|
||||||
throw new Error('Consumer Key and Secret are required for WooCommerce');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await request('/site/create', {
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
name: values.name,
|
|
||||||
description: values.description,
|
|
||||||
apiUrl: apiUrl,
|
|
||||||
type: values.type || 'woocommerce',
|
|
||||||
consumerKey: isShopyy ? undefined : values.consumerKey,
|
|
||||||
consumerSecret: isShopyy ? undefined : values.consumerSecret,
|
|
||||||
token: isShopyy ? values.token : undefined,
|
|
||||||
skuPrefix: values.skuPrefix,
|
|
||||||
areas: values.areas ?? [],
|
|
||||||
stockPointIds: values.stockPointIds ?? [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
message.success('提交成功');
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
setEditing(null);
|
|
||||||
actionRef.current?.reload();
|
actionRef.current?.reload();
|
||||||
return true;
|
return true;
|
||||||
} catch (e: any) {
|
} catch (error: any) {
|
||||||
message.error(e?.message || '提交失败');
|
message.error(error.message || '操作失败');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -381,7 +262,7 @@ const SiteList: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ProTable<SiteItem>
|
<ProTable<SiteItem>
|
||||||
scroll={{ x: 'max-content' }}
|
scroll={{ x: 'max-content' }}
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|
@ -392,161 +273,35 @@ const SiteList: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
toolBarRender={() => [
|
toolBarRender={() => [
|
||||||
<Button
|
<Button
|
||||||
key="new"
|
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditing(null);
|
setEditing(null);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
新增站点
|
新建站点
|
||||||
</Button>,
|
</Button>,
|
||||||
// 同步包括 orders subscriptions 等等
|
|
||||||
<Button
|
<Button
|
||||||
key="sync"
|
|
||||||
disabled={!selectedRowKeys.length}
|
disabled={!selectedRowKeys.length}
|
||||||
type="primary"
|
|
||||||
onClick={() => handleSync(selectedRowKeys as number[])}
|
onClick={() => handleSync(selectedRowKeys as number[])}
|
||||||
>
|
>
|
||||||
同步站点数据
|
批量同步
|
||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DrawerForm<SiteFormValues>
|
<EditSiteForm
|
||||||
title={editing ? '编辑站点' : '新增站点'}
|
|
||||||
open={open}
|
open={open}
|
||||||
onOpenChange={setOpen}
|
onOpenChange={(visible) => {
|
||||||
formRef={formRef}
|
setOpen(visible);
|
||||||
onFinish={handleSubmit}
|
if (!visible) {
|
||||||
>
|
setEditing(null);
|
||||||
{/* 站点名称,必填 */}
|
}
|
||||||
<ProFormText
|
}}
|
||||||
name="name"
|
initialValues={editing}
|
||||||
label="站点名称"
|
isEdit={!!editing}
|
||||||
placeholder="例如:本地商店"
|
onFinish={handleFinish}
|
||||||
rules={[{ required: true, message: '站点名称为必填项' }]}
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
<ProFormText
|
|
||||||
name="description"
|
|
||||||
label="描述"
|
|
||||||
placeholder="请输入站点描述"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 仓库选择 */}
|
|
||||||
<ProFormSelect
|
|
||||||
name="stockPointIds"
|
|
||||||
label="关联仓库"
|
|
||||||
mode="multiple"
|
|
||||||
placeholder="请选择关联仓库"
|
|
||||||
request={async () => {
|
|
||||||
try {
|
|
||||||
const resp = await request('/stock/stock-point/all', {
|
|
||||||
method: 'GET',
|
|
||||||
});
|
|
||||||
if (resp.success) {
|
|
||||||
return resp.data.map((item: any) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
} catch (e) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 区域选择 - 暂时隐藏 */}
|
|
||||||
{/* <ProFormSelect
|
|
||||||
name="areas"
|
|
||||||
label="区域"
|
|
||||||
mode="multiple"
|
|
||||||
placeholder="留空表示全球"
|
|
||||||
request={async () => {
|
|
||||||
try {
|
|
||||||
const resp = await request('/area', {
|
|
||||||
method: 'GET',
|
|
||||||
params: { pageSize: 1000 },
|
|
||||||
});
|
|
||||||
if (resp.success) {
|
|
||||||
return resp.data.list.map((area: AreaItem) => ({
|
|
||||||
label: area.name,
|
|
||||||
value: area.code,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
} catch (e) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
{/* 平台类型选择 */}
|
|
||||||
<ProFormSelect
|
|
||||||
name="type"
|
|
||||||
label="平台"
|
|
||||||
options={[
|
|
||||||
{ label: 'WooCommerce', value: 'woocommerce' },
|
|
||||||
{ label: 'Shopyy', value: 'shopyy' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ProFormDependency name={['type']}>
|
|
||||||
{({ type }) => {
|
|
||||||
const isShopyy = type === 'shopyy';
|
|
||||||
return isShopyy ? (
|
|
||||||
<>
|
|
||||||
<ProFormText
|
|
||||||
name="apiUrl"
|
|
||||||
label="API 地址"
|
|
||||||
disabled
|
|
||||||
initialValue="https://openapi.oemapps.com"
|
|
||||||
placeholder="https://openapi.oemapps.com"
|
|
||||||
/>
|
|
||||||
<ProFormText
|
|
||||||
name="token"
|
|
||||||
label="Token"
|
|
||||||
placeholder={editing ? '留空表示不修改' : '必填'}
|
|
||||||
rules={editing ? [] : [{ required: true, message: 'Token 为必填项' }]}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<ProFormText
|
|
||||||
name="apiUrl"
|
|
||||||
label="API 地址"
|
|
||||||
placeholder="例如:https://shop.example.com"
|
|
||||||
rules={[{ required: true, message: 'API 地址为必填项' }]}
|
|
||||||
/>
|
|
||||||
{/* WooCommerce REST consumer key */}
|
|
||||||
<ProFormText
|
|
||||||
name="consumerKey"
|
|
||||||
label="Key"
|
|
||||||
placeholder={editing ? '留空表示不修改' : '必填'}
|
|
||||||
rules={editing ? [] : [{ required: true, message: 'Key 为必填项' }]}
|
|
||||||
/>
|
|
||||||
{/* WooCommerce REST consumer secret */}
|
|
||||||
<ProFormText
|
|
||||||
name="consumerSecret"
|
|
||||||
label="Secret"
|
|
||||||
placeholder={editing ? '留空表示不修改' : '必填'}
|
|
||||||
rules={editing ? [] : [{ required: true, message: 'Secret 为必填项' }]}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</ProFormDependency>
|
|
||||||
|
|
||||||
{editing && <ProFormSwitch name="isDisabled" label="禁用" />}
|
|
||||||
|
|
||||||
<ProFormText
|
|
||||||
name="skuPrefix"
|
|
||||||
label="SKU 前缀"
|
|
||||||
placeholder={editing ? '留空表示不修改' : '可选'}
|
|
||||||
/>
|
|
||||||
</DrawerForm>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -144,18 +144,6 @@ 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',
|
||||||
|
|
@ -173,23 +161,6 @@ 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',
|
||||||
|
|
@ -268,21 +239,9 @@ 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: items,
|
data: data?.items || [],
|
||||||
success: true,
|
success: true,
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
|
|
@ -411,19 +370,42 @@ const CustomerPage: React.FC = () => {
|
||||||
search={false}
|
search={false}
|
||||||
pagination={{ pageSize: 20 }}
|
pagination={{ pageSize: 20 }}
|
||||||
columns={[
|
columns={[
|
||||||
{ title: '订单ID', dataIndex: 'id' },
|
{ title: '订单号', dataIndex: 'number', copyable: true },
|
||||||
{ title: '订单号', dataIndex: 'number' },
|
{
|
||||||
{ title: '状态', dataIndex: 'status' },
|
title: '客户邮箱',
|
||||||
{ title: '币种', dataIndex: 'currency' },
|
dataIndex: 'email',
|
||||||
{ title: '金额', dataIndex: 'total' },
|
copyable: true,
|
||||||
{ title: '创建时间', dataIndex: 'date_created', valueType: 'dateTime' },
|
render: () => {
|
||||||
|
return ordersCustomer?.email;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: '支付时间', dataIndex: 'date_paid', valueType: 'dateTime', hideInSearch: true },
|
||||||
|
{ title: '订单金额', dataIndex: 'total', hideInSearch: true },
|
||||||
|
{ title: '状态', dataIndex: 'status', hideInSearch: true },
|
||||||
|
{ title: '来源', dataIndex: 'created_via', hideInSearch: true },
|
||||||
|
{
|
||||||
|
title: '订单内容',
|
||||||
|
dataIndex: 'line_items',
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (_, record) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{record.line_items?.map((item: any) => (
|
||||||
|
<div key={item.id}>
|
||||||
|
{item.name} x {item.quantity}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
request={async (params) => {
|
request={async (params) => {
|
||||||
if (!siteId || !ordersCustomer?.id) return { data: [], total: 0, success: true };
|
if (!siteId || !ordersCustomer?.id) return { data: [], total: 0, success: true };
|
||||||
const res = await request(`/site-api/${siteId}/customers/${ordersCustomer.id}/orders`, {
|
const res = await request(`/site-api/${siteId}/customers/${ordersCustomer.id}/orders`, {
|
||||||
params: {
|
params: {
|
||||||
page: params.current,
|
page: params.current,
|
||||||
page_size: params.pageSize,
|
per_page: params.pageSize,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!res?.success) {
|
if (!res?.success) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
|
||||||
|
import {
|
||||||
|
DrawerForm,
|
||||||
|
ProFormDependency,
|
||||||
|
ProFormSelect,
|
||||||
|
ProFormSwitch,
|
||||||
|
ProFormText,
|
||||||
|
ProFormTextArea,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { Form } from 'antd';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { areacontrollerGetarealist } from '@/servers/api/area';
|
||||||
|
import { stockcontrollerGetallstockpoints } from '@/servers/api/stock';
|
||||||
|
|
||||||
|
// 定义组件的 props 类型
|
||||||
|
interface EditSiteFormProps {
|
||||||
|
open: boolean; // 控制抽屉表单的显示和隐藏
|
||||||
|
onOpenChange: (visible: boolean) => void; // 当抽屉表单显示状态改变时调用
|
||||||
|
onFinish: (values: any) => Promise<boolean | void>; // 表单提交成功时的回调
|
||||||
|
initialValues?: any; // 表单的初始值
|
||||||
|
isEdit: boolean; // 标记当前是编辑模式还是新建模式
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditSiteForm: React.FC<EditSiteFormProps> = ({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
onFinish,
|
||||||
|
initialValues,
|
||||||
|
isEdit,
|
||||||
|
}) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
// 当 initialValues 或 open 状态变化时, 更新表单的值
|
||||||
|
useEffect(() => {
|
||||||
|
// 如果抽屉是打开的
|
||||||
|
if (open) {
|
||||||
|
// 如果是编辑模式并且有初始值
|
||||||
|
if (isEdit && initialValues) {
|
||||||
|
// 编辑模式下, 设置表单值为初始值
|
||||||
|
form.setFieldsValue({
|
||||||
|
...initialValues,
|
||||||
|
isDisabled: initialValues.isDisabled === 1, // 将后端的 1/0 转换成 true/false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 新建模式或抽屉关闭时, 重置表单
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [initialValues, isEdit, open, form]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DrawerForm
|
||||||
|
title={isEdit ? '编辑站点' : '新建站点'}
|
||||||
|
form={form}
|
||||||
|
open={open}
|
||||||
|
onOpenChange={onOpenChange}
|
||||||
|
onFinish={async (values) => {
|
||||||
|
// 直接将表单值传递给 onFinish 回调
|
||||||
|
// 后端需要布尔值, 而 ProFormSwitch 已经提供了布尔值
|
||||||
|
return onFinish(values);
|
||||||
|
}}
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<ProFormText
|
||||||
|
name="name"
|
||||||
|
label="名称"
|
||||||
|
rules={[{ required: true, message: '请输入名称' }]}
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
|
<ProFormTextArea name="description" label="描述" placeholder="请输入描述" />
|
||||||
|
<ProFormText
|
||||||
|
name="apiUrl"
|
||||||
|
label="API 地址"
|
||||||
|
rules={[{ required: true, message: '请输入 API 地址' }]}
|
||||||
|
placeholder="请输入 API 地址"
|
||||||
|
/>
|
||||||
|
<ProFormText
|
||||||
|
name="websiteUrl"
|
||||||
|
label="网站地址"
|
||||||
|
placeholder="请输入网站地址"
|
||||||
|
/>
|
||||||
|
<ProFormSelect
|
||||||
|
name="type"
|
||||||
|
label="平台"
|
||||||
|
options={[
|
||||||
|
{ label: 'WooCommerce', value: 'woocommerce' },
|
||||||
|
{ label: 'Shopyy', value: 'shopyy' },
|
||||||
|
]}
|
||||||
|
rules={[{ required: true, message: '请选择平台' }]}
|
||||||
|
placeholder="请选择平台"
|
||||||
|
/>
|
||||||
|
{/* 根据选择的平台动态显示不同的认证字段 */}
|
||||||
|
<ProFormDependency name={['type']}>
|
||||||
|
{({ type }) => {
|
||||||
|
// 如果平台是 woocommerce
|
||||||
|
if (type === 'woocommerce') {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ProFormText
|
||||||
|
name="consumerKey"
|
||||||
|
label="Consumer Key"
|
||||||
|
rules={[{ required: !isEdit, message: '请输入 Consumer Key' }]}
|
||||||
|
placeholder={isEdit ? '留空表示不修改' : '请输入 Consumer Key'}
|
||||||
|
/>
|
||||||
|
<ProFormText
|
||||||
|
name="consumerSecret"
|
||||||
|
label="Consumer Secret"
|
||||||
|
rules={[{ required: !isEdit, message: '请输入 Consumer Secret' }]}
|
||||||
|
placeholder={isEdit ? '留空表示不修改' : '请输入 Consumer Secret'}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 如果平台是 shopyy
|
||||||
|
if (type === 'shopyy') {
|
||||||
|
return (
|
||||||
|
<ProFormText
|
||||||
|
name="token"
|
||||||
|
label="Token"
|
||||||
|
rules={[{ required: !isEdit, message: '请输入 Token' }]}
|
||||||
|
placeholder={isEdit ? '留空表示不修改' : '请输入 Token'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}}
|
||||||
|
</ProFormDependency>
|
||||||
|
<ProFormText name="skuPrefix" label="SKU 前缀" placeholder="请输入 SKU 前缀" />
|
||||||
|
<ProFormSelect
|
||||||
|
name="areas"
|
||||||
|
label="区域"
|
||||||
|
mode="multiple"
|
||||||
|
placeholder="请选择区域"
|
||||||
|
request={async () => {
|
||||||
|
// 从后端接口获取区域数据
|
||||||
|
const res = await areacontrollerGetarealist({ pageSize: 1000 });
|
||||||
|
// areacontrollerGetarealist 直接返回数组, 所以不需要 .data.list
|
||||||
|
return res.map((area: any) => ({ label: area.name, value: area.code }));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ProFormSelect
|
||||||
|
name="stockPointIds"
|
||||||
|
label="关联仓库"
|
||||||
|
mode="multiple"
|
||||||
|
placeholder="请选择关联仓库"
|
||||||
|
request={async () => {
|
||||||
|
// 从后端接口获取仓库数据
|
||||||
|
const res = await stockcontrollerGetallstockpoints();
|
||||||
|
// 使用可选链和空值合并运算符来安全地处理可能未定义的数据
|
||||||
|
return res?.data?.map((sp: any) => ({ label: sp.name, value: sp.id })) ?? [];
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ProFormSwitch name="isDisabled" label="是否禁用" />
|
||||||
|
</DrawerForm>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditSiteForm;
|
||||||
|
|
@ -1,52 +1,51 @@
|
||||||
|
import { EditOutlined } from '@ant-design/icons';
|
||||||
|
import { request } from '@umijs/max';
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
import {
|
import {
|
||||||
PageContainer,
|
PageContainer,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { Outlet, history, useLocation, useParams } from '@umijs/max';
|
import { Outlet, history, useLocation, useParams } from '@umijs/max';
|
||||||
import { App, Button, Card, Col, Menu, Row, Select, Spin } from 'antd';
|
import { Button, Card, Col, Menu, Row, Select, Spin, message } from 'antd';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import EditSiteForm from './EditSiteForm';
|
||||||
|
import type { SiteItem } from '../List/index';
|
||||||
|
|
||||||
const ShopLayout: React.FC = () => {
|
const ShopLayout: React.FC = () => {
|
||||||
const [sites, setSites] = useState<{ label: string; value: number }[]>([]);
|
const [sites, setSites] = useState<any[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const { siteId } = useParams<{ siteId: string }>();
|
const { siteId } = useParams<{ siteId: string }>();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { message } = App.useApp();
|
|
||||||
|
const [editModalOpen, setEditModalOpen] = useState(false);
|
||||||
|
const [editingSite, setEditingSite] = useState<SiteItem | null>(null);
|
||||||
|
|
||||||
|
const fetchSites = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const { data = [] } = await sitecontrollerAll();
|
||||||
|
setSites(data);
|
||||||
|
|
||||||
|
if (!siteId && data.length > 0) {
|
||||||
|
history.replace(`/site/shop/${data[0].id}/products`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch sites', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchSites = async () => {
|
|
||||||
try {
|
|
||||||
const { data = [] } = await sitecontrollerAll();
|
|
||||||
const siteOptions = data.map((item: any) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
}));
|
|
||||||
setSites(siteOptions);
|
|
||||||
|
|
||||||
// 如果 URL 中没有 siteId,且有站点数据,默认跳转到第一个站点的 products 页面
|
|
||||||
if (!siteId && siteOptions.length > 0) {
|
|
||||||
history.replace(`/site/shop/${siteOptions[0].value}/products`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch sites', error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fetchSites();
|
fetchSites();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSiteChange = (value: number) => {
|
const handleSiteChange = (value: number) => {
|
||||||
// 切换站点时,保持当前的功能模块(products/orders/etc),只改变 siteId
|
|
||||||
const currentPath = location.pathname;
|
const currentPath = location.pathname;
|
||||||
const parts = currentPath.split('/');
|
const parts = currentPath.split('/');
|
||||||
// 假设路径结构是 /site/shop/:siteId/module...
|
|
||||||
// parts: ['', 'site', 'shop', '123', 'products']
|
|
||||||
if (parts.length >= 5) {
|
if (parts.length >= 5) {
|
||||||
parts[3] = String(value);
|
parts[3] = String(value);
|
||||||
history.push(parts.join('/'));
|
history.push(parts.join('/'));
|
||||||
} else {
|
} else {
|
||||||
// Fallback
|
|
||||||
history.push(`/site/shop/${value}/products`);
|
history.push(`/site/shop/${value}/products`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -56,18 +55,14 @@ const ShopLayout: React.FC = () => {
|
||||||
history.push(`/site/shop/${siteId}/${e.key}`);
|
history.push(`/site/shop/${siteId}/${e.key}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取当前选中的菜单项
|
|
||||||
const getSelectedKey = () => {
|
const getSelectedKey = () => {
|
||||||
const parts = location.pathname.split('/');
|
const parts = location.pathname.split('/');
|
||||||
// /site/shop/:siteId/:module
|
|
||||||
if (parts.length >= 5) {
|
if (parts.length >= 5) {
|
||||||
return parts[4]; // products, orders, subscriptions, logistics
|
return parts[4];
|
||||||
}
|
}
|
||||||
return 'products';
|
return 'products';
|
||||||
};
|
};
|
||||||
|
|
||||||
// 已移除店铺同步逻辑,页面加载即从站点实时拉取数据
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Spin
|
<Spin
|
||||||
|
|
@ -77,8 +72,33 @@ const ShopLayout: React.FC = () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleFinish = async (values: any) => {
|
||||||
|
if (!editingSite) {
|
||||||
|
message.error('未找到要编辑的站点');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await request(`/site/${editingSite.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
data: values,
|
||||||
|
});
|
||||||
|
message.success('更新成功');
|
||||||
|
setEditModalOpen(false);
|
||||||
|
fetchSites(); // 重新获取站点列表以更新数据
|
||||||
|
return true;
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message || '操作失败');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer header={{ title: null, breadcrumb: undefined }}>
|
<PageContainer
|
||||||
|
header={{ title: null, breadcrumb: undefined }}
|
||||||
|
contentStyle={{
|
||||||
|
padding: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Row gutter={16} style={{ height: 'calc(100vh - 100px)' }}>
|
<Row gutter={16} style={{ height: 'calc(100vh - 100px)' }}>
|
||||||
<Col span={4} style={{ height: '100%' }}>
|
<Col span={4} style={{ height: '100%' }}>
|
||||||
<Card
|
<Card
|
||||||
|
|
@ -86,17 +106,30 @@ const ShopLayout: React.FC = () => {
|
||||||
style={{ height: '100%', overflow: 'hidden' }}
|
style={{ height: '100%', overflow: 'hidden' }}
|
||||||
>
|
>
|
||||||
<div style={{ padding: '0 10px 16px' }}>
|
<div style={{ padding: '0 10px 16px' }}>
|
||||||
<div style={{ marginBottom: 8, color: '#666', fontSize: '12px' }}>选择店铺:</div>
|
<div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}>
|
||||||
<Select
|
<Select
|
||||||
style={{ width: '100%' }}
|
style={{ flex: 1 }}
|
||||||
placeholder="请选择店铺"
|
placeholder="请选择店铺"
|
||||||
options={sites}
|
options={sites.map(site => ({ label: site.name, value: site.id }))}
|
||||||
value={siteId ? Number(siteId) : undefined}
|
value={siteId ? Number(siteId) : undefined}
|
||||||
onChange={handleSiteChange}
|
onChange={handleSiteChange}
|
||||||
showSearch
|
showSearch
|
||||||
optionFilterProp="label"
|
optionFilterProp="label"
|
||||||
/>
|
/>
|
||||||
{/* 店铺同步功能已废弃 */}
|
<Button
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
style={{ marginLeft: 8 }}
|
||||||
|
onClick={() => {
|
||||||
|
const currentSite = sites.find(site => site.id === Number(siteId));
|
||||||
|
if (currentSite) {
|
||||||
|
setEditingSite(currentSite);
|
||||||
|
setEditModalOpen(true);
|
||||||
|
} else {
|
||||||
|
message.warning('请先选择一个店铺');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 1, overflowY: 'auto' }}>
|
<div style={{ flex: 1, overflowY: 'auto' }}>
|
||||||
<Menu
|
<Menu
|
||||||
|
|
@ -108,21 +141,31 @@ const ShopLayout: React.FC = () => {
|
||||||
{ key: 'products', label: '产品管理' },
|
{ key: 'products', label: '产品管理' },
|
||||||
{ key: 'orders', label: '订单管理' },
|
{ key: 'orders', label: '订单管理' },
|
||||||
{ key: 'subscriptions', label: '订阅管理' },
|
{ key: 'subscriptions', label: '订阅管理' },
|
||||||
{ key: 'logistics', label: '物流管理' },
|
|
||||||
{ key: 'media', label: '媒体管理' },
|
{ key: 'media', label: '媒体管理' },
|
||||||
{ key: 'customers', label: '客户管理' },
|
{ key: 'customers', label: '客户管理' },
|
||||||
|
{ key: 'reviews', label: '评论管理' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={20} style={{ height: '100%', overflowY: 'auto' }}>
|
<Col span={20} style={{ height: '100%', overflowY: 'auto' }}>
|
||||||
{/* 这里的 Outlet 会渲染子路由组件,如 Products, Orders 等 */}
|
|
||||||
{siteId ? <Outlet /> : <div>请选择店铺</div>}
|
{siteId ? <Outlet /> : <div>请选择店铺</div>}
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{/* 店铺同步弹窗已移除 */}
|
<EditSiteForm
|
||||||
|
open={editModalOpen}
|
||||||
|
onOpenChange={(visible: boolean) => {
|
||||||
|
setEditModalOpen(visible);
|
||||||
|
if (!visible) {
|
||||||
|
setEditingSite(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
initialValues={editingSite}
|
||||||
|
isEdit={!!editingSite}
|
||||||
|
onFinish={handleFinish}
|
||||||
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -209,41 +209,28 @@ const LogisticsPage: React.FC = () => {
|
||||||
<Button onClick={handleBatchPrint} type="primary">
|
<Button onClick={handleBatchPrint} type="primary">
|
||||||
批量打印
|
批量打印
|
||||||
</Button>
|
</Button>
|
||||||
<Popconfirm
|
<Button
|
||||||
title="确定批量删除选中项吗?"
|
danger
|
||||||
okText="确定"
|
type="primary"
|
||||||
cancelText="取消"
|
onClick={async () => {
|
||||||
onConfirm={async () => {
|
|
||||||
// 条件判断 如果当前未选择任何行则直接返回
|
|
||||||
if (!selectedRows || selectedRows.length === 0) return;
|
|
||||||
try {
|
try {
|
||||||
// 进入批量删除处理流程
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
let successCount = 0;
|
let ok = 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>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,30 @@ const OrdersPage: React.FC = () => {
|
||||||
dataIndex: 'customer_name',
|
dataIndex: 'customer_name',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '商品',
|
||||||
|
dataIndex: 'line_items',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
render: (_, record) => {
|
||||||
|
// 检查 record.line_items 是否是数组并且有内容
|
||||||
|
if (Array.isArray(record.line_items) && record.line_items.length > 0) {
|
||||||
|
// 遍历 line_items 数组, 显示每个商品的名称和数量
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{record.line_items.map((item: any) => (
|
||||||
|
<div key={item.id}>
|
||||||
|
{`${item.name} x ${item.quantity}`}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 如果 line_items 不存在或不是数组, 则显示占位符
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '支付方式',
|
title: '支付方式',
|
||||||
dataIndex: 'payment_method',
|
dataIndex: 'payment_method',
|
||||||
|
|
@ -372,7 +396,7 @@ const OrdersPage: React.FC = () => {
|
||||||
const res = await request(`/site-api/${siteId}/orders`, {
|
const res = await request(`/site-api/${siteId}/orders`, {
|
||||||
params: {
|
params: {
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 1,
|
per_page: 1,
|
||||||
where: { ...baseWhere, status: rawStatus },
|
where: { ...baseWhere, status: rawStatus },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import {
|
||||||
ProTable,
|
ProTable,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { App, Button, Divider, Popconfirm, Tag } from 'antd';
|
import { App, Button, Divider, Popconfirm, Tag } from 'antd';
|
||||||
import { DeleteFilled } from '@ant-design/icons';
|
import { DeleteFilled, LinkOutlined } from '@ant-design/icons';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
BatchEditProducts,
|
BatchEditProducts,
|
||||||
|
|
@ -21,13 +21,13 @@ import {
|
||||||
CreateProduct,
|
CreateProduct,
|
||||||
} from '../components/Product/Forms';
|
} from '../components/Product/Forms';
|
||||||
import { TagConfig } from '../components/Product/utils';
|
import { TagConfig } from '../components/Product/utils';
|
||||||
|
|
||||||
const ProductsPage: React.FC = () => {
|
const ProductsPage: React.FC = () => {
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
const [selectedRows, setSelectedRows] = useState<any[]>([]); // Use any or unified DTO type
|
const [selectedRows, setSelectedRows] = useState<any[]>([]); // Use any or unified DTO type
|
||||||
const { siteId } = useParams<{ siteId: string }>();
|
const { siteId } = useParams<{ siteId: string }>();
|
||||||
|
const [siteInfo, setSiteInfo] = useState<any>();
|
||||||
const [config, setConfig] = useState<TagConfig>({
|
const [config, setConfig] = useState<TagConfig>({
|
||||||
brands: [],
|
brands: [],
|
||||||
fruits: [],
|
fruits: [],
|
||||||
|
|
@ -43,6 +43,20 @@ const ProductsPage: React.FC = () => {
|
||||||
actionRef.current?.reload();
|
actionRef.current?.reload();
|
||||||
}, [siteId]);
|
}, [siteId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadSiteInfo = async () => {
|
||||||
|
try {
|
||||||
|
const res = await request(`/site/get/${siteId}`);
|
||||||
|
if (res?.success && res?.data) {
|
||||||
|
setSiteInfo(res.data);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
};
|
||||||
|
if (siteId) {
|
||||||
|
loadSiteInfo();
|
||||||
|
}
|
||||||
|
}, [siteId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAllConfigs = async () => {
|
const fetchAllConfigs = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -86,8 +100,9 @@ const ProductsPage: React.FC = () => {
|
||||||
fetchAllConfigs();
|
fetchAllConfigs();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const columns: ProColumns<any>[] = [
|
const columns: ProColumns<API.UnifiedProductDTO>[] = [
|
||||||
{
|
{
|
||||||
|
// ID
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
|
|
@ -98,36 +113,43 @@ const ProductsPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// sku
|
||||||
title: 'sku',
|
title: 'sku',
|
||||||
dataIndex: 'sku',
|
dataIndex: 'sku',
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 名称
|
||||||
title: '名称',
|
title: '名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 产品状态
|
||||||
title: '产品状态',
|
title: '产品状态',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
valueType: 'select',
|
valueType: 'select',
|
||||||
valueEnum: PRODUCT_STATUS_ENUM,
|
valueEnum: PRODUCT_STATUS_ENUM,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 产品类型
|
||||||
title: '产品类型',
|
title: '产品类型',
|
||||||
dataIndex: 'type',
|
dataIndex: 'type',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 库存状态
|
||||||
title: '库存状态',
|
title: '库存状态',
|
||||||
dataIndex: 'stock_status',
|
dataIndex: 'stock_status',
|
||||||
valueType: 'select',
|
valueType: 'select',
|
||||||
valueEnum: PRODUCT_STOCK_STATUS_ENUM,
|
valueEnum: PRODUCT_STOCK_STATUS_ENUM,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 库存
|
||||||
title: '库存',
|
title: '库存',
|
||||||
dataIndex: 'stock_quantity',
|
dataIndex: 'stock_quantity',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 图片
|
||||||
title: '图片',
|
title: '图片',
|
||||||
dataIndex: 'images',
|
dataIndex: 'images',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
|
|
@ -139,16 +161,101 @@ const ProductsPage: React.FC = () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 常规价格
|
||||||
title: '常规价格',
|
title: '常规价格',
|
||||||
dataIndex: 'regular_price',
|
dataIndex: 'regular_price',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 销售价格
|
||||||
title: '销售价格',
|
title: '销售价格',
|
||||||
dataIndex: 'sale_price',
|
dataIndex: 'sale_price',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 标签
|
||||||
|
title: '标签',
|
||||||
|
dataIndex: 'tags',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 250,
|
||||||
|
render: (_, record) => {
|
||||||
|
// 检查 record.tags 是否存在并且是一个数组
|
||||||
|
if (record.tags && Array.isArray(record.tags)) {
|
||||||
|
// 遍历 tags 数组并为每个 tag 对象渲染一个 Tag 组件
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
|
||||||
|
{record.tags.map((tag: any) => (
|
||||||
|
// 使用 tag.name 作为 key, 因为 tag.id 可能是对象, 会导致 React key 错误
|
||||||
|
<Tag key={tag.name}>{tag.name}</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 如果 record.tags 不是一个有效的数组,则不渲染任何内容
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 分类
|
||||||
|
title: '分类',
|
||||||
|
dataIndex: 'categories',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 250,
|
||||||
|
render: (_, record) => {
|
||||||
|
// 检查 record.categories 是否存在并且是一个数组
|
||||||
|
if (record.categories && Array.isArray(record.categories)) {
|
||||||
|
// 遍历 categories 数组并为每个 category 对象渲染一个 Tag 组件
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
|
||||||
|
{record.categories.map((cat: any) => (
|
||||||
|
// 使用 cat.name 作为 key
|
||||||
|
<Tag key={cat.name}>{cat.name}</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 如果 record.categories 不是一个有效的数组,则不渲染任何内容
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 属性
|
||||||
|
title: '属性',
|
||||||
|
dataIndex: 'attributes',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 250,
|
||||||
|
render: (_, record) => {
|
||||||
|
// 检查 record.attributes 是否存在并且是一个数组
|
||||||
|
if (record.attributes && Array.isArray(record.attributes)) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
||||||
|
{(record.attributes as any[]).map((attr: any) => (
|
||||||
|
<div key={attr.name}>
|
||||||
|
<strong>{attr.name}:</strong> {Array.isArray(attr.options) ? attr.options.join(', ') : ''}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 创建时间
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'date_created',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 修改时间
|
||||||
|
title: '修改时间',
|
||||||
|
dataIndex: 'date_modified',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 操作
|
||||||
title: '操作',
|
title: '操作',
|
||||||
dataIndex: 'option',
|
dataIndex: 'option',
|
||||||
valueType: 'option',
|
valueType: 'option',
|
||||||
|
|
@ -158,6 +265,19 @@ const ProductsPage: React.FC = () => {
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
||||||
<UpdateForm tableRef={actionRef} values={record} config={config} siteId={siteId} />
|
<UpdateForm tableRef={actionRef} values={record} config={config} siteId={siteId} />
|
||||||
<UpdateStatus tableRef={actionRef} values={record} siteId={siteId} />
|
<UpdateStatus tableRef={actionRef} values={record} siteId={siteId} />
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
title="店铺链接"
|
||||||
|
icon={<LinkOutlined />}
|
||||||
|
disabled={!record.frontendUrl}
|
||||||
|
onClick={() => {
|
||||||
|
if (record.frontendUrl) {
|
||||||
|
window.open(record.frontendUrl, '_blank', 'noopener,noreferrer');
|
||||||
|
} else {
|
||||||
|
message.warning('未能生成店铺链接');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
key="delete"
|
key="delete"
|
||||||
title="删除"
|
title="删除"
|
||||||
|
|
@ -192,10 +312,10 @@ const ProductsPage: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer header={{ title: null, breadcrumb: undefined }}>
|
<PageContainer header={{ title: null, breadcrumb: undefined }}>
|
||||||
<ProTable<any>
|
<ProTable<API.UnifiedProductDTO>
|
||||||
scroll={{ x: 'max-content' }}
|
scroll={{ x: 'max-content' }}
|
||||||
pagination={{
|
pagination={{
|
||||||
pageSizeOptions: ['10', '20', '50', '100', '1000'],
|
pageSizeOptions: ['10', '20', '50', '100', '1000','2000'],
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
defaultPageSize: 10,
|
defaultPageSize: 10,
|
||||||
}}
|
}}
|
||||||
|
|
@ -209,21 +329,24 @@ const ProductsPage: React.FC = () => {
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
request={async (params, sort, filter) => {
|
request={async (params, sort, filter) => {
|
||||||
const { current, pageSize, ...rest } = params || {};
|
// 从参数中解构分页和筛选条件, ProTable 使用 current 作为页码, 但后端需要 page, 所以在这里进行重命名
|
||||||
|
const { current: page, pageSize, ...rest } = params || {};
|
||||||
const where = { ...rest, ...(filter || {}) };
|
const where = { ...rest, ...(filter || {}) };
|
||||||
let orderObj: Record<string, 'asc' | 'desc'> | undefined = undefined;
|
let orderObj: Record<string, 'asc' | 'desc'> | undefined = undefined;
|
||||||
|
// 如果存在排序条件, 则进行处理
|
||||||
if (sort && typeof sort === 'object') {
|
if (sort && typeof sort === 'object') {
|
||||||
const [field, dir] = Object.entries(sort)[0] || [];
|
const [field, dir] = Object.entries(sort)[0] || [];
|
||||||
if (field && dir) {
|
if (field && dir) {
|
||||||
orderObj = { [field]: dir === 'descend' ? 'desc' : 'asc' };
|
orderObj = { [field]: dir === 'descend' ? 'desc' : 'asc' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 发起获取产品列表的请求
|
||||||
const response = await request(`/site-api/${siteId}/products`, {
|
const response = await request(`/site-api/${siteId}/products`, {
|
||||||
params: {
|
params: {
|
||||||
page: current,
|
page,
|
||||||
page_size: pageSize,
|
per_page: pageSize,
|
||||||
where,
|
...where,
|
||||||
...(orderObj ? { order: orderObj } : {}),
|
...(orderObj ? { sortField: Object.keys(orderObj)[0], sortOrder: Object.values(orderObj)[0] } : {}),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -279,7 +402,7 @@ const ProductsPage: React.FC = () => {
|
||||||
expandable={{
|
expandable={{
|
||||||
rowExpandable: (record) => record.type === 'variable',
|
rowExpandable: (record) => record.type === 'variable',
|
||||||
expandedRowRender: (record) => {
|
expandedRowRender: (record) => {
|
||||||
const productExternalId = record.externalProductId || record.external_product_id || record.id;
|
const productExternalId = (record as any).externalProductId || (record as any).external_product_id || record.id;
|
||||||
const innerColumns: ProColumns<any>[] = [
|
const innerColumns: ProColumns<any>[] = [
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'ID',
|
||||||
|
|
@ -294,6 +417,26 @@ const ProductsPage: React.FC = () => {
|
||||||
{ title: 'sku', dataIndex: 'sku' },
|
{ title: 'sku', dataIndex: 'sku' },
|
||||||
{ title: '常规价格', dataIndex: 'regular_price', hideInSearch: true },
|
{ title: '常规价格', dataIndex: 'regular_price', hideInSearch: true },
|
||||||
{ title: '销售价格', dataIndex: 'sale_price', hideInSearch: true },
|
{ title: '销售价格', dataIndex: 'sale_price', hideInSearch: true },
|
||||||
|
{
|
||||||
|
title: 'Attributes',
|
||||||
|
dataIndex: 'attributes',
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (_, row) => {
|
||||||
|
// 检查 row.attributes 是否存在并且是一个数组
|
||||||
|
if (row.attributes && Array.isArray(row.attributes)) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
||||||
|
{(row.attributes as any[]).map((attr: any) => (
|
||||||
|
<div key={attr.name}>
|
||||||
|
<strong>{attr.name}:</strong> {attr.option}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
dataIndex: 'option',
|
dataIndex: 'option',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface ReviewFormProps {
|
||||||
|
open: boolean;
|
||||||
|
editing: any;
|
||||||
|
siteId: number;
|
||||||
|
onClose: () => void;
|
||||||
|
onSuccess: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReviewForm: React.FC<ReviewFormProps> = ({ open, editing, siteId, onClose, onSuccess }) => {
|
||||||
|
// // 这是一个临时的占位符组件
|
||||||
|
// // 你可以在这里实现表单逻辑
|
||||||
|
if (!open) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Review Form</h2>
|
||||||
|
<p>Site ID: {siteId}</p>
|
||||||
|
<p>Editing: {editing ? 'Yes' : 'No'}</p>
|
||||||
|
<button onClick={onClose}>Close</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReviewForm;
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
import { ActionType, ProTable, ProColumns, ProCard } from '@ant-design/pro-components';
|
||||||
|
import { Button, Popconfirm, message, Space } from 'antd';
|
||||||
|
import { siteapicontrollerGetreviews, siteapicontrollerDeletereview } from '@/servers/api/siteApi';
|
||||||
|
import ReviewForm from './ReviewForm';
|
||||||
|
import { useParams } from '@umijs/max';
|
||||||
|
|
||||||
|
const ReviewsPage: React.FC = () => {
|
||||||
|
const params = useParams();
|
||||||
|
const siteId = Number(params.siteId);
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [editing, setEditing] = useState<any>(null);
|
||||||
|
|
||||||
|
const columns: ProColumns<API.UnifiedReviewDTO>[] = [
|
||||||
|
{ title: 'ID', dataIndex: 'id', key: 'id', width: 50 },
|
||||||
|
{ title: '产品ID', dataIndex: 'product_id', key: 'product_id', width: 80 },
|
||||||
|
{ title: '作者', dataIndex: 'author', key: 'author' },
|
||||||
|
{ title: '评分', dataIndex: 'rating', key: 'rating', width: 80 },
|
||||||
|
{ title: '状态', dataIndex: 'status', key: 'status', width: 100 },
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'date_created',
|
||||||
|
key: 'date_created',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
width: 150,
|
||||||
|
render: (_, record) => (
|
||||||
|
<Space>
|
||||||
|
<Button type="link" style={{padding:0}} onClick={() => {
|
||||||
|
setEditing(record);
|
||||||
|
setOpen(true);
|
||||||
|
}}>编辑</Button>
|
||||||
|
<Popconfirm title="确定删除吗?" onConfirm={async () => {
|
||||||
|
if (record.id) {
|
||||||
|
try {
|
||||||
|
const response = await siteapicontrollerDeletereview({ siteId, id: String(record.id) });
|
||||||
|
if (response.success) {
|
||||||
|
message.success('删除成功');
|
||||||
|
actionRef.current?.reload();
|
||||||
|
} else {
|
||||||
|
message.error('删除失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error('删除失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<Button type='link' danger>删除</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProCard>
|
||||||
|
<ProTable<API.UnifiedReviewDTO>
|
||||||
|
columns={columns}
|
||||||
|
actionRef={actionRef}
|
||||||
|
request={async (params) => {
|
||||||
|
const response = await siteapicontrollerGetreviews({ ...params, siteId, page: params.current, per_page: params.pageSize });
|
||||||
|
return {
|
||||||
|
data: response.data.items,
|
||||||
|
success: true,
|
||||||
|
total: response.data.total,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
rowKey="id"
|
||||||
|
search={{
|
||||||
|
labelWidth: 'auto',
|
||||||
|
}}
|
||||||
|
headerTitle="评论列表"
|
||||||
|
toolBarRender={() => [
|
||||||
|
<Button type="primary" onClick={() => {
|
||||||
|
setEditing(null);
|
||||||
|
setOpen(true);
|
||||||
|
}}>
|
||||||
|
新建评论
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<ReviewForm
|
||||||
|
open={open}
|
||||||
|
editing={editing}
|
||||||
|
siteId={siteId}
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
onSuccess={() => {
|
||||||
|
setOpen(false);
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReviewsPage;
|
||||||
|
|
@ -56,7 +56,6 @@ const SubscriptionsPage: React.FC = () => {
|
||||||
title: '订阅ID',
|
title: '订阅ID',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
width: 120,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
|
|
@ -70,25 +69,21 @@ const SubscriptionsPage: React.FC = () => {
|
||||||
) : (
|
) : (
|
||||||
'-'
|
'-'
|
||||||
),
|
),
|
||||||
width: 120,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '客户ID',
|
title: '客户ID',
|
||||||
dataIndex: 'customer_id',
|
dataIndex: 'customer_id',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
width: 120,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '计费周期',
|
title: '计费周期',
|
||||||
dataIndex: 'billing_period',
|
dataIndex: 'billing_period',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
width: 120,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '计费间隔',
|
title: '计费间隔',
|
||||||
dataIndex: 'billing_interval',
|
dataIndex: 'billing_interval',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
width: 120,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '开始时间',
|
title: '开始时间',
|
||||||
|
|
@ -102,10 +97,23 @@ const SubscriptionsPage: React.FC = () => {
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
width: 160,
|
width: 160,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// 创建时间
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'date_created',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 修改时间
|
||||||
|
title: '修改时间',
|
||||||
|
dataIndex: 'date_modified',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
valueType: 'option',
|
valueType: 'option',
|
||||||
width: 120,
|
|
||||||
render: (_, row) => (
|
render: (_, row) => (
|
||||||
<Space>
|
<Space>
|
||||||
<Button type="link" title="编辑" icon={<EditOutlined />} onClick={() => setEditing(row)} />
|
<Button type="link" title="编辑" icon={<EditOutlined />} onClick={() => setEditing(row)} />
|
||||||
|
|
@ -127,23 +135,13 @@ const SubscriptionsPage: React.FC = () => {
|
||||||
* 列表数据请求;保持与后端分页参数一致
|
* 列表数据请求;保持与后端分页参数一致
|
||||||
* 兼容后端 data.items 或 data.list 返回字段
|
* 兼容后端 data.items 或 data.list 返回字段
|
||||||
*/
|
*/
|
||||||
request={async (params, sort, filter) => {
|
request={async (params) => {
|
||||||
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: {
|
||||||
page: current,
|
...params,
|
||||||
page_size: pageSize,
|
page: params.current,
|
||||||
where,
|
per_page: params.pageSize,
|
||||||
...(orderObj ? { order: orderObj } : {}),
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -173,8 +171,7 @@ const SubscriptionsPage: React.FC = () => {
|
||||||
title="批量导出"
|
title="批量导出"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!siteId) return;
|
if (!siteId) return;
|
||||||
const idsParam = selectedRowKeys.length ? (selectedRowKeys as any[]).join(',') : undefined;
|
const res = await request(`/site-api/${siteId}/subscriptions/export`, { params: {} });
|
||||||
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);
|
||||||
|
|
@ -188,23 +185,8 @@ const SubscriptionsPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>,
|
/>,
|
||||||
<Popconfirm
|
<Button title="批量删除" danger icon={<DeleteFilled />} onClick={() => message.info('订阅删除未实现')} />
|
||||||
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}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import {
|
||||||
DeleteFilled,
|
DeleteFilled,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
FileDoneOutlined,
|
FileDoneOutlined,
|
||||||
|
PlusOutlined,
|
||||||
TagsOutlined,
|
TagsOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
|
|
@ -578,7 +579,7 @@ export const CreateOrder: React.FC<{
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
trigger={
|
trigger={
|
||||||
<Button type="primary" size="small" icon={<CodeSandboxOutlined />}>
|
<Button type="primary" icon={<PlusOutlined />}>
|
||||||
创建订单
|
创建订单
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -355,16 +355,17 @@ export const UpdateForm: React.FC<{
|
||||||
placeholder="请输入SKU"
|
placeholder="请输入SKU"
|
||||||
rules={[{ required: true, message: '请输入SKU' }]}
|
rules={[{ required: true, message: '请输入SKU' }]}
|
||||||
/>
|
/>
|
||||||
<ProFormTextArea
|
|
||||||
name="description"
|
|
||||||
label="描述"
|
|
||||||
width="lg"
|
|
||||||
/>
|
|
||||||
<ProFormTextArea
|
<ProFormTextArea
|
||||||
name="short_description"
|
name="short_description"
|
||||||
label="简短描述"
|
label="简短描述"
|
||||||
width="lg"
|
width="lg"
|
||||||
/>
|
/>
|
||||||
|
<ProFormTextArea
|
||||||
|
name="description"
|
||||||
|
label="描述"
|
||||||
|
width="lg"
|
||||||
|
/>
|
||||||
|
|
||||||
{initialValues.type === 'simple' ? (
|
{initialValues.type === 'simple' ? (
|
||||||
<>
|
<>
|
||||||
<ProFormDigit
|
<ProFormDigit
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import * as logistics from './logistics';
|
||||||
import * as media from './media';
|
import * as media from './media';
|
||||||
import * as order from './order';
|
import * as order from './order';
|
||||||
import * as product from './product';
|
import * as product from './product';
|
||||||
|
import * as review from './review';
|
||||||
import * as site from './site';
|
import * as site from './site';
|
||||||
import * as siteApi from './siteApi';
|
import * as siteApi from './siteApi';
|
||||||
import * as statistics from './statistics';
|
import * as statistics from './statistics';
|
||||||
|
|
@ -30,6 +31,7 @@ export default {
|
||||||
media,
|
media,
|
||||||
order,
|
order,
|
||||||
product,
|
product,
|
||||||
|
review,
|
||||||
siteApi,
|
siteApi,
|
||||||
site,
|
site,
|
||||||
statistics,
|
statistics,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
// @ts-ignore
|
||||||
|
/* eslint-disable */
|
||||||
|
import { request } from 'umi';
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 POST /review/create */
|
||||||
|
export async function reviewcontrollerCreate(
|
||||||
|
body: API.CreateReviewDTO,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
return request<any>('/review/create', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 DELETE /review/delete/${param0} */
|
||||||
|
export async function reviewcontrollerDelete(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.reviewcontrollerDeleteParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { id: param0, ...queryParams } = params;
|
||||||
|
return request<any>(`/review/delete/${param0}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
params: { ...queryParams },
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 GET /review/get/${param0} */
|
||||||
|
export async function reviewcontrollerGet(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.reviewcontrollerGetParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { id: param0, ...queryParams } = params;
|
||||||
|
return request<any>(`/review/get/${param0}`, {
|
||||||
|
method: 'GET',
|
||||||
|
params: { ...queryParams },
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 GET /review/list */
|
||||||
|
export async function reviewcontrollerList(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.reviewcontrollerListParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
return request<any>('/review/list', {
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
},
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 PUT /review/update/${param0} */
|
||||||
|
export async function reviewcontrollerUpdate(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.reviewcontrollerUpdateParams,
|
||||||
|
body: API.UpdateReviewDTO,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { id: param0, ...queryParams } = params;
|
||||||
|
return request<any>(`/review/update/${param0}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
params: { ...queryParams },
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -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 || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -162,6 +174,10 @@ export async function siteapicontrollerExportmedia(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -178,6 +194,10 @@ export async function siteapicontrollerGetorders(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -232,6 +252,10 @@ export async function siteapicontrollerExportorders(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -269,6 +293,10 @@ export async function siteapicontrollerGetproducts(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
},
|
},
|
||||||
|
|
@ -324,6 +352,10 @@ export async function siteapicontrollerExportproducts(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -340,6 +372,10 @@ export async function siteapicontrollerExportproductsspecial(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -386,6 +422,48 @@ export async function siteapicontrollerImportproductsspecial(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 GET /site-api/${param0}/reviews */
|
||||||
|
export async function siteapicontrollerGetreviews(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.siteapicontrollerGetreviewsParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { siteId: param0, ...queryParams } = params;
|
||||||
|
return request<API.UnifiedReviewPaginationDTO>(
|
||||||
|
`/site-api/${param0}/reviews`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
|
},
|
||||||
|
...(options || {}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 POST /site-api/${param0}/reviews */
|
||||||
|
export async function siteapicontrollerCreatereview(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.siteapicontrollerCreatereviewParams,
|
||||||
|
body: API.CreateReviewDTO,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { siteId: param0, ...queryParams } = params;
|
||||||
|
return request<API.UnifiedReviewDTO>(`/site-api/${param0}/reviews`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
params: { ...queryParams },
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 此处后端没有提供注释 GET /site-api/${param0}/subscriptions */
|
/** 此处后端没有提供注释 GET /site-api/${param0}/subscriptions */
|
||||||
export async function siteapicontrollerGetsubscriptions(
|
export async function siteapicontrollerGetsubscriptions(
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
|
@ -399,6 +477,10 @@ export async function siteapicontrollerGetsubscriptions(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
},
|
},
|
||||||
|
|
@ -416,6 +498,10 @@ export async function siteapicontrollerExportsubscriptions(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
|
|
@ -490,6 +576,10 @@ export async function siteapicontrollerGetcustomerorders(
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...queryParams,
|
...queryParams,
|
||||||
|
where: undefined,
|
||||||
|
...queryParams['where'],
|
||||||
|
order: undefined,
|
||||||
|
...queryParams['order'],
|
||||||
},
|
},
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
},
|
},
|
||||||
|
|
@ -671,6 +761,42 @@ export async function siteapicontrollerDeleteproduct(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 PUT /site-api/${param1}/reviews/${param0} */
|
||||||
|
export async function siteapicontrollerUpdatereview(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.siteapicontrollerUpdatereviewParams,
|
||||||
|
body: API.UpdateReviewDTO,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { id: param0, siteId: param1, ...queryParams } = params;
|
||||||
|
return request<API.UnifiedReviewDTO>(
|
||||||
|
`/site-api/${param1}/reviews/${param0}`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
params: { ...queryParams },
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 DELETE /site-api/${param1}/reviews/${param0} */
|
||||||
|
export async function siteapicontrollerDeletereview(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.siteapicontrollerDeletereviewParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { id: param0, siteId: param1, ...queryParams } = params;
|
||||||
|
return request<Record<string, any>>(`/site-api/${param1}/reviews/${param0}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
params: { ...queryParams },
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 此处后端没有提供注释 PUT /site-api/${param2}/products/${param1}/variations/${param0} */
|
/** 此处后端没有提供注释 PUT /site-api/${param2}/products/${param1}/variations/${param0} */
|
||||||
export async function siteapicontrollerUpdatevariation(
|
export async function siteapicontrollerUpdatevariation(
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,25 @@ declare namespace API {
|
||||||
items?: PurchaseOrderItem[];
|
items?: PurchaseOrderItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CreateReviewDTO = {
|
||||||
|
/** 站点ID */
|
||||||
|
siteId?: number;
|
||||||
|
/** 产品ID */
|
||||||
|
productId?: number;
|
||||||
|
/** 客户ID */
|
||||||
|
customerId?: number;
|
||||||
|
/** 评论者姓名 */
|
||||||
|
author?: string;
|
||||||
|
/** 评论者电子邮件 */
|
||||||
|
email?: string;
|
||||||
|
/** 评论内容 */
|
||||||
|
content?: string;
|
||||||
|
/** 评分 (1-5) */
|
||||||
|
rating?: number;
|
||||||
|
/** 状态 */
|
||||||
|
status?: 'pending' | 'approved' | 'rejected';
|
||||||
|
};
|
||||||
|
|
||||||
type CreateSiteDTO = {
|
type CreateSiteDTO = {
|
||||||
/** 区域 */
|
/** 区域 */
|
||||||
areas?: any;
|
areas?: any;
|
||||||
|
|
@ -1211,6 +1230,19 @@ declare namespace API {
|
||||||
stockPointId?: number;
|
stockPointId?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type QueryReviewDTO = {
|
||||||
|
/** 当前页码 */
|
||||||
|
current?: number;
|
||||||
|
/** 每页数量 */
|
||||||
|
pageSize?: number;
|
||||||
|
/** 站点ID */
|
||||||
|
siteId?: number;
|
||||||
|
/** 产品ID */
|
||||||
|
productId?: number;
|
||||||
|
/** 状态 */
|
||||||
|
status?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type QueryServiceDTO = {
|
type QueryServiceDTO = {
|
||||||
/** 页码 */
|
/** 页码 */
|
||||||
current?: number;
|
current?: number;
|
||||||
|
|
@ -1316,6 +1348,31 @@ declare namespace API {
|
||||||
data?: RateDTO[];
|
data?: RateDTO[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type reviewcontrollerDeleteParams = {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type reviewcontrollerGetParams = {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type reviewcontrollerListParams = {
|
||||||
|
/** 当前页码 */
|
||||||
|
current?: number;
|
||||||
|
/** 每页数量 */
|
||||||
|
pageSize?: number;
|
||||||
|
/** 站点ID */
|
||||||
|
siteId?: number;
|
||||||
|
/** 产品ID */
|
||||||
|
productId?: number;
|
||||||
|
/** 状态 */
|
||||||
|
status?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type reviewcontrollerUpdateParams = {
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
type Service = {
|
type Service = {
|
||||||
id?: string;
|
id?: string;
|
||||||
carrier_name?: string;
|
carrier_name?: string;
|
||||||
|
|
@ -1442,6 +1499,10 @@ declare namespace API {
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type siteapicontrollerCreatereviewParams = {
|
||||||
|
siteId: number;
|
||||||
|
};
|
||||||
|
|
||||||
type siteapicontrollerDeletecustomerParams = {
|
type siteapicontrollerDeletecustomerParams = {
|
||||||
id: string;
|
id: string;
|
||||||
siteId: number;
|
siteId: number;
|
||||||
|
|
@ -1462,6 +1523,11 @@ declare namespace API {
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type siteapicontrollerDeletereviewParams = {
|
||||||
|
id: number;
|
||||||
|
siteId: number;
|
||||||
|
};
|
||||||
|
|
||||||
type siteapicontrollerExportcustomersParams = {
|
type siteapicontrollerExportcustomersParams = {
|
||||||
/** 页码 */
|
/** 页码 */
|
||||||
page?: number;
|
page?: number;
|
||||||
|
|
@ -1476,9 +1542,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1502,9 +1568,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1528,9 +1594,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1554,9 +1620,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1580,9 +1646,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1606,9 +1672,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1632,9 +1698,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1664,9 +1730,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1690,9 +1756,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1726,9 +1792,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1757,9 +1823,35 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
|
/** 排序字段(兼容旧入参) */
|
||||||
|
orderby?: string;
|
||||||
|
/** 排序方式(兼容旧入参) */
|
||||||
|
orderDir?: string;
|
||||||
|
/** 选中ID列表,逗号分隔 */
|
||||||
|
ids?: string;
|
||||||
|
siteId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type siteapicontrollerGetreviewsParams = {
|
||||||
|
/** 页码 */
|
||||||
|
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;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1783,9 +1875,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -1831,6 +1923,11 @@ declare namespace API {
|
||||||
siteId: number;
|
siteId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type siteapicontrollerUpdatereviewParams = {
|
||||||
|
id: number;
|
||||||
|
siteId: number;
|
||||||
|
};
|
||||||
|
|
||||||
type siteapicontrollerUpdatevariationParams = {
|
type siteapicontrollerUpdatevariationParams = {
|
||||||
variationId: string;
|
variationId: string;
|
||||||
productId: string;
|
productId: string;
|
||||||
|
|
@ -2193,6 +2290,40 @@ declare namespace API {
|
||||||
minute?: string;
|
minute?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UnifiedAddressDTO = {
|
||||||
|
/** 名 */
|
||||||
|
first_name?: string;
|
||||||
|
/** 姓 */
|
||||||
|
last_name?: string;
|
||||||
|
/** 全名 */
|
||||||
|
fullname?: string;
|
||||||
|
/** 公司 */
|
||||||
|
company?: string;
|
||||||
|
/** 地址1 */
|
||||||
|
address_1?: string;
|
||||||
|
/** 地址2 */
|
||||||
|
address_2?: string;
|
||||||
|
/** 城市 */
|
||||||
|
city?: string;
|
||||||
|
/** 省/州 */
|
||||||
|
state?: string;
|
||||||
|
/** 邮政编码 */
|
||||||
|
postcode?: string;
|
||||||
|
/** 国家 */
|
||||||
|
country?: string;
|
||||||
|
/** 邮箱 */
|
||||||
|
email?: string;
|
||||||
|
/** 电话 */
|
||||||
|
phone?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UnifiedCategoryDTO = {
|
||||||
|
/** 分类ID */
|
||||||
|
id?: Record<string, any>;
|
||||||
|
/** 分类名称 */
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type UnifiedCustomerDTO = {
|
type UnifiedCustomerDTO = {
|
||||||
/** 客户ID */
|
/** 客户ID */
|
||||||
id?: Record<string, any>;
|
id?: Record<string, any>;
|
||||||
|
|
@ -2219,11 +2350,11 @@ declare namespace API {
|
||||||
/** 电话 */
|
/** 电话 */
|
||||||
phone?: string;
|
phone?: string;
|
||||||
/** 账单地址 */
|
/** 账单地址 */
|
||||||
billing?: any;
|
billing?: UnifiedAddressDTO;
|
||||||
/** 收货地址 */
|
/** 收货地址 */
|
||||||
shipping?: any;
|
shipping?: UnifiedAddressDTO;
|
||||||
/** 原始数据 */
|
/** 原始数据 */
|
||||||
raw?: any;
|
raw?: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UnifiedCustomerPaginationDTO = {
|
type UnifiedCustomerPaginationDTO = {
|
||||||
|
|
@ -2301,14 +2432,14 @@ declare namespace API {
|
||||||
customer_name?: string;
|
customer_name?: string;
|
||||||
/** 客户邮箱 */
|
/** 客户邮箱 */
|
||||||
email?: string;
|
email?: string;
|
||||||
/** 订单项 */
|
/** 订单项(具体的商品) */
|
||||||
line_items?: any;
|
line_items?: UnifiedOrderLineItemDTO[];
|
||||||
/** 销售项(兼容前端) */
|
/** 销售项(兼容前端) */
|
||||||
sales?: any;
|
sales?: UnifiedOrderLineItemDTO[];
|
||||||
/** 账单地址 */
|
/** 账单地址 */
|
||||||
billing?: any;
|
billing?: UnifiedAddressDTO;
|
||||||
/** 收货地址 */
|
/** 收货地址 */
|
||||||
shipping?: any;
|
shipping?: UnifiedAddressDTO;
|
||||||
/** 账单地址全称 */
|
/** 账单地址全称 */
|
||||||
billing_full_address?: string;
|
billing_full_address?: string;
|
||||||
/** 收货地址全称 */
|
/** 收货地址全称 */
|
||||||
|
|
@ -2320,7 +2451,24 @@ declare namespace API {
|
||||||
/** 更新时间 */
|
/** 更新时间 */
|
||||||
date_modified?: string;
|
date_modified?: string;
|
||||||
/** 原始数据 */
|
/** 原始数据 */
|
||||||
raw?: any;
|
raw?: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UnifiedOrderLineItemDTO = {
|
||||||
|
/** 订单项ID */
|
||||||
|
id?: Record<string, any>;
|
||||||
|
/** 产品名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 产品ID */
|
||||||
|
product_id?: Record<string, any>;
|
||||||
|
/** 变体ID */
|
||||||
|
variation_id?: Record<string, any>;
|
||||||
|
/** 数量 */
|
||||||
|
quantity?: number;
|
||||||
|
/** 总计 */
|
||||||
|
total?: string;
|
||||||
|
/** SKU */
|
||||||
|
sku?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UnifiedOrderPaginationDTO = {
|
type UnifiedOrderPaginationDTO = {
|
||||||
|
|
@ -2338,6 +2486,21 @@ declare namespace API {
|
||||||
totalPages?: number;
|
totalPages?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UnifiedProductAttributeDTO = {
|
||||||
|
/** 属性ID */
|
||||||
|
id?: Record<string, any>;
|
||||||
|
/** 属性名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 属性位置 */
|
||||||
|
position?: number;
|
||||||
|
/** 对变体是否可见 */
|
||||||
|
visible?: boolean;
|
||||||
|
/** 是否为变体属性 */
|
||||||
|
variation?: boolean;
|
||||||
|
/** 属性选项 */
|
||||||
|
options?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
type UnifiedProductDTO = {
|
type UnifiedProductDTO = {
|
||||||
/** 产品ID */
|
/** 产品ID */
|
||||||
id?: Record<string, any>;
|
id?: Record<string, any>;
|
||||||
|
|
@ -2362,17 +2525,21 @@ declare namespace API {
|
||||||
/** 产品图片 */
|
/** 产品图片 */
|
||||||
images?: UnifiedImageDTO[];
|
images?: UnifiedImageDTO[];
|
||||||
/** 产品标签 */
|
/** 产品标签 */
|
||||||
tags?: any;
|
tags?: UnifiedTagDTO[];
|
||||||
|
/** 产品分类 */
|
||||||
|
categories?: UnifiedCategoryDTO[];
|
||||||
/** 产品属性 */
|
/** 产品属性 */
|
||||||
attributes?: any;
|
attributes?: UnifiedProductAttributeDTO[];
|
||||||
/** 产品变体 */
|
/** 产品变体 */
|
||||||
variations?: any;
|
variations?: UnifiedProductVariationDTO[];
|
||||||
/** 创建时间 */
|
/** 创建时间 */
|
||||||
date_created?: string;
|
date_created?: string;
|
||||||
/** 更新时间 */
|
/** 更新时间 */
|
||||||
date_modified?: string;
|
date_modified?: string;
|
||||||
|
/** 产品链接 */
|
||||||
|
frontendUrl?: string;
|
||||||
/** 原始数据(保留备用) */
|
/** 原始数据(保留备用) */
|
||||||
raw?: any;
|
raw?: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UnifiedProductPaginationDTO = {
|
type UnifiedProductPaginationDTO = {
|
||||||
|
|
@ -2390,6 +2557,59 @@ declare namespace API {
|
||||||
totalPages?: number;
|
totalPages?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UnifiedProductVariationDTO = {
|
||||||
|
/** 变体ID */
|
||||||
|
id?: Record<string, any>;
|
||||||
|
/** 变体SKU */
|
||||||
|
sku?: string;
|
||||||
|
/** 常规价格 */
|
||||||
|
regular_price?: string;
|
||||||
|
/** 销售价格 */
|
||||||
|
sale_price?: string;
|
||||||
|
/** 当前价格 */
|
||||||
|
price?: string;
|
||||||
|
/** 库存状态 */
|
||||||
|
stock_status?: string;
|
||||||
|
/** 库存数量 */
|
||||||
|
stock_quantity?: number;
|
||||||
|
/** 变体图片 */
|
||||||
|
image?: UnifiedImageDTO;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UnifiedReviewDTO = {
|
||||||
|
/** 评论ID */
|
||||||
|
id?: Record<string, any>;
|
||||||
|
/** 产品ID */
|
||||||
|
product_id?: Record<string, any>;
|
||||||
|
/** 评论者 */
|
||||||
|
author?: string;
|
||||||
|
/** 评论者邮箱 */
|
||||||
|
email?: string;
|
||||||
|
/** 评论内容 */
|
||||||
|
content?: string;
|
||||||
|
/** 评分 */
|
||||||
|
rating?: number;
|
||||||
|
/** 状态 */
|
||||||
|
status?: string;
|
||||||
|
/** 创建时间 */
|
||||||
|
date_created?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UnifiedReviewPaginationDTO = {
|
||||||
|
/** 列表数据 */
|
||||||
|
items?: UnifiedReviewDTO[];
|
||||||
|
/** 总数 */
|
||||||
|
total?: number;
|
||||||
|
/** 当前页 */
|
||||||
|
page?: number;
|
||||||
|
/** 每页数量 */
|
||||||
|
per_page?: number;
|
||||||
|
/** 每页数量别名 */
|
||||||
|
page_size?: number;
|
||||||
|
/** 总页数 */
|
||||||
|
totalPages?: number;
|
||||||
|
};
|
||||||
|
|
||||||
type UnifiedSearchParamsDTO = {
|
type UnifiedSearchParamsDTO = {
|
||||||
/** 页码 */
|
/** 页码 */
|
||||||
page?: number;
|
page?: number;
|
||||||
|
|
@ -2404,9 +2624,9 @@ declare namespace API {
|
||||||
/** 客户ID,用于筛选订单 */
|
/** 客户ID,用于筛选订单 */
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
/** 过滤条件对象 */
|
/** 过滤条件对象 */
|
||||||
where?: any;
|
where?: Record<string, any>;
|
||||||
/** 排序对象,例如 { "sku": "desc" } */
|
/** 排序对象,例如 { "sku": "desc" } */
|
||||||
order?: any;
|
order?: Record<string, any>;
|
||||||
/** 排序字段(兼容旧入参) */
|
/** 排序字段(兼容旧入参) */
|
||||||
orderby?: string;
|
orderby?: string;
|
||||||
/** 排序方式(兼容旧入参) */
|
/** 排序方式(兼容旧入参) */
|
||||||
|
|
@ -2435,9 +2655,9 @@ declare namespace API {
|
||||||
/** 下次支付时间 */
|
/** 下次支付时间 */
|
||||||
next_payment_date?: string;
|
next_payment_date?: string;
|
||||||
/** 订单项 */
|
/** 订单项 */
|
||||||
line_items?: any;
|
line_items?: UnifiedOrderLineItemDTO[];
|
||||||
/** 原始数据 */
|
/** 原始数据 */
|
||||||
raw?: any;
|
raw?: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UnifiedSubscriptionPaginationDTO = {
|
type UnifiedSubscriptionPaginationDTO = {
|
||||||
|
|
@ -2455,6 +2675,13 @@ declare namespace API {
|
||||||
totalPages?: number;
|
totalPages?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UnifiedTagDTO = {
|
||||||
|
/** 标签ID */
|
||||||
|
id?: Record<string, any>;
|
||||||
|
/** 标签名称 */
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type UpdateAreaDTO = {
|
type UpdateAreaDTO = {
|
||||||
/** 编码 */
|
/** 编码 */
|
||||||
code?: string;
|
code?: string;
|
||||||
|
|
@ -2499,6 +2726,15 @@ declare namespace API {
|
||||||
items?: PurchaseOrderItem[];
|
items?: PurchaseOrderItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UpdateReviewDTO = {
|
||||||
|
/** 评论内容 */
|
||||||
|
content?: string;
|
||||||
|
/** 评分 (1-5) */
|
||||||
|
rating?: number;
|
||||||
|
/** 状态 */
|
||||||
|
status?: 'pending' | 'approved' | 'rejected';
|
||||||
|
};
|
||||||
|
|
||||||
type UpdateSiteDTO = {
|
type UpdateSiteDTO = {
|
||||||
/** 区域 */
|
/** 区域 */
|
||||||
areas?: any;
|
areas?: any;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue