zksu
/
WEB
forked from yoone/WEB
1
0
Fork 0

feat: 添加产品CSV工具页面并优化多处代码

refactor: 移除media相关API及调整代码格式
style: 统一代码中的标点符号格式
fix: 修复产品列表页分页及查询参数问题
This commit is contained in:
tikkhun 2026-01-08 19:05:10 +08:00
parent 6677114c46
commit 7247015e4c
27 changed files with 167 additions and 228 deletions

View File

@ -170,6 +170,11 @@ export default defineConfig({
path: '/product/sync', path: '/product/sync',
component: './Product/Sync', component: './Product/Sync',
}, },
{
name: '产品CSV 工具',
path: '/product/csvtool',
component: './Product/CsvTool',
}
], ],
}, },
{ {

View File

@ -7,7 +7,7 @@ WORKDIR /app
# 复制 package.json 和 package-lock.json # 复制 package.json 和 package-lock.json
COPY package*.json ./ COPY package*.json ./
# 安装依赖(使用 --legacy-peer-deps 解决依赖冲突) # 安装依赖(使用 --legacy-peer-deps 解决依赖冲突)
RUN npm install --legacy-peer-deps RUN npm install --legacy-peer-deps
# 复制源代码 # 复制源代码

View File

@ -36,7 +36,7 @@ export const showSyncResult = (
} = result; } = result;
// 构建结果消息 // 构建结果消息
let resultMessage = `同步完成!共处理 ${processed}${entityType}(总数 ${total} 个):`; let resultMessage = `同步完成!共处理 ${processed}${entityType}(总数 ${total} 个):`;
if (created > 0) resultMessage += ` 新建 ${created}`; if (created > 0) resultMessage += ` 新建 ${created}`;
if (updated > 0) resultMessage += ` 更新 ${updated}`; if (updated > 0) resultMessage += ` 更新 ${updated}`;
if (synced > 0) resultMessage += ` 同步成功 ${synced}`; if (synced > 0) resultMessage += ` 同步成功 ${synced}`;
@ -50,7 +50,7 @@ export const showSyncResult = (
<div> <div>
<div>{resultMessage}</div> <div>{resultMessage}</div>
<div style={{ marginTop: 8, fontSize: 12, color: '#faad14' }}> <div style={{ marginTop: 8, fontSize: 12, color: '#faad14' }}>
:
{errors {errors
.slice(0, 3) .slice(0, 3)
.map((err: any) => `${err.identifier}: ${err.error}`) .map((err: any) => `${err.identifier}: ${err.error}`)

View File

@ -8,7 +8,7 @@ interface Site {
[key: string]: any; [key: string]: any;
} }
// 自定义 Hook管理站点数据 // 自定义 Hook:管理站点数据
const useSites = () => { const useSites = () => {
// 添加站点数据状态 // 添加站点数据状态
const [sites, setSites] = useState<Site[]>([]); const [sites, setSites] = useState<Site[]>([]);

View File

@ -60,7 +60,7 @@ const HistoryOrders: React.FC<HistoryOrdersProps> = ({ customer, siteId }) => {
const items = order.line_items || order.items || []; const items = order.line_items || order.items || [];
if (Array.isArray(items)) { if (Array.isArray(items)) {
items.forEach((item: any) => { items.forEach((item: any) => {
// 检查商品名称或SKU是否包含yoone(不区分大小写) // 检查商品名称或SKU是否包含yoone(不区分大小写)
const itemName = (item.name || '').toLowerCase(); const itemName = (item.name || '').toLowerCase();
const sku = (item.sku || '').toLowerCase(); const sku = (item.sku || '').toLowerCase();

View File

@ -502,7 +502,7 @@ const SyncCustomersModal: React.FC<{
}; };
} }
// 添加日期范围过滤(使用 after 和 before 参数) // 添加日期范围过滤(使用 after 和 before 参数)
if (values.dateRange && values.dateRange[0] && values.dateRange[1]) { if (values.dateRange && values.dateRange[0] && values.dateRange[1]) {
params.where = { params.where = {
...params.where, ...params.where,
@ -536,7 +536,7 @@ const SyncCustomersModal: React.FC<{
errors = [], errors = [],
} = result; } = result;
let resultMessage = `同步完成!共处理 ${total} 个客户`; let resultMessage = `同步完成!共处理 ${total} 个客户:`;
if (created > 0) resultMessage += ` 新建 ${created}`; if (created > 0) resultMessage += ` 新建 ${created}`;
if (updated > 0) resultMessage += ` 更新 ${updated}`; if (updated > 0) resultMessage += ` 更新 ${updated}`;
if (synced > 0) resultMessage += ` 同步成功 ${synced}`; if (synced > 0) resultMessage += ` 同步成功 ${synced}`;
@ -549,7 +549,7 @@ const SyncCustomersModal: React.FC<{
<div> <div>
<div>{resultMessage}</div> <div>{resultMessage}</div>
<div style={{ marginTop: 8, fontSize: 12, color: '#faad14' }}> <div style={{ marginTop: 8, fontSize: 12, color: '#faad14' }}>
:
{errors {errors
.slice(0, 3) .slice(0, 3)
.map((err: any) => err.email || err.error) .map((err: any) => err.email || err.error)
@ -653,12 +653,12 @@ const SyncCustomersModal: React.FC<{
placeholder="选择排序方式" placeholder="选择排序方式"
options={[ options={[
{ label: '默认排序', value: '' }, { label: '默认排序', value: '' },
{ label: '注册时间(升序)', value: 'date_created:asc' }, { label: '注册时间(升序)', value: 'date_created:asc' },
{ label: '注册时间(降序)', value: 'date_created:desc' }, { label: '注册时间(降序)', value: 'date_created:desc' },
{ label: '邮箱(升序)', value: 'email:asc' }, { label: '邮箱(升序)', value: 'email:asc' },
{ label: '邮箱(降序)', value: 'email:desc' }, { label: '邮箱(降序)', value: 'email:desc' },
{ label: '姓名(升序)', value: 'first_name:asc' }, { label: '姓名(升序)', value: 'first_name:asc' },
{ label: '姓名(降序)', value: 'first_name:desc' }, { label: '姓名(降序)', value: 'first_name:desc' },
]} ]}
fieldProps={{ fieldProps={{
allowClear: true, allowClear: true,

View File

@ -38,7 +38,7 @@ const DictPage: React.FC = () => {
const [isEditDictModalVisible, setIsEditDictModalVisible] = useState(false); const [isEditDictModalVisible, setIsEditDictModalVisible] = useState(false);
const [editDictData, setEditDictData] = useState<any>(null); const [editDictData, setEditDictData] = useState<any>(null);
// 字典项模态框状态(由 DictItemModal 组件管理) // 字典项模态框状态(由 DictItemModal 组件管理)
const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false); const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false);
const [isEditDictItem, setIsEditDictItem] = useState(false); const [isEditDictItem, setIsEditDictItem] = useState(false);
const [editingDictItemData, setEditingDictItemData] = useState<any>(null); const [editingDictItemData, setEditingDictItemData] = useState<any>(null);
@ -137,7 +137,7 @@ const DictPage: React.FC = () => {
link.remove(); link.remove();
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);
} catch (error: any) { } catch (error: any) {
message.error('下载字典模板失败' + (error.message || '未知错误')); message.error('下载字典模板失败:' + (error.message || '未知错误'));
} }
}; };
@ -173,7 +173,7 @@ const DictPage: React.FC = () => {
} }
}; };
// 处理字典项模态框提交(添加或编辑) // 处理字典项模态框提交(添加或编辑)
const handleDictItemModalOk = async (values: any) => { const handleDictItemModalOk = async (values: any) => {
try { try {
if (isEditDictItem && editingDictItemData) { if (isEditDictItem && editingDictItemData) {
@ -205,7 +205,7 @@ const DictPage: React.FC = () => {
}, 100); }, 100);
} catch (error: any) { } catch (error: any) {
message.error( message.error(
`${isEditDictItem ? '更新' : '添加'}失败${ `${isEditDictItem ? '更新' : '添加'}失败:${
error.message || '未知错误' error.message || '未知错误'
}`, }`,
); );
@ -275,7 +275,7 @@ const DictPage: React.FC = () => {
message.success(`成功导出 ${response.length} 条数据`); message.success(`成功导出 ${response.length} 条数据`);
} catch (error: any) { } catch (error: any) {
message.error('导出字典项失败' + (error.message || '未知错误')); message.error('导出字典项失败:' + (error.message || '未知错误'));
} }
}; };
@ -477,7 +477,7 @@ const DictPage: React.FC = () => {
}; };
} }
// 兼容旧的响应格式(直接返回数组) // 兼容旧的响应格式(直接返回数组)
return { return {
data: res || [], data: res || [],
success: true, success: true,
@ -524,7 +524,7 @@ const DictPage: React.FC = () => {
</Content> </Content>
</Layout> </Layout>
{/* 字典项 Modal(添加或编辑) */} {/* 字典项 Modal(添加或编辑) */}
<DictItemModal <DictItemModal
visible={isDictItemModalVisible} visible={isDictItemModalVisible}
isEdit={isEditDictItem} isEdit={isEditDictItem}

View File

@ -11,19 +11,19 @@ interface DictItemActionsProps {
selectedDict?: any; selectedDict?: any;
// ProTable 的 actionRef用于刷新列表 // ProTable 的 actionRef用于刷新列表
actionRef?: React.MutableRefObject<ActionType | undefined>; actionRef?: React.MutableRefObject<ActionType | undefined>;
// 是否显示导出按钮(某些页面可能不需要导出功能) // 是否显示导出按钮(某些页面可能不需要导出功能)
showExport?: boolean; showExport?: boolean;
// 导入字典项的回调函数(如果不提供,则使用默认的导入逻辑) // 导入字典项的回调函数(如果不提供,则使用默认的导入逻辑)
onImport?: (file: File, dictId: number) => Promise<any>; onImport?: (file: File, dictId: number) => Promise<any>;
// 导出字典项的回调函数 // 导出字典项的回调函数
onExport?: () => Promise<void>; onExport?: () => Promise<void>;
// 添加字典项的回调函数 // 添加字典项的回调函数
onAdd?: () => void; onAdd?: () => void;
// 刷新字典列表的回调函数(导入成功后可能需要刷新左侧字典列表) // 刷新字典列表的回调函数(导入成功后可能需要刷新左侧字典列表)
onRefreshDicts?: () => void; onRefreshDicts?: () => void;
} }
// 字典项操作组合组件(包含添加、导入、导出按钮) // 字典项操作组合组件(包含添加、导入、导出按钮)
const DictItemActions: React.FC<DictItemActionsProps> = ({ const DictItemActions: React.FC<DictItemActionsProps> = ({
selectedDict, selectedDict,
actionRef, actionRef,

View File

@ -34,7 +34,7 @@ const DictItemExportButton: React.FC<DictItemExportButtonProps> = ({
message.warning('未提供导出函数'); message.warning('未提供导出函数');
} }
} catch (error: any) { } catch (error: any) {
message.error('导出字典项失败' + (error.message || '未知错误')); message.error('导出字典项失败:' + (error.message || '未知错误'));
} }
}; };

View File

@ -12,7 +12,7 @@ interface DictItemImportButtonProps {
actionRef?: React.MutableRefObject<ActionType | undefined>; actionRef?: React.MutableRefObject<ActionType | undefined>;
// 是否禁用按钮 // 是否禁用按钮
disabled?: boolean; disabled?: boolean;
// 自定义导入函数,返回 Promise(如果不提供,则使用默认的导入逻辑) // 自定义导入函数,返回 Promise(如果不提供,则使用默认的导入逻辑)
onImport?: (file: File, dictId: number) => Promise<any>; onImport?: (file: File, dictId: number) => Promise<any>;
// 导入成功后刷新字典列表的回调函数 // 导入成功后刷新字典列表的回调函数
onRefreshDicts?: () => void; onRefreshDicts?: () => void;
@ -73,7 +73,7 @@ const DictItemImportButton: React.FC<DictItemImportButtonProps> = ({
// 显示导入结果详情 // 显示导入结果详情
const showImportResult = (result: any) => { const showImportResult = (result: any) => {
// 从 result.data 中获取实际数据(因为后端返回格式为 { success: true, data: {...} } // 从 result.data 中获取实际数据(因为后端返回格式为 { success: true, data: {...} })
const data = result.data || result; const data = result.data || result;
const { total, processed, updated, created, errors } = data; const { total, processed, updated, created, errors } = data;

View File

@ -24,7 +24,7 @@ const AttributePage: React.FC = () => {
// 右侧字典项 ProTable 的引用 // 右侧字典项 ProTable 的引用
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
// 字典项模态框状态(由 DictItemModal 组件管理) // 字典项模态框状态(由 DictItemModal 组件管理)
const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false); const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false);
const [isEditDictItem, setIsEditDictItem] = useState(false); const [isEditDictItem, setIsEditDictItem] = useState(false);
const [editingDictItemData, setEditingDictItemData] = useState<any>(null); const [editingDictItemData, setEditingDictItemData] = useState<any>(null);
@ -99,7 +99,7 @@ const AttributePage: React.FC = () => {
message.success(`成功导出 ${data.length} 条数据`); message.success(`成功导出 ${data.length} 条数据`);
} catch (error: any) { } catch (error: any) {
message.error('导出字典项失败' + (error.message || '未知错误')); message.error('导出字典项失败:' + (error.message || '未知错误'));
} }
}; };
@ -389,7 +389,7 @@ const AttributePage: React.FC = () => {
</Content> </Content>
</Layout> </Layout>
{/* 字典项 Modal(添加或编辑) */} {/* 字典项 Modal(添加或编辑) */}
<DictItemModal <DictItemModal
visible={isDictItemModalVisible} visible={isDictItemModalVisible}
isEdit={isEditDictItem} isEdit={isEditDictItem}

View File

@ -0,0 +1,7 @@
export default function CsvTool() {
return (
<div>
<h1>CSV </h1>
</div>
);
}

View File

@ -77,7 +77,7 @@ const CreateForm: React.FC<{
const strengthName: string = String(strengthValues?.[0] || ''); const strengthName: string = String(strengthValues?.[0] || '');
const flavorName: string = String(flavorValues?.[0] || ''); const flavorName: string = String(flavorValues?.[0] || '');
const humidityName: string = String(humidityValues?.[0] || ''); const humidityName: string = String(humidityValues?.[0] || '');
console.log(formValues)
// 调用模板渲染API来生成SKU // 调用模板渲染API来生成SKU
const { const {
data: rendered, data: rendered,
@ -86,10 +86,25 @@ const CreateForm: React.FC<{
} = await templatecontrollerRendertemplate( } = await templatecontrollerRendertemplate(
{ name: 'product.sku' }, { name: 'product.sku' },
{ {
brand: brandName || '', category: formValues.category,
strength: strengthName || '', attributes: [
flavor: flavorName || '', {
humidity: humidityName ? capitalize(humidityName) : '', dict: {name: "brand"},
shortName: brandName || '',
},
{
dict: {name: "flavor"},
shortName: flavorName || '',
},
{
dict: {name: "strength"},
shortName: strengthName || '',
},
{
dict: {name: "humidity"},
shortName: humidityName ? capitalize(humidityName) : '',
},
]
}, },
); );
if (!success) { if (!success) {

View File

@ -217,7 +217,7 @@ const List: React.FC = () => {
sorter: true, sorter: true,
}, },
{ {
title: '商品SKU', title: '关联商品',
dataIndex: 'siteSkus', dataIndex: 'siteSkus',
render: (_, record) => ( render: (_, record) => (
<> <>
@ -244,13 +244,7 @@ const List: React.FC = () => {
}, },
}, },
{
title: '商品类型',
dataIndex: 'category',
render: (_, record: any) => {
return record.category?.title || record.category?.name || '-';
},
},
{ {
title: '价格', title: '价格',
dataIndex: 'price', dataIndex: 'price',
@ -262,6 +256,13 @@ const List: React.FC = () => {
dataIndex: 'promotionPrice', dataIndex: 'promotionPrice',
hideInSearch: true, hideInSearch: true,
sorter: true, sorter: true,
},
{
title: '商品类型',
dataIndex: 'category',
render: (_, record: any) => {
return record.category?.title || record.category?.name || '-';
},
}, },
{ {
title: '属性', title: '属性',
@ -442,6 +443,7 @@ const List: React.FC = () => {
onError?.(error); onError?.(error);
} }
}} }}
> >
<Button></Button> <Button></Button>
</Upload>, </Upload>,
@ -541,6 +543,11 @@ const List: React.FC = () => {
rowSelection={{ rowSelection={{
onChange: (_, selectedRows) => setSelectedRows(selectedRows), onChange: (_, selectedRows) => setSelectedRows(selectedRows),
}} }}
pagination={{
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['10', '20', '50', '100', '1000', '2000'],
}}
/> />
<BatchEditModal <BatchEditModal
visible={batchEditModalVisible} visible={batchEditModalVisible}

View File

@ -211,7 +211,7 @@ const PermutationPage: React.FC = () => {
return; return;
} }
// 生成CSV表头包含所有属性列和SKU列 // 生成CSV表头(包含所有属性列和SKU列)
const headers = [ const headers = [
...attributes.map((attr) => attr.title || attr.name), ...attributes.map((attr) => attr.title || attr.name),
'SKU', 'SKU',
@ -239,7 +239,7 @@ const PermutationPage: React.FC = () => {
// 将表头和数据行合并 // 将表头和数据行合并
const csvContent = [headers, ...rows] const csvContent = [headers, ...rows]
.map((row) => .map((row) =>
// 处理CSV中的特殊字符(逗号、双引号、换行符) // 处理CSV中的特殊字符(逗号、双引号、换行符)
row row
.map((cell) => { .map((cell) => {
const cellStr = String(cell || ''); const cellStr = String(cell || '');
@ -268,7 +268,7 @@ const PermutationPage: React.FC = () => {
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
link.setAttribute('href', url); link.setAttribute('href', url);
// 生成文件名(包含当前分类名称和日期) // 生成文件名(包含当前分类名称和日期)
const category = categories.find((c) => c.id === categoryId); const category = categories.find((c) => c.id === categoryId);
const categoryName = category?.name || '产品'; const categoryName = category?.name || '产品';
const date = new Date().toISOString().slice(0, 10); const date = new Date().toISOString().slice(0, 10);

View File

@ -17,7 +17,7 @@ interface Site {
isDisabled?: boolean; isDisabled?: boolean;
} }
// 定义本地产品接口(与后端 Product 实体匹配) // 定义本地产品接口(与后端 Product 实体匹配)
interface SiteProduct { interface SiteProduct {
id: number; id: number;
sku: string; sku: string;
@ -115,7 +115,7 @@ const SiteProductCell: React.FC<SiteProductCellProps> = ({
setLoading(true); setLoading(true);
try { try {
// 首先查找该产品在该站点的实际SKU // 首先查找该产品在该站点的实际SKU
// 注意siteSkus 现在是字符串数组,无法直接匹配站点 // 注意:siteSkus 现在是字符串数组,无法直接匹配站点
// 这里使用模板生成的 SKU 作为默认值 // 这里使用模板生成的 SKU 作为默认值
let siteProductSku = ''; let siteProductSku = '';
// 如果需要更精确的站点 SKU 匹配,需要后端提供额外的接口 // 如果需要更精确的站点 SKU 匹配,需要后端提供额外的接口
@ -363,7 +363,7 @@ const SiteProductCell: React.FC<SiteProductCellProps> = ({
// 如果没有找到站点产品,显示同步按钮 // 如果没有找到站点产品,显示同步按钮
if (!siteProduct) { if (!siteProduct) {
// 首先查找该产品在该站点的实际SKU // 首先查找该产品在该站点的实际SKU
// 注意siteSkus 现在是字符串数组,无法直接匹配站点 // 注意:siteSkus 现在是字符串数组,无法直接匹配站点
// 这里使用模板生成的 SKU 作为默认值 // 这里使用模板生成的 SKU 作为默认值
let siteProductSku = ''; let siteProductSku = '';
// 如果需要更精确的站点 SKU 匹配,需要后端提供额外的接口 // 如果需要更精确的站点 SKU 匹配,需要后端提供额外的接口

View File

@ -29,7 +29,7 @@ interface Site {
isDisabled?: boolean; isDisabled?: boolean;
} }
// 定义本地产品接口(与后端 Product 实体匹配) // 定义本地产品接口(与后端 Product 实体匹配)
interface SiteProduct { interface SiteProduct {
id: number; id: number;
sku: string; sku: string;
@ -438,7 +438,7 @@ const ProductSyncPage: React.FC = () => {
> >
<div style={{ marginBottom: 16 }}> <div style={{ marginBottom: 16 }}>
<p> <p>
:
<strong>{sites.find((s) => s.id === selectedSiteId)?.name}</strong> <strong>{sites.find((s) => s.id === selectedSiteId)?.name}</strong>
</p> </p>
{selectedRows.length > 0 ? ( {selectedRows.length > 0 ? (
@ -452,17 +452,17 @@ const ProductSyncPage: React.FC = () => {
{syncing && ( {syncing && (
<div style={{ marginBottom: 16 }}> <div style={{ marginBottom: 16 }}>
<div style={{ marginBottom: 8 }}></div> <div style={{ marginBottom: 8 }}>:</div>
<Progress percent={syncProgress} status="active" /> <Progress percent={syncProgress} status="active" />
<div style={{ marginTop: 8, fontSize: 12, color: '#666' }}> <div style={{ marginTop: 8, fontSize: 12, color: '#666' }}>
{syncResults.success} | {syncResults.failed} :{syncResults.success} | :{syncResults.failed}
</div> </div>
</div> </div>
)} )}
{syncResults.errors.length > 0 && ( {syncResults.errors.length > 0 && (
<div style={{ marginBottom: 16, maxHeight: 200, overflow: 'auto' }}> <div style={{ marginBottom: 16, maxHeight: 200, overflow: 'auto' }}>
<div style={{ marginBottom: 8, color: '#ff4d4f' }}></div> <div style={{ marginBottom: 8, color: '#ff4d4f' }}>:</div>
{syncResults.errors.slice(0, 10).map((error, index) => ( {syncResults.errors.slice(0, 10).map((error, index) => (
<div <div
key={index} key={index}

View File

@ -411,7 +411,7 @@ const SiteList: React.FC = () => {
name="areas" name="areas"
label="区域" label="区域"
mode="multiple" mode="multiple"
placeholder="请选择区域(留空表示不修改)" placeholder="请选择区域(留空表示不修改)"
showSearch showSearch
filterOption={(input, option) => filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase()) (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
@ -422,7 +422,7 @@ const SiteList: React.FC = () => {
name="stockPointIds" name="stockPointIds"
label="关联仓库" label="关联仓库"
mode="multiple" mode="multiple"
placeholder="请选择关联仓库(留空表示不修改)" placeholder="请选择关联仓库(留空表示不修改)"
request={async () => { request={async () => {
// 从后端接口获取仓库数据 // 从后端接口获取仓库数据
const res = await stockcontrollerGetallstockpoints(); const res = await stockcontrollerGetallstockpoints();

View File

@ -116,7 +116,6 @@ const ProductsPage: React.FC = () => {
// ID // ID
title: 'ID', title: 'ID',
dataIndex: 'id', dataIndex: 'id',
hideInSearch: true,
width: 120, width: 120,
copyable: true, copyable: true,
render: (_, record) => { render: (_, record) => {
@ -156,7 +155,7 @@ const ProductsPage: React.FC = () => {
}, },
{ {
// 库存 // 库存
title: '库存', title: '库存数量',
dataIndex: 'stock_quantity', dataIndex: 'stock_quantity',
hideInSearch: true, hideInSearch: true,
}, },
@ -423,7 +422,7 @@ const ProductsPage: React.FC = () => {
params: { params: {
page, page,
per_page: pageSize, per_page: pageSize,
...where, where,
...(orderObj ...(orderObj
? { ? {
sortField: Object.keys(orderObj)[0], sortField: Object.keys(orderObj)[0],

View File

@ -121,7 +121,7 @@ const WebhooksPage: React.FC = () => {
message.error(isEditMode ? '更新失败' : '创建失败'); message.error(isEditMode ? '更新失败' : '创建失败');
} }
} catch (error: any) { } catch (error: any) {
message.error('表单验证失败' + error.message); message.error('表单验证失败:' + error.message);
} }
}; };
@ -305,12 +305,12 @@ const WebhooksPage: React.FC = () => {
{ type: 'url', message: '请输入有效的URL' }, { type: 'url', message: '请输入有效的URL' },
]} ]}
> >
<Input placeholder="请输入回调URLhttps://example.com/webhook" /> <Input placeholder="请输入回调URL:https://example.com/webhook" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="secret" name="secret"
label="密钥(可选)" label="密钥(可选)"
rules={[{ max: 255, message: '密钥不能超过255个字符' }]} rules={[{ max: 255, message: '密钥不能超过255个字符' }]}
> >
<Input placeholder="请输入密钥用于验证webhook请求" /> <Input placeholder="请输入密钥用于验证webhook请求" />

View File

@ -69,12 +69,12 @@ export const ErpProductBindModal: React.FC<ErpProductBindModalProps> = ({
onFinish={handleBind} onFinish={handleBind}
> >
<div style={{ marginBottom: 16 }}> <div style={{ marginBottom: 16 }}>
<strong></strong> <strong>:</strong>
<div>SKU: {siteProduct.sku}</div> <div>SKU: {siteProduct.sku}</div>
<div>: {siteProduct.name}</div> <div>: {siteProduct.name}</div>
{siteProduct.erpProduct && ( {siteProduct.erpProduct && (
<div style={{ color: '#ff4d4f' }}> <div style={{ color: '#ff4d4f' }}>
ERP产品{siteProduct.erpProduct.sku} -{' '} ERP产品:{siteProduct.erpProduct.sku} -{' '}
{siteProduct.erpProduct.name} {siteProduct.erpProduct.name}
</div> </div>
)} )}
@ -164,7 +164,7 @@ export const ErpProductBindModal: React.FC<ErpProductBindModalProps> = ({
border: '1px solid #b7eb8f', border: '1px solid #b7eb8f',
}} }}
> >
<strong></strong> <strong>:</strong>
<div>SKU: {selectedProduct.sku}</div> <div>SKU: {selectedProduct.sku}</div>
<div>: {selectedProduct.name}</div> <div>: {selectedProduct.name}</div>
{selectedProduct.nameCn && ( {selectedProduct.nameCn && (

View File

@ -1,14 +1,13 @@
// @ts-ignore // @ts-ignore
/* eslint-disable */ /* eslint-disable */
// API 更新时间 // API 更新时间:
// API 唯一标识 // API 唯一标识:
import * as area from './area'; import * as area from './area';
import * as category from './category'; import * as category from './category';
import * as customer from './customer'; import * as customer from './customer';
import * as dict from './dict'; import * as dict from './dict';
import * as locales from './locales'; import * as locales from './locales';
import * as logistics from './logistics'; import * as logistics from './logistics';
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 site from './site'; import * as site from './site';
@ -26,7 +25,6 @@ export default {
dict, dict,
locales, locales,
logistics, logistics,
media,
order, order,
product, product,
siteApi, siteApi,

View File

@ -1,92 +0,0 @@
// @ts-ignore
/* eslint-disable */
import { request } from 'umi';
/** 此处后端没有提供注释 DELETE /media/${param0} */
export async function mediacontrollerDelete(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.mediacontrollerDeleteParams,
options?: { [key: string]: any },
) {
const { id: param0, ...queryParams } = params;
return request<any>(`/media/${param0}`, {
method: 'DELETE',
params: {
...queryParams,
},
...(options || {}),
});
}
/** 此处后端没有提供注释 GET /media/list */
export async function mediacontrollerList(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.mediacontrollerListParams,
options?: { [key: string]: any },
) {
return request<any>('/media/list', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** 此处后端没有提供注释 POST /media/update/${param0} */
export async function mediacontrollerUpdate(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.mediacontrollerUpdateParams,
body: Record<string, any>,
options?: { [key: string]: any },
) {
const { id: param0, ...queryParams } = params;
return request<any>(`/media/update/${param0}`, {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
params: { ...queryParams },
data: body,
...(options || {}),
});
}
/** 此处后端没有提供注释 POST /media/upload */
export async function mediacontrollerUpload(
body: {},
files?: File[],
options?: { [key: string]: any },
) {
const formData = new FormData();
if (files) {
files.forEach((f) => formData.append('files', f || ''));
}
Object.keys(body).forEach((ele) => {
const item = (body as any)[ele];
if (item !== undefined && item !== null) {
if (typeof item === 'object' && !(item instanceof File)) {
if (item instanceof Array) {
item.forEach((f) => formData.append(ele, f || ''));
} else {
formData.append(
ele,
new Blob([JSON.stringify(item)], { type: 'application/json' }),
);
}
} else {
formData.append(ele, item);
}
}
});
return request<any>('/media/upload', {
method: 'POST',
data: formData,
requestType: 'form',
...(options || {}),
});
}

View File

@ -282,6 +282,20 @@ export async function siteapicontrollerBatchfulfillorders(
); );
} }
/** 此处后端没有提供注释 GET /site-api/${param0}/orders/count */
export async function siteapicontrollerCountorders(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.siteapicontrollerCountordersParams,
options?: { [key: string]: any },
) {
const { siteId: param0, ...queryParams } = params;
return request<Record<string, any>>(`/site-api/${param0}/orders/count`, {
method: 'GET',
params: { ...queryParams },
...(options || {}),
});
}
/** 此处后端没有提供注释 GET /site-api/${param0}/orders/export */ /** 此处后端没有提供注释 GET /site-api/${param0}/orders/export */
export async function siteapicontrollerExportorders( export async function siteapicontrollerExportorders(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象) // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
@ -776,23 +790,18 @@ export async function siteapicontrollerCancelfulfillment(
); );
} }
/** 此处后端没有提供注释 POST /site-api/${param1}/orders/${param0}/fulfill */ /** 此处后端没有提供注释 GET /site-api/${param1}/orders/${param0}/fulfillments */
export async function siteapicontrollerFulfillorder( export async function siteapicontrollerGetorderfulfillments(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象) // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.siteapicontrollerFulfillorderParams, params: API.siteapicontrollerGetorderfulfillmentsParams,
body: API.FulfillmentDTO,
options?: { [key: string]: any }, options?: { [key: string]: any },
) { ) {
const { id: param0, siteId: param1, ...queryParams } = params; const { orderId: param0, siteId: param1, ...queryParams } = params;
return request<Record<string, any>>( return request<Record<string, any>>(
`/site-api/${param1}/orders/${param0}/fulfill`, `/site-api/${param1}/orders/${param0}/fulfillments`,
{ {
method: 'POST', method: 'GET',
headers: {
'Content-Type': 'application/json',
},
params: { ...queryParams }, params: { ...queryParams },
data: body,
...(options || {}), ...(options || {}),
}, },
); );
@ -859,23 +868,6 @@ export async function siteapicontrollerCreateordernote(
); );
} }
/** 此处后端没有提供注释 GET /site-api/${param1}/orders/${param0}/trackings */
export async function siteapicontrollerGetordertrackings(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.siteapicontrollerGetordertrackingsParams,
options?: { [key: string]: any },
) {
const { orderId: param0, siteId: param1, ...queryParams } = params;
return request<Record<string, any>>(
`/site-api/${param1}/orders/${param0}/trackings`,
{
method: 'GET',
params: { ...queryParams },
...(options || {}),
},
);
}
/** 此处后端没有提供注释 GET /site-api/${param1}/products/${param0} */ /** 此处后端没有提供注释 GET /site-api/${param1}/products/${param0} */
export async function siteapicontrollerGetproduct( export async function siteapicontrollerGetproduct(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象) // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)

View File

@ -62,7 +62,7 @@ declare namespace API {
}; };
type BatchErrorItemDTO = { type BatchErrorItemDTO = {
/** 错误项标识如ID、邮箱等 */ /** 错误项标识(如ID、邮箱等) */
identifier?: string; identifier?: string;
/** 错误信息 */ /** 错误信息 */
error?: string; error?: string;
@ -591,22 +591,6 @@ declare namespace API {
weight?: Cubid; weight?: Cubid;
}; };
type mediacontrollerDeleteParams = {
force?: boolean;
siteId?: number;
id: number;
};
type mediacontrollerListParams = {
pageSize?: number;
page?: number;
siteId?: number;
};
type mediacontrollerUpdateParams = {
id: number;
};
type Money = { type Money = {
currency?: string; currency?: string;
value?: string; value?: string;
@ -1433,7 +1417,7 @@ declare namespace API {
keyword?: string; keyword?: string;
/** 是否禁用 */ /** 是否禁用 */
isDisabled?: boolean; isDisabled?: boolean;
/** 站点ID列表(逗号分隔) */ /** 站点ID列表(逗号分隔) */
ids?: string; ids?: string;
}; };
@ -1635,6 +1619,10 @@ declare namespace API {
siteId: number; siteId: number;
}; };
type siteapicontrollerCountordersParams = {
siteId: number;
};
type siteapicontrollerCreatecustomerParams = { type siteapicontrollerCreatecustomerParams = {
siteId: number; siteId: number;
}; };
@ -1813,11 +1801,6 @@ declare namespace API {
siteId: number; siteId: number;
}; };
type siteapicontrollerFulfillorderParams = {
id: string;
siteId: number;
};
type siteapicontrollerGetcustomerordersParams = { type siteapicontrollerGetcustomerordersParams = {
/** 页码 */ /** 页码 */
page?: number; page?: number;
@ -1882,6 +1865,11 @@ declare namespace API {
siteId: number; siteId: number;
}; };
type siteapicontrollerGetorderfulfillmentsParams = {
orderId: string;
siteId: number;
};
type siteapicontrollerGetordernotesParams = { type siteapicontrollerGetordernotesParams = {
id: string; id: string;
siteId: number; siteId: number;
@ -1910,11 +1898,6 @@ declare namespace API {
siteId: number; siteId: number;
}; };
type siteapicontrollerGetordertrackingsParams = {
orderId: string;
siteId: number;
};
type siteapicontrollerGetproductParams = { type siteapicontrollerGetproductParams = {
id: string; id: string;
siteId: number; siteId: number;
@ -2095,7 +2078,7 @@ declare namespace API {
keyword?: string; keyword?: string;
/** 是否禁用 */ /** 是否禁用 */
isDisabled?: boolean; isDisabled?: boolean;
/** 站点ID列表(逗号分隔) */ /** 站点ID列表(逗号分隔) */
ids?: string; ids?: string;
}; };
@ -2406,7 +2389,7 @@ declare namespace API {
type SyncCustomersDTO = { type SyncCustomersDTO = {
/** 站点ID */ /** 站点ID */
siteId?: number; siteId?: number;
/** 查询参数支持where和orderBy */ /** 查询参数(支持where和orderBy) */
params?: UnifiedSearchParamsDTO; params?: UnifiedSearchParamsDTO;
}; };
@ -2787,7 +2770,7 @@ declare namespace API {
variation?: boolean; variation?: boolean;
/** 属性选项 */ /** 属性选项 */
options?: string[]; options?: string[];
/** 变体属性值(单个值) */ /** 变体属性值(单个值) */
option?: string; option?: string;
}; };
@ -3222,6 +3205,11 @@ declare namespace API {
id?: number; id?: number;
}; };
type webhookcontrollerHandleshoppywebhookParams = {
signature?: string;
siteId?: string;
};
type webhookcontrollerHandlewoowebhookParams = { type webhookcontrollerHandlewoowebhookParams = {
siteId?: string; siteId?: string;
}; };

View File

@ -10,6 +10,26 @@ export async function webhookcontrollerTest(options?: { [key: string]: any }) {
}); });
} }
/** 此处后端没有提供注释 POST /webhook/shoppy */
export async function webhookcontrollerHandleshoppywebhook(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.webhookcontrollerHandleshoppywebhookParams,
body: Record<string, any>,
options?: { [key: string]: any },
) {
return request<any>('/webhook/shoppy', {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
params: {
...params,
},
data: body,
...(options || {}),
});
}
/** 此处后端没有提供注释 POST /webhook/woocommerce */ /** 此处后端没有提供注释 POST /webhook/woocommerce */
export async function webhookcontrollerHandlewoowebhook( export async function webhookcontrollerHandlewoowebhook(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象) // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)

View File

@ -22,7 +22,7 @@ export interface BatchOperationResult {
} }
/** /**
* * ()
* @param result * @param result
* @param operationType * @param operationType
*/ */
@ -30,12 +30,12 @@ export function showBatchOperationResult(
result: BatchOperationResult, result: BatchOperationResult,
operationType: string = '操作', operationType: string = '操作',
): string { ): string {
// 从 result.data 中获取实际数据(因为后端返回格式为 { success: true, data: {...} } // 从 result.data 中获取实际数据(因为后端返回格式为 { success: true, data: {...} })
const data = (result as any).data || result; const data = (result as any).data || result;
const { total, processed, created, updated, deleted, errors } = data; const { total, processed, created, updated, deleted, errors } = data;
// 构建结果消息 // 构建结果消息
let messageContent = `${operationType}结果${total} 条,成功 ${processed}`; let messageContent = `${operationType}结果:${total} 条,成功 ${processed}`;
if (created) { if (created) {
messageContent += `,创建 ${created}`; messageContent += `,创建 ${created}`;
@ -54,7 +54,7 @@ export function showBatchOperationResult(
const errorDetails = errors const errorDetails = errors
.map((err: BatchErrorItem) => `${err.identifier}: ${err.error}`) .map((err: BatchErrorItem) => `${err.identifier}: ${err.error}`)
.join('\n'); .join('\n');
message.warning(messageContent + '\n\n错误详情\n' + errorDetails); message.warning(messageContent + '\n\n错误详情:\n' + errorDetails);
} else { } else {
message.success(messageContent); message.success(messageContent);
} }