From 8524cc1ec0b5f2f9887cb964ad86995571fca859 Mon Sep 17 00:00:00 2001 From: tikkhun Date: Tue, 23 Dec 2025 19:38:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=AE=A2=E6=88=B7):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E6=95=B0=E6=8D=AE=E5=88=86=E6=9E=90=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(产品): 优化属性字典项获取逻辑,增加错误处理 fix(订单): 修复取消发货按钮在已完成订单中显示的问题 style: 统一代码格式,修复缩进和导入顺序问题 perf(字典): 优化字典配置加载逻辑,增加重试机制 docs(API): 更新API类型定义,添加客户统计相关接口 chore: 更新package.json文件格式 --- .umirc.ts | 5 + package.json | 2 +- src/pages/Category/index.tsx | 4 +- src/pages/Customer/List/index.tsx | 512 ++++++++++++++++----- src/pages/Customer/Statistic/index.tsx | 294 ++++++++++++ src/pages/Dict/List/index.tsx | 92 ++-- src/pages/Order/List/index.tsx | 419 +++++++++-------- src/pages/Product/Attribute/consts.ts | 6 +- src/pages/Product/Attribute/index.tsx | 78 +++- src/pages/Product/Category/index.tsx | 5 +- src/pages/Product/List/index.tsx | 2 +- src/pages/Product/Permutation/index.tsx | 18 +- src/pages/Product/Sync/index.tsx | 232 ++++++---- src/pages/Site/Shop/Layout.tsx | 9 +- src/pages/Site/Shop/Links/index.tsx | 18 +- src/pages/Site/Shop/Orders/index.tsx | 48 +- src/pages/Site/Shop/Products/index.tsx | 6 +- src/pages/Site/Shop/Reviews/ReviewForm.tsx | 31 +- src/pages/Site/Shop/Reviews/index.tsx | 14 +- src/pages/Site/Shop/Webhooks/index.tsx | 87 ++-- src/pages/Template/index.tsx | 51 +- src/pages/Woo/Product/TagTool/index.tsx | 46 +- src/servers/api/customer.ts | 30 ++ src/servers/api/site.ts | 2 +- src/servers/api/typings.d.ts | 34 +- 25 files changed, 1426 insertions(+), 619 deletions(-) create mode 100644 src/pages/Customer/Statistic/index.tsx diff --git a/.umirc.ts b/.umirc.ts index 171327a..b585857 100644 --- a/.umirc.ts +++ b/.umirc.ts @@ -131,6 +131,11 @@ export default defineConfig({ path: '/customer/list', component: './Customer/List', }, + { + name: '数据分析列表', + path: '/customer/statistic', + component: './Customer/Statistic', + }, ], }, { diff --git a/package.json b/package.json index 58e91ac..7ceb5ad 100644 --- a/package.json +++ b/package.json @@ -47,4 +47,4 @@ "prettier-plugin-packagejson": "^2.4.3", "typescript": "^5.7.3" } -} \ No newline at end of file +} diff --git a/src/pages/Category/index.tsx b/src/pages/Category/index.tsx index 9bd852e..3b0cc79 100644 --- a/src/pages/Category/index.tsx +++ b/src/pages/Category/index.tsx @@ -114,7 +114,9 @@ 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) => !notAttributes.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), diff --git a/src/pages/Customer/List/index.tsx b/src/pages/Customer/List/index.tsx index 7bd1877..a9a309d 100644 --- a/src/pages/Customer/List/index.tsx +++ b/src/pages/Customer/List/index.tsx @@ -1,11 +1,12 @@ -import { HistoryOrder } from '@/pages/Statistics/Order'; import { customercontrollerAddtag, customercontrollerDeltag, customercontrollerGetcustomerlist, customercontrollerGettags, customercontrollerSetrate, + customercontrollerSynccustomers, } from '@/servers/api/customer'; +import { sitecontrollerAll } from '@/servers/api/site'; import { ActionType, ModalForm, @@ -14,97 +15,219 @@ import { ProFormSelect, ProTable, } from '@ant-design/pro-components'; -import { App, Button, Rate, Space, Tag } from 'antd'; +import { App, Avatar, Button, Rate, Space, Tag, Tooltip } from 'antd'; import dayjs from 'dayjs'; -import { useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; -const ListPage: React.FC = () => { +// 地址格式化函数 +const formatAddress = (address: any) => { + if (!address) return '-'; + + if (typeof address === 'string') { + try { + address = JSON.parse(address); + } catch (e) { + return address; + } + } + + const { + first_name, + last_name, + company, + address_1, + address_2, + city, + state, + postcode, + country, + phone: addressPhone, + email: addressEmail + } = address; + + const parts = []; + + // 姓名 + const fullName = [first_name, last_name].filter(Boolean).join(' '); + if (fullName) parts.push(fullName); + + // 公司 + if (company) parts.push(company); + + // 地址行 + if (address_1) parts.push(address_1); + if (address_2) parts.push(address_2); + + // 城市、州、邮编 + const locationParts = [city, state, postcode].filter(Boolean).join(', '); + if (locationParts) parts.push(locationParts); + + // 国家 + if (country) parts.push(country); + + // 联系方式 + if (addressPhone) parts.push(`电话: ${addressPhone}`); + if (addressEmail) parts.push(`邮箱: ${addressEmail}`); + + return parts.join(', '); +}; + +// 地址卡片组件 +const AddressCell: React.FC<{ address: any; title: string }> = ({ address, title }) => { + const formattedAddress = formatAddress(address); + + if (formattedAddress === '-') { + return -; + } + + return ( + + {title}: +
+ {formattedAddress} + + } + placement="topLeft" + > +
+ {formattedAddress} +
+
+ ); +}; + +const CustomerList: React.FC = () => { const actionRef = useRef(); const { message } = App.useApp(); - const columns: ProColumns[] = [ + const [syncModalVisible, setSyncModalVisible] = useState(false); + const [syncLoading, setSyncLoading] = useState(false); + const [sites, setSites] = useState([]); // 添加站点数据状态 + + // 获取站点数据 + const fetchSites = async () => { + try { + const { data, success } = await sitecontrollerAll(); + if (success) { + setSites(data || []); + } + } catch (error) { + console.error('获取站点数据失败:', error); + } + }; + + // 根据站点ID获取站点名称 + const getSiteName = (siteId: number | undefined | null) => { + if (!siteId) return '-'; + const site = sites.find(s => s.id === siteId); + console.log(`site`,site) + return site ? site.name : String(siteId); + }; + + // 组件加载时获取站点数据 + useEffect(() => { + fetchSites(); + }, []); + + const columns: ProColumns[] = [ + { + title: 'ID', + dataIndex: 'id', + hideInSearch: true, + }, + { + title: '站点', + dataIndex: 'site_id', + valueType: 'select', + request: async () => { + try { + const { data, success } = await sitecontrollerAll(); + if (success && data) { + return data.map((site: any) => ({ + label: site.name, + value: site.id, + })); + } + return []; + } catch (error) { + console.error('获取站点列表失败:', error); + return []; + } + }, + render: (siteId, record) => { + return siteId + return getSiteName(record.site_id) || '-'; + }, + }, + { + title: '头像', + dataIndex: 'avatar', + hideInSearch: true, + width: 60, + render: (_, record) => ( + + {!record.avatar && record.fullname?.charAt(0)?.toUpperCase()} + + ), + }, + { + title: '姓名', + dataIndex: 'fullname', + sorter: true, + render: (_, record) => { + return ( + record.fullName || + `${record.firstName || ''} ${record.lastName || ''}`.trim() || + record.username || + '-' + ); + }, + }, { title: '用户名', dataIndex: 'username', hideInSearch: true, - render: (_, record) => { - if (record.billing.first_name || record.billing.last_name) - return record.billing.first_name + ' ' + record.billing.last_name; - return record.shipping.first_name + ' ' + record.shipping.last_name; - }, }, { title: '邮箱', dataIndex: 'email', + copyable: true, }, { - title: '客户编号', - dataIndex: 'customerId', - render: (_, record) => { - if (!record.customerId) return '-'; - return String(record.customerId).padStart(6, 0); - }, - sorter: true, - }, - { - title: '首单时间', - dataIndex: 'first_purchase_date', - valueType: 'dateMonth', - sorter: true, - render: (_, record) => - record.first_purchase_date - ? dayjs(record.first_purchase_date).format('YYYY-MM-DD HH:mm:ss') - : '-', - // search: { - // transform: (value: string) => { - // return { month: value }; - // }, - // }, - }, - { - title: '尾单时间', + title: '电话', + dataIndex: 'phone', hideInSearch: true, - dataIndex: 'last_purchase_date', - valueType: 'dateTime', - sorter: true, + copyable: true, }, { - title: '订单数', - dataIndex: 'orders', + title: '账单地址', + dataIndex: 'billing', hideInSearch: true, - sorter: true, - }, - { - title: '金额', - dataIndex: 'total', - hideInSearch: true, - sorter: true, - }, - { - title: 'YOONE订单数', - dataIndex: 'yoone_orders', - hideInSearch: true, - sorter: true, - }, - { - title: 'YOONE金额', - dataIndex: 'yoone_total', - hideInSearch: true, - sorter: true, - }, - { - title: '等级', - hideInSearch: true, - render: (_, record) => { - if (!record.yoone_orders || !record.yoone_total) return '-'; - if (Number(record.yoone_orders) === 1 && Number(record.yoone_total) > 0) - return 'B'; - return '-'; - }, - }, - { - title: '评星', - dataIndex: 'rate', width: 200, + render: (billing) => , + }, + { + title: '物流地址', + dataIndex: 'shipping', + hideInSearch: true, + width: 200, + render: (shipping) => , + }, + { + title: '评分', + dataIndex: 'rate', + width: 120, render: (_, record) => { return ( { try { const { success, message: msg } = await customercontrollerSetrate({ - id: record.customerId, + id: record.id, rate: val, }); if (success) { message.success(msg); actionRef.current?.reload(); } - } catch (e) { - message.error(e.message); + } catch (e: any) { + message.error(e?.message || '设置评分失败'); } }} value={record.rate} + allowHalf /> ); }, }, - { - title: 'phone', - dataIndex: 'phone', - hideInSearch: true, - render: (_, record) => record?.billing.phone || record?.shipping.phone, - }, - { - title: 'state', - dataIndex: 'state', - render: (_, record) => record?.billing.state || record?.shipping.state, - }, - { - title: 'city', - dataIndex: 'city', - hideInSearch: true, - render: (_, record) => record?.billing.city || record?.shipping.city, - }, { title: '标签', dataIndex: 'tags', + hideInSearch: true, render: (_, record) => { return ( - - {(record.tags || []).map((tag) => { + + {(record.tags || []).map((tag: string) => { return ( { email: record.email, tag, }); - return false; + if (!success) { + message.error(msg); + return false; + } + actionRef.current?.reload(); + return true; }} + style={{ marginBottom: 4 }} > {tag} @@ -173,31 +287,55 @@ const ListPage: React.FC = () => { ); }, }, + { + title: '创建时间', + dataIndex: 'site_created_at', + valueType: 'dateTime', + hideInSearch: true, + sorter: true, + width: 140, + }, + { + title: '更新时间', + dataIndex: 'site_created_at', + valueType: 'dateTime', + hideInSearch: true, + sorter: true, + width: 140, + + }, { title: '操作', dataIndex: 'option', valueType: 'option', fixed: 'right', + width: 120, render: (_, record) => { return ( - + - + ); }, }, ]; + return ( - + { const key = Object.keys(sorter)[0]; const { data, success } = await customercontrollerGetcustomerlist({ ...params, - ...(key ? { sorterKey: key, sorterValue: sorter[key] } : {}), + current: params.current?.toString(), + pageSize: params.pageSize?.toString(), + ...(key ? { sorterKey: key, sorterValue: sorter[key] as string } : {}), }); return { @@ -217,12 +357,37 @@ const ListPage: React.FC = () => { }; }} columns={columns} + search={{ + labelWidth: 'auto', + span: 6, + }} + pagination={{ + pageSize: 20, + showSizeChanger: true, + showTotal: (total, range) => + `第 ${range[0]}-${range[1]} 条/总共 ${total} 条`, + }} + toolBarRender={() => [ + , + // 这里可以添加导出、导入等功能按钮 + ]} + /> + setSyncModalVisible(false)} + tableRef={actionRef} /> ); }; -export const AddTag: React.FC<{ +const AddTag: React.FC<{ email: string; tags?: string[]; tableRef: React.MutableRefObject; @@ -233,7 +398,11 @@ export const AddTag: React.FC<{ return ( 修改标签} + trigger={ + + } width={800} modalProps={{ destroyOnHidden: true, @@ -250,16 +419,16 @@ export const AddTag: React.FC<{ if (!success) return []; setTagList(tags || []); return data - .filter((tag) => { + .filter((tag: string) => { return !(tags || []).includes(tag); }) - .map((tag) => ({ label: tag, value: tag })); + .map((tag: string) => ({ label: tag, value: tag })); }} fieldProps={{ value: tagList, // 当前值 onChange: async (newValue) => { - const added = newValue.filter((x) => !tagList.includes(x)); - const removed = tagList.filter((x) => !newValue.includes(x)); + const added = newValue.filter((x) => !(tags || []).includes(x)); + const removed = (tags || []).filter((x) => !newValue.includes(x)); if (added.length) { const { success, message: msg } = await customercontrollerAddtag({ @@ -282,7 +451,6 @@ export const AddTag: React.FC<{ } } tableRef?.current?.reload(); - setTagList(newValue); }, }} @@ -291,4 +459,124 @@ export const AddTag: React.FC<{ ); }; -export default ListPage; +const SyncCustomersModal: React.FC<{ + visible: boolean; + onClose: () => void; + tableRef: React.MutableRefObject; +}> = ({ visible, onClose, tableRef }) => { + const { message } = App.useApp(); + const [sites, setSites] = useState([]); + const [loading, setLoading] = useState(false); + + // 获取站点列表 + useEffect(() => { + if (visible) { + setLoading(true); + sitecontrollerAll() + .then((res: any) => { + setSites(res?.data || []); + }) + .catch((error: any) => { + message.error('获取站点列表失败: ' + (error.message || '未知错误')); + }) + .finally(() => { + setLoading(false); + }); + } + }, [visible]); + + const handleSync = async (values: { siteId: number }) => { + try { + setLoading(true); + const { success, message: msg, data } = await customercontrollerSynccustomers({ + siteId: values.siteId, + }); + + if (success) { + // 显示详细的同步结果 + const result = data || {}; + const { + total = 0, + synced = 0, + created = 0, + updated = 0, + errors = [] + } = result; + + let resultMessage = `同步完成!共处理 ${total} 个客户:`; + if (created > 0) resultMessage += ` 新建 ${created} 个`; + if (updated > 0) resultMessage += ` 更新 ${updated} 个`; + if (synced > 0) resultMessage += ` 同步成功 ${synced} 个`; + if (errors.length > 0) resultMessage += ` 失败 ${errors.length} 个`; + + if (errors.length > 0) { + // 如果有错误,显示警告消息 + message.warning({ + content: ( +
+
{resultMessage}
+
+ 失败详情:{errors.slice(0, 3).map((err: any) => err.email || err.error).join(', ')} + {errors.length > 3 && ` 等 ${errors.length - 3} 个错误...`} +
+
+ ), + duration: 8, + key: 'sync-result' + }); + } else { + // 完全成功 + message.success({ + content: resultMessage, + duration: 4, + key: 'sync-result' + }); + } + + onClose(); + // 刷新表格数据 + tableRef.current?.reload(); + return true; + } else { + message.error(msg || '同步失败'); + return false; + } + } catch (error: any) { + message.error('同步失败: ' + (error.message || '未知错误')); + return false; + } finally { + setLoading(false); + } + }; + + return ( + !open && onClose()} + modalProps={{ + destroyOnClose: true, + confirmLoading: loading, + }} + onFinish={handleSync} + > + ({ + label: site.name, + value: site.id, + }))} + rules={[{ required: true, message: '请选择站点' }]} + fieldProps={{ + loading: loading, + }} + /> + + ); +}; + +export { AddTag }; + +export default CustomerList; diff --git a/src/pages/Customer/Statistic/index.tsx b/src/pages/Customer/Statistic/index.tsx new file mode 100644 index 0000000..7bd1877 --- /dev/null +++ b/src/pages/Customer/Statistic/index.tsx @@ -0,0 +1,294 @@ +import { HistoryOrder } from '@/pages/Statistics/Order'; +import { + customercontrollerAddtag, + customercontrollerDeltag, + customercontrollerGetcustomerlist, + customercontrollerGettags, + customercontrollerSetrate, +} from '@/servers/api/customer'; +import { + ActionType, + ModalForm, + PageContainer, + ProColumns, + ProFormSelect, + ProTable, +} from '@ant-design/pro-components'; +import { App, Button, Rate, Space, Tag } from 'antd'; +import dayjs from 'dayjs'; +import { useRef, useState } from 'react'; + +const ListPage: React.FC = () => { + const actionRef = useRef(); + const { message } = App.useApp(); + const columns: ProColumns[] = [ + { + title: '用户名', + dataIndex: 'username', + hideInSearch: true, + render: (_, record) => { + if (record.billing.first_name || record.billing.last_name) + return record.billing.first_name + ' ' + record.billing.last_name; + return record.shipping.first_name + ' ' + record.shipping.last_name; + }, + }, + { + title: '邮箱', + dataIndex: 'email', + }, + { + title: '客户编号', + dataIndex: 'customerId', + render: (_, record) => { + if (!record.customerId) return '-'; + return String(record.customerId).padStart(6, 0); + }, + sorter: true, + }, + { + title: '首单时间', + dataIndex: 'first_purchase_date', + valueType: 'dateMonth', + sorter: true, + render: (_, record) => + record.first_purchase_date + ? dayjs(record.first_purchase_date).format('YYYY-MM-DD HH:mm:ss') + : '-', + // search: { + // transform: (value: string) => { + // return { month: value }; + // }, + // }, + }, + { + title: '尾单时间', + hideInSearch: true, + dataIndex: 'last_purchase_date', + valueType: 'dateTime', + sorter: true, + }, + { + title: '订单数', + dataIndex: 'orders', + hideInSearch: true, + sorter: true, + }, + { + title: '金额', + dataIndex: 'total', + hideInSearch: true, + sorter: true, + }, + { + title: 'YOONE订单数', + dataIndex: 'yoone_orders', + hideInSearch: true, + sorter: true, + }, + { + title: 'YOONE金额', + dataIndex: 'yoone_total', + hideInSearch: true, + sorter: true, + }, + { + title: '等级', + hideInSearch: true, + render: (_, record) => { + if (!record.yoone_orders || !record.yoone_total) return '-'; + if (Number(record.yoone_orders) === 1 && Number(record.yoone_total) > 0) + return 'B'; + return '-'; + }, + }, + { + title: '评星', + dataIndex: 'rate', + width: 200, + render: (_, record) => { + return ( + { + try { + const { success, message: msg } = + await customercontrollerSetrate({ + id: record.customerId, + rate: val, + }); + if (success) { + message.success(msg); + actionRef.current?.reload(); + } + } catch (e) { + message.error(e.message); + } + }} + value={record.rate} + /> + ); + }, + }, + { + title: 'phone', + dataIndex: 'phone', + hideInSearch: true, + render: (_, record) => record?.billing.phone || record?.shipping.phone, + }, + { + title: 'state', + dataIndex: 'state', + render: (_, record) => record?.billing.state || record?.shipping.state, + }, + { + title: 'city', + dataIndex: 'city', + hideInSearch: true, + render: (_, record) => record?.billing.city || record?.shipping.city, + }, + { + title: '标签', + dataIndex: 'tags', + render: (_, record) => { + return ( + + {(record.tags || []).map((tag) => { + return ( + { + const { success, message: msg } = + await customercontrollerDeltag({ + email: record.email, + tag, + }); + return false; + }} + > + {tag} + + ); + })} + + ); + }, + }, + { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + render: (_, record) => { + return ( + + + + + ); + }, + }, + ]; + return ( + + { + const key = Object.keys(sorter)[0]; + const { data, success } = await customercontrollerGetcustomerlist({ + ...params, + ...(key ? { sorterKey: key, sorterValue: sorter[key] } : {}), + }); + + return { + total: data?.total || 0, + data: data?.items || [], + success, + }; + }} + columns={columns} + /> + + ); +}; + +export const AddTag: React.FC<{ + email: string; + tags?: string[]; + tableRef: React.MutableRefObject; +}> = ({ email, tags, tableRef }) => { + const { message } = App.useApp(); + const [tagList, setTagList] = useState([]); + + return ( + 修改标签} + width={800} + modalProps={{ + destroyOnHidden: true, + }} + submitter={false} + > + { + const { data, success } = await customercontrollerGettags(); + if (!success) return []; + setTagList(tags || []); + return data + .filter((tag) => { + return !(tags || []).includes(tag); + }) + .map((tag) => ({ label: tag, value: tag })); + }} + fieldProps={{ + value: tagList, // 当前值 + onChange: async (newValue) => { + const added = newValue.filter((x) => !tagList.includes(x)); + const removed = tagList.filter((x) => !newValue.includes(x)); + + if (added.length) { + const { success, message: msg } = await customercontrollerAddtag({ + email, + tag: added[0], + }); + if (!success) { + message.error(msg); + return; + } + } + if (removed.length) { + const { success, message: msg } = await customercontrollerDeltag({ + email, + tag: removed[0], + }); + if (!success) { + message.error(msg); + return; + } + } + tableRef?.current?.reload(); + + setTagList(newValue); + }, + }} + > + + ); +}; + +export default ListPage; diff --git a/src/pages/Dict/List/index.tsx b/src/pages/Dict/List/index.tsx index 885e53b..54e96f9 100644 --- a/src/pages/Dict/List/index.tsx +++ b/src/pages/Dict/List/index.tsx @@ -1,3 +1,4 @@ +import * as dictApi from '@/servers/api/dict'; import { UploadOutlined } from '@ant-design/icons'; import { ActionType, @@ -16,7 +17,6 @@ import { message, } from 'antd'; import React, { useEffect, useRef, useState } from 'react'; -import * as dictApi from '@/servers/api/dict'; const { Sider, Content } = Layout; @@ -26,19 +26,21 @@ const DictPage: React.FC = () => { const [loadingDicts, setLoadingDicts] = useState(false); const [searchText, setSearchText] = useState(''); const [selectedDict, setSelectedDict] = useState(null); - + // 添加字典 modal 状态 const [isAddDictModalVisible, setIsAddDictModalVisible] = useState(false); const [addDictName, setAddDictName] = useState(''); const [addDictTitle, setAddDictTitle] = useState(''); - + // 编辑字典 modal 状态 const [isEditDictModalVisible, setIsEditDictModalVisible] = useState(false); const [editDictData, setEditDictData] = useState(null); // 右侧字典项列表的状态 - const [isAddDictItemModalVisible, setIsAddDictItemModalVisible] = useState(false); - const [isEditDictItemModalVisible, setIsEditDictItemModalVisible] = useState(false); + const [isAddDictItemModalVisible, setIsAddDictItemModalVisible] = + useState(false); + const [isEditDictItemModalVisible, setIsEditDictItemModalVisible] = + useState(false); const [editDictItemData, setEditDictItemData] = useState(null); const [dictItemForm] = Form.useForm(); const actionRef = useRef(); @@ -95,15 +97,15 @@ const DictPage: React.FC = () => { const handleDeleteDict = async (id: number) => { try { const result = await dictApi.dictcontrollerDeletedict({ id }); - if(!result.success){ - throw new Error(result.message || '删除失败') + if (!result.success) { + throw new Error(result.message || '删除失败'); } message.success('删除成功'); fetchDicts(); if (selectedDict?.id === id) { setSelectedDict(null); } - } catch (error:any) { + } catch (error: any) { message.error(`删除失败,原因为:${error.message}`); } }; @@ -157,16 +159,16 @@ const DictPage: React.FC = () => { const handleDeleteDictItem = async (id: number) => { try { const result = await dictApi.dictcontrollerDeletedictitem({ id }); - if(!result.success){ - throw new Error(result.message || '删除失败') + if (!result.success) { + throw new Error(result.message || '删除失败'); } message.success('删除成功'); - + // 强制刷新字典项列表 setTimeout(() => { actionRef.current?.reload(); }, 100); - } catch (error:any) { + } catch (error: any) { message.error(`删除失败,原因为:${error.message}`); } }; @@ -174,18 +176,18 @@ const DictPage: React.FC = () => { // 添加字典项表单提交 const handleAddDictItemFormSubmit = async (values: any) => { try { - const result = await dictApi.dictcontrollerCreatedictitem({ - ...values, - dictId: selectedDict.id + 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(); @@ -199,16 +201,19 @@ const DictPage: React.FC = () => { const handleEditDictItemFormSubmit = async (values: any) => { if (!editDictItemData) return; try { - const result = await dictApi.dictcontrollerUpdatedictitem({ id: editDictItemData.id }, values); - + 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(); @@ -227,7 +232,9 @@ const DictPage: React.FC = () => { try { // 获取当前字典的所有数据 - const response = await dictApi.dictcontrollerGetdictitems({ dictId: selectedDict.id }); + const response = await dictApi.dictcontrollerGetdictitems({ + dictId: selectedDict.id, + }); if (!response || response.length === 0) { message.warning('当前字典没有数据可导出'); @@ -390,13 +397,13 @@ const DictPage: React.FC = () => { allowClear /> - + { - +
{ name, title, }); - + // 适配新的响应格式,检查是否有 successResponse 包裹 if (res && res.success !== undefined) { return { @@ -468,7 +475,7 @@ const DictPage: React.FC = () => { total: res.data?.length || 0, }; } - + // 兼容旧的响应格式(直接返回数组) return { data: res || [], @@ -503,10 +510,11 @@ const DictPage: React.FC = () => { customRequest={async (options) => { const { file, onSuccess, onError } = options; try { - const result = await dictApi.dictcontrollerImportdictitems( - { dictId: selectedDict?.id }, - [file as File] - ); + const result = + await dictApi.dictcontrollerImportdictitems( + { dictId: selectedDict?.id }, + [file as File], + ); onSuccess?.(result); } catch (error) { onError?.(error as Error); @@ -515,7 +523,7 @@ const DictPage: React.FC = () => { showUploadList={false} disabled={!selectedDict} onChange={(info) => { - console.log(`info`,info) + console.log(`info`, info); if (info.file.status === 'done') { message.success(`${info.file.name} 文件上传成功`); // 重新加载字典项列表 @@ -677,14 +685,18 @@ const DictPage: React.FC = () => { setEditDictData({ ...editDictData, name: e.target.value })} + onChange={(e) => + setEditDictData({ ...editDictData, name: e.target.value }) + } /> setEditDictData({ ...editDictData, title: e.target.value })} + onChange={(e) => + setEditDictData({ ...editDictData, title: e.target.value }) + } /> diff --git a/src/pages/Order/List/index.tsx b/src/pages/Order/List/index.tsx index 5c7c5d3..9b323d3 100644 --- a/src/pages/Order/List/index.tsx +++ b/src/pages/Order/List/index.tsx @@ -456,7 +456,6 @@ const ListPage: React.FC = () => { selectedRowKeys, onChange: (keys) => setSelectedRowKeys(keys), }} - rowClassName={(record) => { return record.id === activeLine ? styles['selected-line-order-protable'] @@ -485,9 +484,9 @@ const ListPage: React.FC = () => { }} tableRef={actionRef} />, - // - + , ]} request={async ({ date, ...param }: any) => { if (param.status === 'all') { @@ -608,33 +607,33 @@ const Detail: React.FC<{ ) ? [] : [ - , - , - ]), + }} + > + 同步订单 + , + ]), // ...(['processing', 'pending_reshipment'].includes(record.orderStatus) // ? [ // , @@ -653,152 +652,152 @@ const Detail: React.FC<{ 'pending_refund', ].includes(record.orderStatus) ? [ - , - { - try { - if (!record.id) { - message.error('订单ID不存在'); - return; + , + { + try { + if (!record.id) { + message.error('订单ID不存在'); + return; + } + const { success, message: errMsg } = + await ordercontrollerChangestatus( + { + id: record.id, + }, + { + status: 'after_sale_pending', + }, + ); + if (!success) { + throw new Error(errMsg); + } + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); } - const { success, message: errMsg } = - await ordercontrollerChangestatus( - { - id: record.id, - }, - { - status: 'after_sale_pending', - }, - ); - if (!success) { - throw new Error(errMsg); - } - tableRef.current?.reload(); - } catch (error: any) { - message.error(error.message); - } - }} - > - - , - ] + }} + > + + , + ] : []), ...(record.orderStatus === 'after_sale_pending' ? [ - , - { - try { - if (!record.id) { - message.error('订单ID不存在'); - return; - } - const { success, message: errMsg } = - await ordercontrollerCancelorder({ - id: record.id, - }); - if (!success) { - throw new Error(errMsg); - } - tableRef.current?.reload(); - } catch (error: any) { - message.error(error.message); - } - }} - > - - , - , - { - try { - if (!record.id) { - message.error('订单ID不存在'); - return; - } - const { success, message: errMsg } = - await ordercontrollerRefundorder({ - id: record.id, - }); - if (!success) { - throw new Error(errMsg); - } - tableRef.current?.reload(); - } catch (error: any) { - message.error(error.message); - } - }} - > - - , - , - { - try { - if (!record.id) { - message.error('订单ID不存在'); - return; - } - const { success, message: errMsg } = - await ordercontrollerCompletedorder({ - id: record.id, - }); - if (!success) { - throw new Error(errMsg); - } - tableRef.current?.reload(); - } catch (error: any) { - message.error(error.message); - } - }} - > - - , - , - { - try { - const { success, message: errMsg } = - await ordercontrollerChangestatus( - { + , + { + try { + if (!record.id) { + message.error('订单ID不存在'); + return; + } + const { success, message: errMsg } = + await ordercontrollerCancelorder({ id: record.id, - }, - { - status: 'pending_reshipment', - }, - ); - if (!success) { - throw new Error(errMsg); + }); + if (!success) { + throw new Error(errMsg); + } + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); } - tableRef.current?.reload(); - } catch (error: any) { - message.error(error.message); - } - }} - > - - , - ] + }} + > + + , + , + { + try { + if (!record.id) { + message.error('订单ID不存在'); + return; + } + const { success, message: errMsg } = + await ordercontrollerRefundorder({ + id: record.id, + }); + if (!success) { + throw new Error(errMsg); + } + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); + } + }} + > + + , + , + { + try { + if (!record.id) { + message.error('订单ID不存在'); + return; + } + const { success, message: errMsg } = + await ordercontrollerCompletedorder({ + id: record.id, + }); + if (!success) { + throw new Error(errMsg); + } + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); + } + }} + > + + , + , + { + try { + const { success, message: errMsg } = + await ordercontrollerChangestatus( + { + id: record.id, + }, + { + status: 'pending_reshipment', + }, + ); + if (!success) { + throw new Error(errMsg); + } + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); + } + }} + > + + , + ] : []), ]} > @@ -1060,31 +1059,31 @@ const Detail: React.FC<{ } actions={ v.state === 'waiting-for-scheduling' || - v.state === 'waiting-for-transit' + v.state === 'waiting-for-transit' ? [ - { - try { - const { success, message: errMsg } = - await logisticscontrollerDelshipment({ - id: v.id, - }); - if (!success) { - throw new Error(errMsg); + { + try { + const { success, message: errMsg } = + await logisticscontrollerDelshipment({ + id: v.id, + }); + if (!success) { + throw new Error(errMsg); + } + tableRef.current?.reload(); + initRequest(); + } catch (error: any) { + message.error(error.message); } - tableRef.current?.reload(); - initRequest(); - } catch (error: any) { - message.error(error.message); - } - }} - > - - 取消运单 - , - ] + }} + > + + 取消运单 + , + ] : [] } > @@ -1472,16 +1471,16 @@ const Shipping: React.FC<{ - // value && value.length > 0 - // ? Promise.resolve() - // : Promise.reject('至少需要一个商品'), - // }, - // ]} + // rules={[ + // { + // required: true, + // message: '至少需要一个商品', + // validator: (_, value) => + // value && value.length > 0 + // ? Promise.resolve() + // : Promise.reject('至少需要一个商品'), + // }, + // ]} > diff --git a/src/pages/Product/Attribute/consts.ts b/src/pages/Product/Attribute/consts.ts index 53bc40b..b7c66ac 100644 --- a/src/pages/Product/Attribute/consts.ts +++ b/src/pages/Product/Attribute/consts.ts @@ -1,5 +1 @@ -export const notAttributes = new Set([ - 'zh-cn', - 'en-us', - 'category' -]); \ No newline at end of file +export const notAttributes = new Set(['zh-cn', 'en-us', 'category']); diff --git a/src/pages/Product/Attribute/index.tsx b/src/pages/Product/Attribute/index.tsx index 3fb99f2..f7cce34 100644 --- a/src/pages/Product/Attribute/index.tsx +++ b/src/pages/Product/Attribute/index.tsx @@ -41,11 +41,14 @@ const AttributePage: React.FC = () => { setLoadingDicts(true); try { const res = await request('/dict/list', { params: { title } }); - // 条件判断,过滤只保留 allowedDictNames 中的字典 - const filtered = (res || []).filter((d: any) => !notAttributes.has(d?.name)); + // 条件判断,确保res是数组再进行过滤 + const dataList = Array.isArray(res) ? res : res?.data || []; + const filtered = dataList.filter((d: any) => !notAttributes.has(d?.name)); setDicts(filtered); } catch (error) { + console.error('获取字典列表失败:', error); message.error('获取字典列表失败'); + setDicts([]); } setLoadingDicts(false); }; @@ -114,16 +117,23 @@ const AttributePage: React.FC = () => { return; } if (selectedDict?.id) { - const list = await request('/dict/items', { - params: { - dictId: selectedDict.id, - }, - }); - const exists = - Array.isArray(list) && list.some((it: any) => it.id === itemId); - if (exists) { - message.error('删除失败'); - } else { + try { + const list = await request('/dict/items', { + params: { + dictId: selectedDict.id, + }, + }); + // 确保list是数组再进行some操作 + const dataList = Array.isArray(list) ? list : list?.data || []; + const exists = dataList.some((it: any) => it.id === itemId); + if (exists) { + message.error('删除失败'); + } else { + message.success('删除成功'); + actionRef.current?.reload(); + } + } catch (error) { + console.error('验证删除结果失败:', error); message.success('删除成功'); actionRef.current?.reload(); } @@ -245,24 +255,44 @@ const AttributePage: React.FC = () => { }; } const { name, title } = params; - const res = await request('/dict/items', { - params: { - dictId: selectedDict.id, - name, - title, - }, - }); - return { - data: res, - success: true, - }; + try { + const res = await request('/dict/items', { + params: { + dictId: selectedDict.id, + name, + title, + }, + }); + // 确保返回的是数组 + const data = Array.isArray(res) ? res : res?.data || []; + return { + data: data, + success: true, + }; + } catch (error) { + console.error('获取字典项失败:', error); + return { + data: [], + success: false, + }; + } }} rowKey="id" search={{ layout: 'vertical', }} pagination={false} - options={false} + options={{ + reload: true, + density: false, + setting: { + draggable: true, + checkable: true, + checkedReset: false, + }, + search: false, + fullScreen: false, + }} size="small" key={selectedDict?.id} headerTitle={ diff --git a/src/pages/Product/Category/index.tsx b/src/pages/Product/Category/index.tsx index 587f8bd..7c516cf 100644 --- a/src/pages/Product/Category/index.tsx +++ b/src/pages/Product/Category/index.tsx @@ -244,7 +244,10 @@ const CategoryPage: React.FC = () => { , ]} > - + )} /> diff --git a/src/pages/Product/List/index.tsx b/src/pages/Product/List/index.tsx index e29d712..d0db50d 100644 --- a/src/pages/Product/List/index.tsx +++ b/src/pages/Product/List/index.tsx @@ -609,7 +609,7 @@ const List: React.FC = () => { toolBarRender={() => [ // 新建按钮 , - // 导入 CSV(使用 customRequest 以支持 request 拦截器和鉴权) + // 导入 CSV(使用 customRequest 以支持 request 拦截器和鉴权) { // 2. Fetch Attribute Values (Dict Items) const valuesMap: Record = {}; for (const attr of attrs) { - const dictId = attr.dict?.id || attr.dictId; - if (dictId) { - const itemsRes = await request('/dict/items', { - params: { dictId }, - }); - valuesMap[attr.name] = itemsRes || []; + // 使用属性中直接包含的items,而不是额外请求 + if (attr.items && Array.isArray(attr.items)) { + valuesMap[attr.name] = attr.items; + } else { + // 如果没有items,尝试通过dictId获取 + const dictId = attr.dict?.id || attr.dictId; + if (dictId) { + const itemsRes = await request('/dict/items', { + params: { dictId }, + }); + valuesMap[attr.name] = itemsRes || []; + } } } setAttributeValues(valuesMap); diff --git a/src/pages/Product/Sync/index.tsx b/src/pages/Product/Sync/index.tsx index 5477df1..bd6c9ae 100644 --- a/src/pages/Product/Sync/index.tsx +++ b/src/pages/Product/Sync/index.tsx @@ -9,7 +9,16 @@ import { ProTable, } from '@ant-design/pro-components'; import { request } from '@umijs/max'; -import { Button, Card, Spin, Tag, message, Select, Progress, Modal } from 'antd'; +import { + Button, + Card, + message, + Modal, + Progress, + Select, + Spin, + Tag, +} from 'antd'; import React, { useEffect, useRef, useState } from 'react'; import EditForm from '../List/EditForm'; @@ -89,7 +98,13 @@ const ProductSyncPage: React.FC = () => { const [batchSyncModalVisible, setBatchSyncModalVisible] = useState(false); const [syncProgress, setSyncProgress] = useState(0); const [syncing, setSyncing] = useState(false); - const [syncResults, setSyncResults] = useState<{ success: number; failed: number; errors: string[] }>({ success: 0, failed: 0, errors: [] }); + const [syncResults, setSyncResults] = useState<{ + success: number; + failed: number; + errors: string[]; + }>({ success: 0, failed: 0, errors: [] }); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [selectedRows, setSelectedRows] = useState([]); // 初始化数据:获取站点和所有 WP 产品 useEffect(() => { @@ -197,41 +212,51 @@ const ProductSyncPage: React.FC = () => { }; // 批量同步产品到指定站点 - const batchSyncProducts = async () => { + const batchSyncProducts = async (productsToSync?: ProductWithWP[]) => { if (!selectedSiteId) { message.error('请选择要同步到的站点'); return; } - const targetSite = sites.find(site => site.id === selectedSiteId); + const targetSite = sites.find((site) => site.id === selectedSiteId); if (!targetSite) { message.error('选择的站点不存在'); return; } + // 如果没有传入产品列表,则使用选中的产品 + let products = productsToSync || selectedRows; + + // 如果既没有传入产品也没有选中产品,则同步所有产品 + if (!products || products.length === 0) { + try { + const { data, success } = await productcontrollerGetproductlist({ + current: 1, + pageSize: 10000, // 获取所有产品 + } as any); + + if (!success || !data?.items) { + message.error('获取产品列表失败'); + return; + } + products = data.items as ProductWithWP[]; + } catch (error) { + message.error('获取产品列表失败'); + return; + } + } + setSyncing(true); setSyncProgress(0); setSyncResults({ success: 0, failed: 0, errors: [] }); + const totalProducts = products.length; + let processed = 0; + let successCount = 0; + let failedCount = 0; + const errors: string[] = []; + try { - // 获取所有产品 - const { data, success } = await productcontrollerGetproductlist({ - current: 1, - pageSize: 10000, // 获取所有产品 - } as any); - - if (!success || !data?.items) { - message.error('获取产品列表失败'); - return; - } - - const products = data.items as ProductWithWP[]; - const totalProducts = products.length; - let processed = 0; - let successCount = 0; - let failedCount = 0; - const errors: string[] = []; - // 逐个同步产品 for (const product of products) { try { @@ -239,7 +264,10 @@ const ProductSyncPage: React.FC = () => { let siteProductSku = ''; if (product.siteSkus && product.siteSkus.length > 0) { const siteSkuInfo = product.siteSkus.find((sku: any) => { - return sku.siteSku && sku.siteSku.includes(targetSite.skuPrefix || targetSite.name); + return ( + sku.siteSku && + sku.siteSku.includes(targetSite.skuPrefix || targetSite.name) + ); }); if (siteSkuInfo) { siteProductSku = siteSkuInfo.siteSku; @@ -247,15 +275,15 @@ const ProductSyncPage: React.FC = () => { } // 如果没有找到实际的siteSku,则根据模板生成 - const expectedSku = siteProductSku || ( - skuTemplate + const expectedSku = + siteProductSku || + (skuTemplate ? renderSiteSku(skuTemplate, { site: targetSite, product }) - : `${targetSite.skuPrefix || ''}-${product.sku}` - ); + : `${targetSite.skuPrefix || ''}-${product.sku}`); // 检查是否已存在 const existingProduct = wpProductMap.get(expectedSku); - + // 准备同步数据 const syncData = { name: product.name, @@ -272,10 +300,13 @@ const ProductSyncPage: React.FC = () => { let res; if (existingProduct?.externalProductId) { // 更新现有产品 - res = await request(`/site-api/${targetSite.id}/products/${existingProduct.externalProductId}`, { - method: 'PUT', - data: syncData, - }); + res = await request( + `/site-api/${targetSite.id}/products/${existingProduct.externalProductId}`, + { + method: 'PUT', + data: syncData, + }, + ); } else { // 创建新产品 res = await request(`/site-api/${targetSite.id}/products`, { @@ -300,7 +331,6 @@ const ProductSyncPage: React.FC = () => { failedCount++; errors.push(`产品 ${product.sku}: ${res.message || '同步失败'}`); } - } catch (error: any) { failedCount++; errors.push(`产品 ${product.sku}: ${error.message || '未知错误'}`); @@ -311,16 +341,17 @@ const ProductSyncPage: React.FC = () => { } setSyncResults({ success: successCount, failed: failedCount, errors }); - + if (failedCount === 0) { message.success(`批量同步完成,成功同步 ${successCount} 个产品`); } else { - message.warning(`批量同步完成,成功 ${successCount} 个,失败 ${failedCount} 个`); + message.warning( + `批量同步完成,成功 ${successCount} 个,失败 ${failedCount} 个`, + ); } // 刷新表格 actionRef.current?.reload(); - } catch (error: any) { message.error('批量同步失败: ' + (error.message || error.toString())); } finally { @@ -453,7 +484,9 @@ const ProductSyncPage: React.FC = () => { const siteSkuInfo = record.siteSkus.find((sku: any) => { // 这里假设可以根据站点名称或其他标识来匹配 // 如果需要更精确的匹配逻辑,可以根据实际需求调整 - return sku.siteSku && sku.siteSku.includes(site.skuPrefix || site.name); + return ( + sku.siteSku && sku.siteSku.includes(site.skuPrefix || site.name) + ); }); if (siteSkuInfo) { siteProductSku = siteSkuInfo.siteSku; @@ -461,18 +494,21 @@ const ProductSyncPage: React.FC = () => { } // 如果没有找到实际的siteSku,则根据模板或默认规则生成期望的SKU - const expectedSku = siteProductSku || ( - skuTemplate + const expectedSku = + siteProductSku || + (skuTemplate ? renderSiteSku(skuTemplate, { site, product: record }) - : `${site.skuPrefix || ''}-${record.sku}` - ); + : `${site.skuPrefix || ''}-${record.sku}`); // 尝试用确定的SKU获取WP产品 let wpProduct = wpProductMap.get(expectedSku); // 如果根据实际SKU没找到,再尝试用模板生成的SKU查找 if (!wpProduct && siteProductSku && skuTemplate) { - const templateSku = renderSiteSku(skuTemplate, { site, product: record }); + const templateSku = renderSiteSku(skuTemplate, { + site, + product: record, + }); wpProduct = wpProductMap.get(templateSku); } @@ -490,12 +526,12 @@ const ProductSyncPage: React.FC = () => { return await syncProductToSite(values, record, site); }} initialValues={{ - sku: siteProductSku || ( - skuTemplate - ? renderSiteSku(skuTemplate, { site, product: record }) - : `${site.skuPrefix || ''}-${record.sku}` - ), - }} + sku: + siteProductSku || + (skuTemplate + ? renderSiteSku(skuTemplate, { site, product: record }) + : `${site.skuPrefix || ''}-${record.sku}`), + }} > { } return ( - - ({ + label: site.name, + value: site.id, + }))} + />, + , + ]} request={async (params, sort, filter) => { // 调用本地获取产品列表 API const { data, success } = await productcontrollerGetproductlist({ @@ -650,7 +696,7 @@ const ProductSyncPage: React.FC = () => { }} dateFormatter="string" /> - + {/* 批量同步模态框 */} { maskClosable={!syncing} >
-

目标站点:{sites.find(s => s.id === selectedSiteId)?.name}

-

此操作将同步所有库存产品到指定站点,请确认是否继续?

+

+ 目标站点: + {sites.find((s) => s.id === selectedSiteId)?.name} +

+ {selectedRows.length > 0 ? ( +

+ 已选择 {selectedRows.length} 个产品进行同步 +

+ ) : ( +

此操作将同步所有库存产品到指定站点,请确认是否继续?

+ )}
- + {syncing && (
同步进度:
@@ -674,32 +729,37 @@ const ProductSyncPage: React.FC = () => {
)} - + {syncResults.errors.length > 0 && (
错误详情:
{syncResults.errors.slice(0, 10).map((error, index) => ( -
+
{error}
))} {syncResults.errors.length > 10 && ( -
...还有 {syncResults.errors.length - 10} 个错误
+
+ ...还有 {syncResults.errors.length - 10} 个错误 +
)}
)} - +
- -
{ // 获取链接列表的函数 const fetchLinks = async () => { if (!siteId) return; - + setLoading(true); try { const response = await request(`/site-api/${siteId}/links`); @@ -48,10 +47,7 @@ const LinksPage: React.FC = () => { return (
- + { target="_blank" > 访问 - + , ]} > { ); }; -export default LinksPage; \ No newline at end of file +export default LinksPage; diff --git a/src/pages/Site/Shop/Orders/index.tsx b/src/pages/Site/Shop/Orders/index.tsx index 4eaeb05..e92ac31 100644 --- a/src/pages/Site/Shop/Orders/index.tsx +++ b/src/pages/Site/Shop/Orders/index.tsx @@ -208,30 +208,32 @@ const OrdersPage: React.FC = () => { > 发货 - { - 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 || '取消发货失败'); + {record.status === 'completed' && ( + { + 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('取消发货失败'); } - } catch (e) { - message.error('取消发货失败'); - } - }} - > - - + }} + > + + + )} { diff --git a/src/pages/Site/Shop/Products/index.tsx b/src/pages/Site/Shop/Products/index.tsx index fbaeebb..aa243f2 100644 --- a/src/pages/Site/Shop/Products/index.tsx +++ b/src/pages/Site/Shop/Products/index.tsx @@ -188,9 +188,9 @@ const ProductsPage: React.FC = () => { 分类: {record.erpProduct.category.name}
)} -
- 库存: {record.erpProduct.stock_quantity ?? '-'} -
+
+ 库存: {record.erpProduct.stock_quantity ?? '-'} +
); } diff --git a/src/pages/Site/Shop/Reviews/ReviewForm.tsx b/src/pages/Site/Shop/Reviews/ReviewForm.tsx index 24a062f..3f38d4d 100644 --- a/src/pages/Site/Shop/Reviews/ReviewForm.tsx +++ b/src/pages/Site/Shop/Reviews/ReviewForm.tsx @@ -1,16 +1,9 @@ -import React, { useEffect } from 'react'; -import { - Modal, - Form, - Input, - InputNumber, - Select, - message, -} from 'antd'; import { siteapicontrollerCreatereview, siteapicontrollerUpdatereview, } from '@/servers/api/siteApi'; +import { Form, Input, InputNumber, Modal, Select, message } from 'antd'; +import React, { useEffect } from 'react'; const { TextArea } = Input; const { Option } = Select; @@ -52,7 +45,7 @@ const ReviewForm: React.FC = ({ const handleSubmit = async (values: any) => { try { let response; - + if (editing) { // 更新评论 response = await siteapicontrollerUpdatereview( @@ -64,7 +57,7 @@ const ReviewForm: React.FC = ({ review: values.content, rating: values.rating, status: values.status, - } + }, ); } else { // 创建新评论 @@ -78,7 +71,7 @@ const ReviewForm: React.FC = ({ rating: values.rating, author: values.author, author_email: values.email, - } + }, ); } @@ -136,27 +129,27 @@ const ReviewForm: React.FC = ({ label="邮箱" rules={[ { required: true, message: '请输入邮箱' }, - { type: 'email', message: '请输入有效的邮箱地址' } + { type: 'email', message: '请输入有效的邮箱地址' }, ]} > )} - + -