chore:根据API删减调整前端。 #36
12
.umirc.ts
12
.umirc.ts
|
|
@ -100,6 +100,18 @@ export default defineConfig({
|
|||
path: '/site/shop/:siteId/customers',
|
||||
component: './Site/Shop/Customers',
|
||||
},
|
||||
{
|
||||
path: '/site/shop/:siteId/reviews',
|
||||
component: './Site/Shop/Reviews',
|
||||
},
|
||||
{
|
||||
path: '/site/shop/:siteId/webhooks',
|
||||
component: './Site/Shop/Webhooks',
|
||||
},
|
||||
{
|
||||
path: '/site/shop/:siteId/links',
|
||||
component: './Site/Shop/Links',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import {
|
|||
message,
|
||||
} from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { attributes } from '../Attribute/consts';
|
||||
import { notAttributes } from '../Product/Attribute/consts';
|
||||
|
||||
const { Sider, Content } = Layout;
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ const CategoryPage: React.FC = () => {
|
|||
// Fetch all dicts and filter those that are allowed attributes
|
||||
try {
|
||||
const res = await request('/dict/list');
|
||||
const filtered = (res || []).filter((d: any) => attributes.has(d.name));
|
||||
const filtered = (res || []).filter((d: any) => !notAttributes.has(d.name));
|
||||
// Filter out already added attributes
|
||||
const existingDictIds = new Set(
|
||||
categoryAttributes.map((ca: any) => ca.dict.id),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import {
|
|||
PageContainer,
|
||||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import { request } from '@umijs/max';
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
|
|
@ -17,6 +16,7 @@ import {
|
|||
message,
|
||||
} from 'antd';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import * as dictApi from '@/servers/api/dict';
|
||||
|
||||
const { Sider, Content } = Layout;
|
||||
|
||||
|
|
@ -26,14 +26,20 @@ const DictPage: React.FC = () => {
|
|||
const [loadingDicts, setLoadingDicts] = useState(false);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [selectedDict, setSelectedDict] = useState<any>(null);
|
||||
|
||||
// 添加字典 modal 状态
|
||||
const [isAddDictModalVisible, setIsAddDictModalVisible] = useState(false);
|
||||
const [editingDict, setEditingDict] = useState<any>(null);
|
||||
const [newDictName, setNewDictName] = useState('');
|
||||
const [newDictTitle, setNewDictTitle] = useState('');
|
||||
const [addDictName, setAddDictName] = useState('');
|
||||
const [addDictTitle, setAddDictTitle] = useState('');
|
||||
|
||||
// 编辑字典 modal 状态
|
||||
const [isEditDictModalVisible, setIsEditDictModalVisible] = useState(false);
|
||||
const [editDictData, setEditDictData] = useState<any>(null);
|
||||
|
||||
// 右侧字典项列表的状态
|
||||
const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false);
|
||||
const [editingDictItem, setEditingDictItem] = useState<any>(null);
|
||||
const [isAddDictItemModalVisible, setIsAddDictItemModalVisible] = useState(false);
|
||||
const [isEditDictItemModalVisible, setIsEditDictItemModalVisible] = useState(false);
|
||||
const [editDictItemData, setEditDictItemData] = useState<any>(null);
|
||||
const [dictItemForm] = Form.useForm();
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
|
|
@ -41,7 +47,7 @@ const DictPage: React.FC = () => {
|
|||
const fetchDicts = async (name = '') => {
|
||||
setLoadingDicts(true);
|
||||
try {
|
||||
const res = await request('/dict/list', { params: { name } });
|
||||
const res = await dictApi.dictcontrollerGetdicts({ name });
|
||||
setDicts(res);
|
||||
} catch (error) {
|
||||
message.error('获取字典列表失败');
|
||||
|
|
@ -55,60 +61,66 @@ const DictPage: React.FC = () => {
|
|||
fetchDicts(value);
|
||||
};
|
||||
|
||||
// 添加或编辑字典
|
||||
// 添加字典
|
||||
const handleAddDict = async () => {
|
||||
const values = { name: newDictName, title: newDictTitle };
|
||||
const values = { name: addDictName, title: addDictTitle };
|
||||
try {
|
||||
if (editingDict) {
|
||||
await request(`/dict/${editingDict.id}`, {
|
||||
method: 'PUT',
|
||||
data: values,
|
||||
});
|
||||
message.success('更新成功');
|
||||
} else {
|
||||
await request('/dict', { method: 'POST', data: values });
|
||||
message.success('添加成功');
|
||||
}
|
||||
await dictApi.dictcontrollerCreatedict(values);
|
||||
message.success('添加成功');
|
||||
setIsAddDictModalVisible(false);
|
||||
setEditingDict(null);
|
||||
setNewDictName('');
|
||||
setNewDictTitle('');
|
||||
setAddDictName('');
|
||||
setAddDictTitle('');
|
||||
fetchDicts(); // 重新获取列表
|
||||
} catch (error) {
|
||||
message.error(editingDict ? '更新失败' : '添加失败');
|
||||
message.error('添加失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 编辑字典
|
||||
const handleEditDict = async () => {
|
||||
if (!editDictData) return;
|
||||
const values = { name: editDictData.name, title: editDictData.title };
|
||||
try {
|
||||
await dictApi.dictcontrollerUpdatedict({ id: editDictData.id }, values);
|
||||
message.success('更新成功');
|
||||
setIsEditDictModalVisible(false);
|
||||
setEditDictData(null);
|
||||
fetchDicts(); // 重新获取列表
|
||||
} catch (error) {
|
||||
message.error('更新失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 删除字典
|
||||
const handleDeleteDict = async (id: number) => {
|
||||
try {
|
||||
await request(`/dict/${id}`, { method: 'DELETE' });
|
||||
const result = await dictApi.dictcontrollerDeletedict({ id });
|
||||
if(!result.success){
|
||||
throw new Error(result.message || '删除失败')
|
||||
}
|
||||
message.success('删除成功');
|
||||
fetchDicts();
|
||||
if (selectedDict?.id === id) {
|
||||
setSelectedDict(null);
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('删除失败');
|
||||
} catch (error:any) {
|
||||
message.error(`删除失败,原因为:${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 编辑字典
|
||||
const handleEditDict = (record: any) => {
|
||||
setEditingDict(record);
|
||||
setNewDictName(record.name);
|
||||
setNewDictTitle(record.title);
|
||||
setIsAddDictModalVisible(true);
|
||||
// 打开编辑字典 modal
|
||||
const openEditDictModal = (record: any) => {
|
||||
setEditDictData(record);
|
||||
setIsEditDictModalVisible(true);
|
||||
};
|
||||
|
||||
// 下载字典导入模板
|
||||
const handleDownloadDictTemplate = async () => {
|
||||
try {
|
||||
// 使用 request 函数获取带认证的文件数据
|
||||
const response = await request('/dict/template', {
|
||||
method: 'GET',
|
||||
responseType: 'blob', // 指定响应类型为 blob
|
||||
skipErrorHandler: true, // 跳过默认错误处理,自己处理错误
|
||||
// 使用 dictApi.dictcontrollerDownloaddicttemplate 获取字典模板
|
||||
const response = await dictApi.dictcontrollerDownloaddicttemplate({
|
||||
responseType: 'blob',
|
||||
skipErrorHandler: true,
|
||||
});
|
||||
|
||||
// 创建 blob 对象和下载链接
|
||||
|
|
@ -130,46 +142,79 @@ const DictPage: React.FC = () => {
|
|||
|
||||
// 添加字典项
|
||||
const handleAddDictItem = () => {
|
||||
setEditingDictItem(null);
|
||||
dictItemForm.resetFields();
|
||||
setIsDictItemModalVisible(true);
|
||||
setIsAddDictItemModalVisible(true);
|
||||
};
|
||||
|
||||
// 编辑字典项
|
||||
const handleEditDictItem = (record: any) => {
|
||||
setEditingDictItem(record);
|
||||
setEditDictItemData(record);
|
||||
dictItemForm.setFieldsValue(record);
|
||||
setIsDictItemModalVisible(true);
|
||||
setIsEditDictItemModalVisible(true);
|
||||
};
|
||||
|
||||
// 删除字典项
|
||||
const handleDeleteDictItem = async (id: number) => {
|
||||
try {
|
||||
await request(`/dict/item/${id}`, { method: 'DELETE' });
|
||||
const result = await dictApi.dictcontrollerDeletedictitem({ id });
|
||||
if(!result.success){
|
||||
throw new Error(result.message || '删除失败')
|
||||
}
|
||||
message.success('删除成功');
|
||||
actionRef.current?.reload();
|
||||
} catch (error) {
|
||||
message.error('删除失败');
|
||||
|
||||
// 强制刷新字典项列表
|
||||
setTimeout(() => {
|
||||
actionRef.current?.reload();
|
||||
}, 100);
|
||||
} catch (error:any) {
|
||||
message.error(`删除失败,原因为:${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 字典项表单提交
|
||||
const handleDictItemFormSubmit = async (values: any) => {
|
||||
const url = editingDictItem
|
||||
? `/dict/item/${editingDictItem.id}`
|
||||
: '/dict/item';
|
||||
const method = editingDictItem ? 'PUT' : 'POST';
|
||||
const data = editingDictItem
|
||||
? { ...values }
|
||||
: { ...values, dict: { id: selectedDict.id } };
|
||||
|
||||
// 添加字典项表单提交
|
||||
const handleAddDictItemFormSubmit = async (values: any) => {
|
||||
try {
|
||||
await request(url, { method, data });
|
||||
message.success(editingDictItem ? '更新成功' : '添加成功');
|
||||
setIsDictItemModalVisible(false);
|
||||
actionRef.current?.reload();
|
||||
} catch (error) {
|
||||
message.error(editingDictItem ? '更新失败' : '添加失败');
|
||||
const result = await dictApi.dictcontrollerCreatedictitem({
|
||||
...values,
|
||||
dictId: selectedDict.id
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || '添加失败');
|
||||
}
|
||||
|
||||
message.success('添加成功');
|
||||
setIsAddDictItemModalVisible(false);
|
||||
|
||||
// 强制刷新字典项列表
|
||||
setTimeout(() => {
|
||||
actionRef.current?.reload();
|
||||
}, 100);
|
||||
} catch (error: any) {
|
||||
message.error(`添加失败:${error.message || '未知错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 编辑字典项表单提交
|
||||
const handleEditDictItemFormSubmit = async (values: any) => {
|
||||
if (!editDictItemData) return;
|
||||
try {
|
||||
const result = await dictApi.dictcontrollerUpdatedictitem({ id: editDictItemData.id }, values);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.message || '更新失败');
|
||||
}
|
||||
|
||||
message.success('更新成功');
|
||||
setIsEditDictItemModalVisible(false);
|
||||
setEditDictItemData(null);
|
||||
|
||||
// 强制刷新字典项列表
|
||||
setTimeout(() => {
|
||||
actionRef.current?.reload();
|
||||
}, 100);
|
||||
} catch (error: any) {
|
||||
message.error(`更新失败:${error.message || '未知错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -182,10 +227,7 @@ const DictPage: React.FC = () => {
|
|||
|
||||
try {
|
||||
// 获取当前字典的所有数据
|
||||
const response = await request('/dict/items', {
|
||||
method: 'GET',
|
||||
params: { dictId: selectedDict.id },
|
||||
});
|
||||
const response = await dictApi.dictcontrollerGetdictitems({ dictId: selectedDict.id });
|
||||
|
||||
if (!response || response.length === 0) {
|
||||
message.warning('当前字典没有数据可导出');
|
||||
|
|
@ -257,7 +299,7 @@ const DictPage: React.FC = () => {
|
|||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => handleEditDict(record)}
|
||||
onClick={() => openEditDictModal(record)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
|
|
@ -331,7 +373,7 @@ const DictPage: React.FC = () => {
|
|||
<PageContainer>
|
||||
<Layout style={{ background: '#fff' }}>
|
||||
<Sider
|
||||
width={240}
|
||||
width={300}
|
||||
style={{
|
||||
background: '#fff',
|
||||
padding: '8px',
|
||||
|
|
@ -347,6 +389,7 @@ const DictPage: React.FC = () => {
|
|||
enterButton
|
||||
allowClear
|
||||
/>
|
||||
<Space size="small">
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => setIsAddDictModalVisible(true)}
|
||||
|
|
@ -354,10 +397,9 @@ const DictPage: React.FC = () => {
|
|||
>
|
||||
添加字典
|
||||
</Button>
|
||||
<Space size="small">
|
||||
<Upload
|
||||
name="file"
|
||||
action="/dict/import"
|
||||
action="/api/dict/import"
|
||||
showUploadList={false}
|
||||
onChange={(info) => {
|
||||
if (info.file.status === 'done') {
|
||||
|
|
@ -375,7 +417,7 @@ const DictPage: React.FC = () => {
|
|||
<Button size="small" onClick={handleDownloadDictTemplate}>
|
||||
导出模板
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
<Table
|
||||
dataSource={dicts}
|
||||
columns={dictColumns}
|
||||
|
|
@ -401,53 +443,6 @@ const DictPage: React.FC = () => {
|
|||
</Sider>
|
||||
<Content style={{ padding: '8px' }}>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: '2px',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleAddDictItem}
|
||||
disabled={!selectedDict}
|
||||
size="small"
|
||||
>
|
||||
添加字典项
|
||||
</Button>
|
||||
<Upload
|
||||
name="file"
|
||||
action={`/dict/item/import`}
|
||||
data={{ dictId: selectedDict?.id }}
|
||||
showUploadList={false}
|
||||
disabled={!selectedDict}
|
||||
onChange={(info) => {
|
||||
if (info.file.status === 'done') {
|
||||
message.success(`${info.file.name} 文件上传成功`);
|
||||
actionRef.current?.reload();
|
||||
} else if (info.file.status === 'error') {
|
||||
message.error(`${info.file.name} 文件上传失败`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<UploadOutlined />}
|
||||
disabled={!selectedDict}
|
||||
>
|
||||
导入字典项
|
||||
</Button>
|
||||
</Upload>
|
||||
<Button
|
||||
onClick={handleExportDictItems}
|
||||
disabled={!selectedDict}
|
||||
size="small"
|
||||
>
|
||||
导出数据
|
||||
</Button>
|
||||
</div>
|
||||
<ProTable
|
||||
columns={dictItemColumns}
|
||||
request={async (params) => {
|
||||
|
|
@ -459,15 +454,24 @@ const DictPage: React.FC = () => {
|
|||
};
|
||||
}
|
||||
const { name, title } = params;
|
||||
const res = await request('/dict/items', {
|
||||
params: {
|
||||
dictId: selectedDict?.id,
|
||||
name,
|
||||
title,
|
||||
},
|
||||
const res = await dictApi.dictcontrollerGetdictitems({
|
||||
dictId: selectedDict?.id,
|
||||
name,
|
||||
title,
|
||||
});
|
||||
|
||||
// 适配新的响应格式,检查是否有 successResponse 包裹
|
||||
if (res && res.success !== undefined) {
|
||||
return {
|
||||
data: res.data || [],
|
||||
success: res.success,
|
||||
total: res.data?.length || 0,
|
||||
};
|
||||
}
|
||||
|
||||
// 兼容旧的响应格式(直接返回数组)
|
||||
return {
|
||||
data: res,
|
||||
data: res || [],
|
||||
success: true,
|
||||
};
|
||||
}}
|
||||
|
|
@ -476,25 +480,85 @@ const DictPage: React.FC = () => {
|
|||
layout: 'vertical',
|
||||
}}
|
||||
pagination={false}
|
||||
options={false}
|
||||
options={{
|
||||
reload: true,
|
||||
density: true,
|
||||
setting: true,
|
||||
}}
|
||||
size="small"
|
||||
key={selectedDict?.id}
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleAddDictItem}
|
||||
disabled={!selectedDict}
|
||||
size="small"
|
||||
key="add"
|
||||
>
|
||||
添加字典项
|
||||
</Button>,
|
||||
<Upload
|
||||
name="file"
|
||||
action={undefined}
|
||||
customRequest={async (options) => {
|
||||
const { file, onSuccess, onError } = options;
|
||||
try {
|
||||
const result = await dictApi.dictcontrollerImportdictitems(
|
||||
{ dictId: selectedDict?.id },
|
||||
[file as File]
|
||||
);
|
||||
onSuccess?.(result);
|
||||
} catch (error) {
|
||||
onError?.(error as Error);
|
||||
}
|
||||
}}
|
||||
showUploadList={false}
|
||||
disabled={!selectedDict}
|
||||
onChange={(info) => {
|
||||
console.log(`info`,info)
|
||||
if (info.file.status === 'done') {
|
||||
message.success(`${info.file.name} 文件上传成功`);
|
||||
// 重新加载字典项列表
|
||||
setTimeout(() => {
|
||||
actionRef.current?.reload();
|
||||
}, 100);
|
||||
// 重新加载字典列表以更新字典项数量
|
||||
fetchDicts();
|
||||
} else if (info.file.status === 'error') {
|
||||
message.error(`${info.file.name} 文件上传失败`);
|
||||
}
|
||||
}}
|
||||
key="import"
|
||||
>
|
||||
<Button size="small" icon={<UploadOutlined />}>
|
||||
导入字典项
|
||||
</Button>
|
||||
</Upload>,
|
||||
<Button
|
||||
onClick={handleExportDictItems}
|
||||
disabled={!selectedDict}
|
||||
size="small"
|
||||
key="export"
|
||||
>
|
||||
导出数据
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
</Space>
|
||||
</Content>
|
||||
</Layout>
|
||||
|
||||
{/* 添加字典项 Modal */}
|
||||
<Modal
|
||||
title={editingDictItem ? '编辑字典项' : '添加字典项'}
|
||||
open={isDictItemModalVisible}
|
||||
title={`添加字典项 - ${selectedDict?.title || '未选择字典'}`}
|
||||
open={isAddDictItemModalVisible}
|
||||
onOk={() => dictItemForm.submit()}
|
||||
onCancel={() => setIsDictItemModalVisible(false)}
|
||||
destroyOnClose
|
||||
onCancel={() => setIsAddDictItemModalVisible(false)}
|
||||
>
|
||||
<Form
|
||||
form={dictItemForm}
|
||||
layout="vertical"
|
||||
onFinish={handleDictItemFormSubmit}
|
||||
onFinish={handleAddDictItemFormSubmit}
|
||||
>
|
||||
<Form.Item
|
||||
label="名称"
|
||||
|
|
@ -525,25 +589,102 @@ const DictPage: React.FC = () => {
|
|||
</Form>
|
||||
</Modal>
|
||||
|
||||
{/* 编辑字典项 Modal */}
|
||||
<Modal
|
||||
title={editingDict ? '编辑字典' : '添加新字典'}
|
||||
visible={isAddDictModalVisible}
|
||||
title="编辑字典项"
|
||||
open={isEditDictItemModalVisible}
|
||||
onOk={() => dictItemForm.submit()}
|
||||
onCancel={() => {
|
||||
setIsEditDictItemModalVisible(false);
|
||||
setEditDictItemData(null);
|
||||
}}
|
||||
>
|
||||
<Form
|
||||
form={dictItemForm}
|
||||
layout="vertical"
|
||||
onFinish={handleEditDictItemFormSubmit}
|
||||
>
|
||||
<Form.Item
|
||||
label="名称"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '请输入名称' }]}
|
||||
>
|
||||
<Input placeholder="名称 (e.g., zyn)" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="标题"
|
||||
name="title"
|
||||
rules={[{ required: true, message: '请输入标题' }]}
|
||||
>
|
||||
<Input placeholder="标题 (e.g., ZYN)" />
|
||||
</Form.Item>
|
||||
<Form.Item label="中文标题" name="titleCN">
|
||||
<Input placeholder="中文标题 (e.g., 品牌)" />
|
||||
</Form.Item>
|
||||
<Form.Item label="简称 (可选)" name="shortName">
|
||||
<Input placeholder="简称 (可选)" />
|
||||
</Form.Item>
|
||||
<Form.Item label="图片 (可选)" name="image">
|
||||
<Input placeholder="图片链接 (可选)" />
|
||||
</Form.Item>
|
||||
<Form.Item label="值 (可选)" name="value">
|
||||
<Input placeholder="值 (可选)" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
{/* 添加字典 Modal */}
|
||||
<Modal
|
||||
title="添加新字典"
|
||||
open={isAddDictModalVisible}
|
||||
onOk={handleAddDict}
|
||||
onCancel={() => setIsAddDictModalVisible(false)}
|
||||
onCancel={() => {
|
||||
setIsAddDictModalVisible(false);
|
||||
setAddDictName('');
|
||||
setAddDictTitle('');
|
||||
}}
|
||||
>
|
||||
<Form layout="vertical">
|
||||
<Form.Item label="字典名称">
|
||||
<Input
|
||||
placeholder="字典名称 (e.g., brand)"
|
||||
value={newDictName}
|
||||
onChange={(e) => setNewDictName(e.target.value)}
|
||||
value={addDictName}
|
||||
onChange={(e) => setAddDictName(e.target.value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="字典标题">
|
||||
<Input
|
||||
placeholder="字典标题 (e.g., 品牌)"
|
||||
value={newDictTitle}
|
||||
onChange={(e) => setNewDictTitle(e.target.value)}
|
||||
value={addDictTitle}
|
||||
onChange={(e) => setAddDictTitle(e.target.value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
{/* 编辑字典 Modal */}
|
||||
<Modal
|
||||
title="编辑字典"
|
||||
open={isEditDictModalVisible}
|
||||
onOk={handleEditDict}
|
||||
onCancel={() => {
|
||||
setIsEditDictModalVisible(false);
|
||||
setEditDictData(null);
|
||||
}}
|
||||
>
|
||||
<Form layout="vertical">
|
||||
<Form.Item label="字典名称">
|
||||
<Input
|
||||
placeholder="字典名称 (e.g., brand)"
|
||||
value={editDictData?.name || ''}
|
||||
onChange={(e) => setEditDictData({ ...editDictData, name: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="字典标题">
|
||||
<Input
|
||||
placeholder="字典标题 (e.g., 品牌)"
|
||||
value={editDictData?.title || ''}
|
||||
onChange={(e) => setEditDictData({ ...editDictData, title: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
// 限定允许管理的字典名称集合
|
||||
export const attributes = new Set([
|
||||
'brand',
|
||||
'strength',
|
||||
'flavor',
|
||||
'size',
|
||||
'humidity',
|
||||
export const notAttributes = new Set([
|
||||
'zh-cn',
|
||||
'en-us',
|
||||
'category'
|
||||
]);
|
||||
|
|
@ -20,7 +20,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
|||
|
||||
const { Sider, Content } = Layout;
|
||||
|
||||
import { attributes } from './consts';
|
||||
import { notAttributes } from './consts';
|
||||
|
||||
const AttributePage: React.FC = () => {
|
||||
// 左侧字典列表状态
|
||||
|
|
@ -42,7 +42,7 @@ const AttributePage: React.FC = () => {
|
|||
try {
|
||||
const res = await request('/dict/list', { params: { title } });
|
||||
// 条件判断,过滤只保留 allowedDictNames 中的字典
|
||||
const filtered = (res || []).filter((d: any) => attributes.has(d?.name));
|
||||
const filtered = (res || []).filter((d: any) => !notAttributes.has(d?.name));
|
||||
setDicts(filtered);
|
||||
} catch (error) {
|
||||
message.error('获取字典列表失败');
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import {
|
|||
message,
|
||||
} from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { attributes } from '../Attribute/consts';
|
||||
import { notAttributes } from '../Attribute/consts';
|
||||
|
||||
const { Sider, Content } = Layout;
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ const CategoryPage: React.FC = () => {
|
|||
const res = await request('/dict/list');
|
||||
// Defensive check for response structure: handle both raw array and wrapped response
|
||||
const list = Array.isArray(res) ? res : res?.data || [];
|
||||
const filtered = list.filter((d: any) => attributes.has(d.name));
|
||||
const filtered = list.filter((d: any) => !notAttributes.has(d.name));
|
||||
// Filter out already added attributes
|
||||
const existingDictIds = new Set(
|
||||
categoryAttributes.map((ca: any) => ca.dictId),
|
||||
|
|
@ -244,7 +244,7 @@ const CategoryPage: React.FC = () => {
|
|||
</Popconfirm>,
|
||||
]}
|
||||
>
|
||||
<List.Item.Meta title={item.title} description={item.name} />
|
||||
<List.Item.Meta title={`${item.title}(${item.titleCN??'-'})`} description={item.name} />
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
|
|
@ -310,16 +310,18 @@ const CategoryPage: React.FC = () => {
|
|||
onFinish={handleCategorySubmit}
|
||||
layout="vertical"
|
||||
>
|
||||
<Form.Item name="title" label="标题" rules={[{ required: true }]}>
|
||||
<Form.Item name="title" label="标题">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="标识 (Code)"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Form.Item name="titleCN" label="中文名称">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="name" label="标识 (Code)">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="sort" label="排序">
|
||||
<Input type="number" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
|
|
|
|||
|
|
@ -462,9 +462,9 @@ const List: React.FC = () => {
|
|||
dataIndex: 'siteSkus',
|
||||
render: (_, record) => (
|
||||
<>
|
||||
{record.siteSkus?.map((code, index) => (
|
||||
{record.siteSkus?.map((siteSku, index) => (
|
||||
<Tag key={index} color="cyan">
|
||||
{code}
|
||||
{siteSku.siteSku}
|
||||
</Tag>
|
||||
))}
|
||||
</>
|
||||
|
|
@ -609,56 +609,7 @@ const List: React.FC = () => {
|
|||
toolBarRender={() => [
|
||||
// 新建按钮
|
||||
<CreateForm tableRef={actionRef} />,
|
||||
// 批量编辑按钮
|
||||
<Button
|
||||
disabled={selectedRows.length <= 0}
|
||||
onClick={() => setBatchEditModalVisible(true)}
|
||||
>
|
||||
批量修改
|
||||
</Button>,
|
||||
// 批量同步按钮
|
||||
<Button
|
||||
disabled={selectedRows.length <= 0}
|
||||
onClick={() => {
|
||||
setSyncProductIds(selectedRows.map((row) => row.id));
|
||||
setSyncModalVisible(true);
|
||||
}}
|
||||
>
|
||||
批量同步到站点
|
||||
</Button>,
|
||||
// 批量删除按钮
|
||||
<Button
|
||||
danger
|
||||
disabled={selectedRows.length <= 0}
|
||||
onClick={() => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除选中的 ${selectedRows.length} 个产品吗?此操作不可恢复。`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
const { success, message: errMsg } =
|
||||
await productcontrollerBatchdeleteproduct({
|
||||
ids: selectedRows.map((row) => row.id),
|
||||
});
|
||||
if (success) {
|
||||
message.success('批量删除成功');
|
||||
setSelectedRows([]);
|
||||
actionRef.current?.reload();
|
||||
} else {
|
||||
message.error(errMsg || '删除失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '删除失败');
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
批量删除
|
||||
</Button>,
|
||||
// 导出 CSV(后端返回 text/csv,直接新窗口下载)
|
||||
<Button onClick={handleDownloadProductsCSV}>导出CSV</Button>,
|
||||
// 导入 CSV(使用 customRequest 以支持 request 拦截器和鉴权)
|
||||
// 导入 CSV(使用 customRequest 以支持 request 拦截器和鉴权)
|
||||
<Upload
|
||||
name="file"
|
||||
accept=".csv"
|
||||
|
|
@ -733,8 +684,57 @@ const List: React.FC = () => {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<Button>导入CSV</Button>
|
||||
<Button>批量导入</Button>
|
||||
</Upload>,
|
||||
// 批量编辑按钮
|
||||
<Button
|
||||
disabled={selectedRows.length <= 0}
|
||||
onClick={() => setBatchEditModalVisible(true)}
|
||||
>
|
||||
批量修改
|
||||
</Button>,
|
||||
// 批量同步按钮
|
||||
<Button
|
||||
disabled={selectedRows.length <= 0}
|
||||
onClick={() => {
|
||||
setSyncProductIds(selectedRows.map((row) => row.id));
|
||||
setSyncModalVisible(true);
|
||||
}}
|
||||
>
|
||||
批量同步到站点
|
||||
</Button>,
|
||||
// 批量删除按钮
|
||||
<Button
|
||||
danger
|
||||
disabled={selectedRows.length <= 0}
|
||||
onClick={() => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除选中的 ${selectedRows.length} 个产品吗?此操作不可恢复。`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
const { success, message: errMsg } =
|
||||
await productcontrollerBatchdeleteproduct({
|
||||
ids: selectedRows.map((row) => row.id),
|
||||
});
|
||||
if (success) {
|
||||
message.success('批量删除成功');
|
||||
setSelectedRows([]);
|
||||
actionRef.current?.reload();
|
||||
} else {
|
||||
message.error(errMsg || '删除失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '删除失败');
|
||||
}
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
批量删除
|
||||
</Button>,
|
||||
// 导出 CSV(后端返回 text/csv,直接新窗口下载)
|
||||
<Button onClick={handleDownloadProductsCSV}>导出CSV</Button>,
|
||||
]}
|
||||
request={async (params, sort) => {
|
||||
let sortField = undefined;
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ const PermutationPage: React.FC = () => {
|
|||
const valB = b[attr.name]?.name || '';
|
||||
return valA.localeCompare(valB);
|
||||
},
|
||||
filters: attributeValues[attr.name]?.map((v: any) => ({
|
||||
filters: attributeValues?.[attr.name]?.map?.((v: any) => ({
|
||||
text: v.name,
|
||||
value: v.name,
|
||||
})),
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ const ProductSyncPage: React.FC = () => {
|
|||
// 如果没有找到实际的siteSku,则根据模板生成
|
||||
const expectedSku = siteProductSku || (
|
||||
skuTemplate
|
||||
? renderSku(skuTemplate, { site: targetSite, product })
|
||||
? renderSiteSku(skuTemplate, { site: targetSite, product })
|
||||
: `${targetSite.skuPrefix || ''}-${product.sku}`
|
||||
);
|
||||
|
||||
|
|
@ -329,7 +329,7 @@ const ProductSyncPage: React.FC = () => {
|
|||
};
|
||||
|
||||
// 简单的模板渲染函数
|
||||
const renderSku = (template: string, data: any) => {
|
||||
const renderSiteSku = (template: string, data: any) => {
|
||||
if (!template) return '';
|
||||
// 支持 <%= it.path %> (Eta) 和 {{ path }} (Mustache/Handlebars)
|
||||
return template.replace(
|
||||
|
|
@ -463,7 +463,7 @@ const ProductSyncPage: React.FC = () => {
|
|||
// 如果没有找到实际的siteSku,则根据模板或默认规则生成期望的SKU
|
||||
const expectedSku = siteProductSku || (
|
||||
skuTemplate
|
||||
? renderSku(skuTemplate, { site, product: record })
|
||||
? renderSiteSku(skuTemplate, { site, product: record })
|
||||
: `${site.skuPrefix || ''}-${record.sku}`
|
||||
);
|
||||
|
||||
|
|
@ -472,7 +472,7 @@ const ProductSyncPage: React.FC = () => {
|
|||
|
||||
// 如果根据实际SKU没找到,再尝试用模板生成的SKU查找
|
||||
if (!wpProduct && siteProductSku && skuTemplate) {
|
||||
const templateSku = renderSku(skuTemplate, { site, product: record });
|
||||
const templateSku = renderSiteSku(skuTemplate, { site, product: record });
|
||||
wpProduct = wpProductMap.get(templateSku);
|
||||
}
|
||||
|
||||
|
|
@ -492,7 +492,7 @@ const ProductSyncPage: React.FC = () => {
|
|||
initialValues={{
|
||||
sku: siteProductSku || (
|
||||
skuTemplate
|
||||
? renderSku(skuTemplate, { site, product: record })
|
||||
? renderSiteSku(skuTemplate, { site, product: record })
|
||||
: `${site.skuPrefix || ''}-${record.sku}`
|
||||
),
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -203,11 +203,17 @@ const CustomerPage: React.FC = () => {
|
|||
},
|
||||
},
|
||||
{
|
||||
title: '注册时间',
|
||||
title: '创建时间',
|
||||
dataIndex: 'date_created',
|
||||
valueType: 'dateTime',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'date_modified',
|
||||
valueType: 'dateTime',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
valueType: 'option',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { Button, Card, Col, Menu, Row, Select, Spin, message } from 'antd';
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import type { SiteItem } from '../List/index';
|
||||
import EditSiteForm from './EditSiteForm';
|
||||
import Sider from 'antd/es/layout/Sider';
|
||||
|
||||
const ShopLayout: React.FC = () => {
|
||||
const [sites, setSites] = useState<any[]>([]);
|
||||
|
|
@ -92,20 +93,12 @@ const ShopLayout: React.FC = () => {
|
|||
return (
|
||||
<PageContainer
|
||||
header={{ title: null, breadcrumb: undefined }}
|
||||
contentStyle={{
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
|
||||
<Row gutter={16} style={{ height: 'calc(100vh - 100px)' }}>
|
||||
<Col span={4} style={{ height: '100%' }}>
|
||||
<Card
|
||||
bodyStyle={{
|
||||
padding: '10px 0',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
style={{ height: '100%', overflow: 'hidden' }}
|
||||
<Sider
|
||||
style={{ background: 'white', height: '100%', overflow: 'hidden' }}
|
||||
>
|
||||
<div style={{ padding: '0 10px 16px' }}>
|
||||
<div
|
||||
|
|
@ -157,10 +150,12 @@ const ShopLayout: React.FC = () => {
|
|||
{ key: 'media', label: '媒体管理' },
|
||||
{ key: 'customers', label: '客户管理' },
|
||||
{ key: 'reviews', label: '评论管理' },
|
||||
{ key: 'webhooks', label: 'Webhooks管理' },
|
||||
{ key: 'links', label: '链接管理' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Sider>
|
||||
</Col>
|
||||
<Col span={20} style={{ height: '100%', overflowY: 'auto' }}>
|
||||
{siteId ? <Outlet /> : <div>请选择店铺</div>}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
import { Button, Card, List } from 'antd';
|
||||
import { request, useParams } from '@umijs/max';
|
||||
import { App } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { LinkOutlined } from '@ant-design/icons';
|
||||
import { PageHeader } from '@ant-design/pro-layout';
|
||||
|
||||
// 定义链接项的类型
|
||||
interface LinkItem {
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
const LinksPage: React.FC = () => {
|
||||
const { siteId } = useParams<{ siteId: string }>();
|
||||
const { message: antMessage } = App.useApp();
|
||||
const [links, setLinks] = useState<LinkItem[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
// 获取链接列表的函数
|
||||
const fetchLinks = async () => {
|
||||
if (!siteId) return;
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await request(`/site-api/${siteId}/links`);
|
||||
if (response.success && response.data) {
|
||||
setLinks(response.data);
|
||||
} else {
|
||||
antMessage.error(response.message || '获取链接列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
antMessage.error('获取链接列表失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 页面加载时获取链接列表
|
||||
useEffect(() => {
|
||||
fetchLinks();
|
||||
}, [siteId]);
|
||||
|
||||
// 处理链接点击事件,在新标签页打开
|
||||
const handleLinkClick = (url: string) => {
|
||||
window.open(url, '_blank', 'noopener,noreferrer');
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader
|
||||
title="站点链接"
|
||||
breadcrumb={{ items: [] }}
|
||||
/>
|
||||
<Card
|
||||
title="常用链接"
|
||||
bordered={false}
|
||||
extra={
|
||||
<Button type="primary" onClick={fetchLinks} loading={loading}>
|
||||
刷新列表
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<List
|
||||
loading={loading}
|
||||
dataSource={links}
|
||||
renderItem={(item) => (
|
||||
<List.Item
|
||||
key={item.title}
|
||||
actions={[
|
||||
<Button
|
||||
key={`visit-${item.title}`}
|
||||
type="link"
|
||||
icon={<LinkOutlined />}
|
||||
onClick={() => handleLinkClick(item.url)}
|
||||
target="_blank"
|
||||
>
|
||||
访问
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
<List.Item.Meta
|
||||
title={item.title}
|
||||
description={
|
||||
<a
|
||||
href={item.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#1890ff' }}
|
||||
>
|
||||
{item.url}
|
||||
</a>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinksPage;
|
||||
|
|
@ -186,6 +186,52 @@ const OrdersPage: React.FC = () => {
|
|||
>
|
||||
<Button type="text" icon={<EllipsisOutlined />} />
|
||||
</Dropdown>
|
||||
<Button
|
||||
type="link"
|
||||
title="发货"
|
||||
onClick={async () => {
|
||||
try {
|
||||
const res = await request(
|
||||
`/site-api/${siteId}/orders/${record.id}/ship`,
|
||||
{ method: 'POST' },
|
||||
);
|
||||
if (res.success) {
|
||||
message.success('发货成功');
|
||||
actionRef.current?.reload();
|
||||
} else {
|
||||
message.error(res.message || '发货失败');
|
||||
}
|
||||
} catch (e) {
|
||||
message.error('发货失败');
|
||||
}
|
||||
}}
|
||||
>
|
||||
发货
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确定取消发货?"
|
||||
description="取消发货后订单状态将恢复为处理中"
|
||||
onConfirm={async () => {
|
||||
try {
|
||||
const res = await request(
|
||||
`/site-api/${siteId}/orders/${record.id}/cancel-ship`,
|
||||
{ method: 'POST' },
|
||||
);
|
||||
if (res.success) {
|
||||
message.success('取消发货成功');
|
||||
actionRef.current?.reload();
|
||||
} else {
|
||||
message.error(res.message || '取消发货失败');
|
||||
}
|
||||
} catch (e) {
|
||||
message.error('取消发货失败');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button type="link" danger title="取消发货">
|
||||
取消发货
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
<Popconfirm
|
||||
title="确定删除订单?"
|
||||
onConfirm={async () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import {
|
||||
Modal,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
siteapicontrollerCreatereview,
|
||||
siteapicontrollerUpdatereview,
|
||||
} from '@/servers/api/siteApi';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const { Option } = Select;
|
||||
|
||||
interface ReviewFormProps {
|
||||
open: boolean;
|
||||
|
|
@ -15,19 +30,161 @@ const ReviewForm: React.FC<ReviewFormProps> = ({
|
|||
onClose,
|
||||
onSuccess,
|
||||
}) => {
|
||||
// // 这是一个临时的占位符组件
|
||||
// // 你可以在这里实现表单逻辑
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// 当编辑状态改变时,重置表单数据
|
||||
useEffect(() => {
|
||||
if (editing) {
|
||||
form.setFieldsValue({
|
||||
product_id: editing.product_id,
|
||||
author: editing.author,
|
||||
email: editing.email,
|
||||
content: editing.content,
|
||||
rating: editing.rating,
|
||||
status: editing.status,
|
||||
});
|
||||
} else {
|
||||
form.resetFields();
|
||||
}
|
||||
}, [editing, form]);
|
||||
|
||||
// 处理表单提交
|
||||
const handleSubmit = async (values: any) => {
|
||||
try {
|
||||
let response;
|
||||
|
||||
if (editing) {
|
||||
// 更新评论
|
||||
response = await siteapicontrollerUpdatereview(
|
||||
{
|
||||
siteId,
|
||||
id: editing.id,
|
||||
},
|
||||
{
|
||||
review: values.content,
|
||||
rating: values.rating,
|
||||
status: values.status,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// 创建新评论
|
||||
response = await siteapicontrollerCreatereview(
|
||||
{
|
||||
siteId,
|
||||
},
|
||||
{
|
||||
product_id: values.product_id,
|
||||
review: values.content,
|
||||
rating: values.rating,
|
||||
author: values.author,
|
||||
author_email: values.email,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (response.success) {
|
||||
message.success(editing ? '更新成功' : '创建成功');
|
||||
onSuccess();
|
||||
onClose();
|
||||
form.resetFields();
|
||||
} else {
|
||||
message.error(response.message || '操作失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交评论表单失败:', error);
|
||||
message.error('提交失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Review Form</h2>
|
||||
<p>Site ID: {siteId}</p>
|
||||
<p>Editing: {editing ? 'Yes' : 'No'}</p>
|
||||
<button onClick={onClose}>Close</button>
|
||||
</div>
|
||||
<Modal
|
||||
title={editing ? '编辑评论' : '新建评论'}
|
||||
open={open}
|
||||
onCancel={onClose}
|
||||
onOk={() => form.submit()}
|
||||
okText="保存"
|
||||
cancelText="取消"
|
||||
width={600}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={handleSubmit}
|
||||
initialValues={{
|
||||
status: 'approved',
|
||||
rating: 5,
|
||||
}}
|
||||
>
|
||||
{!editing && (
|
||||
<>
|
||||
<Form.Item
|
||||
name="product_id"
|
||||
label="产品ID"
|
||||
rules={[{ required: true, message: '请输入产品ID' }]}
|
||||
>
|
||||
<Input placeholder="请输入产品ID" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="author"
|
||||
label="评论者"
|
||||
rules={[{ required: true, message: '请输入评论者姓名' }]}
|
||||
>
|
||||
<Input placeholder="请输入评论者姓名" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="email"
|
||||
label="邮箱"
|
||||
rules={[
|
||||
{ required: true, message: '请输入邮箱' },
|
||||
{ type: 'email', message: '请输入有效的邮箱地址' }
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入邮箱" />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="content"
|
||||
label="评论内容"
|
||||
rules={[{ required: true, message: '请输入评论内容' }]}
|
||||
>
|
||||
<TextArea
|
||||
rows={4}
|
||||
placeholder="请输入评论内容"
|
||||
maxLength={1000}
|
||||
showCount
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="rating"
|
||||
label="评分"
|
||||
rules={[{ required: true, message: '请选择评分' }]}
|
||||
>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={5}
|
||||
precision={0}
|
||||
placeholder="评分 (1-5)"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="status"
|
||||
label="状态"
|
||||
rules={[{ required: true, message: '请选择状态' }]}
|
||||
>
|
||||
<Select placeholder="请选择状态">
|
||||
<Option value="approved">已批准</Option>
|
||||
<Option value="pending">待审核</Option>
|
||||
<Option value="spam">垃圾评论</Option>
|
||||
<Option value="trash">回收站</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -85,17 +85,44 @@ const ReviewsPage: React.FC = () => {
|
|||
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,
|
||||
};
|
||||
try {
|
||||
const response = await siteapicontrollerGetreviews({
|
||||
...params,
|
||||
siteId,
|
||||
page: params.current,
|
||||
per_page: params.pageSize,
|
||||
});
|
||||
// 确保 response.data 存在
|
||||
if (!response || !response.data) {
|
||||
return {
|
||||
data: [],
|
||||
success: true,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
// 确保 response.data.items 是数组
|
||||
const items = Array.isArray(response.data.items) ? response.data.items : [];
|
||||
// 确保每个 item 有有效的 id
|
||||
const processedItems = items.map((item, index) => ({
|
||||
...item,
|
||||
// 如果 id 是对象,转换为字符串,否则使用索引作为后备
|
||||
id: typeof item.id === 'object' ? JSON.stringify(item.id) : (item.id || index),
|
||||
// 如果 product_id 是对象,转换为字符串
|
||||
product_id: typeof item.product_id === 'object' ? JSON.stringify(item.product_id) : item.product_id,
|
||||
}));
|
||||
return {
|
||||
data: processedItems,
|
||||
success: true,
|
||||
total: Number(response.data.total) || 0,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取评论失败:', error);
|
||||
return {
|
||||
data: [],
|
||||
success: true,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
}}
|
||||
rowKey="id"
|
||||
search={{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,297 @@
|
|||
import { siteapicontrollerGetwebhooks, siteapicontrollerDeletewebhook, siteapicontrollerCreatewebhook, siteapicontrollerUpdatewebhook, siteapicontrollerGetwebhook } from '@/servers/api/siteApi';
|
||||
import {
|
||||
ActionType,
|
||||
ProCard,
|
||||
ProColumns,
|
||||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import { useParams } from '@umijs/max';
|
||||
import { Button, message, Popconfirm, Space, Modal, Form, Input, Select } from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
|
||||
const WebhooksPage: React.FC = () => {
|
||||
const params = useParams();
|
||||
const siteId = Number(params.siteId);
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
// 模态框状态
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [isEditMode, setIsEditMode] = useState(false);
|
||||
const [currentWebhook, setCurrentWebhook] = useState<API.UnifiedWebhookDTO | null>(null);
|
||||
|
||||
// 表单实例
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// webhook主题选项
|
||||
const webhookTopics = [
|
||||
{ label: '订单创建', value: 'order.created' },
|
||||
{ label: '订单更新', value: 'order.updated' },
|
||||
{ label: '订单删除', value: 'order.deleted' },
|
||||
{ label: '产品创建', value: 'product.created' },
|
||||
{ label: '产品更新', value: 'product.updated' },
|
||||
{ label: '产品删除', value: 'product.deleted' },
|
||||
{ label: '客户创建', value: 'customer.created' },
|
||||
{ label: '客户更新', value: 'customer.updated' },
|
||||
{ label: '客户删除', value: 'customer.deleted' },
|
||||
];
|
||||
|
||||
// webhook状态选项
|
||||
const webhookStatuses = [
|
||||
{ label: '活跃', value: 'active' },
|
||||
{ label: '非活跃', value: 'inactive' },
|
||||
];
|
||||
|
||||
// 打开新建模态框
|
||||
const showCreateModal = () => {
|
||||
setIsEditMode(false);
|
||||
setCurrentWebhook(null);
|
||||
form.resetFields();
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
// 打开编辑模态框
|
||||
const showEditModal = async (record: API.UnifiedWebhookDTO) => {
|
||||
setIsEditMode(true);
|
||||
setCurrentWebhook(record);
|
||||
try {
|
||||
// 如果需要获取最新的webhook数据,可以取消下面的注释
|
||||
// const response = await siteapicontrollerGetwebhook({ siteId, id: String(record.id) });
|
||||
// if (response.success && response.data) {
|
||||
// form.setFieldsValue(response.data);
|
||||
// } else {
|
||||
// form.setFieldsValue(record);
|
||||
// }
|
||||
form.setFieldsValue(record);
|
||||
setIsModalVisible(true);
|
||||
} catch (error) {
|
||||
message.error('加载webhook数据失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭模态框
|
||||
const handleCancel = () => {
|
||||
setIsModalVisible(false);
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
|
||||
// 准备提交数据
|
||||
const webhookData = {
|
||||
...values,
|
||||
siteId,
|
||||
};
|
||||
|
||||
let response;
|
||||
if (isEditMode && currentWebhook?.id) {
|
||||
// 更新webhook
|
||||
response = await siteapicontrollerUpdatewebhook({
|
||||
...webhookData,
|
||||
id: String(currentWebhook.id),
|
||||
});
|
||||
} else {
|
||||
// 创建新webhook
|
||||
response = await siteapicontrollerCreatewebhook(webhookData);
|
||||
}
|
||||
|
||||
if (response.success) {
|
||||
message.success(isEditMode ? '更新成功' : '创建成功');
|
||||
setIsModalVisible(false);
|
||||
form.resetFields();
|
||||
actionRef.current?.reload();
|
||||
} else {
|
||||
message.error(isEditMode ? '更新失败' : '创建失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error('表单验证失败:' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const columns: ProColumns<API.UnifiedWebhookDTO>[] = [
|
||||
{ title: 'ID', dataIndex: 'id', key: 'id', width: 50 },
|
||||
{ title: '名称', dataIndex: 'name', key: 'name' },
|
||||
{ title: '主题', dataIndex: 'topic', key: 'topic' },
|
||||
{ title: '回调URL', dataIndex: 'delivery_url', key: 'delivery_url', ellipsis: true },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', width: 80 },
|
||||
{ title: '创建时间', dataIndex: 'date_created', key: 'date_created', valueType: 'dateTime' },
|
||||
{ title: '更新时间', dataIndex: 'date_modified', key: 'date_modified', valueType: 'dateTime' },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button
|
||||
type="link"
|
||||
style={{ padding: 0 }}
|
||||
onClick={() => showEditModal(record)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确定删除吗?"
|
||||
onConfirm={async () => {
|
||||
if (record.id) {
|
||||
try {
|
||||
const response = await siteapicontrollerDeletewebhook({
|
||||
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.UnifiedWebhookDTO>
|
||||
columns={columns}
|
||||
actionRef={actionRef}
|
||||
request={async (params) => {
|
||||
try {
|
||||
const response = await siteapicontrollerGetwebhooks({
|
||||
...params,
|
||||
siteId,
|
||||
page: params.current,
|
||||
per_page: params.pageSize,
|
||||
});
|
||||
// 确保 response.data 存在
|
||||
if (!response || !response.data) {
|
||||
return {
|
||||
data: [],
|
||||
success: true,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
// 确保 response.data.items 是数组
|
||||
const items = Array.isArray(response.data.items) ? response.data.items : [];
|
||||
// 确保每个 item 有有效的 id
|
||||
const processedItems = items.map((item, index) => ({
|
||||
...item,
|
||||
// 如果 id 是对象,转换为字符串,否则使用索引作为后备
|
||||
id: typeof item.id === 'object' ? JSON.stringify(item.id) : (item.id || index),
|
||||
}));
|
||||
return {
|
||||
data: processedItems,
|
||||
success: true,
|
||||
total: Number(response.data.total) || 0,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取webhooks失败:', error);
|
||||
return {
|
||||
data: [],
|
||||
success: true,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
}}
|
||||
rowKey="id"
|
||||
search={{
|
||||
labelWidth: 'auto',
|
||||
}}
|
||||
headerTitle="Webhooks列表"
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={showCreateModal}
|
||||
>
|
||||
新建Webhook
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
</ProCard>
|
||||
|
||||
{/* Webhook编辑/新建模态框 */}
|
||||
<Modal
|
||||
title={isEditMode ? '编辑Webhook' : '新建Webhook'}
|
||||
open={isModalVisible}
|
||||
onCancel={handleCancel}
|
||||
footer={[
|
||||
<Button key="back" onClick={handleCancel}>
|
||||
取消
|
||||
</Button>,
|
||||
<Button key="submit" type="primary" onClick={handleSubmit}>
|
||||
{isEditMode ? '更新' : '创建'}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
status: 'active',
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="名称"
|
||||
rules={[{ required: true, message: '请输入webhook名称' }, { max: 100, message: '名称不能超过100个字符' }]}
|
||||
>
|
||||
<Input placeholder="请输入webhook名称" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="topic"
|
||||
label="主题"
|
||||
rules={[{ required: true, message: '请选择webhook主题' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择webhook主题"
|
||||
options={webhookTopics}
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="delivery_url"
|
||||
label="回调URL"
|
||||
rules={[{ required: true, message: '请输入回调URL' }, { type: 'url', message: '请输入有效的URL' }]}
|
||||
>
|
||||
<Input placeholder="请输入回调URL,如:https://example.com/webhook" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="secret"
|
||||
label="密钥(可选)"
|
||||
rules={[{ max: 255, message: '密钥不能超过255个字符' }]}
|
||||
>
|
||||
<Input placeholder="请输入密钥,用于验证webhook请求" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="status"
|
||||
label="状态"
|
||||
rules={[{ required: true, message: '请选择webhook状态' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择webhook状态"
|
||||
options={webhookStatuses}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default WebhooksPage;
|
||||
|
|
@ -3,132 +3,87 @@ import {
|
|||
templatecontrollerDeletetemplate,
|
||||
templatecontrollerGettemplatelist,
|
||||
templatecontrollerRendertemplate,
|
||||
templatecontrollerRendertemplatedirect,
|
||||
templatecontrollerUpdatetemplate,
|
||||
} from '@/servers/api/template';
|
||||
import { BugOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { EditOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ActionType,
|
||||
DrawerForm,
|
||||
ModalForm,
|
||||
PageContainer,
|
||||
ProColumns,
|
||||
ProForm,
|
||||
ProFormText,
|
||||
ProFormTextArea,
|
||||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import Editor from '@monaco-editor/react';
|
||||
import { App, Button, Card, Popconfirm, Typography } from 'antd';
|
||||
import { App, Button, Card, Popconfirm, Space, Typography } from 'antd';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import ReactJson from 'react-json-view';
|
||||
|
||||
const TestModal: React.FC<{
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
template: API.Template | null;
|
||||
}> = ({ visible, onClose, template }) => {
|
||||
const { message } = App.useApp();
|
||||
const [inputData, setInputData] = useState<Record<string, any>>({});
|
||||
// 自定义hook,用于处理模板预览逻辑
|
||||
const useTemplatePreview = () => {
|
||||
const [renderedResult, setRenderedResult] = useState<string>('');
|
||||
const [previewData, setPreviewData] = useState<any>(null);
|
||||
|
||||
// 当模板改变时,重置数据
|
||||
// 防抖的预览效果
|
||||
useEffect(() => {
|
||||
if (visible && template) {
|
||||
// 尝试解析模板中可能的变量作为初始数据(可选优化,这里先置空)
|
||||
// 或者根据模板类型提供一些默认值
|
||||
if (template.testData) {
|
||||
try {
|
||||
setInputData(JSON.parse(template.testData));
|
||||
} catch (e) {
|
||||
console.error('Failed to parse testData:', e);
|
||||
setInputData({});
|
||||
}
|
||||
} else {
|
||||
setInputData({});
|
||||
}
|
||||
setRenderedResult('');
|
||||
if (!previewData || !previewData.value) {
|
||||
setRenderedResult('请输入模板内容');
|
||||
return;
|
||||
}
|
||||
}, [visible, template]);
|
||||
|
||||
// 监听 inputData 变化并调用渲染 API
|
||||
useEffect(() => {
|
||||
if (!visible || !template) return;
|
||||
|
||||
const timer = setTimeout(async () => {
|
||||
let testData = {};
|
||||
try {
|
||||
const res = await templatecontrollerRendertemplate(
|
||||
{ name: template.name || '' },
|
||||
inputData,
|
||||
);
|
||||
if (previewData.testData) {
|
||||
testData = JSON.parse(previewData.testData);
|
||||
}
|
||||
} catch (e) {
|
||||
testData = {};
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用新的直接渲染API,传入模板内容和测试数据
|
||||
const res = await templatecontrollerRendertemplatedirect({
|
||||
template: previewData.value,
|
||||
data: testData,
|
||||
});
|
||||
if (res.success) {
|
||||
setRenderedResult(res.data as unknown as string);
|
||||
} else {
|
||||
setRenderedResult(`Error: ${res.message}`);
|
||||
setRenderedResult(`错误: ${res.message}`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
setRenderedResult(`Error: ${error.message}`);
|
||||
setRenderedResult(`错误: ${error.message}`);
|
||||
}
|
||||
}, 500); // 防抖 500ms
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [inputData, visible, template]);
|
||||
}, [previewData]);
|
||||
|
||||
return (
|
||||
<ModalForm
|
||||
title={`测试模板: ${template?.name || '未知模板'}`}
|
||||
open={visible}
|
||||
onOpenChange={(open) => !open && onClose()}
|
||||
modalProps={{ destroyOnClose: true, onCancel: onClose }}
|
||||
submitter={false} // 不需要提交按钮
|
||||
width={800}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: '20px' }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Typography.Title level={5}>输入数据 (JSON)</Typography.Title>
|
||||
<Card bodyStyle={{ padding: 0, height: '300px', overflow: 'auto' }}>
|
||||
<ReactJson
|
||||
src={inputData}
|
||||
onEdit={(edit) =>
|
||||
setInputData(edit.updated_src as Record<string, any>)
|
||||
}
|
||||
onAdd={(add) =>
|
||||
setInputData(add.updated_src as Record<string, any>)
|
||||
}
|
||||
onDelete={(del) =>
|
||||
setInputData(del.updated_src as Record<string, any>)
|
||||
}
|
||||
name={false}
|
||||
displayDataTypes={false}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Typography.Title level={5}>渲染结果</Typography.Title>
|
||||
<Card
|
||||
bodyStyle={{
|
||||
padding: '16px',
|
||||
height: '300px',
|
||||
overflow: 'auto',
|
||||
backgroundColor: '#f5f5f5',
|
||||
}}
|
||||
>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', margin: 0 }}>
|
||||
{renderedResult}
|
||||
</pre>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</ModalForm>
|
||||
);
|
||||
// 处理实时预览逻辑
|
||||
const handlePreview = (_changedValues: any, allValues: any) => {
|
||||
setPreviewData(allValues);
|
||||
};
|
||||
|
||||
// 手动刷新预览
|
||||
const refreshPreview = (formValues: any) => {
|
||||
setPreviewData(formValues);
|
||||
};
|
||||
|
||||
return {
|
||||
renderedResult,
|
||||
handlePreview,
|
||||
refreshPreview,
|
||||
setPreviewData
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
const List: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const { message } = App.useApp();
|
||||
const [testModalVisible, setTestModalVisible] = useState(false);
|
||||
const [currentTemplate, setCurrentTemplate] = useState<API.Template | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
const columns: ProColumns<API.Template>[] = [
|
||||
{
|
||||
|
|
@ -169,17 +124,7 @@ const List: React.FC = () => {
|
|||
dataIndex: 'option',
|
||||
valueType: 'option',
|
||||
render: (_, record) => (
|
||||
<>
|
||||
<Button
|
||||
type="link"
|
||||
icon={<BugOutlined />}
|
||||
onClick={() => {
|
||||
setCurrentTemplate(record);
|
||||
setTestModalVisible(true);
|
||||
}}
|
||||
>
|
||||
测试
|
||||
</Button>
|
||||
<Space>
|
||||
<UpdateForm tableRef={actionRef} values={record} />
|
||||
<Popconfirm
|
||||
title="删除"
|
||||
|
|
@ -198,7 +143,7 @@ const List: React.FC = () => {
|
|||
删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
|
@ -222,11 +167,6 @@ const List: React.FC = () => {
|
|||
}}
|
||||
columns={columns}
|
||||
/>
|
||||
<TestModal
|
||||
visible={testModalVisible}
|
||||
onClose={() => setTestModalVisible(false)}
|
||||
template={currentTemplate}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
|
@ -235,9 +175,13 @@ const CreateForm: React.FC<{
|
|||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||||
}> = ({ tableRef }) => {
|
||||
const { message } = App.useApp();
|
||||
const [form] = ProForm.useForm();
|
||||
const { renderedResult, handlePreview, refreshPreview } = useTemplatePreview();
|
||||
|
||||
return (
|
||||
<DrawerForm<API.CreateTemplateDTO>
|
||||
title="新建"
|
||||
form={form}
|
||||
trigger={
|
||||
<Button type="primary">
|
||||
<PlusOutlined />
|
||||
|
|
@ -247,7 +191,9 @@ const CreateForm: React.FC<{
|
|||
autoFocusFirstInput
|
||||
drawerProps={{
|
||||
destroyOnHidden: true,
|
||||
width: 1200, // 增加抽屉宽度以容纳调试面板
|
||||
}}
|
||||
onValuesChange={handlePreview}
|
||||
onFinish={async (values) => {
|
||||
try {
|
||||
await templatecontrollerCreatetemplate(values);
|
||||
|
|
@ -260,46 +206,92 @@ const CreateForm: React.FC<{
|
|||
}
|
||||
}}
|
||||
>
|
||||
<ProFormText
|
||||
name="name"
|
||||
label="模板名称"
|
||||
placeholder="请输入名称"
|
||||
rules={[{ required: true, message: '请输入名称' }]}
|
||||
/>
|
||||
<ProForm.Item
|
||||
name="value"
|
||||
label="值"
|
||||
rules={[{ required: true, message: '请输入值' }]}
|
||||
>
|
||||
<Editor
|
||||
height="500px"
|
||||
defaultLanguage="html"
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true,
|
||||
}}
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProFormTextArea
|
||||
name="testData"
|
||||
label="测试数据 (JSON)"
|
||||
placeholder="请输入JSON格式的测试数据"
|
||||
rules={[
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (!value) return Promise.resolve();
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('请输入有效的JSON格式'));
|
||||
<div style={{ display: 'flex', gap: '20px' }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<ProFormText
|
||||
name="name"
|
||||
label="模板名称"
|
||||
placeholder="请输入名称"
|
||||
rules={[{ required: true, message: '请输入名称' }]}
|
||||
/>
|
||||
<ProForm.Item
|
||||
name="value"
|
||||
label="模板内容"
|
||||
rules={[{ required: true, message: '请输入模板内容' }]}
|
||||
>
|
||||
<Editor
|
||||
height="400px"
|
||||
defaultLanguage="html"
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true,
|
||||
}}
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
name="testData"
|
||||
label="测试数据 (JSON)"
|
||||
rules={[
|
||||
{
|
||||
validator: (_: any, value: any) => {
|
||||
if (!value) return Promise.resolve();
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('请输入有效的JSON格式'));
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Editor
|
||||
height="200px"
|
||||
defaultLanguage="json"
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true,
|
||||
formatOnPaste: true,
|
||||
formatOnType: true,
|
||||
}}
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '8px' }}>
|
||||
<Typography.Title level={5} style={{ margin: 0 }}>实时预览</Typography.Title>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={() => {
|
||||
// 获取当前表单数据并触发预览
|
||||
const currentValues = form.getFieldsValue();
|
||||
refreshPreview(currentValues);
|
||||
}}
|
||||
title="手动刷新预览"
|
||||
/>
|
||||
</div>
|
||||
<Card
|
||||
styles={{
|
||||
body: {
|
||||
padding: '16px',
|
||||
height: '600px',
|
||||
overflow: 'auto',
|
||||
backgroundColor: '#f5f5f5',
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}}
|
||||
>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', margin: 0 }}>
|
||||
{renderedResult || '修改模板或测试数据后将自动预览结果...'}
|
||||
</pre>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DrawerForm>
|
||||
);
|
||||
};
|
||||
|
|
@ -309,9 +301,24 @@ const UpdateForm: React.FC<{
|
|||
values: API.Template;
|
||||
}> = ({ tableRef, values: initialValues }) => {
|
||||
const { message } = App.useApp();
|
||||
return (
|
||||
const [form] = ProForm.useForm();
|
||||
const { renderedResult, handlePreview, refreshPreview, setPreviewData } = useTemplatePreview();
|
||||
|
||||
// 组件挂载时初始化预览数据
|
||||
useEffect(() => {
|
||||
if (initialValues) {
|
||||
setPreviewData({
|
||||
name: initialValues.name,
|
||||
value: initialValues.value,
|
||||
testData: initialValues.testData
|
||||
});
|
||||
}
|
||||
}, [initialValues, setPreviewData]);
|
||||
|
||||
return (
|
||||
<DrawerForm<API.UpdateTemplateDTO>
|
||||
title="编辑"
|
||||
form={form}
|
||||
initialValues={initialValues}
|
||||
trigger={
|
||||
<Button type="primary">
|
||||
|
|
@ -322,7 +329,9 @@ const UpdateForm: React.FC<{
|
|||
autoFocusFirstInput
|
||||
drawerProps={{
|
||||
destroyOnHidden: true,
|
||||
width: 1200, // 增加抽屉宽度以容纳调试面板
|
||||
}}
|
||||
onValuesChange={handlePreview}
|
||||
onFinish={async (values) => {
|
||||
if (!initialValues.id) return false;
|
||||
try {
|
||||
|
|
@ -339,46 +348,92 @@ const UpdateForm: React.FC<{
|
|||
}
|
||||
}}
|
||||
>
|
||||
<ProFormText
|
||||
name="name"
|
||||
label="模板名称"
|
||||
placeholder="请输入名称"
|
||||
rules={[{ required: true, message: '请输入名称' }]}
|
||||
/>
|
||||
<ProForm.Item
|
||||
name="value"
|
||||
label="值"
|
||||
rules={[{ required: true, message: '请输入值' }]}
|
||||
>
|
||||
<Editor
|
||||
height="500px"
|
||||
defaultLanguage="html"
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true,
|
||||
}}
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProFormTextArea
|
||||
name="testData"
|
||||
label="测试数据 (JSON)"
|
||||
placeholder="请输入JSON格式的测试数据"
|
||||
rules={[
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (!value) return Promise.resolve();
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('请输入有效的JSON格式'));
|
||||
<div style={{ display: 'flex', gap: '20px' }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<ProFormText
|
||||
name="name"
|
||||
label="模板名称"
|
||||
placeholder="请输入名称"
|
||||
rules={[{ required: true, message: '请输入名称' }]}
|
||||
/>
|
||||
<ProForm.Item
|
||||
name="value"
|
||||
label="模板内容"
|
||||
rules={[{ required: true, message: '请输入模板内容' }]}
|
||||
>
|
||||
<Editor
|
||||
height="400px"
|
||||
defaultLanguage="html"
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true,
|
||||
}}
|
||||
/>
|
||||
</ProForm.Item>
|
||||
<ProForm.Item
|
||||
name="testData"
|
||||
label="测试数据 (JSON)"
|
||||
rules={[
|
||||
{
|
||||
validator: (_: any, value: any) => {
|
||||
if (!value) return Promise.resolve();
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('请输入有效的JSON格式'));
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Editor
|
||||
height="200px"
|
||||
defaultLanguage="json"
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true,
|
||||
formatOnPaste: true,
|
||||
formatOnType: true,
|
||||
}}
|
||||
/>
|
||||
</ProForm.Item>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '8px' }}>
|
||||
<Typography.Title level={5} style={{ margin: 0 }}>实时预览</Typography.Title>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={() => {
|
||||
// 获取当前表单数据并触发预览
|
||||
const currentValues = form.getFieldsValue();
|
||||
refreshPreview(currentValues);
|
||||
}}
|
||||
title="手动刷新预览"
|
||||
/>
|
||||
</div>
|
||||
<Card
|
||||
styles={{
|
||||
body: {
|
||||
padding: '16px',
|
||||
height: '600px',
|
||||
overflow: 'auto',
|
||||
backgroundColor: '#f5f5f5',
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}}
|
||||
>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', margin: 0 }}>
|
||||
{renderedResult || '修改模板或测试数据后将自动预览结果...'}
|
||||
</pre>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DrawerForm>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -177,20 +177,6 @@ export async function ordercontrollerCreateorder(
|
|||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 PUT /order/order/export/${param0} */
|
||||
export async function ordercontrollerExportorder(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.ordercontrollerExportorderParams,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { ids: param0, ...queryParams } = params;
|
||||
return request<any>(`/order/order/export/${param0}`, {
|
||||
method: 'PUT',
|
||||
params: { ...queryParams },
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /order/order/pengding/items */
|
||||
export async function ordercontrollerPengdingitems(
|
||||
body: Record<string, any>,
|
||||
|
|
|
|||
|
|
@ -102,6 +102,20 @@ export async function siteapicontrollerImportcustomers(
|
|||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /site-api/${param0}/links */
|
||||
export async function siteapicontrollerGetlinks(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerGetlinksParams,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { siteId: param0, ...queryParams } = params;
|
||||
return request<any>(`/site-api/${param0}/links`, {
|
||||
method: 'GET',
|
||||
params: { ...queryParams },
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /site-api/${param0}/media */
|
||||
export async function siteapicontrollerGetmedia(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
|
|
@ -263,6 +277,25 @@ export async function siteapicontrollerBatchorders(
|
|||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /site-api/${param0}/orders/batch-ship */
|
||||
export async function siteapicontrollerBatchshiporders(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerBatchshipordersParams,
|
||||
body: API.BatchShipOrdersDTO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { siteId: param0, ...queryParams } = params;
|
||||
return request<Record<string, any>>(`/site-api/${param0}/orders/batch-ship`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /site-api/${param0}/orders/export */
|
||||
export async function siteapicontrollerExportorders(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
|
|
@ -529,6 +562,45 @@ export async function siteapicontrollerExportsubscriptions(
|
|||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /site-api/${param0}/webhooks */
|
||||
export async function siteapicontrollerGetwebhooks(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerGetwebhooksParams,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { siteId: param0, ...queryParams } = params;
|
||||
return request<API.UnifiedPaginationDTO>(`/site-api/${param0}/webhooks`, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
...queryParams,
|
||||
where: undefined,
|
||||
...queryParams['where'],
|
||||
order: undefined,
|
||||
...queryParams['order'],
|
||||
},
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /site-api/${param0}/webhooks */
|
||||
export async function siteapicontrollerCreatewebhook(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerCreatewebhookParams,
|
||||
body: API.CreateWebhookDTO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { siteId: param0, ...queryParams } = params;
|
||||
return request<API.UnifiedWebhookDTO>(`/site-api/${param0}/webhooks`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /site-api/${param1}/customers/${param0} */
|
||||
export async function siteapicontrollerGetcustomer(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
|
|
@ -688,6 +760,28 @@ export async function siteapicontrollerDeleteorder(
|
|||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /site-api/${param1}/orders/${param0}/cancel-ship */
|
||||
export async function siteapicontrollerCancelshiporder(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerCancelshiporderParams,
|
||||
body: API.CancelShipOrderDTO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { id: param0, siteId: param1, ...queryParams } = params;
|
||||
return request<Record<string, any>>(
|
||||
`/site-api/${param1}/orders/${param0}/cancel-ship`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || {}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /site-api/${param1}/orders/${param0}/notes */
|
||||
export async function siteapicontrollerGetordernotes(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
|
|
@ -727,6 +821,28 @@ export async function siteapicontrollerCreateordernote(
|
|||
);
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /site-api/${param1}/orders/${param0}/ship */
|
||||
export async function siteapicontrollerShiporder(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerShiporderParams,
|
||||
body: API.ShipOrderDTO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { id: param0, siteId: param1, ...queryParams } = params;
|
||||
return request<Record<string, any>>(
|
||||
`/site-api/${param1}/orders/${param0}/ship`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || {}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /site-api/${param1}/products/${param0} */
|
||||
export async function siteapicontrollerGetproduct(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
|
|
@ -819,6 +935,62 @@ export async function siteapicontrollerDeletereview(
|
|||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /site-api/${param1}/webhooks/${param0} */
|
||||
export async function siteapicontrollerGetwebhook(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerGetwebhookParams,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { id: param0, siteId: param1, ...queryParams } = params;
|
||||
return request<API.UnifiedWebhookDTO>(
|
||||
`/site-api/${param1}/webhooks/${param0}`,
|
||||
{
|
||||
method: 'GET',
|
||||
params: { ...queryParams },
|
||||
...(options || {}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 PUT /site-api/${param1}/webhooks/${param0} */
|
||||
export async function siteapicontrollerUpdatewebhook(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerUpdatewebhookParams,
|
||||
body: API.UpdateWebhookDTO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { id: param0, siteId: param1, ...queryParams } = params;
|
||||
return request<API.UnifiedWebhookDTO>(
|
||||
`/site-api/${param1}/webhooks/${param0}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || {}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 DELETE /site-api/${param1}/webhooks/${param0} */
|
||||
export async function siteapicontrollerDeletewebhook(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerDeletewebhookParams,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { id: param0, siteId: param1, ...queryParams } = params;
|
||||
return request<Record<string, any>>(
|
||||
`/site-api/${param1}/webhooks/${param0}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
params: { ...queryParams },
|
||||
...(options || {}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 PUT /site-api/${param2}/products/${param1}/variations/${param0} */
|
||||
export async function siteapicontrollerUpdatevariation(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
|
|
|
|||
|
|
@ -84,6 +84,21 @@ export async function templatecontrollerGettemplatelist(options?: {
|
|||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /template/render-direct */
|
||||
export async function templatecontrollerRendertemplatedirect(
|
||||
body: API.RenderTemplateDTO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
return request<Record<string, any>>('/template/render-direct', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: body,
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /template/render/${param0} */
|
||||
export async function templatecontrollerRendertemplate(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,24 @@ declare namespace API {
|
|||
ids: any[];
|
||||
};
|
||||
|
||||
type BatchShipOrderItemDTO = {
|
||||
/** 订单ID */
|
||||
order_id?: string;
|
||||
/** 物流单号 */
|
||||
tracking_number?: string;
|
||||
/** 物流公司 */
|
||||
shipping_provider?: string;
|
||||
/** 发货方式 */
|
||||
shipping_method?: string;
|
||||
/** 发货商品项 */
|
||||
items?: ShipOrderItemDTO[];
|
||||
};
|
||||
|
||||
type BatchShipOrdersDTO = {
|
||||
/** 批量发货订单列表 */
|
||||
orders?: BatchShipOrderItemDTO[];
|
||||
};
|
||||
|
||||
type BatchSyncProductsDTO = {
|
||||
/** 产品ID列表 */
|
||||
productIds?: number[];
|
||||
|
|
@ -85,14 +103,14 @@ declare namespace API {
|
|||
tags?: string[];
|
||||
/** 状态 */
|
||||
status?:
|
||||
| 'publish'
|
||||
| 'draft'
|
||||
| 'pending'
|
||||
| 'private'
|
||||
| 'trash'
|
||||
| 'auto-draft'
|
||||
| 'future'
|
||||
| 'inherit';
|
||||
| 'publish'
|
||||
| 'draft'
|
||||
| 'pending'
|
||||
| 'private'
|
||||
| 'trash'
|
||||
| 'auto-draft'
|
||||
| 'future'
|
||||
| 'inherit';
|
||||
};
|
||||
|
||||
type BatchUpdateTagsDTO = {
|
||||
|
|
@ -113,6 +131,13 @@ declare namespace API {
|
|||
data?: boolean;
|
||||
};
|
||||
|
||||
type CancelShipOrderDTO = {
|
||||
/** 取消原因 */
|
||||
reason?: string;
|
||||
/** 发货单ID */
|
||||
shipment_id?: string;
|
||||
};
|
||||
|
||||
type categorycontrollerDeletecategoryattributeParams = {
|
||||
id: number;
|
||||
};
|
||||
|
|
@ -226,6 +251,21 @@ declare namespace API {
|
|||
testData?: string;
|
||||
};
|
||||
|
||||
type CreateWebhookDTO = {
|
||||
/** 名称 */
|
||||
name?: string;
|
||||
/** 主题/事件 */
|
||||
topic?: string;
|
||||
/** 目标URL */
|
||||
delivery_url?: string;
|
||||
/** 秘密密钥 */
|
||||
secret?: string;
|
||||
/** 头部信息 */
|
||||
headers?: Record<string, any>;
|
||||
/** API版本 */
|
||||
api_version?: string;
|
||||
};
|
||||
|
||||
type Cubid = {
|
||||
w?: number;
|
||||
h?: number;
|
||||
|
|
@ -488,10 +528,6 @@ declare namespace API {
|
|||
orderId: number;
|
||||
};
|
||||
|
||||
type ordercontrollerExportorderParams = {
|
||||
ids: number[];
|
||||
};
|
||||
|
||||
type ordercontrollerGetorderitemlistParams = {
|
||||
/** 页码 */
|
||||
current?: number;
|
||||
|
|
@ -544,18 +580,18 @@ declare namespace API {
|
|||
startDate?: string;
|
||||
endDate?: string;
|
||||
status?:
|
||||
| 'pending'
|
||||
| 'processing'
|
||||
| 'completed'
|
||||
| 'cancelled'
|
||||
| 'refunded'
|
||||
| 'failed'
|
||||
| 'after_sale_pending'
|
||||
| 'pending_reshipment'
|
||||
| 'pending_refund'
|
||||
| 'return-requested'
|
||||
| 'return-approved'
|
||||
| 'return-cancelled';
|
||||
| 'pending'
|
||||
| 'processing'
|
||||
| 'completed'
|
||||
| 'cancelled'
|
||||
| 'refunded'
|
||||
| 'failed'
|
||||
| 'after_sale_pending'
|
||||
| 'pending_reshipment'
|
||||
| 'pending_refund'
|
||||
| 'return-requested'
|
||||
| 'return-approved'
|
||||
| 'return-cancelled';
|
||||
payment_method?: string;
|
||||
/** 仅订阅订单(父订阅订单或包含订阅商品) */
|
||||
isSubscriptionOnly?: boolean;
|
||||
|
|
@ -804,7 +840,7 @@ declare namespace API {
|
|||
startDate?: string;
|
||||
endDate?: string;
|
||||
keyword?: string;
|
||||
siteId?: string;
|
||||
siteId?: number;
|
||||
purchaseType?: 'all' | 'first_purchase' | 'repeat_purchase';
|
||||
orderType?: 'all' | 'cpc' | 'non_cpc';
|
||||
brand?: 'all' | 'zyn' | 'yoone' | 'zolt';
|
||||
|
|
@ -1160,18 +1196,18 @@ declare namespace API {
|
|||
startDate?: string;
|
||||
endDate?: string;
|
||||
status?:
|
||||
| 'pending'
|
||||
| 'processing'
|
||||
| 'completed'
|
||||
| 'cancelled'
|
||||
| 'refunded'
|
||||
| 'failed'
|
||||
| 'after_sale_pending'
|
||||
| 'pending_reshipment'
|
||||
| 'pending_refund'
|
||||
| 'return-requested'
|
||||
| 'return-approved'
|
||||
| 'return-cancelled';
|
||||
| 'pending'
|
||||
| 'processing'
|
||||
| 'completed'
|
||||
| 'cancelled'
|
||||
| 'refunded'
|
||||
| 'failed'
|
||||
| 'after_sale_pending'
|
||||
| 'pending_reshipment'
|
||||
| 'pending_refund'
|
||||
| 'return-requested'
|
||||
| 'return-approved'
|
||||
| 'return-cancelled';
|
||||
payment_method?: string;
|
||||
/** 仅订阅订单(父订阅订单或包含订阅商品) */
|
||||
isSubscriptionOnly?: boolean;
|
||||
|
|
@ -1282,12 +1318,12 @@ declare namespace API {
|
|||
siteId?: string;
|
||||
/** 订阅状态 */
|
||||
status?:
|
||||
| 'active'
|
||||
| 'pending'
|
||||
| 'on-hold'
|
||||
| 'cancelled'
|
||||
| 'expired'
|
||||
| 'pending-cancel';
|
||||
| 'active'
|
||||
| 'pending'
|
||||
| 'on-hold'
|
||||
| 'cancelled'
|
||||
| 'expired'
|
||||
| 'pending-cancel';
|
||||
/** 客户邮箱 */
|
||||
customer_email?: string;
|
||||
/** 关键字(订阅ID,邮箱等) */
|
||||
|
|
@ -1305,14 +1341,14 @@ declare namespace API {
|
|||
siteId?: string;
|
||||
/** 产品状态 */
|
||||
status?:
|
||||
| 'publish'
|
||||
| 'draft'
|
||||
| 'pending'
|
||||
| 'private'
|
||||
| 'trash'
|
||||
| 'auto-draft'
|
||||
| 'future'
|
||||
| 'inherit';
|
||||
| 'publish'
|
||||
| 'draft'
|
||||
| 'pending'
|
||||
| 'private'
|
||||
| 'trash'
|
||||
| 'auto-draft'
|
||||
| 'future'
|
||||
| 'inherit';
|
||||
/** SKU列表 */
|
||||
skus?: any[];
|
||||
};
|
||||
|
|
@ -1341,6 +1377,13 @@ declare namespace API {
|
|||
data?: RateDTO[];
|
||||
};
|
||||
|
||||
type RenderTemplateDTO = {
|
||||
/** 模板内容 */
|
||||
template: string;
|
||||
/** 渲染数据 */
|
||||
data: Record<string, any>;
|
||||
};
|
||||
|
||||
type Service = {
|
||||
id?: string;
|
||||
carrier_name?: string;
|
||||
|
|
@ -1393,6 +1436,24 @@ declare namespace API {
|
|||
weightUom?: string;
|
||||
};
|
||||
|
||||
type ShipOrderDTO = {
|
||||
/** 物流单号 */
|
||||
tracking_number?: string;
|
||||
/** 物流公司 */
|
||||
shipping_provider?: string;
|
||||
/** 发货方式 */
|
||||
shipping_method?: string;
|
||||
/** 发货商品项 */
|
||||
items?: ShipOrderItemDTO[];
|
||||
};
|
||||
|
||||
type ShipOrderItemDTO = {
|
||||
/** 订单项ID */
|
||||
order_item_id?: number;
|
||||
/** 数量 */
|
||||
quantity?: number;
|
||||
};
|
||||
|
||||
type ShippingAddress = {
|
||||
id?: number;
|
||||
name?: string;
|
||||
|
|
@ -1446,6 +1507,15 @@ declare namespace API {
|
|||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerBatchshipordersParams = {
|
||||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerCancelshiporderParams = {
|
||||
id: string;
|
||||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerConvertmediatowebpParams = {
|
||||
siteId: number;
|
||||
};
|
||||
|
|
@ -1475,6 +1545,10 @@ declare namespace API {
|
|||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerCreatewebhookParams = {
|
||||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerDeletecustomerParams = {
|
||||
id: string;
|
||||
siteId: number;
|
||||
|
|
@ -1500,6 +1574,11 @@ declare namespace API {
|
|||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerDeletewebhookParams = {
|
||||
id: string;
|
||||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerExportcustomersParams = {
|
||||
/** 页码 */
|
||||
page?: number;
|
||||
|
|
@ -1714,6 +1793,10 @@ declare namespace API {
|
|||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerGetlinksParams = {
|
||||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerGetmediaParams = {
|
||||
/** 页码 */
|
||||
page?: number;
|
||||
|
|
@ -1859,6 +1942,37 @@ declare namespace API {
|
|||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerGetwebhookParams = {
|
||||
id: string;
|
||||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerGetwebhooksParams = {
|
||||
/** 页码 */
|
||||
page?: number;
|
||||
/** 每页数量 */
|
||||
per_page?: number;
|
||||
/** 每页数量别名 */
|
||||
page_size?: number;
|
||||
/** 搜索关键词 */
|
||||
search?: string;
|
||||
/** 状态 */
|
||||
status?: string;
|
||||
/** 客户ID,用于筛选订单 */
|
||||
customer_id?: number;
|
||||
/** 过滤条件对象 */
|
||||
where?: Record<string, any>;
|
||||
/** 排序对象,例如 { "sku": "desc" } */
|
||||
order?: Record<string, any>;
|
||||
/** 排序字段(兼容旧入参) */
|
||||
orderby?: string;
|
||||
/** 排序方式(兼容旧入参) */
|
||||
orderDir?: string;
|
||||
/** 选中ID列表,逗号分隔 */
|
||||
ids?: string;
|
||||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerImportcustomersParams = {
|
||||
siteId: number;
|
||||
};
|
||||
|
|
@ -1875,6 +1989,11 @@ declare namespace API {
|
|||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerShiporderParams = {
|
||||
id: string;
|
||||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerUpdatecustomerParams = {
|
||||
id: string;
|
||||
siteId: number;
|
||||
|
|
@ -1906,6 +2025,11 @@ declare namespace API {
|
|||
siteId: number;
|
||||
};
|
||||
|
||||
type siteapicontrollerUpdatewebhookParams = {
|
||||
id: string;
|
||||
siteId: number;
|
||||
};
|
||||
|
||||
type SiteConfig = {
|
||||
/** 站点 ID */
|
||||
id?: string;
|
||||
|
|
@ -2183,12 +2307,12 @@ declare namespace API {
|
|||
siteId?: string;
|
||||
/** 订阅状态 */
|
||||
status?:
|
||||
| 'active'
|
||||
| 'pending'
|
||||
| 'on-hold'
|
||||
| 'cancelled'
|
||||
| 'expired'
|
||||
| 'pending-cancel';
|
||||
| 'active'
|
||||
| 'pending'
|
||||
| 'on-hold'
|
||||
| 'cancelled'
|
||||
| 'expired'
|
||||
| 'pending-cancel';
|
||||
/** 客户邮箱 */
|
||||
customer_email?: string;
|
||||
/** 关键字(订阅ID,邮箱等) */
|
||||
|
|
@ -2458,6 +2582,21 @@ declare namespace API {
|
|||
totalPages?: number;
|
||||
};
|
||||
|
||||
type UnifiedPaginationDTO = {
|
||||
/** 列表数据 */
|
||||
items?: any;
|
||||
/** 总数 */
|
||||
total?: number;
|
||||
/** 当前页 */
|
||||
page?: number;
|
||||
/** 每页数量 */
|
||||
per_page?: number;
|
||||
/** 每页数量别名 */
|
||||
page_size?: number;
|
||||
/** 总页数 */
|
||||
totalPages?: number;
|
||||
};
|
||||
|
||||
type UnifiedProductAttributeDTO = {
|
||||
/** 属性ID */
|
||||
id?: Record<string, any>;
|
||||
|
|
@ -2567,6 +2706,10 @@ declare namespace API {
|
|||
status?: string;
|
||||
/** 创建时间 */
|
||||
date_created?: string;
|
||||
/** 更新时间 */
|
||||
date_modified?: string;
|
||||
/** 原始数据 */
|
||||
raw?: Record<string, any>;
|
||||
};
|
||||
|
||||
type UnifiedReviewPaginationDTO = {
|
||||
|
|
@ -2656,6 +2799,29 @@ declare namespace API {
|
|||
name?: string;
|
||||
};
|
||||
|
||||
type UnifiedWebhookDTO = {
|
||||
/** Webhook ID */
|
||||
id?: Record<string, any>;
|
||||
/** 名称 */
|
||||
name?: string;
|
||||
/** 状态 */
|
||||
status?: string;
|
||||
/** 主题/事件 */
|
||||
topic?: string;
|
||||
/** 目标URL */
|
||||
delivery_url?: string;
|
||||
/** 秘密密钥 */
|
||||
secret?: string;
|
||||
/** 创建时间 */
|
||||
date_created?: string;
|
||||
/** 更新时间 */
|
||||
date_modified?: string;
|
||||
/** 头部信息 */
|
||||
headers?: Record<string, any>;
|
||||
/** API版本 */
|
||||
api_version?: string;
|
||||
};
|
||||
|
||||
type UpdateAreaDTO = {
|
||||
/** 编码 */
|
||||
code?: string;
|
||||
|
|
@ -2762,6 +2928,23 @@ declare namespace API {
|
|||
on_sale?: boolean;
|
||||
};
|
||||
|
||||
type UpdateWebhookDTO = {
|
||||
/** 名称 */
|
||||
name?: string;
|
||||
/** 状态 */
|
||||
status?: string;
|
||||
/** 主题/事件 */
|
||||
topic?: string;
|
||||
/** 目标URL */
|
||||
delivery_url?: string;
|
||||
/** 秘密密钥 */
|
||||
secret?: string;
|
||||
/** 头部信息 */
|
||||
headers?: Record<string, any>;
|
||||
/** API版本 */
|
||||
api_version?: string;
|
||||
};
|
||||
|
||||
type UpdateWpProductDTO = {
|
||||
/** 变体名称 */
|
||||
name?: string;
|
||||
|
|
@ -2848,14 +3031,14 @@ declare namespace API {
|
|||
siteId?: string;
|
||||
/** 产品状态 */
|
||||
status?:
|
||||
| 'publish'
|
||||
| 'draft'
|
||||
| 'pending'
|
||||
| 'private'
|
||||
| 'trash'
|
||||
| 'auto-draft'
|
||||
| 'future'
|
||||
| 'inherit';
|
||||
| 'publish'
|
||||
| 'draft'
|
||||
| 'pending'
|
||||
| 'private'
|
||||
| 'trash'
|
||||
| 'auto-draft'
|
||||
| 'future'
|
||||
| 'inherit';
|
||||
/** SKU列表 */
|
||||
skus?: any[];
|
||||
};
|
||||
|
|
@ -2906,14 +3089,14 @@ declare namespace API {
|
|||
name: string;
|
||||
/** 产品状态 */
|
||||
status?:
|
||||
| 'publish'
|
||||
| 'draft'
|
||||
| 'pending'
|
||||
| 'private'
|
||||
| 'trash'
|
||||
| 'auto-draft'
|
||||
| 'future'
|
||||
| 'inherit';
|
||||
| 'publish'
|
||||
| 'draft'
|
||||
| 'pending'
|
||||
| 'private'
|
||||
| 'trash'
|
||||
| 'auto-draft'
|
||||
| 'future'
|
||||
| 'inherit';
|
||||
/** 是否为特色产品 */
|
||||
featured?: boolean;
|
||||
/** 目录可见性 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue