分类图片上传和用户等级图片上传

This commit is contained in:
liu
2026-08-05 13:32:50 +08:00
parent b296ea9bb5
commit 38338a8833
16 changed files with 218 additions and 52 deletions
@@ -14,6 +14,7 @@ import { useTranslation } from 'react-i18next';
const ImageUploader: React.FC<ImageUploaderProps> = ({
action,
value,
changeType,
onChange,
mode = 'single',
disabled = false,
@@ -115,9 +116,17 @@ const ImageUploader: React.FC<ImageUploaderProps> = ({
.filter((file) => file.status === 'done' && file.response)
.map((file) => file.response.data as ISysFileInfo);
if( mode === 'single' ) {
onChange?.(uploadedFiles[0]);
if (changeType !== 'id') {
onChange?.(uploadedFiles[0]);
} else {
onChange?.(uploadedFiles[0].id);
}
} else {
onChange?.(uploadedFiles);
if (changeType !== 'id') {
onChange?.(uploadedFiles);
} else {
onChange?.(uploadedFiles.map(item => item.id!));
}
}
// 上传失败的文件
const errorFiles = newFileList.filter((file) => file.status === 'error');
@@ -17,11 +17,17 @@ export interface ImageUploaderProps {
*/
value?: ISysFileInfo | ISysFileInfo[] | null;
/**
* 赋值类型
* 上传完成后赋值的类型。
*/
changeType?: 'id' | 'object';
/**
* 上传完成回调
* @param value ISysFileInfo | ISysFileInfo[] | null
*/
onChange?: (value: ISysFileInfo | ISysFileInfo[] | null) => void;
onChange?: (value?: ISysFileInfo | ISysFileInfo[] | number | number[] | null) => void;
/**
* 上传模式
@@ -65,9 +71,9 @@ export interface ImageUploaderProps {
*/
croppable?: boolean;
/**
/**
* 剪裁参数
*/
cropperOptions?: Omit<ImgCropProps, 'children'>;
}
}
+2 -1
View File
@@ -5,7 +5,8 @@ export default interface ICustomerLevel {
name?: string;
sort?: number;
status?: number;
remark?: string;
icon?: number;
icon_url?: string;
created_at?: string;
updated_at?: string;
}
+2
View File
@@ -5,6 +5,8 @@ export default interface IProductCategory {
name?: string;
sort?: number;
status?: number;
icon?: number;
icon_url?: string;
children?: IProductCategory[];
created_at?: string;
}
+33 -5
View File
@@ -1,5 +1,5 @@
import React from 'react';
import { Tag, Typography } from 'antd';
import { Tag, Typography, Image } from 'antd';
import XinTable from '@/components/XinTable';
import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings.ts';
import type ICustomerLevel from '@/domain/iCustomerLevel.ts';
@@ -25,6 +25,7 @@ const CustomerLevelPage: React.FC = () => {
dataIndex: 'name',
valueType: 'text',
required: true,
align: 'center',
rules: [{ required: true, message: '请输入等级名称' }],
},
{
@@ -32,8 +33,34 @@ const CustomerLevelPage: React.FC = () => {
dataIndex: 'sort',
valueType: 'digit',
hideInSearch: true,
initialValue: 0,
fieldProps: { min: 0 },
},
{
title: '图片等级',
dataIndex: 'icon',
valueType: 'image',
fieldProps: {
action: '/customer/level/upload',
mode: 'single',
maxCount: 1,
changeType: "id"
},
render: (_, record: ICustomerLevel) => {
const url = record.icon_url
if (!url) return '-';
return (
<Image
src={url}
width={60}
height={60}
style={{ objectFit: 'cover', borderRadius: 4 }}
/>
);
},
align: 'center',
hideInSearch: true,
},
{
title: '状态',
dataIndex: 'status',
@@ -51,11 +78,11 @@ const CustomerLevelPage: React.FC = () => {
},
},
{
title: '备注',
dataIndex: 'remark',
valueType: 'textarea',
title: '创建时间',
dataIndex: 'updated_at',
hideInForm: true,
hideInSearch: true,
fieldProps: { rows: 2 },
align: 'center',
},
{
title: '创建时间',
@@ -74,6 +101,7 @@ const CustomerLevelPage: React.FC = () => {
formProps: {
grid: true,
colProps: { span: 12 },
rowProps: { gutter: 20 },
layout: 'vertical',
},
modalProps: { width: 640 },
+60 -31
View File
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import { Button, Space, Tag, Typography } from 'antd';
import { NodeExpandOutlined } from '@ant-design/icons';
import React, { useState } from 'react';
import {Button, Image, Tag, Typography} from 'antd';
import {NodeExpandOutlined} from '@ant-design/icons';
import XinTable from '@/components/XinTable';
import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings.ts';
import type IProductCategory from '@/domain/iProductCategory.ts';
@@ -36,17 +36,6 @@ const ProductCategoryPage: React.FC = () => {
const [allIds, setAllIds] = useState<number[]>([]);
const [categoryTree, setCategoryTree] = useState<IProductCategory[]>([]);
const loadTree = async () => {
const res = await getCategoryTable();
const tree = res.data.data ?? [];
setCategoryTree(tree);
setAllIds(collectAllIds(tree));
};
useEffect(() => {
loadTree();
}, []);
const columns: XinTableColumn<IProductCategory>[] = [
{
title: '分类名称',
@@ -95,6 +84,45 @@ const ProductCategoryPage: React.FC = () => {
},
align: 'center',
},
{
title: '分类图标',
dataIndex: 'icon',
valueType: 'image',
fieldProps: {
action: '/product/category/upload',
mode: 'single',
maxCount: 1,
changeType: "id"
},
render: (_, record: IProductCategory) => {
const url = record.icon_url
if (!url) return '-';
return (
<Image
src={url}
width={60}
height={60}
style={{ objectFit: 'cover', borderRadius: 4 }}
/>
);
},
align: 'center',
hideInSearch: true,
},
{
title: '创建时间',
dataIndex: 'updated_at',
hideInForm: true,
hideInSearch: true,
align: 'center',
},
{
title: '创建时间',
dataIndex: 'created_at',
hideInForm: true,
hideInSearch: true,
align: 'center',
},
];
const tableProps: XinTableProps<IProductCategory> = {
@@ -115,9 +143,22 @@ const ProductCategoryPage: React.FC = () => {
expandedRowKeys: expandedKeys,
onExpandedRowsChange: (keys) => setExpandedKeys([...keys] as number[]),
},
actionBarRender: (dom) => [
dom.add,
<Button
icon={<NodeExpandOutlined />}
onClick={() =>
setExpandedKeys(expandedKeys.length ? [] : allIds)
}
>
{expandedKeys.length ? '全部收起' : '全部展开'}
</Button>,
dom.keywordSearch,
],
formProps: {
grid: true,
colProps: { span: 12 },
rowProps: { gutter: 20 },
layout: 'vertical',
},
modalProps: { width: 640 },
@@ -125,23 +166,11 @@ const ProductCategoryPage: React.FC = () => {
return (
<>
<div className="mb-5 flex items-start justify-between">
<div>
<Title level={3}></Title>
<Text type="secondary">
//
</Text>
</div>
<Space>
<Button
icon={<NodeExpandOutlined />}
onClick={() =>
setExpandedKeys(expandedKeys.length ? [] : allIds)
}
>
{expandedKeys.length ? '全部收起' : '全部展开'}
</Button>
</Space>
<div className={"mb-5"}>
<Title level={3}></Title>
<Text type="secondary">
//
</Text>
</div>
<XinTable<IProductCategory> {...tableProps} />
</>
+2 -1
View File
@@ -344,6 +344,7 @@ const ProductGoodsPage: React.FC = () => {
formProps: {
grid: true,
colProps: { span: 12 },
rowProps: { gutter: 20 },
layout: 'vertical',
},
modalProps: { width: 800 },
@@ -363,7 +364,7 @@ const ProductGoodsPage: React.FC = () => {
title="价格矩阵 · 批量调价"
open={matrixOpen}
onClose={() => setMatrixOpen(false)}
width={matrixLevels.length * 150 + 260}
size={800}
extra={
<Space>
<Button onClick={() => loadMatrix()}></Button>