From 2263402cd36ff1bd1da7f90fde0cba7a6792441a Mon Sep 17 00:00:00 2001 From: Joe Date: Fri, 12 Dec 2025 11:44:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- .../TinyMCEEditor/AdvancedEditor.tsx | 2 +- src/components/TinyMCEEditor/config.ts | 2 +- .../components/LinkProductModal.tsx | 185 ++++++++++++++++++ .../FashionTrend/components/UpdateForm.tsx | 46 ++++- src/pages/FashionTrend/index.tsx | 25 ++- .../components/UpdateForm.tsx | 6 - src/services/pop-b2b2c/errorController.ts | 30 +-- .../pop-b2b2c/pbcOrderAddressController.ts | 4 +- src/services/pop-b2b2c/typings.d.ts | 14 +- 10 files changed, 283 insertions(+), 33 deletions(-) create mode 100644 src/pages/FashionTrend/components/LinkProductModal.tsx diff --git a/package.json b/package.json index 5849852..868f297 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@dnd-kit/utilities": "^3.2.2", - "@tinymce/tinymce-react": "^6.2.1", + "@tinymce/tinymce-react": "^6.3.0", "@umijs/route-utils": "^2.2.2", "aliyun-aliplayer": "^2.29.1", "aliyun-upload-vod": "^1.0.6", diff --git a/src/components/TinyMCEEditor/AdvancedEditor.tsx b/src/components/TinyMCEEditor/AdvancedEditor.tsx index 35a712d..d1e7d12 100644 --- a/src/components/TinyMCEEditor/AdvancedEditor.tsx +++ b/src/components/TinyMCEEditor/AdvancedEditor.tsx @@ -147,7 +147,7 @@ const AdvancedEditor: React.FC = ({ const initConfig = { height, - language: 'zh_CN', + language: 'zh-CN', onboarding: false, languageUrl: '/tinymce/langs/zh_CN.js', plugins: customConfig.plugins || [ diff --git a/src/components/TinyMCEEditor/config.ts b/src/components/TinyMCEEditor/config.ts index 617e863..966aec8 100644 --- a/src/components/TinyMCEEditor/config.ts +++ b/src/components/TinyMCEEditor/config.ts @@ -47,7 +47,7 @@ export interface TinyMCEConfig { // 默认配置 export const defaultConfig: TinyMCEConfig = { height: 400, - language: 'zh_CN', + language: 'zh-CN', languageUrl: '/tinymce/langs/zh_CN.js', plugins: [ 'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview', diff --git a/src/pages/FashionTrend/components/LinkProductModal.tsx b/src/pages/FashionTrend/components/LinkProductModal.tsx new file mode 100644 index 0000000..4f102da --- /dev/null +++ b/src/pages/FashionTrend/components/LinkProductModal.tsx @@ -0,0 +1,185 @@ +import { listAdminTreeUsingGet } from '@/services/pop-b2b2c/pbcCategoryController'; +import { + addOrUpdateFashionTrendUsingPost, + fashionTrendDetailForAdminUsingGet, +} from '@/services/pop-b2b2c/pbcFashionTrendController'; +import { getProductPageForAdminUsingPost } from '@/services/pop-b2b2c/pbcProductController'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { ProTable } from '@ant-design/pro-components'; +import { useAccess, useModel } from '@umijs/max'; +import { message, Modal } from 'antd'; +import React, { useEffect, useRef, useState } from 'react'; + +interface LinkProductModalProps { + visible: boolean; + onCancel: () => void; + trendId?: number; + onSuccess: () => void; +} + +const LinkProductModal: React.FC = ({ + visible, + onCancel, + trendId, + onSuccess, +}) => { + const actionRef = useRef(); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [loading, setLoading] = useState(false); + const [detailInfo, setDetailInfo] = useState(); + const access: any = useAccess(); + const { initialState } = useModel('@@initialState'); + const { currentUser } = initialState || { currentUser: {} }; + + useEffect(() => { + if (visible && trendId) { + fetchTrendDetail(trendId); + } else { + setSelectedRowKeys([]); + } + }, [visible, trendId]); + + const fetchTrendDetail = async (id: number) => { + try { + const res = await fashionTrendDetailForAdminUsingGet({ pbcId: id }); + if (res.retcode && res.data) { + const productIds = res.data.pbcProductIds + ? res.data.pbcProductIds.split(',').map(Number) + : []; + setSelectedRowKeys(productIds); + setDetailInfo(res.data); + } + } catch (error) { + message.error('获取详情失败'); + } + }; + + const handleOk = async () => { + if (!trendId) return; + setLoading(true); + try { + const pbcProductIds = selectedRowKeys.join(','); + const res = await addOrUpdateFashionTrendUsingPost({ + ...detailInfo, + pbcId: trendId, + pbcProductIds: pbcProductIds, + }); + if (res.retcode) { + message.success('关联成功'); + onSuccess(); + onCancel(); + } else { + message.error(res.retmsg || '关联失败'); + } + } catch (error) { + message.error('关联失败'); + } finally { + setLoading(false); + } + }; + + const columns: ProColumns[] = [ + { + title: '商品ID', + dataIndex: 'pbcId', + search: false, + }, + { + title: '商品大类', + dataIndex: 'pbcProductTopCategoryName', + search: false, + }, + { + title: '商品中类', + dataIndex: 'pbcProductParentCategoryName', + search: false, + }, + { + title: '商品小类', + dataIndex: 'pbcProductCategoryName', + search: false, + }, + { + title: '商品类目', + dataIndex: 'pbcProductCategoryId', + hideInTable: true, + valueType: 'cascader', + fieldProps: { + fieldNames: { label: 'pbcCategoryName', value: 'pbcId', children: 'children' }, + }, + request: async () => { + const msg = await listAdminTreeUsingGet({ type: 2 }); + if (msg.retcode && msg.data) { + return msg.data; + } + return []; + }, + }, + { + title: '商品名称', + dataIndex: 'pbcProductTitle', + }, + { + title: '款号', + dataIndex: 'pbcProductCode', + }, + ]; + + return ( + + + columns={columns} + actionRef={actionRef} + request={async (params) => { + const queryParam: API.PbcProductPageDTO = { + ...params, + startDate: + params.pbcCreateAt && params.pbcCreateAt.length > 1 + ? params.pbcCreateAt[0] + : undefined, + endDate: + params.pbcCreateAt && params.pbcCreateAt.length > 1 + ? params.pbcCreateAt[1] + ' 23:59:59' + : undefined, + }; + if (params.pbcProductCategoryId && params.pbcProductCategoryId.length === 3) { + queryParam.pbcProductCategoryId = params.pbcProductCategoryId[2]; + } + queryParam.pbcUserType = 2; + if (access.isBusiness) { + queryParam.pbcBusinessId = currentUser?.pbcBusinessId; + } + const msg = await getProductPageForAdminUsingPost(queryParam); + return { + data: msg.data?.records || [], + success: !!msg.retcode, + total: msg.data?.total, + }; + }} + rowKey="pbcId" + search={{ + labelWidth: 'auto', + }} + pagination={{ + pageSize: 10, + }} + rowSelection={{ + selectedRowKeys, + onChange: (keys) => setSelectedRowKeys(keys), + preserveSelectedRowKeys: true, + }} + toolBarRender={false} + /> + + ); +}; + +export default LinkProductModal; diff --git a/src/pages/FashionTrend/components/UpdateForm.tsx b/src/pages/FashionTrend/components/UpdateForm.tsx index 9b540fa..7386cdc 100644 --- a/src/pages/FashionTrend/components/UpdateForm.tsx +++ b/src/pages/FashionTrend/components/UpdateForm.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { DrawerForm, ProFormInstance, ProFormText, ProFormUploadButton, ProFormRadio, ProForm } from '@ant-design/pro-components'; -import { message } from 'antd'; +import { message, Input, Button } from 'antd'; +import { PlusOutlined } from '@ant-design/icons'; import Upload, { RcFile } from 'antd/es/upload'; import AdvancedEditor from '@/components/TinyMCEEditor/AdvancedEditor'; import { fullConfig } from '@/components/TinyMCEEditor/config'; @@ -24,11 +25,19 @@ const UpdateForm: React.FC = (props) => { const formRef = useRef(); const [pbcType, setPbcType] = useState(1); const [videoThumbnail, setVideoThumbnail] = useState(""); + const [tags, setTags] = useState([]); useEffect(() => { - setPbcType(props.values.pbcType || 1) - setVideoThumbnail(props.values.pbcThumbNail || "") - }, [props.values.pbcId]); + if (props.updateModalVisible) { + setPbcType(props.values.pbcType || 1) + setVideoThumbnail(props.values.pbcThumbNail || "") + if (props.values.pbcTags) { + setTags(props.values.pbcTags.split(',')); + } else { + setTags([]); + } + } + }, [props.updateModalVisible, props.values]); // 生成视频缩略图的函数 const generateVideoThumbnail = async (videoUrl: string) => { @@ -108,7 +117,8 @@ const UpdateForm: React.FC = (props) => { pbcThumbNail = value.pbcThumbNail[0].url || value.pbcThumbNail[0].response.data; } } - return props.onSubmit({ ...value, pbcPicAddress, pbcThumbNail, pbcContent: value.pbcType === 3 ? '预览文件' : value.pbcContent, pbcId: props.values.pbcId }) + const pbcTags = tags.filter(t => t && t.trim()).join(','); + return props.onSubmit({ ...props.values, ...value, pbcPicAddress, pbcThumbNail, pbcContent: value.pbcType === 3 ? '预览文件' : value.pbcContent, pbcId: props.values.pbcId, pbcTags }) }} drawerProps={{ destroyOnHidden: true, @@ -339,6 +349,32 @@ const UpdateForm: React.FC = (props) => { { required: true, message: `请上传${pbcType === 2 ? '视频' : '文件'}'` }, ]} />} + +
+ {tags.map((tag, index) => ( + { + const newTags = [...tags]; + newTags[index] = e.target.value; + setTags(newTags); + }} + placeholder="请输入标签" + /> + ))} + +
+
{pbcType !== 3 ? = () => { const actionRef = useRef(); const [updateModalVisible, handleUpdateModalVisible] = useState(false); const [stepFormValues, setStepFormValues] = useState({}); + const [linkModalVisible, setLinkModalVisible] = useState(false); + const [currentTrendId, setCurrentTrendId] = useState(); const handleAdd = async (fields: API.PbcFashionTrend_) => { const hide = message.loading('正在提交'); @@ -110,7 +113,7 @@ const TableList: React.FC = () => { { title: '操作', valueType: 'option', - width: 180, + width: 230, render: (text, record) => ( @@ -126,6 +129,18 @@ const TableList: React.FC = () => { }} /> + + +