refactor(material): 重构素材库组件实现
- 移除旧的 MaterialList 组件相关配置和引用 - 删除 formatParam 工具函数的不必要导入 - 新增 MaterialLibraryModal 和 CategoryTree 组件 - 将 UploadMaterial 组件重构为使用新的素材库选择方式 - 实现素材分类树的菜单展示和选择功能 - 添加素材库弹窗的图片和列表两种视图模式 - 集成素材选择、预览和确认功能 - 移除各组件中对旧 MaterialList 的依赖配置
This commit is contained in:
parent
1429319b01
commit
7260b089e9
@ -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<ProFormBizSelectHandles>(null);
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const columns: ProColumns<BusinessAPI.BoxBrandVO, BizValueType>[] = [
|
||||
{
|
||||
@ -132,32 +125,6 @@ export default function BoxBrandList(props: IBoxBrandListProps) {
|
||||
}}
|
||||
fieldProps={{
|
||||
maxCount: 1,
|
||||
actionRef: actionRef,
|
||||
toolBarRender: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
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,
|
||||
|
||||
@ -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<ActionType>();
|
||||
|
||||
const formContext = [
|
||||
<ProFormText
|
||||
@ -87,32 +82,6 @@ export default function ChannelList() {
|
||||
}}
|
||||
fieldProps={{
|
||||
maxCount: 1,
|
||||
actionRef: actionRef,
|
||||
toolBarRender: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
<ProFormUploadMaterial
|
||||
@ -129,32 +98,6 @@ export default function ChannelList() {
|
||||
}}
|
||||
fieldProps={{
|
||||
maxCount: 1,
|
||||
actionRef: actionRef,
|
||||
toolBarRender: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
<ProFormText
|
||||
|
||||
@ -6,7 +6,6 @@ import {
|
||||
ProFormUploadMaterial,
|
||||
} from '@/components';
|
||||
import { business } from '@/services';
|
||||
import { formatParam } from '@/utils/formatParam';
|
||||
import { useIntl } from '@@/exports';
|
||||
import { ProColumns, ProFormText } from '@ant-design/pro-components';
|
||||
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
|
||||
@ -98,22 +97,6 @@ export default function CompanyList(props: ICompanyListProps) {
|
||||
}}
|
||||
fieldProps={{
|
||||
maxCount: 1,
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
<ProFormText
|
||||
|
||||
@ -3,23 +3,20 @@ import {
|
||||
BizValueType,
|
||||
EmployeeDisable,
|
||||
EmployeeRoleUpdate,
|
||||
MaterialList,
|
||||
ModeType,
|
||||
ProFormUploadMaterial,
|
||||
RestPassword,
|
||||
} from '@/components';
|
||||
import { business } from '@/services';
|
||||
import { aesEncrypt } from '@/utils/aes';
|
||||
import { formatParam } from '@/utils/formatParam';
|
||||
import { useIntl } from '@@/exports';
|
||||
import {
|
||||
ActionType,
|
||||
ProColumns,
|
||||
ProFormSelect,
|
||||
ProFormText,
|
||||
} from '@ant-design/pro-components';
|
||||
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
|
||||
import React, { useRef } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export interface IEmployeeListProps {
|
||||
ghost?: boolean;
|
||||
@ -33,7 +30,6 @@ const EmployeeList: React.FC = (props: IEmployeeListProps) => {
|
||||
const { ghost = false, search = true, mode = 'page', onValueChange } = props;
|
||||
const intl = useIntl();
|
||||
const intlPrefix = 'employee';
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const columns: ProColumns<BusinessAPI.EmployeeVO, BizValueType>[] = [
|
||||
{
|
||||
@ -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<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
<ProFormText
|
||||
@ -408,32 +388,6 @@ const EmployeeList: React.FC = (props: IEmployeeListProps) => {
|
||||
}}
|
||||
fieldProps={{
|
||||
maxCount: 1,
|
||||
actionRef: actionRef,
|
||||
toolBarRender: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
];
|
||||
|
||||
@ -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<any, any>['request'];
|
||||
toolBarRender?: ProTableProps<any, any>['toolBarRender'];
|
||||
actionRef?: MutableRefObject<ActionType | undefined>;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -168,6 +168,7 @@ export default function MaterialCategoryList(
|
||||
}}
|
||||
tree={{
|
||||
fieldProps: {
|
||||
bordered: true,
|
||||
ghost,
|
||||
//@ts-ignore
|
||||
search,
|
||||
|
||||
@ -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<CategoryTreeProps> = ({
|
||||
onCategorySelect,
|
||||
selectedCategoryId,
|
||||
}) => {
|
||||
const { styles } = useStyle();
|
||||
const [treeData, setTreeData] = useState<BusinessAPI.CategoryVO[]>([]);
|
||||
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: (
|
||||
<div className={styles.menuItemLabel}>
|
||||
<span className={styles.categoryName}>{node.name}</span>
|
||||
<span className={styles.materialCount}>
|
||||
{/* @ts-ignore */}({node.materialCount || 0})
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
icon:
|
||||
node.children && node.children.length > 0 ? (
|
||||
<FolderOpenOutlined />
|
||||
) : (
|
||||
<FolderOutlined />
|
||||
),
|
||||
};
|
||||
|
||||
// 递归处理子节点
|
||||
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 (
|
||||
<ProCard
|
||||
title={
|
||||
<div className={styles.cardHeader}>
|
||||
<span>素材分类</span>
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
className={styles.categoryCard}
|
||||
loading={loading}
|
||||
>
|
||||
{menuItems.length > 0 ? (
|
||||
<Menu
|
||||
mode="inline"
|
||||
selectedKeys={selectedCategoryId ? [selectedCategoryId] : []}
|
||||
onClick={handleMenuSelect}
|
||||
items={menuItems}
|
||||
className={styles.categoryMenu}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无分类" image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
)}
|
||||
</ProCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default CategoryTree;
|
||||
@ -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<MaterialLibraryModalProps> = ({
|
||||
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<BusinessAPI.MaterialVO[]>([]);
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
// 列表模式列配置
|
||||
const columns: ProColumns<BusinessAPI.MaterialVO>[] = [
|
||||
{
|
||||
title: '素材名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '素材内容',
|
||||
dataIndex: 'url',
|
||||
render: (_, record) => {
|
||||
if (record.type === 'FILE_IMAGE' || record.type === 'FILE_VIDEO') {
|
||||
return (
|
||||
<Image
|
||||
width={40}
|
||||
height={40}
|
||||
src={record.url}
|
||||
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||||
preview={{ src: record.url }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
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 (
|
||||
<div className={styles.emptyTip}>
|
||||
<span>暂无素材,请选择其他分类或添加素材</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.imagePreview}>
|
||||
{materials.map((material) => {
|
||||
const isSelected = selectedMaterials.some(
|
||||
(m) => m.materialId === material.materialId,
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={material.materialId}
|
||||
className={`${styles.imageItem} ${isSelected ? styles.imageItemSelected : ''}`}
|
||||
onClick={() => handleMaterialSelect(material)}
|
||||
>
|
||||
<div className={styles.imageWrapper}>
|
||||
<Image
|
||||
src={material.url}
|
||||
alt={material.name}
|
||||
preview={{ src: material.url }}
|
||||
placeholder={
|
||||
<div className={styles.imagePlaceholder}>加载中...</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.imageInfo}>
|
||||
<div className={styles.imageName}>{material.name}</div>
|
||||
</div>
|
||||
{isSelected && (
|
||||
<div className={styles.imageSelectedCheck}>
|
||||
{mode === 'radio'
|
||||
? '✓'
|
||||
: `${
|
||||
selectedMaterials.findIndex(
|
||||
(m) => m.materialId === material.materialId,
|
||||
) + 1
|
||||
}`}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 请求参数
|
||||
const request: ProTableProps<
|
||||
BusinessAPI.MaterialVO,
|
||||
BusinessAPI.MaterialPageQry
|
||||
>['request'] = async (params, sorter, filter) => {
|
||||
const searchParams: BusinessAPI.MaterialPageQry = {
|
||||
...formatParam<typeof params>(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 (
|
||||
<Modal
|
||||
title="素材库"
|
||||
open={open}
|
||||
onOk={handleOk}
|
||||
onCancel={handleClose}
|
||||
width={1200}
|
||||
destroyOnHidden
|
||||
{...rest}
|
||||
className={styles.materialModal}
|
||||
>
|
||||
<ProTable<BusinessAPI.MaterialVO, BusinessAPI.MaterialPageQry>
|
||||
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 (
|
||||
<div className={styles.modalContent}>
|
||||
{/* 左侧分类树 */}
|
||||
<div className={styles.leftPanel}>
|
||||
<CategoryTree
|
||||
onCategorySelect={handleCategorySelect}
|
||||
selectedCategoryId={selectedCategoryId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 右侧素材内容 */}
|
||||
<div className={styles.rightPanel}>
|
||||
{/* 顶部工具栏 */}
|
||||
<div className={styles.toolbar}>
|
||||
<div className={styles.toolbarLeft}>
|
||||
<Segmented
|
||||
value={viewMode}
|
||||
onChange={(value) =>
|
||||
setViewMode(value as 'list' | 'image')
|
||||
}
|
||||
options={[
|
||||
{ label: '图片', value: 'image' },
|
||||
{ label: '列表', value: 'list' },
|
||||
]}
|
||||
/>
|
||||
{selectedCategoryName && (
|
||||
<span className={styles.currentCategory}>
|
||||
当前分类: <strong>{selectedCategoryName}</strong>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.toolbarRight}>
|
||||
<MaterialList
|
||||
formType={'modal'}
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>
|
||||
{selectedMaterials.length > 0 && (
|
||||
<span className={styles.selectedCount}>
|
||||
已选择 {selectedMaterials.length}
|
||||
{maxCount > 0 && `/${maxCount}`} 个
|
||||
</span>
|
||||
)}
|
||||
{selectedMaterials.length > 0 && mode === 'checkbox' && (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => setSelectedMaterials([])}
|
||||
>
|
||||
清空
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 素材展示区域 */}
|
||||
<div className={styles.materialArea}>
|
||||
{viewMode === 'list' ? (
|
||||
defaultDom
|
||||
) : (
|
||||
<div className={styles.imageGridWrapper}>
|
||||
{renderImagePreview()}
|
||||
<div className={styles.paginationWrapper}>
|
||||
{domList.table}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default MaterialLibraryModal;
|
||||
@ -0,0 +1,3 @@
|
||||
export { default as CategoryTree } from './CategoryTree';
|
||||
export { default as MaterialLibraryModal } from './MaterialLibraryModal';
|
||||
export type { MaterialLibraryModalProps } from './MaterialLibraryModal';
|
||||
@ -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;
|
||||
@ -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 (
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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<ActionType>();
|
||||
|
||||
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: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
maxCount: 9,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@ -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: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ProFormDependency key={'content'} name={['content']}>
|
||||
|
||||
@ -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<ActionType>();
|
||||
return (
|
||||
<ProForm<BusinessAPI.WxMaConfigValue>
|
||||
{...formLayout()}
|
||||
@ -113,32 +109,6 @@ export default function WxMaConfig() {
|
||||
}}
|
||||
fieldProps={{
|
||||
maxCount: 1,
|
||||
actionRef: actionRef,
|
||||
toolBarRender: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ProFormUploadDragger
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
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 } from '@ant-design/pro-components';
|
||||
import { ProForm, ProFormText } from '@ant-design/pro-components';
|
||||
import { Col, message, Row, Space } from 'antd';
|
||||
import { useRef } from 'react';
|
||||
|
||||
export default function WxMaConfig() {
|
||||
const actionRef = useRef<ActionType>();
|
||||
return (
|
||||
<ProForm<BusinessAPI.WxMaConfigValue>
|
||||
{...formLayout()}
|
||||
@ -108,32 +105,6 @@ export default function WxMaConfig() {
|
||||
}}
|
||||
fieldProps={{
|
||||
maxCount: 1,
|
||||
actionRef: actionRef,
|
||||
toolBarRender: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</ProForm>
|
||||
|
||||
@ -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<ActionType>();
|
||||
|
||||
const [showIdCard, setShowIdCard] = useState<Record<string, boolean>>({});
|
||||
const [showBankCard, setShowBankCard] = useState<Record<string, boolean>>({});
|
||||
@ -255,32 +248,6 @@ export default function SupplierFarmerList(props: ISupplierFarmerListProps) {
|
||||
}}
|
||||
fieldProps={{
|
||||
maxCount: 1,
|
||||
actionRef: actionRef,
|
||||
toolBarRender: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
];
|
||||
|
||||
@ -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) {
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
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 || []),
|
||||
];
|
||||
|
||||
|
||||
@ -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<ActionType>();
|
||||
|
||||
const [showBankCard, setShowBankCard] = useState<Record<string, boolean>>({});
|
||||
const [showPhone, setShowPhone] = useState<Record<string, boolean>>({});
|
||||
@ -214,32 +207,6 @@ export default function SupplierStallList(props: ISupplierStallListProps) {
|
||||
}}
|
||||
fieldProps={{
|
||||
maxCount: 1,
|
||||
actionRef: actionRef,
|
||||
toolBarRender: () => [
|
||||
<MaterialList
|
||||
key={'create'}
|
||||
ghost={true}
|
||||
mode={'create'}
|
||||
search={false}
|
||||
onValueChange={() => actionRef.current?.reload()}
|
||||
/>,
|
||||
],
|
||||
request: async (params, sorter, filter) => {
|
||||
const { data, success, totalCount } =
|
||||
await business.material.pageMaterial({
|
||||
materialPageQry: formatParam<typeof params>(
|
||||
params,
|
||||
sorter,
|
||||
filter,
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
data: data || [],
|
||||
total: totalCount,
|
||||
success,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
];
|
||||
|
||||
@ -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<any, any>['request'];
|
||||
toolBarRender?: ProTableProps<any, any>['toolBarRender'];
|
||||
actionRef?: MutableRefObject<ActionType | undefined>;
|
||||
}
|
||||
|
||||
const UploadMaterial: React.FC<UploadMaterialProps> = (props) => {
|
||||
const {
|
||||
maxCount,
|
||||
fileList: initialFileList,
|
||||
onChange,
|
||||
request,
|
||||
toolBarRender,
|
||||
actionRef,
|
||||
} = props;
|
||||
const { maxCount, onChange, fileList: initialFileList } = props;
|
||||
const [fileList, setFileList] = useState<any[]>([]);
|
||||
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 (
|
||||
<>
|
||||
<Upload
|
||||
@ -58,71 +63,20 @@ const UploadMaterial: React.FC<UploadMaterialProps> = (props) => {
|
||||
)}
|
||||
</Upload>
|
||||
|
||||
{/* 选择图片 */}
|
||||
<SelectModal
|
||||
rowKey={'materialId'}
|
||||
modalProps={{
|
||||
title: '选择图片',
|
||||
open: open,
|
||||
onOk: () => setOpen(false),
|
||||
onCancel: () => setOpen(false),
|
||||
{/* 素材库选择弹窗 */}
|
||||
<MaterialLibraryModal
|
||||
open={open}
|
||||
onCancel={() => 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;
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user