From 7260b089e97d6ef58ced7e984cf07d985276cf1f Mon Sep 17 00:00:00 2001 From: shenyifei Date: Wed, 7 Jan 2026 11:25:50 +0800 Subject: [PATCH] =?UTF-8?q?refactor(material):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E7=B4=A0=E6=9D=90=E5=BA=93=E7=BB=84=E4=BB=B6=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除旧的 MaterialList 组件相关配置和引用 - 删除 formatParam 工具函数的不必要导入 - 新增 MaterialLibraryModal 和 CategoryTree 组件 - 将 UploadMaterial 组件重构为使用新的素材库选择方式 - 实现素材分类树的菜单展示和选择功能 - 添加素材库弹窗的图片和列表两种视图模式 - 集成素材选择、预览和确认功能 - 移除各组件中对旧 MaterialList 的依赖配置 --- .../src/components/BasicData/BoxBrandList.tsx | 61 +--- .../src/components/Channel/ChannelList.tsx | 57 --- .../src/components/Company/CompanyList.tsx | 17 - .../src/components/Employee/EmployeeList.tsx | 48 +-- .../FormItem/ProFormUploadMaterial/index.tsx | 9 +- .../Material/MaterialCategoryList.tsx | 1 + .../Material/MaterialLibrary/CategoryTree.tsx | 130 +++++++ .../MaterialLibrary/MaterialLibraryModal.tsx | 336 ++++++++++++++++++ .../Material/MaterialLibrary/index.ts | 3 + .../Material/MaterialLibrary/style.style.ts | 224 ++++++++++++ .../src/components/Material/MaterialList.tsx | 5 +- .../src/components/Material/index.ts | 1 + .../components/PaymentTask/PaymentTaskPay.tsx | 34 +- .../Setting/ChargingPilePurchaseConfig.tsx | 32 +- .../src/components/Setting/WxMaConfig.tsx | 32 +- .../src/components/Setting/WxMpConfig.tsx | 33 +- .../Supplier/SupplierFarmerList.tsx | 37 +- .../src/components/Supplier/SupplierModal.tsx | 16 +- .../components/Supplier/SupplierStallList.tsx | 37 +- .../src/components/UploadMaterial/index.tsx | 108 ++---- .../components/UploadMaterial/style.style.ts | 49 ++- 21 files changed, 775 insertions(+), 495 deletions(-) create mode 100644 packages/app-operation/src/components/Material/MaterialLibrary/CategoryTree.tsx create mode 100644 packages/app-operation/src/components/Material/MaterialLibrary/MaterialLibraryModal.tsx create mode 100644 packages/app-operation/src/components/Material/MaterialLibrary/index.ts create mode 100644 packages/app-operation/src/components/Material/MaterialLibrary/style.style.ts diff --git a/packages/app-operation/src/components/BasicData/BoxBrandList.tsx b/packages/app-operation/src/components/BasicData/BoxBrandList.tsx index eeb3b83..639251e 100644 --- a/packages/app-operation/src/components/BasicData/BoxBrandList.tsx +++ b/packages/app-operation/src/components/BasicData/BoxBrandList.tsx @@ -2,20 +2,14 @@ import { BizContainer, BizValueType, BoxSpecList, - MaterialList, ModeType, ProFormBizSelect, ProFormBizSelectHandles, ProFormUploadMaterial, } from '@/components'; import { business } from '@/services'; -import { formatParam } from '@/utils/formatParam'; import { useIntl } from '@@/exports'; -import { - ActionType, - ProColumns, - ProFormText, -} from '@ant-design/pro-components'; +import { ProColumns, ProFormText } from '@ant-design/pro-components'; import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions'; import { ProListMetas } from '@ant-design/pro-list'; import { Image } from 'antd'; @@ -46,7 +40,6 @@ export default function BoxBrandList(props: IBoxBrandListProps) { const intl = useIntl(); const intlPrefix = 'boxBrand'; const specRef = useRef(null); - const actionRef = useRef(); const columns: ProColumns[] = [ { @@ -132,32 +125,6 @@ export default function BoxBrandList(props: IBoxBrandListProps) { }} fieldProps={{ maxCount: 1, - actionRef: actionRef, - toolBarRender: () => [ - actionRef.current?.reload()} - />, - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} />, @@ -254,14 +221,6 @@ export default function BoxBrandList(props: IBoxBrandListProps) { type: brandType, }), }, - columnsState: { - defaultValue: { - option: { show: true }, - status: { show: true }, - remark: { show: false }, - createdAt: { show: false }, - }, - }, }, columns, }} @@ -270,29 +229,11 @@ export default function BoxBrandList(props: IBoxBrandListProps) { bordered: true, //@ts-ignore search, - pagination: false, - itemLayout: 'vertical', - showActions: 'hover', params: { ...(brandType !== 'ALL' && { type: brandType, }), }, - onItem: (record: any) => { - return { - onClick: async (e) => { - const { data } = await business.boxBrand.showBoxBrand({ - boxBrandShowQry: { - brandId: record.brandId, - }, - }); - if (data) { - onSelect?.(data); - } - e.stopPropagation(); - }, - }; - }, }, metas, columns, diff --git a/packages/app-operation/src/components/Channel/ChannelList.tsx b/packages/app-operation/src/components/Channel/ChannelList.tsx index d371b72..0f69c7b 100644 --- a/packages/app-operation/src/components/Channel/ChannelList.tsx +++ b/packages/app-operation/src/components/Channel/ChannelList.tsx @@ -1,25 +1,20 @@ import { BizContainer, BizValueType, - MaterialList, ProFormUploadMaterial, } from '@/components'; import { business } from '@/services'; -import { formatParam } from '@/utils/formatParam'; import { useIntl } from '@@/exports'; import { - ActionType, ProColumns, ProFormText, ProFormTextArea, } from '@ant-design/pro-components'; import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions'; -import { useRef } from 'react'; export default function ChannelList() { const intl = useIntl(); const intlPrefix = 'channel'; - const actionRef = useRef(); const formContext = [ [ - actionRef.current?.reload()} - />, - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} />, [ - actionRef.current?.reload()} - />, - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} />, { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} />, { const { ghost = false, search = true, mode = 'page', onValueChange } = props; const intl = useIntl(); const intlPrefix = 'employee'; - const actionRef = useRef(); const columns: ProColumns[] = [ { @@ -187,22 +183,6 @@ const EmployeeList: React.FC = (props: IEmployeeListProps) => { }} fieldProps={{ maxCount: 1, - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} />, { }} fieldProps={{ maxCount: 1, - actionRef: actionRef, - toolBarRender: () => [ - actionRef.current?.reload()} - />, - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} />, ]; diff --git a/packages/app-operation/src/components/FormItem/ProFormUploadMaterial/index.tsx b/packages/app-operation/src/components/FormItem/ProFormUploadMaterial/index.tsx index 7c96d0d..b358bcd 100644 --- a/packages/app-operation/src/components/FormItem/ProFormUploadMaterial/index.tsx +++ b/packages/app-operation/src/components/FormItem/ProFormUploadMaterial/index.tsx @@ -1,21 +1,16 @@ +import { UploadMaterial } from '@/components'; import { - ActionType, ProFormDependency, ProFormField, ProFormItemProps, - ProTableProps, } from '@ant-design/pro-components'; -import { UploadMaterial } from '@/components'; -import React, { MutableRefObject } from 'react'; +import React from 'react'; interface ProFormUploadMaterialProps extends ProFormItemProps { fieldProps: { maxCount: number; onChange?: (fileList: any[]) => void; fileList?: any[]; - request: ProTableProps['request']; - toolBarRender?: ProTableProps['toolBarRender']; - actionRef?: MutableRefObject; }; } diff --git a/packages/app-operation/src/components/Material/MaterialCategoryList.tsx b/packages/app-operation/src/components/Material/MaterialCategoryList.tsx index 4f670c7..78002e1 100644 --- a/packages/app-operation/src/components/Material/MaterialCategoryList.tsx +++ b/packages/app-operation/src/components/Material/MaterialCategoryList.tsx @@ -168,6 +168,7 @@ export default function MaterialCategoryList( }} tree={{ fieldProps: { + bordered: true, ghost, //@ts-ignore search, diff --git a/packages/app-operation/src/components/Material/MaterialLibrary/CategoryTree.tsx b/packages/app-operation/src/components/Material/MaterialLibrary/CategoryTree.tsx new file mode 100644 index 0000000..a0195ae --- /dev/null +++ b/packages/app-operation/src/components/Material/MaterialLibrary/CategoryTree.tsx @@ -0,0 +1,130 @@ +import { business } from '@/services'; +import { FolderOpenOutlined, FolderOutlined } from '@ant-design/icons'; +import { ProCard } from '@ant-design/pro-components'; +import type { MenuItemProps } from 'antd'; +import { Empty, Menu, message } from 'antd'; +import React, { useEffect, useState } from 'react'; +import useStyle from './style.style'; + +interface CategoryTreeProps { + onCategorySelect: (categoryId?: string, categoryName?: string) => void; + selectedCategoryId?: string; +} + +/** + * 素材分类树组件(使用 Menu 组件) + * 支持:展开/收起、显示素材数量 + */ +const CategoryTree: React.FC = ({ + onCategorySelect, + selectedCategoryId, +}) => { + const { styles } = useStyle(); + const [treeData, setTreeData] = useState([]); + const [loading, setLoading] = useState(false); + + // 获取分类树数据 + const fetchCategoryTree = async () => { + setLoading(true); + try { + const { data } = await business.materialCategory.treeMaterialCategory({ + categoryTreeQry: {}, + }); + if (data) { + setTreeData(data as BusinessAPI.CategoryVO[]); + } + } catch (error) { + console.error('获取素材分类树失败:', error); + message.error('获取素材分类树失败'); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchCategoryTree(); + }, []); + + // 转换数据为 Menu Item 格式 + const convertToMenuItems = (nodes: BusinessAPI.CategoryVO[]): any[] => { + return nodes.map((node) => { + const menuItem: any = { + key: node.categoryId, + label: ( +
+ {node.name} + + {/* @ts-ignore */}({node.materialCount || 0}) + +
+ ), + icon: + node.children && node.children.length > 0 ? ( + + ) : ( + + ), + }; + + // 递归处理子节点 + if (node.children && node.children.length > 0) { + menuItem.children = convertToMenuItems(node.children); + } + + return menuItem; + }); + }; + + // 处理菜单选择 + const handleMenuSelect: MenuItemProps['onClick'] = ({ key }) => { + const categoryId = key as string; + // 从树数据中查找分类名称 + const findCategoryName = ( + nodes: BusinessAPI.CategoryVO[], + targetId: string, + ): string | undefined => { + for (const node of nodes) { + if (node.categoryId === targetId) { + return node.name; + } + if (node.children) { + const found = findCategoryName(node.children, targetId); + if (found) return found; + } + } + return undefined; + }; + + const categoryName = findCategoryName(treeData, categoryId); + onCategorySelect(categoryId, categoryName); + }; + + const menuItems = convertToMenuItems(treeData); + + return ( + + 素材分类 + + } + size="small" + className={styles.categoryCard} + loading={loading} + > + {menuItems.length > 0 ? ( + + ) : ( + + )} + + ); +}; + +export default CategoryTree; diff --git a/packages/app-operation/src/components/Material/MaterialLibrary/MaterialLibraryModal.tsx b/packages/app-operation/src/components/Material/MaterialLibrary/MaterialLibraryModal.tsx new file mode 100644 index 0000000..8bcc3e7 --- /dev/null +++ b/packages/app-operation/src/components/Material/MaterialLibrary/MaterialLibraryModal.tsx @@ -0,0 +1,336 @@ +import { CategoryTree, MaterialList } from '@/components'; +import { business } from '@/services'; +import { formatParam } from '@/utils/formatParam'; +import { pagination } from '@/utils/pagination'; +import { + ActionType, + ProColumns, + ProTable, + ProTableProps, +} from '@ant-design/pro-components'; +import { Button, Image, message, Modal, ModalProps, Segmented } from 'antd'; +import React, { useRef, useState } from 'react'; +import useStyle from './style.style'; + +export interface MaterialLibraryModalProps extends ModalProps { + /** 最大选择数量,默认 1 */ + maxCount?: number; + /** 选择模式: radio-单选, checkbox-多选 */ + mode?: 'radio' | 'checkbox'; + /** 确认选择回调 */ + onFinish?: (materials: BusinessAPI.MaterialVO[]) => void; + /** 初始已选中的素材列表 */ + initialSelectedMaterials?: BusinessAPI.MaterialVO[]; + /** 素材类型筛选: FILE_IMAGE-图片, FILE_VIDEO-视频 */ + materialType?: 'FILE_IMAGE' | 'FILE_VIDEO'; +} + +const MaterialLibraryModal: React.FC = ({ + open, + onCancel, + maxCount = 1, + mode = 'radio', + onFinish, + initialSelectedMaterials = [], + materialType, + ...rest +}) => { + const { styles } = useStyle(); + const [selectedCategoryId, setSelectedCategoryId] = useState< + string | undefined + >(); + const [selectedCategoryName, setSelectedCategoryName] = useState< + string | undefined + >(); + const [selectedMaterials, setSelectedMaterials] = useState< + BusinessAPI.MaterialVO[] + >(initialSelectedMaterials); + const [viewMode, setViewMode] = useState<'list' | 'image'>('image'); + const [materials, setMaterials] = useState([]); + const actionRef = useRef(); + + // 列表模式列配置 + const columns: ProColumns[] = [ + { + title: '素材名称', + dataIndex: 'name', + ellipsis: true, + }, + { + title: '素材内容', + dataIndex: 'url', + render: (_, record) => { + if (record.type === 'FILE_IMAGE' || record.type === 'FILE_VIDEO') { + return ( + + ); + } + return '-'; + }, + }, + { + title: '素材分类', + dataIndex: ['categoryVO', 'name'], + }, + { + title: '创建时间', + dataIndex: 'createdAt', + valueType: 'dateTime', + }, + ]; + + // 处理分类选择 + const handleCategorySelect = (categoryId?: string, categoryName?: string) => { + setSelectedCategoryId(categoryId); + setSelectedCategoryName(categoryName); + // 刷新素材列表 + actionRef.current?.reload(); + }; + + // 处理素材选择 + const handleMaterialSelect = (material: BusinessAPI.MaterialVO) => { + const isSelected = selectedMaterials.some( + (m) => m.materialId === material.materialId, + ); + + if (isSelected) { + // 取消选择 + setSelectedMaterials((prev) => + prev.filter((m) => m.materialId !== material.materialId), + ); + } else { + // 选择素材 + if (mode === 'radio') { + // 单选模式直接替换 + setSelectedMaterials([material]); + } else { + // 多选模式检查数量限制 + if (maxCount > 0 && selectedMaterials.length >= maxCount) { + message.warning(`最多只能选择 ${maxCount} 个素材`); + return; + } + setSelectedMaterials((prev) => [...prev, material]); + } + } + }; + + // 确认选择 + const handleOk = () => { + if (selectedMaterials.length === 0 && maxCount > 0) { + message.warning('请先选择素材'); + return; + } + onFinish?.(selectedMaterials); + // 重置状态 + setSelectedMaterials([]); + setSelectedCategoryId(undefined); + setSelectedCategoryName(undefined); + }; + + // 关闭弹窗 + const handleClose = () => { + setSelectedMaterials([]); + setSelectedCategoryId(undefined); + setSelectedCategoryName(undefined); + onCancel?.({} as any); + }; + + // 图片预览渲染 + const renderImagePreview = () => { + if (materials.length === 0) { + return ( +
+ 暂无素材,请选择其他分类或添加素材 +
+ ); + } + + return ( +
+ {materials.map((material) => { + const isSelected = selectedMaterials.some( + (m) => m.materialId === material.materialId, + ); + return ( +
handleMaterialSelect(material)} + > +
+ {material.name}加载中...
+ } + /> +
+
+
{material.name}
+
+ {isSelected && ( +
+ {mode === 'radio' + ? '✓' + : `${ + selectedMaterials.findIndex( + (m) => m.materialId === material.materialId, + ) + 1 + }`} +
+ )} +
+ ); + })} + + ); + }; + + // 请求参数 + const request: ProTableProps< + BusinessAPI.MaterialVO, + BusinessAPI.MaterialPageQry + >['request'] = async (params, sorter, filter) => { + const searchParams: BusinessAPI.MaterialPageQry = { + ...formatParam(params, sorter, filter), + categoryId: selectedCategoryId, + type: materialType, + }; + + const { data, success, totalCount } = await business.material.pageMaterial({ + materialPageQry: searchParams, + }); + + // 保存素材数据用于图片预览 + if (data) { + setMaterials(data as BusinessAPI.MaterialVO[]); + } + + return { + data: data || [], + total: totalCount, + success, + }; + }; + + return ( + + + actionRef={actionRef} + rowKey="materialId" + bordered={true} + columns={columns} + request={request} + search={false} + pagination={pagination()} + size="small" + className={styles.materialTable} + rowClassName={(record) => { + const isSelected = selectedMaterials.some( + (m) => m.materialId === record.materialId, + ); + return isSelected ? styles.rowSelected : ''; + }} + onRow={(record) => ({ + onClick: () => handleMaterialSelect(record), + style: { cursor: 'pointer' }, + })} + toolBarRender={false} + tableRender={(props, defaultDom, domList) => { + return ( +
+ {/* 左侧分类树 */} +
+ +
+ + {/* 右侧素材内容 */} +
+ {/* 顶部工具栏 */} +
+
+ + setViewMode(value as 'list' | 'image') + } + options={[ + { label: '图片', value: 'image' }, + { label: '列表', value: 'list' }, + ]} + /> + {selectedCategoryName && ( + + 当前分类: {selectedCategoryName} + + )} +
+
+ actionRef.current?.reload()} + /> + {selectedMaterials.length > 0 && ( + + 已选择 {selectedMaterials.length} + {maxCount > 0 && `/${maxCount}`} 个 + + )} + {selectedMaterials.length > 0 && mode === 'checkbox' && ( + + )} +
+
+ + {/* 素材展示区域 */} +
+ {viewMode === 'list' ? ( + defaultDom + ) : ( +
+ {renderImagePreview()} +
+ {domList.table} +
+
+ )} +
+
+
+ ); + }} + /> +
+ ); +}; + +export default MaterialLibraryModal; diff --git a/packages/app-operation/src/components/Material/MaterialLibrary/index.ts b/packages/app-operation/src/components/Material/MaterialLibrary/index.ts new file mode 100644 index 0000000..b82f424 --- /dev/null +++ b/packages/app-operation/src/components/Material/MaterialLibrary/index.ts @@ -0,0 +1,3 @@ +export { default as CategoryTree } from './CategoryTree'; +export { default as MaterialLibraryModal } from './MaterialLibraryModal'; +export type { MaterialLibraryModalProps } from './MaterialLibraryModal'; diff --git a/packages/app-operation/src/components/Material/MaterialLibrary/style.style.ts b/packages/app-operation/src/components/Material/MaterialLibrary/style.style.ts new file mode 100644 index 0000000..1102d84 --- /dev/null +++ b/packages/app-operation/src/components/Material/MaterialLibrary/style.style.ts @@ -0,0 +1,224 @@ +import { createStyles } from 'antd-style'; + +const useStyle = () => { + return createStyles(() => { + return { + // ==================== Modal 布局 ==================== + materialModal: { + '.ant-modal-body': { + padding: 0, + }, + }, + modalContent: { + display: 'flex', + height: 600, + }, + leftPanel: { + width: 280, + borderRight: '1px solid #f0f0f0', + padding: 16, + overflow: 'auto', + }, + rightPanel: { + flex: 1, + display: 'flex', + flexDirection: 'column', + overflow: 'hidden', + }, + + // ==================== 工具栏 ==================== + toolbar: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + padding: '12px 16px', + borderBottom: '1px solid #f0f0f0', + }, + toolbarLeft: { + display: 'flex', + alignItems: 'center', + gap: 16, + }, + toolbarRight: { + display: 'flex', + alignItems: 'center', + gap: 12, + }, + currentCategory: { + color: '#666', + fontSize: 13, + }, + selectedCount: { + color: '#1890ff', + fontWeight: 500, + }, + + // ==================== 分类菜单 ==================== + categoryCard: { + height: '100%', + }, + cardHeader: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, + categoryMenu: { + borderRight: 'none', + '.ant-menu-item': { + paddingLeft: '8px !important', + height: 'auto', + lineHeight: 'normal', + paddingTop: 8, + paddingBottom: 8, + margin: 0, + }, + '.ant-menu-submenu': { + paddingLeft: '8px !important', + }, + '.ant-menu-submenu-title': { + paddingLeft: '8px !important', + height: 'auto', + lineHeight: 'normal', + paddingTop: 8, + paddingBottom: 8, + margin: 0, + }, + '.ant-menu-item-selected': { + backgroundColor: '#e6f7ff', + }, + '.ant-menu-item:hover': { + backgroundColor: 'rgba(0, 0, 0, 4%)', + }, + '.ant-menu-submenu-title:hover': { + backgroundColor: 'rgba(0, 0, 0, 4%)', + }, + }, + menuItemLabel: { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + flex: 1, + marginLeft: 8, + }, + categoryName: { + flex: 1, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, + materialCount: { + color: '#999', + fontSize: 12, + flexShrink: 0, + marginLeft: 8, + }, + + // ==================== 素材列表 ==================== + materialArea: { + flex: 1, + overflow: 'auto', + padding: 16, + }, + materialTable: { + '.ant-table-row': { + cursor: 'pointer', + }, + border: '1px solid #f0f0f0;', + borderRadius: 8, + }, + rowSelected: { + backgroundColor: '#e6f7ff !important', + }, + + // ==================== 图片预览 ==================== + imageGridWrapper: { + height: '100%', + display: 'flex', + flexDirection: 'column', + }, + imagePreview: { + display: 'flex', + flexWrap: 'wrap', + gap: 12, + overflow: 'auto', + padding: '4px 0', + // flex: 1, + }, + imageItem: { + width: 120, + border: '1px solid #f0f0f0', + borderRadius: 8, + overflow: 'hidden', + cursor: 'pointer', + transition: 'all 0.2s', + position: 'relative', + '&:hover': { + borderColor: '#1890ff', + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', + }, + }, + imageItemSelected: { + borderColor: '#1890ff', + boxShadow: '0 0 0 2px rgba(24, 144, 255, 0.3)', + }, + imageWrapper: { + width: '100%', + height: 100, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#fafafa', + img: { + maxWidth: '100%', + maxHeight: '100%', + objectFit: 'contain', + }, + }, + imagePlaceholder: { + color: '#999', + fontSize: 12, + }, + imageInfo: { + padding: '8px 10px', + backgroundColor: '#fff', + }, + imageName: { + fontSize: 12, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + color: '#333', + }, + imageSelectedCheck: { + position: 'absolute', + top: 4, + right: 4, + width: 20, + height: 20, + borderRadius: '50%', + backgroundColor: '#1890ff', + color: '#fff', + fontSize: 12, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + paginationWrapper: { + borderTop: '1px solid #f0f0f0', + marginTop: 8, + '.ant-table': { + display: 'none', + }, + }, + emptyTip: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: '100%', + color: '#999', + }, + }; + })(); +}; + +export default useStyle; diff --git a/packages/app-operation/src/components/Material/MaterialList.tsx b/packages/app-operation/src/components/Material/MaterialList.tsx index 0b1fc86..b78ef18 100644 --- a/packages/app-operation/src/components/Material/MaterialList.tsx +++ b/packages/app-operation/src/components/Material/MaterialList.tsx @@ -1,6 +1,7 @@ import { BizContainer, BizValueType, + FormType, MaterialCategoryList, ModeType, ProFormBizTreeSelect, @@ -27,6 +28,7 @@ export interface IMaterialListProps { mode?: ModeType; trigger?: () => React.ReactNode; onValueChange?: () => void; + formType: FormType; } export default function MaterialList(props: IMaterialListProps) { @@ -36,6 +38,7 @@ export default function MaterialList(props: IMaterialListProps) { search = true, onValueChange, trigger, + formType = 'drawer', } = props; const intl = useIntl(); const intlPrefix = 'material'; @@ -291,7 +294,7 @@ export default function MaterialList(props: IMaterialListProps) { columns, }} create={{ - formType: 'drawer', + formType: formType, formContext, trigger, request: async ( diff --git a/packages/app-operation/src/components/Material/index.ts b/packages/app-operation/src/components/Material/index.ts index 0e1c17d..bacc880 100644 --- a/packages/app-operation/src/components/Material/index.ts +++ b/packages/app-operation/src/components/Material/index.ts @@ -1,3 +1,4 @@ export { default as MaterialCategoryList } from './MaterialCategoryList'; +export * from './MaterialLibrary'; export { default as MaterialList } from './MaterialList'; export { default as MaterialModal } from './MaterialModal'; diff --git a/packages/app-operation/src/components/PaymentTask/PaymentTaskPay.tsx b/packages/app-operation/src/components/PaymentTask/PaymentTaskPay.tsx index 98ae09e..8df19f0 100644 --- a/packages/app-operation/src/components/PaymentTask/PaymentTaskPay.tsx +++ b/packages/app-operation/src/components/PaymentTask/PaymentTaskPay.tsx @@ -3,7 +3,6 @@ import { ButtonAccess, CompanyPaymentAccountSelect, InsertPosition, - MaterialList, OrderSupplierInvoiceList, ProFormUploadMaterial, SupplierFarmerList, @@ -12,11 +11,9 @@ import { } from '@/components'; import { business } from '@/services'; import { formatCurrency } from '@/utils/format'; -import { formatParam } from '@/utils/formatParam'; import { formLayout } from '@/utils/formLayout'; import { useIntl } from '@@/exports'; import { - ActionType, DrawerForm, ProCard, ProFormDependency, @@ -26,7 +23,6 @@ import { RouteContextType, } from '@ant-design/pro-components'; import { Col, Row, Space, Table } from 'antd'; -import { useRef } from 'react'; export interface IPaymentTaskPayProps { insertPosition?: InsertPosition; @@ -43,8 +39,6 @@ export default function PaymentTaskPay(props: IPaymentTaskPayProps) { const unpaidAmount = (paymentTaskVO.totalAmount || 0) - (paymentTaskVO.paidAmount || 0); - const actionRef = useRef(); - const handleSubmit = async (formData: any) => { console.log('付款数据:', formData); // TODO: 调用付款接口 @@ -370,33 +364,7 @@ export default function PaymentTaskPay(props: IPaymentTaskPayProps) { }; }} fieldProps={{ - maxCount: 1, - actionRef: actionRef, - toolBarRender: () => [ - actionRef.current?.reload()} - />, - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, + maxCount: 9, }} /> diff --git a/packages/app-operation/src/components/Setting/ChargingPilePurchaseConfig.tsx b/packages/app-operation/src/components/Setting/ChargingPilePurchaseConfig.tsx index c91dd18..299b450 100644 --- a/packages/app-operation/src/components/Setting/ChargingPilePurchaseConfig.tsx +++ b/packages/app-operation/src/components/Setting/ChargingPilePurchaseConfig.tsx @@ -1,11 +1,5 @@ -import { - BizEditor, - ButtonAccess, - MaterialList, - ProFormUploadMaterial, -} from '@/components'; +import { BizEditor, ButtonAccess, ProFormUploadMaterial } from '@/components'; import { business } from '@/services'; -import { formatParam } from '@/utils/formatParam'; import { formLayout } from '@/utils/formLayout'; import { DrawerForm, @@ -150,30 +144,6 @@ export default function ChargingPilePurchaseConfig( } fieldProps={{ maxCount: 1, - toolBarRender: () => [ - , - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} /> diff --git a/packages/app-operation/src/components/Setting/WxMaConfig.tsx b/packages/app-operation/src/components/Setting/WxMaConfig.tsx index d137c24..1f2465a 100644 --- a/packages/app-operation/src/components/Setting/WxMaConfig.tsx +++ b/packages/app-operation/src/components/Setting/WxMaConfig.tsx @@ -1,18 +1,14 @@ -import { MaterialList, ProFormUploadMaterial } from '@/components'; +import { ProFormUploadMaterial } from '@/components'; import { business } from '@/services'; -import { formatParam } from '@/utils/formatParam'; import { formLayout } from '@/utils/formLayout'; import { - ActionType, ProForm, ProFormText, ProFormUploadDragger, } from '@ant-design/pro-components'; import { Col, message, Row, Space } from 'antd'; -import { useRef } from 'react'; export default function WxMaConfig() { - const actionRef = useRef(); return ( {...formLayout()} @@ -113,32 +109,6 @@ export default function WxMaConfig() { }} fieldProps={{ maxCount: 1, - actionRef: actionRef, - toolBarRender: () => [ - actionRef.current?.reload()} - />, - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} /> (); return ( {...formLayout()} @@ -108,32 +105,6 @@ export default function WxMaConfig() { }} fieldProps={{ maxCount: 1, - actionRef: actionRef, - toolBarRender: () => [ - actionRef.current?.reload()} - />, - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} /> diff --git a/packages/app-operation/src/components/Supplier/SupplierFarmerList.tsx b/packages/app-operation/src/components/Supplier/SupplierFarmerList.tsx index 3bb846e..8d76acf 100644 --- a/packages/app-operation/src/components/Supplier/SupplierFarmerList.tsx +++ b/packages/app-operation/src/components/Supplier/SupplierFarmerList.tsx @@ -1,22 +1,16 @@ import { BizContainer, BizValueType, - MaterialList, ModeType, ProFormUploadMaterial, } from '@/components'; import { business } from '@/services'; import { formatBankCard, formatIdCard, formatPhone } from '@/utils/format'; -import { formatParam } from '@/utils/formatParam'; import { useIntl } from '@@/exports'; import { EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons'; -import { - ActionType, - ProColumns, - ProFormText, -} from '@ant-design/pro-components'; +import { ProColumns, ProFormText } from '@ant-design/pro-components'; import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions'; -import React, { useRef, useState } from 'react'; +import React, { useState } from 'react'; interface ISupplierFarmerListProps { ghost?: boolean; @@ -38,7 +32,6 @@ export default function SupplierFarmerList(props: ISupplierFarmerListProps) { } = props; const intl = useIntl(); const intlPrefix = 'supplierFarmer'; - const actionRef = useRef(); const [showIdCard, setShowIdCard] = useState>({}); const [showBankCard, setShowBankCard] = useState>({}); @@ -255,32 +248,6 @@ export default function SupplierFarmerList(props: ISupplierFarmerListProps) { }} fieldProps={{ maxCount: 1, - actionRef: actionRef, - toolBarRender: () => [ - actionRef.current?.reload()} - />, - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} />, ]; diff --git a/packages/app-operation/src/components/Supplier/SupplierModal.tsx b/packages/app-operation/src/components/Supplier/SupplierModal.tsx index 82f1bb8..0b09957 100644 --- a/packages/app-operation/src/components/Supplier/SupplierModal.tsx +++ b/packages/app-operation/src/components/Supplier/SupplierModal.tsx @@ -1,3 +1,4 @@ +import { SelectModal } from '@/components'; import { business } from '@/services'; import { formatBankCard, formatIdCard, formatPhone } from '@/utils/format'; import { formatParam } from '@/utils/formatParam'; @@ -10,7 +11,6 @@ import { ProColumns, ProFormText, } from '@ant-design/pro-components'; -import { SelectModal } from '@/components'; import { Alert, ModalProps, Row, Tag } from 'antd'; import React, { useEffect, useRef, useState } from 'react'; @@ -159,13 +159,13 @@ export default function SupplierModal(props: ISupplierModalProps) { ), }, - { - title: intl.formatMessage({ id: intlPrefix + '.column.wechatQr' }), - dataIndex: 'wechatQr', - valueType: 'image', - key: 'wechatQr', - search: false, - }, + // { + // title: intl.formatMessage({ id: intlPrefix + '.column.wechatQr' }), + // dataIndex: 'wechatQr', + // valueType: 'image', + // key: 'wechatQr', + // search: false, + // }, ...(initExtraColumns || []), ]; diff --git a/packages/app-operation/src/components/Supplier/SupplierStallList.tsx b/packages/app-operation/src/components/Supplier/SupplierStallList.tsx index 15c1ea4..dc78f57 100644 --- a/packages/app-operation/src/components/Supplier/SupplierStallList.tsx +++ b/packages/app-operation/src/components/Supplier/SupplierStallList.tsx @@ -1,22 +1,16 @@ import { BizContainer, BizValueType, - MaterialList, ModeType, ProFormUploadMaterial, } from '@/components'; import { business } from '@/services'; import { formatBankCard, formatPhone } from '@/utils/format'; -import { formatParam } from '@/utils/formatParam'; import { useIntl } from '@@/exports'; import { EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons'; -import { - ActionType, - ProColumns, - ProFormText, -} from '@ant-design/pro-components'; +import { ProColumns, ProFormText } from '@ant-design/pro-components'; import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions'; -import React, { useRef, useState } from 'react'; +import React, { useState } from 'react'; interface ISupplierStallListProps { ghost?: boolean; @@ -38,7 +32,6 @@ export default function SupplierStallList(props: ISupplierStallListProps) { } = props; const intl = useIntl(); const intlPrefix = 'supplierStall'; - const actionRef = useRef(); const [showBankCard, setShowBankCard] = useState>({}); const [showPhone, setShowPhone] = useState>({}); @@ -214,32 +207,6 @@ export default function SupplierStallList(props: ISupplierStallListProps) { }} fieldProps={{ maxCount: 1, - actionRef: actionRef, - toolBarRender: () => [ - actionRef.current?.reload()} - />, - ], - request: async (params, sorter, filter) => { - const { data, success, totalCount } = - await business.material.pageMaterial({ - materialPageQry: formatParam( - params, - sorter, - filter, - ), - }); - - return { - data: data || [], - total: totalCount, - success, - }; - }, }} />, ]; diff --git a/packages/app-operation/src/components/UploadMaterial/index.tsx b/packages/app-operation/src/components/UploadMaterial/index.tsx index 4899a90..266d72c 100644 --- a/packages/app-operation/src/components/UploadMaterial/index.tsx +++ b/packages/app-operation/src/components/UploadMaterial/index.tsx @@ -1,36 +1,41 @@ +import { MaterialLibraryModal } from '@/components'; import { UploadOutlined } from '@ant-design/icons'; -import { ActionType, ProTableProps } from '@ant-design/pro-components'; -import { SelectModal } from '@/components'; import { Upload } from 'antd'; -import React, { MutableRefObject, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import useStyle from './style.style'; export interface UploadMaterialProps { maxCount: number; onChange: (fileList: any[]) => void; fileList?: any[]; - request: ProTableProps['request']; - toolBarRender?: ProTableProps['toolBarRender']; - actionRef?: MutableRefObject; } const UploadMaterial: React.FC = (props) => { - const { - maxCount, - fileList: initialFileList, - onChange, - request, - toolBarRender, - actionRef, - } = props; + const { maxCount, onChange, fileList: initialFileList } = props; const [fileList, setFileList] = useState([]); - const { styles } = useStyle(); const [open, setOpen] = useState(false); + const { styles } = useStyle(); useEffect(() => { setFileList(initialFileList || []); }, [initialFileList]); + // 处理素材选择完成 + const handleMaterialSelect = (materials: BusinessAPI.MaterialVO[]) => { + const imageList = materials.map((materialVO) => ({ + uid: materialVO?.materialId, + name: materialVO?.name, + // @ts-ignore + status: 'success', + thumbUrl: materialVO?.url, + url: materialVO?.url, + })); + + const newFileList = [...fileList, ...imageList]; + setFileList(newFileList); + onChange(newFileList); + }; + return ( <> = (props) => { )} - {/* 选择图片 */} - setOpen(false), - onCancel: () => setOpen(false), + {/* 素材库选择弹窗 */} + setOpen(false)} + maxCount={maxCount - fileList.length} + mode="checkbox" + materialType="FILE_IMAGE" + onFinish={(materials) => { + handleMaterialSelect(materials); + setOpen(false); }} - tableProps={{ - actionRef: actionRef, - toolBarRender: toolBarRender, - rowKey: 'materialId', - columns: [ - { - title: '素材内容', - dataIndex: 'url', - valueType: 'image', - }, - { - title: '素材名称', - dataIndex: 'name', - }, - { - title: '素材分类', - dataIndex: ['categoryVO', 'name'], - }, - ], - params: { - materialPageQry: { - type: 'FILE_IMAGE', - }, - }, - request: request, - pagination: { - showSizeChanger: true, - showQuickJumper: true, - showTotal: (total: number) => `共 ${total} 条`, - pageSizeOptions: ['10', '20', '50', '100'], - defaultPageSize: 10, - hideOnSinglePage: true, - position: ['bottomRight'], - }, - }} - onFinish={(materialList) => { - const imageList = materialList.map((materialVO) => { - return { - uid: materialVO?.materialId, - name: materialVO?.name, - // @ts-ignore - status: 'success', - thumbUrl: materialVO?.url, - url: materialVO?.url, - }; - }); - - const newFileList = [...fileList, ...imageList]; - setFileList(newFileList); - - onChange(newFileList); - }} - num={(maxCount || 1) - fileList.length} - type={'checkbox'} /> ); }; + export default UploadMaterial; diff --git a/packages/app-operation/src/components/UploadMaterial/style.style.ts b/packages/app-operation/src/components/UploadMaterial/style.style.ts index 9f6aa6c..68c9648 100644 --- a/packages/app-operation/src/components/UploadMaterial/style.style.ts +++ b/packages/app-operation/src/components/UploadMaterial/style.style.ts @@ -1,30 +1,29 @@ import { createStyles } from 'antd-style'; const useStyle = () => { - return createStyles(() => { - return { - uploadImage: { - width: 102, - height: 102, - marginInlineEnd: 0, - marginBottom: 0, - textAlign: 'center', - verticalAlign: 'top', - backgroundColor: "rgba(0, 0, 0, 2%)", - //border: 1px dashed #d9d9d9, - borderRadius: 8, - cursor: 'pointer', - transition: 'border-color 0.3s', - }, - uploadImageIcon: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - height: '100%', - textAlign: 'center', - }, - }; - })(); -} + return createStyles(() => { + return { + uploadImage: { + width: 102, + height: 102, + marginInlineEnd: 0, + marginBottom: 0, + textAlign: 'center', + verticalAlign: 'top', + backgroundColor: 'rgba(0, 0, 0, 2%)', + borderRadius: 8, + cursor: 'pointer', + transition: 'border-color 0.3s', + }, + uploadImageIcon: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: '100%', + textAlign: 'center', + }, + }; + })(); +}; export default useStyle;