From 944baf64d02db88a6d5d1a0babd0ac1136630a76 Mon Sep 17 00:00:00 2001 From: tikkhun Date: Mon, 22 Dec 2025 17:33:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E4=BA=A7=E5=93=81=E5=B1=9E=E6=80=A7):=20?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=B1=9E=E6=80=A7=E7=AE=A1=E7=90=86=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E4=BD=BF=E7=94=A8=E6=8E=92=E9=99=A4=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=9B=BF=E4=BB=A3=E5=8C=85=E5=90=AB=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(订单管理): 添加发货和取消发货功能 feat(店铺管理): 新增链接管理和Webhooks管理页面 fix(评论管理): 增强评论列表的数据处理和错误处理 feat(模板管理): 实现模板实时预览功能并优化编辑界面 style(布局): 优化店铺管理侧边栏样式和结构 perf(产品同步): 改进SKU生成逻辑和错误处理 docs(API): 更新API类型定义和注释 --- .umirc.ts | 12 + src/pages/Category/index.tsx | 4 +- src/pages/Dict/List/index.tsx | 415 +++++++++++++------- src/pages/Product/Attribute/consts.ts | 13 +- src/pages/Product/Attribute/index.tsx | 4 +- src/pages/Product/Category/index.tsx | 20 +- src/pages/Product/List/index.tsx | 106 ++--- src/pages/Product/Permutation/index.tsx | 2 +- src/pages/Product/Sync/index.tsx | 10 +- src/pages/Site/Shop/Customers/index.tsx | 8 +- src/pages/Site/Shop/Layout.tsx | 19 +- src/pages/Site/Shop/Links/index.tsx | 103 +++++ src/pages/Site/Shop/Orders/index.tsx | 46 +++ src/pages/Site/Shop/Reviews/ReviewForm.tsx | 183 ++++++++- src/pages/Site/Shop/Reviews/index.tsx | 49 ++- src/pages/Site/Shop/Webhooks/index.tsx | 297 ++++++++++++++ src/pages/Template/index.tsx | 427 ++++++++++++--------- src/servers/api/order.ts | 14 - src/servers/api/siteApi.ts | 172 +++++++++ src/servers/api/template.ts | 15 + src/servers/api/typings.d.ts | 329 ++++++++++++---- 21 files changed, 1721 insertions(+), 527 deletions(-) create mode 100644 src/pages/Site/Shop/Links/index.tsx create mode 100644 src/pages/Site/Shop/Webhooks/index.tsx diff --git a/.umirc.ts b/.umirc.ts index be0e479..171327a 100644 --- a/.umirc.ts +++ b/.umirc.ts @@ -100,6 +100,18 @@ export default defineConfig({ path: '/site/shop/:siteId/customers', component: './Site/Shop/Customers', }, + { + path: '/site/shop/:siteId/reviews', + component: './Site/Shop/Reviews', + }, + { + path: '/site/shop/:siteId/webhooks', + component: './Site/Shop/Webhooks', + }, + { + path: '/site/shop/:siteId/links', + component: './Site/Shop/Links', + }, ], }, { diff --git a/src/pages/Category/index.tsx b/src/pages/Category/index.tsx index 74037ee..9bd852e 100644 --- a/src/pages/Category/index.tsx +++ b/src/pages/Category/index.tsx @@ -23,7 +23,7 @@ import { message, } from 'antd'; import React, { useEffect, useState } from 'react'; -import { attributes } from '../Attribute/consts'; +import { notAttributes } from '../Product/Attribute/consts'; const { Sider, Content } = Layout; @@ -114,7 +114,7 @@ const CategoryPage: React.FC = () => { // Fetch all dicts and filter those that are allowed attributes try { const res = await request('/dict/list'); - const filtered = (res || []).filter((d: any) => attributes.has(d.name)); + const filtered = (res || []).filter((d: any) => !notAttributes.has(d.name)); // Filter out already added attributes const existingDictIds = new Set( categoryAttributes.map((ca: any) => ca.dict.id), diff --git a/src/pages/Dict/List/index.tsx b/src/pages/Dict/List/index.tsx index 6e27a21..885e53b 100644 --- a/src/pages/Dict/List/index.tsx +++ b/src/pages/Dict/List/index.tsx @@ -4,7 +4,6 @@ import { PageContainer, ProTable, } from '@ant-design/pro-components'; -import { request } from '@umijs/max'; import { Button, Form, @@ -17,6 +16,7 @@ import { message, } from 'antd'; import React, { useEffect, useRef, useState } from 'react'; +import * as dictApi from '@/servers/api/dict'; const { Sider, Content } = Layout; @@ -26,14 +26,20 @@ const DictPage: React.FC = () => { const [loadingDicts, setLoadingDicts] = useState(false); const [searchText, setSearchText] = useState(''); const [selectedDict, setSelectedDict] = useState(null); + + // 添加字典 modal 状态 const [isAddDictModalVisible, setIsAddDictModalVisible] = useState(false); - const [editingDict, setEditingDict] = useState(null); - const [newDictName, setNewDictName] = useState(''); - const [newDictTitle, setNewDictTitle] = useState(''); + const [addDictName, setAddDictName] = useState(''); + const [addDictTitle, setAddDictTitle] = useState(''); + + // 编辑字典 modal 状态 + const [isEditDictModalVisible, setIsEditDictModalVisible] = useState(false); + const [editDictData, setEditDictData] = useState(null); // 右侧字典项列表的状态 - const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false); - const [editingDictItem, setEditingDictItem] = useState(null); + const [isAddDictItemModalVisible, setIsAddDictItemModalVisible] = useState(false); + const [isEditDictItemModalVisible, setIsEditDictItemModalVisible] = useState(false); + const [editDictItemData, setEditDictItemData] = useState(null); const [dictItemForm] = Form.useForm(); const actionRef = useRef(); @@ -41,7 +47,7 @@ const DictPage: React.FC = () => { const fetchDicts = async (name = '') => { setLoadingDicts(true); try { - const res = await request('/dict/list', { params: { name } }); + const res = await dictApi.dictcontrollerGetdicts({ name }); setDicts(res); } catch (error) { message.error('获取字典列表失败'); @@ -55,60 +61,66 @@ const DictPage: React.FC = () => { fetchDicts(value); }; - // 添加或编辑字典 + // 添加字典 const handleAddDict = async () => { - const values = { name: newDictName, title: newDictTitle }; + const values = { name: addDictName, title: addDictTitle }; try { - if (editingDict) { - await request(`/dict/${editingDict.id}`, { - method: 'PUT', - data: values, - }); - message.success('更新成功'); - } else { - await request('/dict', { method: 'POST', data: values }); - message.success('添加成功'); - } + await dictApi.dictcontrollerCreatedict(values); + message.success('添加成功'); setIsAddDictModalVisible(false); - setEditingDict(null); - setNewDictName(''); - setNewDictTitle(''); + setAddDictName(''); + setAddDictTitle(''); fetchDicts(); // 重新获取列表 } catch (error) { - message.error(editingDict ? '更新失败' : '添加失败'); + message.error('添加失败'); + } + }; + + // 编辑字典 + const handleEditDict = async () => { + if (!editDictData) return; + const values = { name: editDictData.name, title: editDictData.title }; + try { + await dictApi.dictcontrollerUpdatedict({ id: editDictData.id }, values); + message.success('更新成功'); + setIsEditDictModalVisible(false); + setEditDictData(null); + fetchDicts(); // 重新获取列表 + } catch (error) { + message.error('更新失败'); } }; // 删除字典 const handleDeleteDict = async (id: number) => { try { - await request(`/dict/${id}`, { method: 'DELETE' }); + const result = await dictApi.dictcontrollerDeletedict({ id }); + if(!result.success){ + throw new Error(result.message || '删除失败') + } message.success('删除成功'); fetchDicts(); if (selectedDict?.id === id) { setSelectedDict(null); } - } catch (error) { - message.error('删除失败'); + } catch (error:any) { + message.error(`删除失败,原因为:${error.message}`); } }; - // 编辑字典 - const handleEditDict = (record: any) => { - setEditingDict(record); - setNewDictName(record.name); - setNewDictTitle(record.title); - setIsAddDictModalVisible(true); + // 打开编辑字典 modal + const openEditDictModal = (record: any) => { + setEditDictData(record); + setIsEditDictModalVisible(true); }; // 下载字典导入模板 const handleDownloadDictTemplate = async () => { try { - // 使用 request 函数获取带认证的文件数据 - const response = await request('/dict/template', { - method: 'GET', - responseType: 'blob', // 指定响应类型为 blob - skipErrorHandler: true, // 跳过默认错误处理,自己处理错误 + // 使用 dictApi.dictcontrollerDownloaddicttemplate 获取字典模板 + const response = await dictApi.dictcontrollerDownloaddicttemplate({ + responseType: 'blob', + skipErrorHandler: true, }); // 创建 blob 对象和下载链接 @@ -130,46 +142,79 @@ const DictPage: React.FC = () => { // 添加字典项 const handleAddDictItem = () => { - setEditingDictItem(null); dictItemForm.resetFields(); - setIsDictItemModalVisible(true); + setIsAddDictItemModalVisible(true); }; // 编辑字典项 const handleEditDictItem = (record: any) => { - setEditingDictItem(record); + setEditDictItemData(record); dictItemForm.setFieldsValue(record); - setIsDictItemModalVisible(true); + setIsEditDictItemModalVisible(true); }; // 删除字典项 const handleDeleteDictItem = async (id: number) => { try { - await request(`/dict/item/${id}`, { method: 'DELETE' }); + const result = await dictApi.dictcontrollerDeletedictitem({ id }); + if(!result.success){ + throw new Error(result.message || '删除失败') + } message.success('删除成功'); - actionRef.current?.reload(); - } catch (error) { - message.error('删除失败'); + + // 强制刷新字典项列表 + setTimeout(() => { + actionRef.current?.reload(); + }, 100); + } catch (error:any) { + message.error(`删除失败,原因为:${error.message}`); } }; - // 字典项表单提交 - const handleDictItemFormSubmit = async (values: any) => { - const url = editingDictItem - ? `/dict/item/${editingDictItem.id}` - : '/dict/item'; - const method = editingDictItem ? 'PUT' : 'POST'; - const data = editingDictItem - ? { ...values } - : { ...values, dict: { id: selectedDict.id } }; - + // 添加字典项表单提交 + const handleAddDictItemFormSubmit = async (values: any) => { try { - await request(url, { method, data }); - message.success(editingDictItem ? '更新成功' : '添加成功'); - setIsDictItemModalVisible(false); - actionRef.current?.reload(); - } catch (error) { - message.error(editingDictItem ? '更新失败' : '添加失败'); + const result = await dictApi.dictcontrollerCreatedictitem({ + ...values, + dictId: selectedDict.id + }); + + if (!result.success) { + throw new Error(result.message || '添加失败'); + } + + message.success('添加成功'); + setIsAddDictItemModalVisible(false); + + // 强制刷新字典项列表 + setTimeout(() => { + actionRef.current?.reload(); + }, 100); + } catch (error: any) { + message.error(`添加失败:${error.message || '未知错误'}`); + } + }; + + // 编辑字典项表单提交 + const handleEditDictItemFormSubmit = async (values: any) => { + if (!editDictItemData) return; + try { + const result = await dictApi.dictcontrollerUpdatedictitem({ id: editDictItemData.id }, values); + + if (!result.success) { + throw new Error(result.message || '更新失败'); + } + + message.success('更新成功'); + setIsEditDictItemModalVisible(false); + setEditDictItemData(null); + + // 强制刷新字典项列表 + setTimeout(() => { + actionRef.current?.reload(); + }, 100); + } catch (error: any) { + message.error(`更新失败:${error.message || '未知错误'}`); } }; @@ -182,10 +227,7 @@ const DictPage: React.FC = () => { try { // 获取当前字典的所有数据 - const response = await request('/dict/items', { - method: 'GET', - params: { dictId: selectedDict.id }, - }); + const response = await dictApi.dictcontrollerGetdictitems({ dictId: selectedDict.id }); if (!response || response.length === 0) { message.warning('当前字典没有数据可导出'); @@ -257,7 +299,7 @@ const DictPage: React.FC = () => { @@ -331,7 +373,7 @@ const DictPage: React.FC = () => { { enterButton allowClear /> + - { if (info.file.status === 'done') { @@ -375,7 +417,7 @@ const DictPage: React.FC = () => { - + { -
- - { - if (info.file.status === 'done') { - message.success(`${info.file.name} 文件上传成功`); - actionRef.current?.reload(); - } else if (info.file.status === 'error') { - message.error(`${info.file.name} 文件上传失败`); - } - }} - > - - - -
{ @@ -459,15 +454,24 @@ const DictPage: React.FC = () => { }; } const { name, title } = params; - const res = await request('/dict/items', { - params: { - dictId: selectedDict?.id, - name, - title, - }, + const res = await dictApi.dictcontrollerGetdictitems({ + dictId: selectedDict?.id, + name, + title, }); + + // 适配新的响应格式,检查是否有 successResponse 包裹 + if (res && res.success !== undefined) { + return { + data: res.data || [], + success: res.success, + total: res.data?.length || 0, + }; + } + + // 兼容旧的响应格式(直接返回数组) return { - data: res, + data: res || [], success: true, }; }} @@ -476,25 +480,85 @@ const DictPage: React.FC = () => { layout: 'vertical', }} pagination={false} - options={false} + options={{ + reload: true, + density: true, + setting: true, + }} size="small" key={selectedDict?.id} + toolBarRender={() => [ + , + { + const { file, onSuccess, onError } = options; + try { + const result = await dictApi.dictcontrollerImportdictitems( + { dictId: selectedDict?.id }, + [file as File] + ); + onSuccess?.(result); + } catch (error) { + onError?.(error as Error); + } + }} + showUploadList={false} + disabled={!selectedDict} + onChange={(info) => { + console.log(`info`,info) + if (info.file.status === 'done') { + message.success(`${info.file.name} 文件上传成功`); + // 重新加载字典项列表 + setTimeout(() => { + actionRef.current?.reload(); + }, 100); + // 重新加载字典列表以更新字典项数量 + fetchDicts(); + } else if (info.file.status === 'error') { + message.error(`${info.file.name} 文件上传失败`); + } + }} + key="import" + > + + , + , + ]} />
+ {/* 添加字典项 Modal */} dictItemForm.submit()} - onCancel={() => setIsDictItemModalVisible(false)} - destroyOnClose + onCancel={() => setIsAddDictItemModalVisible(false)} >
{
+ {/* 编辑字典项 Modal */} dictItemForm.submit()} + onCancel={() => { + setIsEditDictItemModalVisible(false); + setEditDictItemData(null); + }} + > +
+ + + + + + + + + + + + + + + + + + + +
+ + {/* 添加字典 Modal */} + setIsAddDictModalVisible(false)} + onCancel={() => { + setIsAddDictModalVisible(false); + setAddDictName(''); + setAddDictTitle(''); + }} >
setNewDictName(e.target.value)} + value={addDictName} + onChange={(e) => setAddDictName(e.target.value)} /> setNewDictTitle(e.target.value)} + value={addDictTitle} + onChange={(e) => setAddDictTitle(e.target.value)} + /> + + +
+ + {/* 编辑字典 Modal */} + { + setIsEditDictModalVisible(false); + setEditDictData(null); + }} + > +
+ + setEditDictData({ ...editDictData, name: e.target.value })} + /> + + + setEditDictData({ ...editDictData, title: e.target.value })} /> diff --git a/src/pages/Product/Attribute/consts.ts b/src/pages/Product/Attribute/consts.ts index 23d769b..53bc40b 100644 --- a/src/pages/Product/Attribute/consts.ts +++ b/src/pages/Product/Attribute/consts.ts @@ -1,8 +1,5 @@ -// 限定允许管理的字典名称集合 -export const attributes = new Set([ - 'brand', - 'strength', - 'flavor', - 'size', - 'humidity', -]); +export const notAttributes = new Set([ + 'zh-cn', + 'en-us', + 'category' +]); \ No newline at end of file diff --git a/src/pages/Product/Attribute/index.tsx b/src/pages/Product/Attribute/index.tsx index cd57d58..3fb99f2 100644 --- a/src/pages/Product/Attribute/index.tsx +++ b/src/pages/Product/Attribute/index.tsx @@ -20,7 +20,7 @@ import React, { useEffect, useRef, useState } from 'react'; const { Sider, Content } = Layout; -import { attributes } from './consts'; +import { notAttributes } from './consts'; const AttributePage: React.FC = () => { // 左侧字典列表状态 @@ -42,7 +42,7 @@ const AttributePage: React.FC = () => { try { const res = await request('/dict/list', { params: { title } }); // 条件判断,过滤只保留 allowedDictNames 中的字典 - const filtered = (res || []).filter((d: any) => attributes.has(d?.name)); + const filtered = (res || []).filter((d: any) => !notAttributes.has(d?.name)); setDicts(filtered); } catch (error) { message.error('获取字典列表失败'); diff --git a/src/pages/Product/Category/index.tsx b/src/pages/Product/Category/index.tsx index 8411b75..587f8bd 100644 --- a/src/pages/Product/Category/index.tsx +++ b/src/pages/Product/Category/index.tsx @@ -23,7 +23,7 @@ import { message, } from 'antd'; import React, { useEffect, useState } from 'react'; -import { attributes } from '../Attribute/consts'; +import { notAttributes } from '../Attribute/consts'; const { Sider, Content } = Layout; @@ -116,7 +116,7 @@ const CategoryPage: React.FC = () => { const res = await request('/dict/list'); // Defensive check for response structure: handle both raw array and wrapped response const list = Array.isArray(res) ? res : res?.data || []; - const filtered = list.filter((d: any) => attributes.has(d.name)); + const filtered = list.filter((d: any) => !notAttributes.has(d.name)); // Filter out already added attributes const existingDictIds = new Set( categoryAttributes.map((ca: any) => ca.dictId), @@ -244,7 +244,7 @@ const CategoryPage: React.FC = () => { , ]} > - + )} /> @@ -310,16 +310,18 @@ const CategoryPage: React.FC = () => { onFinish={handleCategorySubmit} layout="vertical" > - + - + + + + + + +
diff --git a/src/pages/Product/List/index.tsx b/src/pages/Product/List/index.tsx index c74b31c..e29d712 100644 --- a/src/pages/Product/List/index.tsx +++ b/src/pages/Product/List/index.tsx @@ -462,9 +462,9 @@ const List: React.FC = () => { dataIndex: 'siteSkus', render: (_, record) => ( <> - {record.siteSkus?.map((code, index) => ( + {record.siteSkus?.map((siteSku, index) => ( - {code} + {siteSku.siteSku} ))} @@ -609,56 +609,7 @@ const List: React.FC = () => { toolBarRender={() => [ // 新建按钮 , - // 批量编辑按钮 - , - // 批量同步按钮 - , - // 批量删除按钮 - , - // 导出 CSV(后端返回 text/csv,直接新窗口下载) - , - // 导入 CSV(使用 customRequest 以支持 request 拦截器和鉴权) + // 导入 CSV(使用 customRequest 以支持 request 拦截器和鉴权) { } }} > - + , + // 批量编辑按钮 + , + // 批量同步按钮 + , + // 批量删除按钮 + , + // 导出 CSV(后端返回 text/csv,直接新窗口下载) + , ]} request={async (params, sort) => { let sortField = undefined; diff --git a/src/pages/Product/Permutation/index.tsx b/src/pages/Product/Permutation/index.tsx index 2edd26f..993bb5a 100644 --- a/src/pages/Product/Permutation/index.tsx +++ b/src/pages/Product/Permutation/index.tsx @@ -206,7 +206,7 @@ const PermutationPage: React.FC = () => { const valB = b[attr.name]?.name || ''; return valA.localeCompare(valB); }, - filters: attributeValues[attr.name]?.map((v: any) => ({ + filters: attributeValues?.[attr.name]?.map?.((v: any) => ({ text: v.name, value: v.name, })), diff --git a/src/pages/Product/Sync/index.tsx b/src/pages/Product/Sync/index.tsx index 3bfaee7..5477df1 100644 --- a/src/pages/Product/Sync/index.tsx +++ b/src/pages/Product/Sync/index.tsx @@ -249,7 +249,7 @@ const ProductSyncPage: React.FC = () => { // 如果没有找到实际的siteSku,则根据模板生成 const expectedSku = siteProductSku || ( skuTemplate - ? renderSku(skuTemplate, { site: targetSite, product }) + ? renderSiteSku(skuTemplate, { site: targetSite, product }) : `${targetSite.skuPrefix || ''}-${product.sku}` ); @@ -329,7 +329,7 @@ const ProductSyncPage: React.FC = () => { }; // 简单的模板渲染函数 - const renderSku = (template: string, data: any) => { + const renderSiteSku = (template: string, data: any) => { if (!template) return ''; // 支持 <%= it.path %> (Eta) 和 {{ path }} (Mustache/Handlebars) return template.replace( @@ -463,7 +463,7 @@ const ProductSyncPage: React.FC = () => { // 如果没有找到实际的siteSku,则根据模板或默认规则生成期望的SKU const expectedSku = siteProductSku || ( skuTemplate - ? renderSku(skuTemplate, { site, product: record }) + ? renderSiteSku(skuTemplate, { site, product: record }) : `${site.skuPrefix || ''}-${record.sku}` ); @@ -472,7 +472,7 @@ const ProductSyncPage: React.FC = () => { // 如果根据实际SKU没找到,再尝试用模板生成的SKU查找 if (!wpProduct && siteProductSku && skuTemplate) { - const templateSku = renderSku(skuTemplate, { site, product: record }); + const templateSku = renderSiteSku(skuTemplate, { site, product: record }); wpProduct = wpProductMap.get(templateSku); } @@ -492,7 +492,7 @@ const ProductSyncPage: React.FC = () => { initialValues={{ sku: siteProductSku || ( skuTemplate - ? renderSku(skuTemplate, { site, product: record }) + ? renderSiteSku(skuTemplate, { site, product: record }) : `${site.skuPrefix || ''}-${record.sku}` ), }} diff --git a/src/pages/Site/Shop/Customers/index.tsx b/src/pages/Site/Shop/Customers/index.tsx index 5153004..fa82d18 100644 --- a/src/pages/Site/Shop/Customers/index.tsx +++ b/src/pages/Site/Shop/Customers/index.tsx @@ -203,11 +203,17 @@ const CustomerPage: React.FC = () => { }, }, { - title: '注册时间', + title: '创建时间', dataIndex: 'date_created', valueType: 'dateTime', hideInSearch: true, }, + { + title: '更新时间', + dataIndex: 'date_modified', + valueType: 'dateTime', + hideInSearch: true, + }, { title: '操作', valueType: 'option', diff --git a/src/pages/Site/Shop/Layout.tsx b/src/pages/Site/Shop/Layout.tsx index 1099eeb..bf6e5f8 100644 --- a/src/pages/Site/Shop/Layout.tsx +++ b/src/pages/Site/Shop/Layout.tsx @@ -6,6 +6,7 @@ import { Button, Card, Col, Menu, Row, Select, Spin, message } from 'antd'; import React, { useEffect, useState } from 'react'; import type { SiteItem } from '../List/index'; import EditSiteForm from './EditSiteForm'; +import Sider from 'antd/es/layout/Sider'; const ShopLayout: React.FC = () => { const [sites, setSites] = useState([]); @@ -92,20 +93,12 @@ const ShopLayout: React.FC = () => { return ( + -
{ { key: 'media', label: '媒体管理' }, { key: 'customers', label: '客户管理' }, { key: 'reviews', label: '评论管理' }, + { key: 'webhooks', label: 'Webhooks管理' }, + { key: 'links', label: '链接管理' }, ]} />
- +
{siteId ? :
请选择店铺
} diff --git a/src/pages/Site/Shop/Links/index.tsx b/src/pages/Site/Shop/Links/index.tsx new file mode 100644 index 0000000..a447df1 --- /dev/null +++ b/src/pages/Site/Shop/Links/index.tsx @@ -0,0 +1,103 @@ +import { Button, Card, List } from 'antd'; +import { request, useParams } from '@umijs/max'; +import { App } from 'antd'; +import React, { useEffect, useState } from 'react'; +import { LinkOutlined } from '@ant-design/icons'; +import { PageHeader } from '@ant-design/pro-layout'; + +// 定义链接项的类型 +interface LinkItem { + title: string; + url: string; +} + +const LinksPage: React.FC = () => { + const { siteId } = useParams<{ siteId: string }>(); + const { message: antMessage } = App.useApp(); + const [links, setLinks] = useState([]); + const [loading, setLoading] = useState(true); + + // 获取链接列表的函数 + const fetchLinks = async () => { + if (!siteId) return; + + setLoading(true); + try { + const response = await request(`/site-api/${siteId}/links`); + if (response.success && response.data) { + setLinks(response.data); + } else { + antMessage.error(response.message || '获取链接列表失败'); + } + } catch (error) { + antMessage.error('获取链接列表失败'); + } finally { + setLoading(false); + } + }; + + // 页面加载时获取链接列表 + useEffect(() => { + fetchLinks(); + }, [siteId]); + + // 处理链接点击事件,在新标签页打开 + const handleLinkClick = (url: string) => { + window.open(url, '_blank', 'noopener,noreferrer'); + }; + + return ( +
+ + + 刷新列表 + + } + > + ( + } + onClick={() => handleLinkClick(item.url)} + target="_blank" + > + 访问 + + ]} + > + + {item.url} + + } + /> + + )} + /> + +
+ ); +}; + +export default LinksPage; \ No newline at end of file diff --git a/src/pages/Site/Shop/Orders/index.tsx b/src/pages/Site/Shop/Orders/index.tsx index 08b8506..4eaeb05 100644 --- a/src/pages/Site/Shop/Orders/index.tsx +++ b/src/pages/Site/Shop/Orders/index.tsx @@ -186,6 +186,52 @@ 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 || '取消发货失败'); + } + } catch (e) { + message.error('取消发货失败'); + } + }} + > + + { diff --git a/src/pages/Site/Shop/Reviews/ReviewForm.tsx b/src/pages/Site/Shop/Reviews/ReviewForm.tsx index c6e577a..24a062f 100644 --- a/src/pages/Site/Shop/Reviews/ReviewForm.tsx +++ b/src/pages/Site/Shop/Reviews/ReviewForm.tsx @@ -1,4 +1,19 @@ -import React from 'react'; +import React, { useEffect } from 'react'; +import { + Modal, + Form, + Input, + InputNumber, + Select, + message, +} from 'antd'; +import { + siteapicontrollerCreatereview, + siteapicontrollerUpdatereview, +} from '@/servers/api/siteApi'; + +const { TextArea } = Input; +const { Option } = Select; interface ReviewFormProps { open: boolean; @@ -15,20 +30,162 @@ const ReviewForm: React.FC = ({ onClose, onSuccess, }) => { - // // 这是一个临时的占位符组件 - // // 你可以在这里实现表单逻辑 - if (!open) { - return null; - } + const [form] = Form.useForm(); + + // 当编辑状态改变时,重置表单数据 + useEffect(() => { + if (editing) { + form.setFieldsValue({ + product_id: editing.product_id, + author: editing.author, + email: editing.email, + content: editing.content, + rating: editing.rating, + status: editing.status, + }); + } else { + form.resetFields(); + } + }, [editing, form]); + + // 处理表单提交 + const handleSubmit = async (values: any) => { + try { + let response; + + if (editing) { + // 更新评论 + response = await siteapicontrollerUpdatereview( + { + siteId, + id: editing.id, + }, + { + review: values.content, + rating: values.rating, + status: values.status, + } + ); + } else { + // 创建新评论 + response = await siteapicontrollerCreatereview( + { + siteId, + }, + { + product_id: values.product_id, + review: values.content, + rating: values.rating, + author: values.author, + author_email: values.email, + } + ); + } + + if (response.success) { + message.success(editing ? '更新成功' : '创建成功'); + onSuccess(); + onClose(); + form.resetFields(); + } else { + message.error(response.message || '操作失败'); + } + } catch (error) { + console.error('提交评论表单失败:', error); + message.error('提交失败,请重试'); + } + }; return ( -
-

Review Form

-

Site ID: {siteId}

-

Editing: {editing ? 'Yes' : 'No'}

- -
+ form.submit()} + okText="保存" + cancelText="取消" + width={600} + > +
+ {!editing && ( + <> + + + + + + + + + + + )} + + +