From a2864713d6475e7a336ff6c68e323c82133fb209 Mon Sep 17 00:00:00 2001 From: iven Date: Tue, 26 May 2026 10:04:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E7=A7=AF=E5=88=86=E5=95=86?= =?UTF-8?q?=E5=93=81=E5=9B=BE=E7=89=87=E9=80=89=E6=8B=A9=E5=99=A8=20?= =?UTF-8?q?=E2=80=94=20=E5=AA=92=E4=BD=93=E5=BA=93=20+=20=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=9B=BF=E4=BB=A3=E6=89=8B=E5=8A=A8=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PointsProductList: 图片字段改为 Input + 媒体库按钮 + 上传按钮 + 图片预览 - DrawerForm: 新增可选 form prop,允许外部控制表单实例 --- apps/web/src/components/DrawerForm.tsx | 5 +- .../src/pages/health/PointsProductList.tsx | 68 ++++++++++++++++++- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/DrawerForm.tsx b/apps/web/src/components/DrawerForm.tsx index d07db5f..9ca1635 100644 --- a/apps/web/src/components/DrawerForm.tsx +++ b/apps/web/src/components/DrawerForm.tsx @@ -19,6 +19,7 @@ interface DrawerFormProps { sections?: FormSection[]; children?: React.ReactNode; columns?: 1 | 2; + form?: ReturnType[0]; } export function DrawerForm({ @@ -32,8 +33,10 @@ export function DrawerForm({ sections, children, columns = 2, + form: externalForm, }: DrawerFormProps) { - const [form] = Form.useForm(); + const [internalForm] = Form.useForm(); + const form = externalForm ?? internalForm; const isDark = useThemeMode(); React.useEffect(() => { diff --git a/apps/web/src/pages/health/PointsProductList.tsx b/apps/web/src/pages/health/PointsProductList.tsx index 8d80f2f..c9330b0 100644 --- a/apps/web/src/pages/health/PointsProductList.tsx +++ b/apps/web/src/pages/health/PointsProductList.tsx @@ -11,12 +11,15 @@ import { Tag, Badge, Switch, + Upload, message, } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined, + PictureOutlined, + UploadOutlined, } from '@ant-design/icons'; import { pointsApi, @@ -26,8 +29,11 @@ import { import { AuthButton } from '../../components/AuthButton'; import { DrawerForm } from '../../components/DrawerForm'; import type { FormSection } from '../../components/DrawerForm'; +import MediaPicker from '../../components/MediaPicker'; import { PageContainer } from '../../components/PageContainer'; +import { uploadFile } from '../../api/upload'; import { usePaginatedData } from '../../hooks/usePaginatedData'; +import { useThemeMode } from '../../hooks/useThemeMode'; import { formatDateTime } from '../../utils/format'; /** 商品类型映射 */ @@ -59,6 +65,9 @@ interface ProductFilters { export default function PointsProductList() { const [modalOpen, setModalOpen] = useState(false); const [editing, setEditing] = useState(null); + const [mediaPickerOpen, setMediaPickerOpen] = useState(false); + const [form] = Form.useForm(); + const isDark = useThemeMode(); const fetchProducts = useCallback( async (page: number, pageSize: number, filters: ProductFilters) => { @@ -309,8 +318,53 @@ export default function PointsProductList() { title: '展示设置', fields: ( <> - - + + + + + { + try { + const result = await uploadFile(file); + form.setFieldValue('image_url', result.url); + message.success('图片上传成功'); + } catch { + message.error('图片上传失败'); + } + return false; + }} + > + + + + + prev.image_url !== cur.image_url}> + {({ getFieldValue }) => { + const url: string | undefined = getFieldValue('image_url'); + if (!url) return null; + return ( +
+ 商品图片预览 { (e.target as HTMLImageElement).style.display = 'none'; }} + /> +
+ ); + }}
@@ -402,6 +456,16 @@ export default function PointsProductList() { width={600} columns={2} sections={formSections} + form={form} + /> + + setMediaPickerOpen(false)} + onSelect={(url) => { + form.setFieldValue('image_url', url); + message.success('已选择图片'); + }} /> );