style: 统一中文标点符号为英文格式并优化注释格式
修复中文标点符号使用不一致的问题,将全角标点替换为半角标点 优化代码注释格式,移除冗余的中文注释标记 调整部分注释内容使其更简洁清晰
This commit is contained in:
parent
3b66a7c49c
commit
04c0d0a756
|
|
@ -15,7 +15,7 @@ import { usercontrollerGetuser } from './servers/api/user';
|
||||||
// 设置 dayjs 全局语言为中文
|
// 设置 dayjs 全局语言为中文
|
||||||
dayjs.locale('zh-cn');
|
dayjs.locale('zh-cn');
|
||||||
|
|
||||||
// 全局初始化数据配置,用于 Layout 用户信息和权限初始化
|
// 全局初始化数据配置,用于 Layout 用户信息和权限初始化
|
||||||
// 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate
|
// 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate
|
||||||
export async function getInitialState(): Promise<{
|
export async function getInitialState(): Promise<{
|
||||||
user?: Record<string, any>;
|
user?: Record<string, any>;
|
||||||
|
|
@ -102,11 +102,11 @@ export const request: RequestConfig = {
|
||||||
export const onRouteChange = ({ location }: { location: Location }) => {
|
export const onRouteChange = ({ location }: { location: Location }) => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
|
||||||
// 白名单,不需要登录的页面
|
// 白名单,不需要登录的页面
|
||||||
const whiteList = ['/login', '/track'];
|
const whiteList = ['/login', '/track'];
|
||||||
|
|
||||||
if (!token && !whiteList.includes(location.pathname)) {
|
if (!token && !whiteList.includes(location.pathname)) {
|
||||||
// 没有 token 且不在白名单内,跳转到登录页
|
// 没有 token 且不在白名单内,跳转到登录页
|
||||||
history.push('/login');
|
history.push('/login');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ const SyncForm: React.FC<SyncFormProps> = ({ tableRef, onFinish }) => {
|
||||||
return (
|
return (
|
||||||
<DrawerForm<API.ordercontrollerSyncorderParams>
|
<DrawerForm<API.ordercontrollerSyncorderParams>
|
||||||
title="同步订单"
|
title="同步订单"
|
||||||
// 表单的触发器,一个带图标的按钮
|
// 表单的触发器,一个带图标的按钮
|
||||||
trigger={
|
trigger={
|
||||||
<Button key="syncSite" type="primary">
|
<Button key="syncSite" type="primary">
|
||||||
<SyncOutlined />
|
<SyncOutlined />
|
||||||
|
|
|
||||||
|
|
@ -29,5 +29,5 @@ export function useDeviceFingerprint() {
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return fingerprint; // 初始为 null,加载后返回指纹 ID
|
return fingerprint; // 初始为 null,加载后返回指纹 ID
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ const AreaList: React.FC = () => {
|
||||||
</Button>
|
</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="删除区域"
|
title="删除区域"
|
||||||
description="确认删除该区域?"
|
description="确认删除该区域?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
await request(`/area/${row.id}`, {
|
await request(`/area/${row.id}`, {
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ const CategoryPage: React.FC = () => {
|
||||||
</a>,
|
</a>,
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
key="delete"
|
key="delete"
|
||||||
title="确定删除该分类吗?"
|
title="确定删除该分类吗?"
|
||||||
onConfirm={(e) => {
|
onConfirm={(e) => {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
handleDeleteCategory(item.id);
|
handleDeleteCategory(item.id);
|
||||||
|
|
@ -218,7 +218,7 @@ const CategoryPage: React.FC = () => {
|
||||||
</Sider>
|
</Sider>
|
||||||
<Content style={{ padding: '24px' }}>
|
<Content style={{ padding: '24px' }}>
|
||||||
{selectedCategory ? (
|
{selectedCategory ? (
|
||||||
<Card title={`分类:${selectedCategory.title} (${selectedCategory.name})`} extra={<Button type="primary" onClick={handleAddAttribute}>添加关联属性</Button>}>
|
<Card title={`分类:${selectedCategory.title} (${selectedCategory.name})`} extra={<Button type="primary" onClick={handleAddAttribute}>添加关联属性</Button>}>
|
||||||
<List
|
<List
|
||||||
loading={loadingAttributes}
|
loading={loadingAttributes}
|
||||||
dataSource={categoryAttributes}
|
dataSource={categoryAttributes}
|
||||||
|
|
@ -226,7 +226,7 @@ const CategoryPage: React.FC = () => {
|
||||||
<List.Item
|
<List.Item
|
||||||
actions={[
|
actions={[
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定移除该属性吗?"
|
title="确定移除该属性吗?"
|
||||||
onConfirm={() => handleDeleteAttribute(item.id)}
|
onConfirm={() => handleDeleteAttribute(item.id)}
|
||||||
>
|
>
|
||||||
<Button type="link" danger>移除</Button>
|
<Button type="link" danger>移除</Button>
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@ const DictPage: React.FC = () => {
|
||||||
size="small"
|
size="small"
|
||||||
onRow={(record) => ({
|
onRow={(record) => ({
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
// 如果点击的是当前已选中的行,则取消选择
|
// 如果点击的是当前已选中的行,则取消选择
|
||||||
if (selectedDict?.id === record.id) {
|
if (selectedDict?.id === record.id) {
|
||||||
setSelectedDict(null);
|
setSelectedDict(null);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -356,7 +356,7 @@ const DictPage: React.FC = () => {
|
||||||
<ProTable
|
<ProTable
|
||||||
columns={dictItemColumns}
|
columns={dictItemColumns}
|
||||||
request={async (params) => {
|
request={async (params) => {
|
||||||
// 当没有选择字典时,不发起请求
|
// 当没有选择字典时,不发起请求
|
||||||
if (!selectedDict?.id) {
|
if (!selectedDict?.id) {
|
||||||
return {
|
return {
|
||||||
data: [],
|
data: [],
|
||||||
|
|
|
||||||
|
|
@ -96,11 +96,11 @@ const Page = () => {
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
placeholder={'请输入密码!'}
|
placeholder={'请输入密码!'}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入密码!',
|
message: '请输入密码!',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ const ListPage: React.FC = () => {
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="删除"
|
title="删除"
|
||||||
description="确认删除?"
|
description="确认删除?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,9 @@ const ListPage: React.FC = () => {
|
||||||
await navigator.clipboard.writeText(
|
await navigator.clipboard.writeText(
|
||||||
record.return_tracking_number,
|
record.return_tracking_number,
|
||||||
);
|
);
|
||||||
message.success('复制成功!');
|
message.success('复制成功!');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
message.error('复制失败!');
|
message.error('复制失败!');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -140,7 +140,7 @@ const ListPage: React.FC = () => {
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
title="删除"
|
title="删除"
|
||||||
description="确认删除?"
|
description="确认删除?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ const OrderItemsPage: React.FC = () => {
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
|
|
||||||
// 列配置(中文标题,符合当前项目风格;显示英文默认语言可后续走国际化)
|
// 列配置(中文标题,符合当前项目风格;显示英文默认语言可后续走国际化)
|
||||||
const columns: ProColumns<OrderItemAggRow>[] = [
|
const columns: ProColumns<OrderItemAggRow>[] = [
|
||||||
{
|
{
|
||||||
title: '商品名称',
|
title: '商品名称',
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,7 @@ const ListPage: React.FC = () => {
|
||||||
label: (
|
label: (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="转至售后"
|
title="转至售后"
|
||||||
description="确认转至售后?"
|
description="确认转至售后?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
if (!record.id) {
|
if (!record.id) {
|
||||||
|
|
@ -513,7 +513,7 @@ const Detail: React.FC<{
|
||||||
orderId,
|
orderId,
|
||||||
});
|
});
|
||||||
if (!success || !data) return { data: {} };
|
if (!success || !data) return { data: {} };
|
||||||
// 合并订单中相同的sku,只显示一次记录总数
|
// 合并订单中相同的sku,只显示一次记录总数
|
||||||
data.sales = data.sales?.reduce(
|
data.sales = data.sales?.reduce(
|
||||||
(acc: API.OrderSale[], cur: API.OrderSale) => {
|
(acc: API.OrderSale[], cur: API.OrderSale) => {
|
||||||
let idx = acc.findIndex((v: any) => v.productId === cur.productId);
|
let idx = acc.findIndex((v: any) => v.productId === cur.productId);
|
||||||
|
|
@ -605,7 +605,7 @@ const Detail: React.FC<{
|
||||||
<Divider type="vertical" />,
|
<Divider type="vertical" />,
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="转至售后"
|
title="转至售后"
|
||||||
description="确认转至售后?"
|
description="确认转至售后?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
if (!record.id) {
|
if (!record.id) {
|
||||||
|
|
@ -641,7 +641,7 @@ const Detail: React.FC<{
|
||||||
<Divider type="vertical" />,
|
<Divider type="vertical" />,
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="转至取消"
|
title="转至取消"
|
||||||
description="确认转至取消?"
|
description="确认转至取消?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
if (!record.id) {
|
if (!record.id) {
|
||||||
|
|
@ -668,7 +668,7 @@ const Detail: React.FC<{
|
||||||
<Divider type="vertical" />,
|
<Divider type="vertical" />,
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="转至退款"
|
title="转至退款"
|
||||||
description="确认转至退款?"
|
description="确认转至退款?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
if (!record.id) {
|
if (!record.id) {
|
||||||
|
|
@ -695,7 +695,7 @@ const Detail: React.FC<{
|
||||||
<Divider type="vertical" />,
|
<Divider type="vertical" />,
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="转至完成"
|
title="转至完成"
|
||||||
description="确认转至完成?"
|
description="确认转至完成?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
if (!record.id) {
|
if (!record.id) {
|
||||||
|
|
@ -722,7 +722,7 @@ const Detail: React.FC<{
|
||||||
<Divider type="vertical" />,
|
<Divider type="vertical" />,
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="转至待补发"
|
title="转至待补发"
|
||||||
description="确认转至待补发?"
|
description="确认转至待补发?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -999,9 +999,9 @@ const Detail: React.FC<{
|
||||||
await navigator.clipboard.writeText(
|
await navigator.clipboard.writeText(
|
||||||
v.tracking_url,
|
v.tracking_url,
|
||||||
);
|
);
|
||||||
message.success('复制成功!');
|
message.success('复制成功!');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
message.error('复制失败!');
|
message.error('复制失败!');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1013,7 +1013,7 @@ const Detail: React.FC<{
|
||||||
? [
|
? [
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="取消运单"
|
title="取消运单"
|
||||||
description="确认取消运单?"
|
description="确认取消运单?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -1461,7 +1461,7 @@ const Shipping: React.FC<{
|
||||||
showSearch: true,
|
showSearch: true,
|
||||||
filterOption: false,
|
filterOption: false,
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
/>
|
/>
|
||||||
<ProFormDigit
|
<ProFormDigit
|
||||||
|
|
@ -2083,7 +2083,7 @@ const SalesChange: React.FC<{
|
||||||
showSearch: true,
|
showSearch: true,
|
||||||
filterOption: false,
|
filterOption: false,
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择订单' }]}
|
rules={[{ required: true, message: '请选择订单' }]}
|
||||||
/>
|
/>
|
||||||
<ProFormDigit
|
<ProFormDigit
|
||||||
|
|
@ -2126,7 +2126,7 @@ const SalesChange: React.FC<{
|
||||||
showSearch: true,
|
showSearch: true,
|
||||||
filterOption: false,
|
filterOption: false,
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
/>
|
/>
|
||||||
<ProFormDigit
|
<ProFormDigit
|
||||||
|
|
@ -2226,7 +2226,7 @@ const CreateOrder: React.FC<{
|
||||||
showSearch: true,
|
showSearch: true,
|
||||||
filterOption: false,
|
filterOption: false,
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
/>
|
/>
|
||||||
<ProFormDigit
|
<ProFormDigit
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ const ListPage: React.FC = () => {
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
width: 800,
|
width: 800,
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
return record?.numbers?.join?.('、');
|
return record?.numbers?.join?.(',');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -72,7 +72,7 @@ const ListPage: React.FC = () => {
|
||||||
|
|
||||||
// 数据行
|
// 数据行
|
||||||
const rows = (data?.items || []).map((item) => {
|
const rows = (data?.items || []).map((item) => {
|
||||||
return [item.name, item.quantity, item.numbers?.join('、')];
|
return [item.name, item.quantity, item.numbers?.join(',')];
|
||||||
});
|
});
|
||||||
|
|
||||||
// 导出
|
// 导出
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ const ListPage: React.FC = () => {
|
||||||
danger={record.isActive}
|
danger={record.isActive}
|
||||||
type="link"
|
type="link"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
// 中文注释:软删除为禁用(isActive=false),再次点击可启用
|
// 软删除为禁用(isActive=false),再次点击可启用
|
||||||
const next = !record.isActive;
|
const next = !record.isActive;
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
await usercontrollerToggleactive({
|
await usercontrollerToggleactive({
|
||||||
|
|
@ -204,7 +204,7 @@ const EditForm: React.FC<{
|
||||||
}}
|
}}
|
||||||
onFinish={async (values: any) => {
|
onFinish={async (values: any) => {
|
||||||
try {
|
try {
|
||||||
// 中文注释:更新用户,密码可选填
|
// 更新用户,密码可选填
|
||||||
const { success, message: err } = await usercontrollerUpdateuser(
|
const { success, message: err } = await usercontrollerUpdateuser(
|
||||||
{ id: record.id },
|
{ id: record.id },
|
||||||
values,
|
values,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// 中文注释:限定允许管理的字典名称集合
|
// 限定允许管理的字典名称集合
|
||||||
export const attributes = new Set([
|
export const attributes = new Set([
|
||||||
'brand',
|
'brand',
|
||||||
'strength',
|
'strength',
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,16 @@ const { Sider, Content } = Layout;
|
||||||
import { attributes } from './consts';
|
import { attributes } from './consts';
|
||||||
|
|
||||||
const AttributePage: React.FC = () => {
|
const AttributePage: React.FC = () => {
|
||||||
// 中文注释:左侧字典列表状态
|
// 左侧字典列表状态
|
||||||
const [dicts, setDicts] = useState<any[]>([]);
|
const [dicts, setDicts] = useState<any[]>([]);
|
||||||
const [loadingDicts, setLoadingDicts] = useState(false);
|
const [loadingDicts, setLoadingDicts] = useState(false);
|
||||||
const [searchText, setSearchText] = useState('');
|
const [searchText, setSearchText] = useState('');
|
||||||
const [selectedDict, setSelectedDict] = useState<any>(null);
|
const [selectedDict, setSelectedDict] = useState<any>(null);
|
||||||
|
|
||||||
// 中文注释:右侧字典项 ProTable 的引用
|
// 右侧字典项 ProTable 的引用
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
|
|
||||||
// 中文注释:字典项新增/编辑模态框控制
|
// 字典项新增/编辑模态框控制
|
||||||
const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false);
|
const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false);
|
||||||
const [editingDictItem, setEditingDictItem] = useState<any>(null);
|
const [editingDictItem, setEditingDictItem] = useState<any>(null);
|
||||||
const [dictItemForm] = Form.useForm();
|
const [dictItemForm] = Form.useForm();
|
||||||
|
|
@ -41,7 +41,7 @@ const AttributePage: React.FC = () => {
|
||||||
setLoadingDicts(true);
|
setLoadingDicts(true);
|
||||||
try {
|
try {
|
||||||
const res = await request('/dict/list', { params: { title } });
|
const res = await request('/dict/list', { params: { title } });
|
||||||
// 中文注释:条件判断,过滤只保留 allowedDictNames 中的字典
|
// 条件判断,过滤只保留 allowedDictNames 中的字典
|
||||||
const filtered = (res || []).filter((d: any) => attributes.has(d?.name));
|
const filtered = (res || []).filter((d: any) => attributes.has(d?.name));
|
||||||
setDicts(filtered);
|
setDicts(filtered);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -50,42 +50,42 @@ const AttributePage: React.FC = () => {
|
||||||
setLoadingDicts(false);
|
setLoadingDicts(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 中文注释:组件挂载时初始化数据
|
// 组件挂载时初始化数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchDicts();
|
fetchDicts();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 中文注释:搜索触发过滤
|
// 搜索触发过滤
|
||||||
const handleSearch = (value: string) => {
|
const handleSearch = (value: string) => {
|
||||||
fetchDicts(value);
|
fetchDicts(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 中文注释:打开添加字典项模态框
|
// 打开添加字典项模态框
|
||||||
const handleAddDictItem = () => {
|
const handleAddDictItem = () => {
|
||||||
setEditingDictItem(null);
|
setEditingDictItem(null);
|
||||||
dictItemForm.resetFields();
|
dictItemForm.resetFields();
|
||||||
setIsDictItemModalVisible(true);
|
setIsDictItemModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 中文注释:打开编辑字典项模态框
|
// 打开编辑字典项模态框
|
||||||
const handleEditDictItem = (item: any) => {
|
const handleEditDictItem = (item: any) => {
|
||||||
setEditingDictItem(item);
|
setEditingDictItem(item);
|
||||||
dictItemForm.setFieldsValue(item);
|
dictItemForm.setFieldsValue(item);
|
||||||
setIsDictItemModalVisible(true);
|
setIsDictItemModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 中文注释:字典项表单提交(新增或编辑)
|
// 字典项表单提交(新增或编辑)
|
||||||
const handleDictItemFormSubmit = async (values: any) => {
|
const handleDictItemFormSubmit = async (values: any) => {
|
||||||
try {
|
try {
|
||||||
if (editingDictItem) {
|
if (editingDictItem) {
|
||||||
// 中文注释:条件判断,存在编辑项则执行更新
|
// 条件判断,存在编辑项则执行更新
|
||||||
await request(`/dict/item/${editingDictItem.id}`, {
|
await request(`/dict/item/${editingDictItem.id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: values,
|
data: values,
|
||||||
});
|
});
|
||||||
message.success('更新成功');
|
message.success('更新成功');
|
||||||
} else {
|
} else {
|
||||||
// 中文注释:否则执行新增,绑定到当前选择的字典
|
// 否则执行新增,绑定到当前选择的字典
|
||||||
await request('/dict/item', {
|
await request('/dict/item', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { ...values, dictId: selectedDict.id },
|
data: { ...values, dictId: selectedDict.id },
|
||||||
|
|
@ -93,30 +93,30 @@ const AttributePage: React.FC = () => {
|
||||||
message.success('添加成功');
|
message.success('添加成功');
|
||||||
}
|
}
|
||||||
setIsDictItemModalVisible(false);
|
setIsDictItemModalVisible(false);
|
||||||
actionRef.current?.reload(); // 中文注释:刷新 ProTable
|
actionRef.current?.reload(); // 刷新 ProTable
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(editingDictItem ? '更新失败' : '添加失败');
|
message.error(editingDictItem ? '更新失败' : '添加失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 中文注释:删除字典项
|
// 删除字典项
|
||||||
const handleDeleteDictItem = async (itemId: number) => {
|
const handleDeleteDictItem = async (itemId: number) => {
|
||||||
try {
|
try {
|
||||||
await request(`/dict/item/${itemId}`, { method: 'DELETE' });
|
await request(`/dict/item/${itemId}`, { method: 'DELETE' });
|
||||||
message.success('删除成功');
|
message.success('删除成功');
|
||||||
actionRef.current?.reload(); // 中文注释:刷新 ProTable
|
actionRef.current?.reload(); // 刷新 ProTable
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('删除失败');
|
message.error('删除失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 中文注释:左侧字典列表列定义(紧凑样式)
|
// 左侧字典列表列定义(紧凑样式)
|
||||||
const dictColumns = [
|
const dictColumns = [
|
||||||
{ title: '名称', dataIndex: 'name', key: 'name' },
|
{ title: '名称', dataIndex: 'name', key: 'name' },
|
||||||
{ title: '标题', dataIndex: 'title', key: 'title' },
|
{ title: '标题', dataIndex: 'title', key: 'title' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// 中文注释:右侧字典项列表列定义(紧凑样式)
|
// 右侧字典项列表列定义(紧凑样式)
|
||||||
const dictItemColumns: any[] = [
|
const dictItemColumns: any[] = [
|
||||||
{ title: '名称', dataIndex: 'name', key: 'name', copyable: true },
|
{ title: '名称', dataIndex: 'name', key: 'name', copyable: true },
|
||||||
{ title: '标题', dataIndex: 'title', key: 'title', copyable: true },
|
{ title: '标题', dataIndex: 'title', key: 'title', copyable: true },
|
||||||
|
|
@ -177,7 +177,7 @@ const AttributePage: React.FC = () => {
|
||||||
size="small"
|
size="small"
|
||||||
onRow={(record) => ({
|
onRow={(record) => ({
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
// 中文注释:条件判断,重复点击同一行则取消选择
|
// 条件判断,重复点击同一行则取消选择
|
||||||
if (selectedDict?.id === record.id) {
|
if (selectedDict?.id === record.id) {
|
||||||
setSelectedDict(null);
|
setSelectedDict(null);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -197,7 +197,7 @@ const AttributePage: React.FC = () => {
|
||||||
columns={dictItemColumns}
|
columns={dictItemColumns}
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
request={async (params) => {
|
request={async (params) => {
|
||||||
// 中文注释:当没有选择字典时,不发起请求
|
// 当没有选择字典时,不发起请求
|
||||||
if (!selectedDict?.id) {
|
if (!selectedDict?.id) {
|
||||||
return {
|
return {
|
||||||
data: [],
|
data: [],
|
||||||
|
|
@ -242,7 +242,7 @@ const AttributePage: React.FC = () => {
|
||||||
showUploadList={false}
|
showUploadList={false}
|
||||||
disabled={!selectedDict}
|
disabled={!selectedDict}
|
||||||
onChange={(info) => {
|
onChange={(info) => {
|
||||||
// 中文注释:条件判断,上传状态处理
|
// 条件判断,上传状态处理
|
||||||
if (info.file.status === 'done') {
|
if (info.file.status === 'done') {
|
||||||
message.success(`${info.file.name} 文件上传成功`);
|
message.success(`${info.file.name} 文件上传成功`);
|
||||||
actionRef.current?.reload();
|
actionRef.current?.reload();
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ const CategoryPage: React.FC = () => {
|
||||||
</a>,
|
</a>,
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
key="delete"
|
key="delete"
|
||||||
title="确定删除该分类吗?"
|
title="确定删除该分类吗?"
|
||||||
onConfirm={(e) => {
|
onConfirm={(e) => {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
handleDeleteCategory(item.id);
|
handleDeleteCategory(item.id);
|
||||||
|
|
@ -223,7 +223,7 @@ const CategoryPage: React.FC = () => {
|
||||||
</Sider>
|
</Sider>
|
||||||
<Content style={{ padding: '24px' }}>
|
<Content style={{ padding: '24px' }}>
|
||||||
{selectedCategory ? (
|
{selectedCategory ? (
|
||||||
<Card title={`分类:${selectedCategory.title} (${selectedCategory.name})`} extra={<Button type="primary" onClick={handleAddAttribute}>添加关联属性</Button>}>
|
<Card title={`分类:${selectedCategory.title} (${selectedCategory.name})`} extra={<Button type="primary" onClick={handleAddAttribute}>添加关联属性</Button>}>
|
||||||
<List
|
<List
|
||||||
loading={loadingAttributes}
|
loading={loadingAttributes}
|
||||||
dataSource={categoryAttributes}
|
dataSource={categoryAttributes}
|
||||||
|
|
@ -231,7 +231,7 @@ const CategoryPage: React.FC = () => {
|
||||||
<List.Item
|
<List.Item
|
||||||
actions={[
|
actions={[
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定移除该属性吗?"
|
title="确定移除该属性吗?"
|
||||||
onConfirm={() => handleDeleteAttribute(item.id)}
|
onConfirm={() => handleDeleteAttribute(item.id)}
|
||||||
>
|
>
|
||||||
<Button type="link" danger>移除</Button>
|
<Button type="link" danger>移除</Button>
|
||||||
|
|
|
||||||
|
|
@ -94,11 +94,11 @@ const ComponentsCell: React.FC<{ productId: number }> = ({ productId }) => {
|
||||||
components.map((component: any) => (
|
components.map((component: any) => (
|
||||||
<Tag key={component.id} color="blue" style={{ marginBottom: 4 }}>
|
<Tag key={component.id} color="blue" style={{ marginBottom: 4 }}>
|
||||||
{component.sku || `#${component.id}`} × {component.quantity}
|
{component.sku || `#${component.id}`} × {component.quantity}
|
||||||
(库存:
|
(库存:
|
||||||
{component.stock
|
{component.stock
|
||||||
?.map((s: any) => `${s.name}:${s.quantity}`)
|
?.map((s: any) => `${s.name}:${s.quantity}`)
|
||||||
.join(', ') || '-'}
|
.join(', ') || '-'}
|
||||||
)
|
)
|
||||||
</Tag>
|
</Tag>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -110,22 +110,22 @@ const ComponentsCell: React.FC<{ productId: number }> = ({ productId }) => {
|
||||||
|
|
||||||
const List: React.FC = () => {
|
const List: React.FC = () => {
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
// 状态:存储当前选中的行
|
// 状态:存储当前选中的行
|
||||||
const [selectedRows, setSelectedRows] = React.useState<API.Product[]>([]);
|
const [selectedRows, setSelectedRows] = React.useState<API.Product[]>([]);
|
||||||
|
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
// 中文注释:导出产品 CSV(带认证请求)
|
// 导出产品 CSV(带认证请求)
|
||||||
const handleDownloadProductsCSV = async () => {
|
const handleDownloadProductsCSV = async () => {
|
||||||
try {
|
try {
|
||||||
// 中文注释:发起认证请求获取 CSV Blob
|
// 发起认证请求获取 CSV Blob
|
||||||
const blob = await request('/product/export', { responseType: 'blob' });
|
const blob = await request('/product/export', { responseType: 'blob' });
|
||||||
// 中文注释:构建下载文件名
|
// 构建下载文件名
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
const pad = (n: number) => String(n).padStart(2, '0');
|
const pad = (n: number) => String(n).padStart(2, '0');
|
||||||
const filename = `products-${d.getFullYear()}${pad(
|
const filename = `products-${d.getFullYear()}${pad(
|
||||||
d.getMonth() + 1,
|
d.getMonth() + 1,
|
||||||
)}${pad(d.getDate())}.csv`;
|
)}${pad(d.getDate())}.csv`;
|
||||||
// 中文注释:创建临时链接并触发下载
|
// 创建临时链接并触发下载
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
|
|
@ -142,10 +142,12 @@ const List: React.FC = () => {
|
||||||
{
|
{
|
||||||
title: 'sku',
|
title: 'sku',
|
||||||
dataIndex: 'sku',
|
dataIndex: 'sku',
|
||||||
|
sorter: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '名称',
|
title: '名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
|
sorter: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '中文名',
|
title: '中文名',
|
||||||
|
|
@ -156,27 +158,7 @@ const List: React.FC = () => {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '产品类型',
|
|
||||||
dataIndex: 'type',
|
|
||||||
valueType: 'select',
|
|
||||||
valueEnum: {
|
|
||||||
single: { text: '单品' },
|
|
||||||
bundle: { text: '套装' },
|
|
||||||
},
|
|
||||||
render: (_, record) => {
|
|
||||||
// 如果类型不存在,则返回-
|
|
||||||
if (!record.type) return '-';
|
|
||||||
// 判断是否为单品
|
|
||||||
const isSingle = record.type === 'single';
|
|
||||||
// 根据类型显示不同颜色的标签
|
|
||||||
return (
|
|
||||||
<Tag color={isSingle ? 'green' : 'orange'}>
|
|
||||||
{isSingle ? '单品' : '套装'}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '商品类型',
|
title: '商品类型',
|
||||||
dataIndex: 'category',
|
dataIndex: 'category',
|
||||||
|
|
@ -188,17 +170,40 @@ const List: React.FC = () => {
|
||||||
title: '价格',
|
title: '价格',
|
||||||
dataIndex: 'price',
|
dataIndex: 'price',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
|
sorter: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '促销价',
|
title: '促销价',
|
||||||
dataIndex: 'promotionPrice',
|
dataIndex: 'promotionPrice',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
|
sorter: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '属性',
|
title: '属性',
|
||||||
dataIndex: 'attributes',
|
dataIndex: 'attributes',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
render: (_, record) => <AttributesCell record={record} />,
|
render: (_, record) => <AttributesCell record={record} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '产品类型',
|
||||||
|
dataIndex: 'type',
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: {
|
||||||
|
single: { text: '单品' },
|
||||||
|
bundle: { text: '套装' },
|
||||||
|
},
|
||||||
|
render: (_, record) => {
|
||||||
|
// 如果类型不存在,则返回-
|
||||||
|
if (!record.type) return '-';
|
||||||
|
// 判断是否为单品
|
||||||
|
const isSingle = record.type === 'single';
|
||||||
|
// 根据类型显示不同颜色的标签
|
||||||
|
return (
|
||||||
|
<Tag color={isSingle ? 'green' : 'orange'}>
|
||||||
|
{isSingle ? '单品' : '套装'}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '构成',
|
title: '构成',
|
||||||
|
|
@ -217,12 +222,14 @@ const List: React.FC = () => {
|
||||||
dataIndex: 'updatedAt',
|
dataIndex: 'updatedAt',
|
||||||
valueType: 'dateTime',
|
valueType: 'dateTime',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
|
sorter: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
dataIndex: 'createdAt',
|
dataIndex: 'createdAt',
|
||||||
valueType: 'dateTime',
|
valueType: 'dateTime',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
|
sorter: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
|
|
@ -234,7 +241,7 @@ const List: React.FC = () => {
|
||||||
<EditForm record={record} tableRef={actionRef} />
|
<EditForm record={record} tableRef={actionRef} />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="删除"
|
title="删除"
|
||||||
description="确认删除?"
|
description="确认删除?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -264,33 +271,53 @@ const List: React.FC = () => {
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
toolBarRender={() => [
|
toolBarRender={() => [
|
||||||
// 中文注释:新建按钮
|
// 新建按钮
|
||||||
<CreateForm tableRef={actionRef} />,
|
<CreateForm tableRef={actionRef} />,
|
||||||
// 中文注释:导出 CSV(后端返回 text/csv,直接新窗口下载)
|
// 导出 CSV(后端返回 text/csv,直接新窗口下载)
|
||||||
<Button onClick={handleDownloadProductsCSV}>导出CSV</Button>,
|
<Button onClick={handleDownloadProductsCSV}>导出CSV</Button>,
|
||||||
// 中文注释:导入 CSV(上传文件成功后刷新表格)
|
// 导入 CSV(使用 customRequest 以支持 request 拦截器和鉴权)
|
||||||
<Upload
|
<Upload
|
||||||
name="file"
|
name="file"
|
||||||
action="/product/import"
|
|
||||||
accept=".csv"
|
accept=".csv"
|
||||||
showUploadList={false}
|
showUploadList={false}
|
||||||
maxCount={1}
|
maxCount={1}
|
||||||
onChange={({ file }) => {
|
customRequest={async (options) => {
|
||||||
if (file.status === 'done') {
|
const { file, onSuccess, onError } = options;
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
try {
|
||||||
|
await request('/product/import', {
|
||||||
|
method: 'POST',
|
||||||
|
data: formData,
|
||||||
|
requestType: 'form',
|
||||||
|
});
|
||||||
message.success('导入完成');
|
message.success('导入完成');
|
||||||
|
onSuccess?.('ok');
|
||||||
actionRef.current?.reload();
|
actionRef.current?.reload();
|
||||||
} else if (file.status === 'error') {
|
} catch (error: any) {
|
||||||
message.error('导入失败');
|
message.error('导入失败');
|
||||||
|
onError?.(error);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button>导入CSV</Button>
|
<Button>导入CSV</Button>
|
||||||
</Upload>,
|
</Upload>,
|
||||||
]}
|
]}
|
||||||
request={async (params) => {
|
request={async (params, sort) => {
|
||||||
const { data, success } = await productcontrollerGetproductlist(
|
let sortField = undefined;
|
||||||
params,
|
let sortOrder = undefined;
|
||||||
);
|
|
||||||
|
if (sort && Object.keys(sort).length > 0) {
|
||||||
|
const field = Object.keys(sort)[0];
|
||||||
|
sortField = field;
|
||||||
|
sortOrder = sort[field];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, success } = await productcontrollerGetproductlist({
|
||||||
|
...params,
|
||||||
|
sortField,
|
||||||
|
sortOrder,
|
||||||
|
} as any);
|
||||||
return {
|
return {
|
||||||
total: data?.total || 0,
|
total: data?.total || 0,
|
||||||
data: data?.items || [],
|
data: data?.items || [],
|
||||||
|
|
@ -355,10 +382,10 @@ const CreateForm: React.FC<{
|
||||||
const { humidityValues, brandValues, strengthValues, flavorValues } =
|
const { humidityValues, brandValues, strengthValues, flavorValues } =
|
||||||
formValues;
|
formValues;
|
||||||
// 检查是否所有必需的字段都已选择
|
// 检查是否所有必需的字段都已选择
|
||||||
// 注意:这里仅检查标准属性,如果当前分类没有这些属性,可能需要调整逻辑
|
// 注意:这里仅检查标准属性,如果当前分类没有这些属性,可能需要调整逻辑
|
||||||
// 暂时保持原样,假设常用属性会被配置
|
// 暂时保持原样,假设常用属性会被配置
|
||||||
|
|
||||||
// 所选值(用于 SKU 模板传入 name)
|
// 所选值(用于 SKU 模板传入 name)
|
||||||
const brandName: string = String(brandValues?.[0] || '');
|
const brandName: string = String(brandValues?.[0] || '');
|
||||||
const strengthName: string = String(strengthValues?.[0] || '');
|
const strengthName: string = String(strengthValues?.[0] || '');
|
||||||
const flavorName: string = String(flavorValues?.[0] || '');
|
const flavorName: string = String(flavorValues?.[0] || '');
|
||||||
|
|
@ -480,14 +507,14 @@ const CreateForm: React.FC<{
|
||||||
formRef.current?.setFieldsValue({ type: 'bundle' });
|
formRef.current?.setFieldsValue({ type: 'bundle' });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果 sku 不存在,则重置状态
|
// 如果 sku 不存在,则重置状态
|
||||||
setStockStatus(null);
|
setStockStatus(null);
|
||||||
formRef.current?.setFieldsValue({ type: null });
|
formRef.current?.setFieldsValue({ type: null });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onFinish={async (values: any) => {
|
onFinish={async (values: any) => {
|
||||||
// 中文注释:组装 attributes(根据 activeAttributes 动态组装)
|
// 组装 attributes(根据 activeAttributes 动态组装)
|
||||||
const attributes = activeAttributes.flatMap((attr: any) => {
|
const attributes = activeAttributes.flatMap((attr: any) => {
|
||||||
const dictName = attr.name;
|
const dictName = attr.name;
|
||||||
const key = `${dictName}Values`;
|
const key = `${dictName}Values`;
|
||||||
|
|
@ -715,12 +742,12 @@ const EditForm: React.FC<{
|
||||||
sku: record.sku,
|
sku: record.sku,
|
||||||
} as any);
|
} as any);
|
||||||
if (stockData && stockData.items && stockData.items.length > 0) {
|
if (stockData && stockData.items && stockData.items.length > 0) {
|
||||||
// 如果有库存,则为单品
|
// 如果有库存,则为单品
|
||||||
setType('single');
|
setType('single');
|
||||||
setStockStatus('in-stock');
|
setStockStatus('in-stock');
|
||||||
formRef.current?.setFieldsValue({ type: 'single' });
|
formRef.current?.setFieldsValue({ type: 'single' });
|
||||||
} else {
|
} else {
|
||||||
// 如果没有库存,则为套装
|
// 如果没有库存,则为套装
|
||||||
setType('bundle');
|
setType('bundle');
|
||||||
setStockStatus('out-of-stock');
|
setStockStatus('out-of-stock');
|
||||||
formRef.current?.setFieldsValue({ type: 'bundle' });
|
formRef.current?.setFieldsValue({ type: 'bundle' });
|
||||||
|
|
@ -785,13 +812,13 @@ const EditForm: React.FC<{
|
||||||
formRef.current?.setFieldsValue({ type: 'bundle' });
|
formRef.current?.setFieldsValue({ type: 'bundle' });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果 sku 不存在,则重置状态
|
// 如果 sku 不存在,则重置状态
|
||||||
formRef.current?.setFieldsValue({ type: null });
|
formRef.current?.setFieldsValue({ type: null });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
// 中文注释:组装 attributes
|
// 组装 attributes
|
||||||
const attributes = activeAttributes.flatMap((attr: any) => {
|
const attributes = activeAttributes.flatMap((attr: any) => {
|
||||||
const dictName = attr.name;
|
const dictName = attr.name;
|
||||||
const key = `${dictName}Values`;
|
const key = `${dictName}Values`;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ interface Site {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义WordPress商品接口
|
// 定义WordPress商品接口
|
||||||
|
|
@ -80,7 +81,9 @@ const ProductSyncPage: React.FC = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
// 获取所有站点
|
// 获取所有站点
|
||||||
const sitesResponse = await getSites();
|
const sitesResponse = await getSites();
|
||||||
const siteList: Site[] = sitesResponse.data || [];
|
const rawSiteList = sitesResponse.data || [];
|
||||||
|
// 过滤掉已禁用的站点
|
||||||
|
const siteList: Site[] = rawSiteList.filter(site => !site.isDisabled);
|
||||||
setSites(siteList);
|
setSites(siteList);
|
||||||
|
|
||||||
// 获取所有 WordPress 商品
|
// 获取所有 WordPress 商品
|
||||||
|
|
@ -120,7 +123,7 @@ const ProductSyncPage: React.FC = () => {
|
||||||
// 转换为数组
|
// 转换为数组
|
||||||
setProducts(Array.from(productMap.values()));
|
setProducts(Array.from(productMap.values()));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('获取数据失败,请重试');
|
message.error('获取数据失败,请重试');
|
||||||
console.error('Error fetching data:', error);
|
console.error('Error fetching data:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import {
|
||||||
ActionType,
|
ActionType,
|
||||||
DrawerForm,
|
DrawerForm,
|
||||||
ProColumns,
|
ProColumns,
|
||||||
|
ProFormDependency,
|
||||||
ProFormInstance,
|
ProFormInstance,
|
||||||
ProFormSelect,
|
ProFormSelect,
|
||||||
ProFormSwitch,
|
ProFormSwitch,
|
||||||
|
|
@ -18,7 +19,7 @@ interface AreaItem {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 站点数据项类型(前端不包含密钥字段,后端列表不返回密钥)
|
// 站点数据项类型(前端不包含密钥字段,后端列表不返回密钥)
|
||||||
interface SiteItem {
|
interface SiteItem {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -29,7 +30,7 @@ interface SiteItem {
|
||||||
areas?: AreaItem[];
|
areas?: AreaItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建/更新表单的值类型,包含可选的密钥字段
|
// 创建/更新表单的值类型,包含可选的密钥字段
|
||||||
interface SiteFormValues {
|
interface SiteFormValues {
|
||||||
name: string;
|
name: string;
|
||||||
apiUrl?: string;
|
apiUrl?: string;
|
||||||
|
|
@ -37,6 +38,7 @@ interface SiteFormValues {
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
consumerKey?: string; // WooCommerce REST API 的 consumer key
|
consumerKey?: string; // WooCommerce REST API 的 consumer key
|
||||||
consumerSecret?: string; // WooCommerce REST API 的 consumer secret
|
consumerSecret?: string; // WooCommerce REST API 的 consumer secret
|
||||||
|
token?: string; // Shopyy token
|
||||||
skuPrefix?: string;
|
skuPrefix?: string;
|
||||||
areas?: string[];
|
areas?: string[];
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +60,7 @@ const SiteList: React.FC = () => {
|
||||||
isDisabled: !!editing.isDisabled,
|
isDisabled: !!editing.isDisabled,
|
||||||
consumerKey: undefined,
|
consumerKey: undefined,
|
||||||
consumerSecret: undefined,
|
consumerSecret: undefined,
|
||||||
|
token: undefined,
|
||||||
areas: editing.areas?.map((area) => area.code) ?? [],
|
areas: editing.areas?.map((area) => area.code) ?? [],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -69,6 +72,7 @@ const SiteList: React.FC = () => {
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
consumerKey: undefined,
|
consumerKey: undefined,
|
||||||
consumerSecret: undefined,
|
consumerSecret: undefined,
|
||||||
|
token: undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [open, editing]);
|
}, [open, editing]);
|
||||||
|
|
@ -148,7 +152,7 @@ const SiteList: React.FC = () => {
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={row.isDisabled ? '启用站点' : '禁用站点'}
|
title={row.isDisabled ? '启用站点' : '禁用站点'}
|
||||||
description={
|
description={
|
||||||
row.isDisabled ? '确认启用该站点?' : '确认禁用该站点?'
|
row.isDisabled ? '确认启用该站点?' : '确认禁用该站点?'
|
||||||
}
|
}
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -198,14 +202,17 @@ const SiteList: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 提交创建/更新逻辑;编辑时未填写密钥则不提交(保持原值)
|
// 提交创建/更新逻辑;编辑时未填写密钥则不提交(保持原值)
|
||||||
const handleSubmit = async (values: SiteFormValues) => {
|
const handleSubmit = async (values: SiteFormValues) => {
|
||||||
try {
|
try {
|
||||||
|
const isShopyy = values.type === 'shopyy';
|
||||||
|
const apiUrl = isShopyy ? 'https://openapi.oemapps.com' : values.apiUrl;
|
||||||
|
|
||||||
if (editing) {
|
if (editing) {
|
||||||
const payload: Record<string, any> = {
|
const payload: Record<string, any> = {
|
||||||
// 仅提交存在的字段,避免覆盖为 null/空
|
// 仅提交存在的字段,避免覆盖为 null/空
|
||||||
...(values.name ? { name: values.name } : {}),
|
...(values.name ? { name: values.name } : {}),
|
||||||
...(values.apiUrl ? { apiUrl: values.apiUrl } : {}),
|
...(apiUrl ? { apiUrl: apiUrl } : {}),
|
||||||
...(values.type ? { type: values.type } : {}),
|
...(values.type ? { type: values.type } : {}),
|
||||||
...(typeof values.isDisabled === 'boolean'
|
...(typeof values.isDisabled === 'boolean'
|
||||||
? { isDisabled: values.isDisabled }
|
? { isDisabled: values.isDisabled }
|
||||||
|
|
@ -213,30 +220,46 @@ const SiteList: React.FC = () => {
|
||||||
...(values.skuPrefix ? { skuPrefix: values.skuPrefix } : {}),
|
...(values.skuPrefix ? { skuPrefix: values.skuPrefix } : {}),
|
||||||
areas: values.areas ?? [],
|
areas: values.areas ?? [],
|
||||||
};
|
};
|
||||||
// 仅当输入了新密钥时才提交,未输入则保持原本值
|
|
||||||
if (values.consumerKey && values.consumerKey.trim()) {
|
if (isShopyy) {
|
||||||
payload.consumerKey = values.consumerKey.trim();
|
if (values.token && values.token.trim()) {
|
||||||
}
|
payload.token = values.token.trim();
|
||||||
if (values.consumerSecret && values.consumerSecret.trim()) {
|
}
|
||||||
payload.consumerSecret = values.consumerSecret.trim();
|
} else {
|
||||||
|
// 仅当输入了新密钥时才提交,未输入则保持原本值
|
||||||
|
if (values.consumerKey && values.consumerKey.trim()) {
|
||||||
|
payload.consumerKey = values.consumerKey.trim();
|
||||||
|
}
|
||||||
|
if (values.consumerSecret && values.consumerSecret.trim()) {
|
||||||
|
payload.consumerSecret = values.consumerSecret.trim();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await request(`/site/update/${editing.id}`, {
|
await request(`/site/update/${editing.id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: payload,
|
data: payload,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 新增站点时要求填写 consumerKey 和 consumerSecret
|
if (isShopyy) {
|
||||||
if (!values.consumerKey || !values.consumerSecret) {
|
if (!values.token) {
|
||||||
throw new Error('Consumer Key and Secret are required');
|
throw new Error('Token is required for Shopyy');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 新增站点时要求填写 consumerKey 和 consumerSecret
|
||||||
|
if (!values.consumerKey || !values.consumerSecret) {
|
||||||
|
throw new Error('Consumer Key and Secret are required for WooCommerce');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await request('/site/create', {
|
await request('/site/create', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
name: values.name,
|
name: values.name,
|
||||||
apiUrl: values.apiUrl,
|
apiUrl: apiUrl,
|
||||||
type: values.type || 'woocommerce',
|
type: values.type || 'woocommerce',
|
||||||
consumerKey: values.consumerKey,
|
consumerKey: isShopyy ? undefined : values.consumerKey,
|
||||||
consumerSecret: values.consumerSecret,
|
consumerSecret: isShopyy ? undefined : values.consumerSecret,
|
||||||
|
token: isShopyy ? values.token : undefined,
|
||||||
skuPrefix: values.skuPrefix,
|
skuPrefix: values.skuPrefix,
|
||||||
areas: values.areas ?? [],
|
areas: values.areas ?? [],
|
||||||
},
|
},
|
||||||
|
|
@ -286,31 +309,14 @@ const SiteList: React.FC = () => {
|
||||||
formRef={formRef}
|
formRef={formRef}
|
||||||
onFinish={handleSubmit}
|
onFinish={handleSubmit}
|
||||||
>
|
>
|
||||||
{/* 站点名称,必填 */}
|
{/* 站点名称,必填 */}
|
||||||
<ProFormText
|
<ProFormText
|
||||||
name="name"
|
name="name"
|
||||||
label="站点名称"
|
label="站点名称"
|
||||||
placeholder="例如:本地商店"
|
placeholder="例如:本地商店"
|
||||||
rules={[{ required: true, message: '站点名称为必填项' }]}
|
rules={[{ required: true, message: '站点名称为必填项' }]}
|
||||||
/>
|
/>
|
||||||
{/* API 地址,可选 */}
|
{/* 区域选择 */}
|
||||||
<ProFormText
|
|
||||||
name="apiUrl"
|
|
||||||
label="API 地址"
|
|
||||||
placeholder="例如:https://shop.example.com"
|
|
||||||
/>
|
|
||||||
{/* 平台类型选择 */}
|
|
||||||
<ProFormSelect
|
|
||||||
name="type"
|
|
||||||
label="平台"
|
|
||||||
options={[
|
|
||||||
{ label: 'WooCommerce', value: 'woocommerce' },
|
|
||||||
{ label: 'Shopyy', value: 'shopyy' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
{/* 是否禁用 */}
|
|
||||||
<ProFormSwitch name="isDisabled" label="禁用" />
|
|
||||||
{/* 区域选择 */}
|
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
name="areas"
|
name="areas"
|
||||||
label="区域"
|
label="区域"
|
||||||
|
|
@ -334,27 +340,69 @@ const SiteList: React.FC = () => {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{/* 平台类型选择 */}
|
||||||
|
<ProFormSelect
|
||||||
|
name="type"
|
||||||
|
label="平台"
|
||||||
|
options={[
|
||||||
|
{ label: 'WooCommerce', value: 'woocommerce' },
|
||||||
|
{ label: 'Shopyy', value: 'shopyy' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProFormDependency name={['type']}>
|
||||||
|
{({ type }) => {
|
||||||
|
const isShopyy = type === 'shopyy';
|
||||||
|
return isShopyy ? (
|
||||||
|
<>
|
||||||
|
<ProFormText
|
||||||
|
name="apiUrl"
|
||||||
|
label="API 地址"
|
||||||
|
disabled
|
||||||
|
initialValue="https://openapi.oemapps.com"
|
||||||
|
placeholder="https://openapi.oemapps.com"
|
||||||
|
/>
|
||||||
|
<ProFormText
|
||||||
|
name="token"
|
||||||
|
label="Token"
|
||||||
|
placeholder={editing ? '留空表示不修改' : '必填'}
|
||||||
|
rules={editing ? [] : [{ required: true, message: 'Token 为必填项' }]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ProFormText
|
||||||
|
name="apiUrl"
|
||||||
|
label="API 地址"
|
||||||
|
placeholder="例如:https://shop.example.com"
|
||||||
|
rules={[{ required: true, message: 'API 地址为必填项' }]}
|
||||||
|
/>
|
||||||
|
{/* WooCommerce REST consumer key */}
|
||||||
|
<ProFormText
|
||||||
|
name="consumerKey"
|
||||||
|
label="Key"
|
||||||
|
placeholder={editing ? '留空表示不修改' : '必填'}
|
||||||
|
rules={editing ? [] : [{ required: true, message: 'Key 为必填项' }]}
|
||||||
|
/>
|
||||||
|
{/* WooCommerce REST consumer secret */}
|
||||||
|
<ProFormText
|
||||||
|
name="consumerSecret"
|
||||||
|
label="Secret"
|
||||||
|
placeholder={editing ? '留空表示不修改' : '必填'}
|
||||||
|
rules={editing ? [] : [{ required: true, message: 'Secret 为必填项' }]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</ProFormDependency>
|
||||||
|
|
||||||
|
{editing && <ProFormSwitch name="isDisabled" label="禁用" />}
|
||||||
|
|
||||||
<ProFormText
|
<ProFormText
|
||||||
name="skuPrefix"
|
name="skuPrefix"
|
||||||
label="SKU 前缀"
|
label="SKU 前缀"
|
||||||
placeholder={editing ? '留空表示不修改' : '可选'}
|
placeholder={editing ? '留空表示不修改' : '可选'}
|
||||||
/>
|
/>
|
||||||
{/* WooCommerce REST consumer key;新增必填,编辑不填则保持原值 */}
|
|
||||||
<ProFormText
|
|
||||||
name="consumerKey"
|
|
||||||
label="Key"
|
|
||||||
placeholder={editing ? '留空表示不修改' : '必填'}
|
|
||||||
rules={editing ? [] : [{ required: true, message: 'Key 为必填项' }]}
|
|
||||||
/>
|
|
||||||
{/* WooCommerce REST consumer secret;新增必填,编辑不填则保持原值 */}
|
|
||||||
<ProFormText
|
|
||||||
name="consumerSecret"
|
|
||||||
label="Secret"
|
|
||||||
placeholder={editing ? '留空表示不修改' : '必填'}
|
|
||||||
rules={
|
|
||||||
editing ? [] : [{ required: true, message: 'Secret 为必填项' }]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</DrawerForm>
|
</DrawerForm>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ const ListPage: React.FC = () => {
|
||||||
|
|
||||||
// 数据行
|
// 数据行
|
||||||
const rows = (data?.items || []).map((item: API.StockDTO) => {
|
const rows = (data?.items || []).map((item: API.StockDTO) => {
|
||||||
// 处理stockPoint可能为undefined的情况,并正确定义类型
|
// 处理stockPoint可能为undefined的情况,并正确定义类型
|
||||||
const stockMap = new Map<number, number>(
|
const stockMap = new Map<number, number>(
|
||||||
(item.stockPoint || []).map((sp: any) => [
|
(item.stockPoint || []).map((sp: any) => [
|
||||||
sp.id || 0,
|
sp.id || 0,
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ const PurchaseOrderPage: React.FC = () => {
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="删除"
|
title="删除"
|
||||||
description="确认删除?"
|
description="确认删除?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -120,7 +120,7 @@ const PurchaseOrderPage: React.FC = () => {
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="入库"
|
title="入库"
|
||||||
description="确认已到达?"
|
description="确认已到达?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -297,7 +297,7 @@ const CreateForm: React.FC<{
|
||||||
transform={(value) => {
|
transform={(value) => {
|
||||||
return value?.value || value;
|
return value?.value || value;
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
onChange={(_, option) => {
|
onChange={(_, option) => {
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
|
|
@ -478,7 +478,7 @@ const UpdateForm: React.FC<{
|
||||||
transform={(value) => {
|
transform={(value) => {
|
||||||
return value?.value || value;
|
return value?.value || value;
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
onChange={(_, option) => {
|
onChange={(_, option) => {
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
|
|
@ -643,7 +643,7 @@ const DetailForm: React.FC<{
|
||||||
transform={(value) => {
|
transform={(value) => {
|
||||||
return value?.value || value;
|
return value?.value || value;
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
onChange={(_, option) => {
|
onChange={(_, option) => {
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ const TransferPage: React.FC = () => {
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="入库"
|
title="入库"
|
||||||
description="确认已到达?"
|
description="确认已到达?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -116,7 +116,7 @@ const TransferPage: React.FC = () => {
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="丢失"
|
title="丢失"
|
||||||
description="确认该批货已丢失?"
|
description="确认该批货已丢失?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -137,7 +137,7 @@ const TransferPage: React.FC = () => {
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="取消"
|
title="取消"
|
||||||
description="确认取消?"
|
description="确认取消?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -354,7 +354,7 @@ const CreateForm: React.FC<{
|
||||||
transform={(value) => {
|
transform={(value) => {
|
||||||
return value?.value || value;
|
return value?.value || value;
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
onChange={(_, option) => {
|
onChange={(_, option) => {
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
|
|
@ -530,7 +530,7 @@ const UpdateForm: React.FC<{
|
||||||
transform={(value) => {
|
transform={(value) => {
|
||||||
return value?.value || value;
|
return value?.value || value;
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
onChange={(_, option) => {
|
onChange={(_, option) => {
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
|
|
@ -687,7 +687,7 @@ const DetailForm: React.FC<{
|
||||||
transform={(value) => {
|
transform={(value) => {
|
||||||
return value?.value || value;
|
return value?.value || value;
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
onChange={(_, option) => {
|
onChange={(_, option) => {
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ const ListPage: React.FC = () => {
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="删除"
|
title="删除"
|
||||||
description="确认删除?"
|
description="确认删除?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ const SUBSCRIPTION_STATUS_ENUM: Record<string, { text: string }> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订阅列表页:展示、筛选、触发订阅同步
|
* 订阅列表页:展示,筛选,触发订阅同步
|
||||||
*/
|
*/
|
||||||
const ListPage: React.FC = () => {
|
const ListPage: React.FC = () => {
|
||||||
// 表格操作引用:用于在同步后触发表格刷新
|
// 表格操作引用:用于在同步后触发表格刷新
|
||||||
|
|
@ -75,7 +75,7 @@ const ListPage: React.FC = () => {
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
valueType: 'select',
|
valueType: 'select',
|
||||||
valueEnum: SUBSCRIPTION_STATUS_ENUM,
|
valueEnum: SUBSCRIPTION_STATUS_ENUM,
|
||||||
// 以 Tag 形式展示,更易辨识
|
// 以 Tag 形式展示,更易辨识
|
||||||
render: (_, row) =>
|
render: (_, row) =>
|
||||||
row?.status ? (
|
row?.status ? (
|
||||||
<Tag>{SUBSCRIPTION_STATUS_ENUM[row.status]?.text || row.status}</Tag>
|
<Tag>{SUBSCRIPTION_STATUS_ENUM[row.status]?.text || row.status}</Tag>
|
||||||
|
|
@ -159,7 +159,7 @@ const ListPage: React.FC = () => {
|
||||||
const candidates: any[] = (
|
const candidates: any[] = (
|
||||||
Array.isArray(data) ? data : []
|
Array.isArray(data) ? data : []
|
||||||
).filter((c: any) => String(c?.externalOrderId) === parentNumber);
|
).filter((c: any) => String(c?.externalOrderId) === parentNumber);
|
||||||
// 拉取详情,补充状态、金额、时间
|
// 拉取详情,补充状态,金额,时间
|
||||||
const details = [] as any[];
|
const details = [] as any[];
|
||||||
for (const c of candidates) {
|
for (const c of candidates) {
|
||||||
const d = await request(`/order/${c.id}`, { method: 'GET' });
|
const d = await request(`/order/${c.id}`, { method: 'GET' });
|
||||||
|
|
@ -205,7 +205,7 @@ const ListPage: React.FC = () => {
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
/**
|
/**
|
||||||
* 列表数据请求;保持与后端分页参数一致
|
* 列表数据请求;保持与后端分页参数一致
|
||||||
* 兼容后端 data.items 或 data.list 返回字段
|
* 兼容后端 data.items 或 data.list 返回字段
|
||||||
*/
|
*/
|
||||||
request={async (params) => {
|
request={async (params) => {
|
||||||
|
|
@ -220,7 +220,7 @@ const ListPage: React.FC = () => {
|
||||||
// 工具栏:订阅同步入口
|
// 工具栏:订阅同步入口
|
||||||
toolBarRender={() => [<SyncForm key="sync" tableRef={actionRef} />]}
|
toolBarRender={() => [<SyncForm key="sync" tableRef={actionRef} />]}
|
||||||
/>
|
/>
|
||||||
{/* 关联订单抽屉:展示订单号、关系、时间、状态与金额 */}
|
{/* 关联订单抽屉:展示订单号,关系,时间,状态与金额 */}
|
||||||
<Drawer
|
<Drawer
|
||||||
open={drawerOpen}
|
open={drawerOpen}
|
||||||
title={drawerTitle}
|
title={drawerTitle}
|
||||||
|
|
@ -234,7 +234,7 @@ const ListPage: React.FC = () => {
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<List.Item.Meta
|
<List.Item.Meta
|
||||||
title={`#${item?.externalOrderId || '-'}`}
|
title={`#${item?.externalOrderId || '-'}`}
|
||||||
description={`关系:${item?.relationship || '-'},站点:${
|
description={`关系:${item?.relationship || '-'},站点:${
|
||||||
item?.siteName || '-'
|
item?.siteName || '-'
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|
@ -281,7 +281,7 @@ const SyncForm: React.FC<{
|
||||||
/**
|
/**
|
||||||
* 提交逻辑:
|
* 提交逻辑:
|
||||||
* 1. 必填校验由 ProForm + rules 保证
|
* 1. 必填校验由 ProForm + rules 保证
|
||||||
* 2. 调用同步接口,失败时友好提示
|
* 2. 调用同步接口,失败时友好提示
|
||||||
* 3. 成功后刷新列表
|
* 3. 成功后刷新列表
|
||||||
*/
|
*/
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ import { ORDER_STATUS_ENUM } from '@/constants';
|
||||||
import { formatShipmentState, formatSource } from '@/utils/format';
|
import { formatShipmentState, formatSource } from '@/utils/format';
|
||||||
import RelatedOrders from './RelatedOrders';
|
import RelatedOrders from './RelatedOrders';
|
||||||
|
|
||||||
// 为保持原文件结构简单,此处从 index.tsx 引入的子组件仍由原文件导出或保持原状
|
// 为保持原文件结构简单,此处从 index.tsx 引入的子组件仍由原文件导出或保持原状
|
||||||
// 若后续需要彻底解耦,可将 OrderNote / Shipping / SalesChange 也独立到文件
|
// 若后续需要彻底解耦,可将 OrderNote / Shipping / SalesChange 也独立到文件
|
||||||
// 当前按你的要求仅抽离详情 Drawer
|
// 当前按你的要求仅抽离详情 Drawer
|
||||||
|
|
||||||
type OrderRecord = API.Order;
|
type OrderRecord = API.Order;
|
||||||
|
|
@ -118,7 +118,7 @@ const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
key="btn-after-sale"
|
key="btn-after-sale"
|
||||||
title="转至售后"
|
title="转至售后"
|
||||||
description="确认转至售后?"
|
description="确认转至售后?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -145,7 +145,7 @@ const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
key="btn-cancel"
|
key="btn-cancel"
|
||||||
title="转至取消"
|
title="转至取消"
|
||||||
description="确认转至取消?"
|
description="确认转至取消?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -168,7 +168,7 @@ const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
key="btn-refund"
|
key="btn-refund"
|
||||||
title="转至退款"
|
title="转至退款"
|
||||||
description="确认转至退款?"
|
description="确认转至退款?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -191,7 +191,7 @@ const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
key="btn-completed"
|
key="btn-completed"
|
||||||
title="转至完成"
|
title="转至完成"
|
||||||
description="确认转至完成?"
|
description="确认转至完成?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
@ -425,9 +425,9 @@ const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
||||||
await navigator.clipboard.writeText(
|
await navigator.clipboard.writeText(
|
||||||
v.tracking_url,
|
v.tracking_url,
|
||||||
);
|
);
|
||||||
message.success('复制成功!');
|
message.success('复制成功!');
|
||||||
} catch {
|
} catch {
|
||||||
message.error('复制失败!');
|
message.error('复制失败!');
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -440,7 +440,7 @@ const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
key="action-cancel"
|
key="action-cancel"
|
||||||
title="取消运单"
|
title="取消运单"
|
||||||
description="确认取消运单?"
|
description="确认取消运单?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ dayjs.extend(relativeTime);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RelatedOrders 表格组件
|
* RelatedOrders 表格组件
|
||||||
* 用于展示订单详情中的关联数据(订阅/订单),按统一表格样式渲染
|
* 用于展示订单详情中的关联数据(订阅/订单),按统一表格样式渲染
|
||||||
* 本组件将订阅与订单统一归一化为五列展示,便于快速浏览
|
* 本组件将订阅与订单统一归一化为五列展示,便于快速浏览
|
||||||
*/
|
*/
|
||||||
const RelatedOrders: React.FC<{ data?: any[] }> = ({ data = [] }) => {
|
const RelatedOrders: React.FC<{ data?: any[] }> = ({ data = [] }) => {
|
||||||
const rows = (Array.isArray(data) ? data : []).map((it: any) => {
|
const rows = (Array.isArray(data) ? data : []).map((it: any) => {
|
||||||
|
|
@ -48,7 +48,7 @@ const RelatedOrders: React.FC<{ data?: any[] }> = ({ data = [] }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100%' }}>
|
<div style={{ width: '100%' }}>
|
||||||
{/* 表头(英文文案,符合国际化默认英文的要求) */}
|
{/* 表头(英文文案,符合国际化默认英文的要求) */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ const List: React.FC = () => {
|
||||||
<UpdateForm tableRef={actionRef} values={record} />
|
<UpdateForm tableRef={actionRef} values={record} />
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="删除"
|
title="删除"
|
||||||
description="确认删除?"
|
description="确认删除?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
if (!record.id) return;
|
if (!record.id) return;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -630,7 +630,7 @@ const SetComponent: React.FC<{
|
||||||
transform={(value) => {
|
transform={(value) => {
|
||||||
return value?.value || value;
|
return value?.value || value;
|
||||||
}}
|
}}
|
||||||
debounceTime={300} // 防抖,减少请求频率
|
debounceTime={300} // 防抖,减少请求频率
|
||||||
rules={[{ required: true, message: '请选择产品' }]}
|
rules={[{ required: true, message: '请选择产品' }]}
|
||||||
/>
|
/>
|
||||||
<ProFormDigit
|
<ProFormDigit
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ interface TagConfig {
|
||||||
// 移植 Python 脚本中的核心函数
|
// 移植 Python 脚本中的核心函数
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 从产品名称中解析出品牌、口味、毫克含量和干燥度
|
* @description 从产品名称中解析出品牌,口味,毫克含量和干燥度
|
||||||
*/
|
*/
|
||||||
const parseName = (
|
const parseName = (
|
||||||
name: string,
|
name: string,
|
||||||
|
|
@ -38,8 +38,8 @@ const parseName = (
|
||||||
const mgMatch = nm.match(/(\d+)\s*MG/i);
|
const mgMatch = nm.match(/(\d+)\s*MG/i);
|
||||||
const mg = mgMatch ? mgMatch[1] : '';
|
const mg = mgMatch ? mgMatch[1] : '';
|
||||||
|
|
||||||
// 确保品牌按长度降序排序,避免部分匹配(如匹配到 VELO 而不是 VELO MAX)
|
// 确保品牌按长度降序排序,避免部分匹配(如匹配到 VELO 而不是 VELO MAX)
|
||||||
// 这一步其实应该在传入 brands 之前就做好了,但这里再保险一下
|
// 这一步其实应该在传入 brands 之前就做好了,但这里再保险一下
|
||||||
// 实际调用时 sortedBrands 已经排好序了
|
// 实际调用时 sortedBrands 已经排好序了
|
||||||
for (const b of brands) {
|
for (const b of brands) {
|
||||||
if (nm.toUpperCase().startsWith(b.toUpperCase())) {
|
if (nm.toUpperCase().startsWith(b.toUpperCase())) {
|
||||||
|
|
@ -84,7 +84,7 @@ const splitFlavorTokens = (flavorPart: string): string[] => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 根据口味分类额外的标签(如 Fruit, Mint)
|
* @description 根据口味分类额外的标签(如 Fruit, Mint)
|
||||||
*/
|
*/
|
||||||
const classifyExtraTags = (
|
const classifyExtraTags = (
|
||||||
flavorPart: string,
|
flavorPart: string,
|
||||||
|
|
@ -111,7 +111,7 @@ const classifyExtraTags = (
|
||||||
const matchAttributes = (text: string, keys: string[]): string[] => {
|
const matchAttributes = (text: string, keys: string[]): string[] => {
|
||||||
const matched = new Set<string>();
|
const matched = new Set<string>();
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
// 使用单词边界匹配,避免部分匹配
|
// 使用单词边界匹配,避免部分匹配
|
||||||
// 转义正则特殊字符
|
// 转义正则特殊字符
|
||||||
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
const regex = new RegExp(`\\b${escapedKey}\\b`, 'i');
|
const regex = new RegExp(`\\b${escapedKey}\\b`, 'i');
|
||||||
|
|
@ -129,7 +129,7 @@ const computeTags = (name: string, sku: string, config: TagConfig): string => {
|
||||||
const [brand, flavorPart, mg, dryness] = parseName(name, config.brands);
|
const [brand, flavorPart, mg, dryness] = parseName(name, config.brands);
|
||||||
const tokens = splitFlavorTokens(flavorPart);
|
const tokens = splitFlavorTokens(flavorPart);
|
||||||
|
|
||||||
// 白名单模式:只保留在 flavorKeys 中的 token
|
// 白名单模式:只保留在 flavorKeys 中的 token
|
||||||
// 且对比时忽略大小写
|
// 且对比时忽略大小写
|
||||||
const flavorKeysLower = config.flavorKeys.map(k => k.toLowerCase());
|
const flavorKeysLower = config.flavorKeys.map(k => k.toLowerCase());
|
||||||
|
|
||||||
|
|
@ -181,7 +181,7 @@ const computeTags = (name: string, sku: string, config: TagConfig): string => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保留原有的 dryness 提取逻辑 (从括号中提取)
|
// 保留原有的 dryness 提取逻辑 (从括号中提取)
|
||||||
// 如果 dict 匹配已经覆盖了,去重时会处理
|
// 如果 dict 匹配已经覆盖了,去重时会处理
|
||||||
if (dryness) {
|
if (dryness) {
|
||||||
if (/moist/i.test(dryness)) {
|
if (/moist/i.test(dryness)) {
|
||||||
tags.push('Moisture');
|
tags.push('Moisture');
|
||||||
|
|
@ -197,8 +197,8 @@ const computeTags = (name: string, sku: string, config: TagConfig): string => {
|
||||||
// 去重并保留顺序
|
// 去重并保留顺序
|
||||||
const seen = new Set<string>();
|
const seen = new Set<string>();
|
||||||
const finalTags = tags.filter((t) => {
|
const finalTags = tags.filter((t) => {
|
||||||
// 简单的去重,忽略大小写差异? 或者完全匹配
|
// 简单的去重,忽略大小写差异? 或者完全匹配
|
||||||
// 这里使用完全匹配,因为前面已经做了一些格式化
|
// 这里使用完全匹配,因为前面已经做了一些格式化
|
||||||
if (t && !seen.has(t)) {
|
if (t && !seen.has(t)) {
|
||||||
seen.add(t);
|
seen.add(t);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -210,7 +210,7 @@ const computeTags = (name: string, sku: string, config: TagConfig): string => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description WordPress 产品工具页面,用于处理产品 CSV 并生成 Tags
|
* @description WordPress 产品工具页面,用于处理产品 CSV 并生成 Tags
|
||||||
*/
|
*/
|
||||||
const WpToolPage: React.FC = () => {
|
const WpToolPage: React.FC = () => {
|
||||||
// 状态管理
|
// 状态管理
|
||||||
|
|
@ -285,9 +285,9 @@ const WpToolPage: React.FC = () => {
|
||||||
* @param {File} uploadedFile - 用户上传的文件
|
* @param {File} uploadedFile - 用户上传的文件
|
||||||
*/
|
*/
|
||||||
const handleFileUpload = (uploadedFile: File) => {
|
const handleFileUpload = (uploadedFile: File) => {
|
||||||
// 检查文件类型,虽然 xlsx 库更宽容,但最好还是保留基本验证
|
// 检查文件类型,虽然 xlsx 库更宽容,但最好还是保留基本验证
|
||||||
if (!uploadedFile.name.match(/\.(csv|xlsx|xls)$/)) {
|
if (!uploadedFile.name.match(/\.(csv|xlsx|xls)$/)) {
|
||||||
message.error('请上传 CSV 或 Excel 格式的文件!');
|
message.error('请上传 CSV 或 Excel 格式的文件!');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setFile(uploadedFile);
|
setFile(uploadedFile);
|
||||||
|
|
@ -302,7 +302,7 @@ const WpToolPage: React.FC = () => {
|
||||||
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||||||
|
|
||||||
if (jsonData.length < 2) {
|
if (jsonData.length < 2) {
|
||||||
message.error('文件为空或缺少表头!');
|
message.error('文件为空或缺少表头!');
|
||||||
setCsvData([]);
|
setCsvData([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -317,17 +317,17 @@ const WpToolPage: React.FC = () => {
|
||||||
return rowData;
|
return rowData;
|
||||||
});
|
});
|
||||||
|
|
||||||
message.success(`成功解析 ${rows.length} 条数据。`);
|
message.success(`成功解析 ${rows.length} 条数据.`);
|
||||||
setCsvData(rows);
|
setCsvData(rows);
|
||||||
setProcessedData([]); // 清空旧的处理结果
|
setProcessedData([]); // 清空旧的处理结果
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('文件解析失败,请检查文件格式!');
|
message.error('文件解析失败,请检查文件格式!');
|
||||||
console.error('File Parse Error:', error);
|
console.error('File Parse Error:', error);
|
||||||
setCsvData([]);
|
setCsvData([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.onerror = (error) => {
|
reader.onerror = (error) => {
|
||||||
message.error('文件读取失败!');
|
message.error('文件读取失败!');
|
||||||
console.error('File Read Error:', error);
|
console.error('File Read Error:', error);
|
||||||
};
|
};
|
||||||
reader.readAsBinaryString(uploadedFile);
|
reader.readAsBinaryString(uploadedFile);
|
||||||
|
|
@ -352,16 +352,16 @@ const WpToolPage: React.FC = () => {
|
||||||
const fileName = `products_with_tags_${Date.now()}.xlsx`;
|
const fileName = `products_with_tags_${Date.now()}.xlsx`;
|
||||||
XLSX.writeFile(workbook, fileName);
|
XLSX.writeFile(workbook, fileName);
|
||||||
|
|
||||||
message.success('下载任务已开始!');
|
message.success('下载任务已开始!');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 核心逻辑:根据配置处理 CSV 数据并生成 Tags
|
* @description 核心逻辑:根据配置处理 CSV 数据并生成 Tags
|
||||||
*/
|
*/
|
||||||
const handleProcessData = async () => {
|
const handleProcessData = async () => {
|
||||||
// 验证是否已上传并解析了数据
|
// 验证是否已上传并解析了数据
|
||||||
if (csvData.length === 0) {
|
if (csvData.length === 0) {
|
||||||
message.warning('请先上传并成功解析一个 CSV 文件。');
|
message.warning('请先上传并成功解析一个 CSV 文件.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -400,7 +400,7 @@ const WpToolPage: React.FC = () => {
|
||||||
|
|
||||||
setProcessedData(dataWithTags);
|
setProcessedData(dataWithTags);
|
||||||
message.success({
|
message.success({
|
||||||
content: 'Tags 生成成功!正在自动下载...',
|
content: 'Tags 生成成功!正在自动下载...',
|
||||||
key: 'processing',
|
key: 'processing',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -409,7 +409,7 @@ const WpToolPage: React.FC = () => {
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error({
|
message.error({
|
||||||
content: '处理失败,请检查配置或文件。',
|
content: '处理失败,请检查配置或文件.',
|
||||||
key: 'processing',
|
key: 'processing',
|
||||||
});
|
});
|
||||||
console.error('Processing Error:', error);
|
console.error('Processing Error:', error);
|
||||||
|
|
@ -421,7 +421,7 @@ const WpToolPage: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<PageContainer title="WordPress 产品工具">
|
<PageContainer title="WordPress 产品工具">
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
{/* 左侧:配置表单 */}
|
{/* 左侧:配置表单 */}
|
||||||
<Col xs={24} md={10}>
|
<Col xs={24} md={10}>
|
||||||
<Card title="1. 配置映射规则">
|
<Card title="1. 配置映射规则">
|
||||||
<ProForm
|
<ProForm
|
||||||
|
|
@ -434,58 +434,58 @@ const WpToolPage: React.FC = () => {
|
||||||
name="brands"
|
name="brands"
|
||||||
label="品牌列表"
|
label="品牌列表"
|
||||||
mode="tags"
|
mode="tags"
|
||||||
placeholder="请输入品牌,按回车确认"
|
placeholder="请输入品牌,按回车确认"
|
||||||
rules={[{ required: true, message: '至少需要一个品牌' }]}
|
rules={[{ required: true, message: '至少需要一个品牌' }]}
|
||||||
tooltip="按品牌名称长度倒序匹配,请将较长的品牌(如 WHITE FOX)放在前面。"
|
tooltip="按品牌名称长度倒序匹配,请将较长的品牌(如 WHITE FOX)放在前面."
|
||||||
/>
|
/>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
name="fruitKeys"
|
name="fruitKeys"
|
||||||
label="水果关键词"
|
label="水果关键词"
|
||||||
mode="tags"
|
mode="tags"
|
||||||
placeholder="请输入关键词,按回车确认"
|
placeholder="请输入关键词,按回车确认"
|
||||||
/>
|
/>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
name="mintKeys"
|
name="mintKeys"
|
||||||
label="薄荷关键词"
|
label="薄荷关键词"
|
||||||
mode="tags"
|
mode="tags"
|
||||||
placeholder="请输入关键词,按回车确认"
|
placeholder="请输入关键词,按回车确认"
|
||||||
/>
|
/>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
name="flavorKeys"
|
name="flavorKeys"
|
||||||
label="口味白名单"
|
label="口味白名单"
|
||||||
mode="tags"
|
mode="tags"
|
||||||
placeholder="请输入关键词,按回车确认"
|
placeholder="请输入关键词,按回车确认"
|
||||||
tooltip="只有在白名单中的词才会被识别为口味。"
|
tooltip="只有在白名单中的词才会被识别为口味."
|
||||||
/>
|
/>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
name="strengthKeys"
|
name="strengthKeys"
|
||||||
label="强度关键词"
|
label="强度关键词"
|
||||||
mode="tags"
|
mode="tags"
|
||||||
placeholder="请输入关键词,按回车确认"
|
placeholder="请输入关键词,按回车确认"
|
||||||
/>
|
/>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
name="sizeKeys"
|
name="sizeKeys"
|
||||||
label="尺寸关键词"
|
label="尺寸关键词"
|
||||||
mode="tags"
|
mode="tags"
|
||||||
placeholder="请输入关键词,按回车确认"
|
placeholder="请输入关键词,按回车确认"
|
||||||
/>
|
/>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
name="humidityKeys"
|
name="humidityKeys"
|
||||||
label="湿度关键词"
|
label="湿度关键词"
|
||||||
mode="tags"
|
mode="tags"
|
||||||
placeholder="请输入关键词,按回车确认"
|
placeholder="请输入关键词,按回车确认"
|
||||||
/>
|
/>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
name="categoryKeys"
|
name="categoryKeys"
|
||||||
label="分类关键词"
|
label="分类关键词"
|
||||||
mode="tags"
|
mode="tags"
|
||||||
placeholder="请输入关键词,按回车确认"
|
placeholder="请输入关键词,按回车确认"
|
||||||
/>
|
/>
|
||||||
</ProForm>
|
</ProForm>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
{/* 右侧:文件上传与操作 */}
|
{/* 右侧:文件上传与操作 */}
|
||||||
<Col xs={24} md={14}>
|
<Col xs={24} md={14}>
|
||||||
<Card title="2. 上传文件并操作">
|
<Card title="2. 上传文件并操作">
|
||||||
<Upload
|
<Upload
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { request } from 'umi';
|
import { request } from 'umi';
|
||||||
|
|
||||||
/** 获取区域列表(分页) GET /area/ */
|
/** 获取区域列表(分页) GET /area/ */
|
||||||
export async function areacontrollerGetarealist(
|
export async function areacontrollerGetarealist(
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
params: API.areacontrollerGetarealistParams,
|
params: API.areacontrollerGetarealistParams,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// @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';
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ declare namespace API {
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
/** 关键词(名称或编码) */
|
/** 关键词(名称或编码) */
|
||||||
keyword?: string;
|
keyword?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -443,7 +443,7 @@ declare namespace API {
|
||||||
| 'return-approved'
|
| 'return-approved'
|
||||||
| 'return-cancelled';
|
| 'return-cancelled';
|
||||||
payment_method?: string;
|
payment_method?: string;
|
||||||
/** 仅订阅订单(父订阅订单或包含订阅商品) */
|
/** 仅订阅订单(父订阅订单或包含订阅商品) */
|
||||||
isSubscriptionOnly?: boolean;
|
isSubscriptionOnly?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -994,7 +994,7 @@ declare namespace API {
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
/** 每页数量 */
|
/** 每页数量 */
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
/** 关键词(名称或编码) */
|
/** 关键词(名称或编码) */
|
||||||
keyword?: string;
|
keyword?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1036,7 +1036,7 @@ declare namespace API {
|
||||||
| 'return-approved'
|
| 'return-approved'
|
||||||
| 'return-cancelled';
|
| 'return-cancelled';
|
||||||
payment_method?: string;
|
payment_method?: string;
|
||||||
/** 仅订阅订单(父订阅订单或包含订阅商品) */
|
/** 仅订阅订单(父订阅订单或包含订阅商品) */
|
||||||
isSubscriptionOnly?: boolean;
|
isSubscriptionOnly?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1115,7 +1115,7 @@ declare namespace API {
|
||||||
sku?: string;
|
sku?: string;
|
||||||
/** 按库存点ID排序 */
|
/** 按库存点ID排序 */
|
||||||
sortPointId?: number;
|
sortPointId?: number;
|
||||||
/** 排序对象,格式如 { productName: "asc", sku: "desc" } */
|
/** 排序对象,格式如 { productName: "asc", sku: "desc" } */
|
||||||
order?: Record<string, any>;
|
order?: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1149,7 +1149,7 @@ declare namespace API {
|
||||||
| 'pending-cancel';
|
| 'pending-cancel';
|
||||||
/** 客户邮箱 */
|
/** 客户邮箱 */
|
||||||
customer_email?: string;
|
customer_email?: string;
|
||||||
/** 关键字(订阅ID、邮箱等) */
|
/** 关键字(订阅ID,邮箱等) */
|
||||||
keyword?: string;
|
keyword?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1383,7 +1383,7 @@ declare namespace API {
|
||||||
sku?: string;
|
sku?: string;
|
||||||
/** 按库存点ID排序 */
|
/** 按库存点ID排序 */
|
||||||
sortPointId?: number;
|
sortPointId?: number;
|
||||||
/** 排序对象,格式如 { productName: "asc", sku: "desc" } */
|
/** 排序对象,格式如 { productName: "asc", sku: "desc" } */
|
||||||
order?: Record<string, any>;
|
order?: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1548,7 +1548,7 @@ declare namespace API {
|
||||||
billing_interval?: number;
|
billing_interval?: number;
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
customer_email?: string;
|
customer_email?: string;
|
||||||
/** 父订单/父订阅ID(如有) */
|
/** 父订单/父订阅ID(如有) */
|
||||||
parent_id?: number;
|
parent_id?: number;
|
||||||
start_date?: string;
|
start_date?: string;
|
||||||
trial_end?: string;
|
trial_end?: string;
|
||||||
|
|
@ -1579,7 +1579,7 @@ declare namespace API {
|
||||||
| 'pending-cancel';
|
| 'pending-cancel';
|
||||||
/** 客户邮箱 */
|
/** 客户邮箱 */
|
||||||
customer_email?: string;
|
customer_email?: string;
|
||||||
/** 关键字(订阅ID、邮箱等) */
|
/** 关键字(订阅ID,邮箱等) */
|
||||||
keyword?: string;
|
keyword?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue