This commit is contained in:
Li1304553726 2025-02-26 08:57:42 +08:00
commit ee087f0dbd
5 changed files with 80 additions and 199 deletions

View File

@ -3,7 +3,7 @@ import { useContext, useEffect, useState } from "react";
import { Button, Form, Input, message, theme } from "antd";
import { useAppConfig } from "@nice/client";
import { useAuth } from "@web/src/providers/auth-provider";
import MultiImageUploader from "@web/src/components/common/uploader/MultiImageUploader";
import MultiAvatarUploader from "@web/src/components/common/uploader/MultiAvatarUploader";
import FixedHeader from "@web/src/components/layout/fix-header";
import { useForm } from "antd/es/form/Form";
@ -129,9 +129,9 @@ export default function BaseSettingPage() {
</div>
<div className="p-2 grid grid-cols-8 gap-2 border-b">
<Form.Item
label="运维单位"
label="首页轮播图"
name={["appConfig", "slides"]}>
<MultiImageUploader></MultiImageUploader>
<MultiAvatarUploader></MultiAvatarUploader>
</Form.Item>
</div>
{/* <div
@ -174,7 +174,7 @@ export default function BaseSettingPage() {
}
}}
type="primary"
ghost>
ghost>
</Button>
</div>

View File

@ -12,6 +12,8 @@ export interface AvatarUploaderProps {
onChange?: (value: string) => void;
compressed?: boolean;
style?: React.CSSProperties; // 添加style属性
successText?: string;
showCover?: boolean;
}
interface UploadingFile {
@ -31,6 +33,8 @@ const AvatarUploader: React.FC<AvatarUploaderProps> = ({
className,
placeholder = "点击上传",
style, // 解构style属性
successText = "上传成功",
showCover = true,
}) => {
const { handleFileUpload, uploadProgress } = useTusUpload();
const [file, setFile] = useState<UploadingFile | null>(null);
@ -92,12 +96,12 @@ const AvatarUploader: React.FC<AvatarUploaderProps> = ({
// 使用 resolved 的最新值调用 onChange
// 强制刷新 Avatar 组件
setAvatarKey((prev) => prev + 1); // 修改 key 强制重新挂载
onChange?.(uploadedUrl);
console.log(uploadedUrl);
toast.success("头像上传成功");
onChange?.(uploadedUrl);
toast.success(successText);
} catch (error) {
console.error("上传错误:", error);
toast.error("头像上传失败");
toast.error("上传失败");
setFile((prev) => ({ ...prev!, status: "error" }));
} finally {
setUploading(false);
@ -124,7 +128,7 @@ const AvatarUploader: React.FC<AvatarUploaderProps> = ({
accept="image/*"
style={{ display: "none" }}
/>
{previewUrl ? (
{(previewUrl && showCover) ? (
<Avatar
key={avatarKey}
ref={avatarRef}

View File

@ -0,0 +1,68 @@
import { useEffect, useState } from "react";
// import UncoverAvatarUploader from "../uploader/UncoverAvatarUploader ";
import { Upload, Progress, Button, Image, Form } from "antd";
import { DeleteOutlined } from "@ant-design/icons";
import AvatarUploader from "./AvatarUploader";
import { isEqual } from 'lodash';
interface MultiAvatarUploaderProps {
value?: string[];
onChange?: (value: string[]) => void;
}
export function MultiAvatarUploader({
value,
onChange,
}: MultiAvatarUploaderProps) {
const [imageList, setImageList] = useState<string[]>(value || [])
const [previewImage, setPreviewImage] = useState<string>("");
useEffect(() => {
if (!isEqual(value, imageList)) {
setImageList(value || []);
}
}, [value]);
useEffect(() => {
onChange?.(imageList)
}, [imageList])
return <>
<div className="flex gap-2 mb-2" style={{ width: "1200px" }}>
{imageList.map((image, index) => {
return (
<div className="mr-2px relative" key={index} style={{ width: "200px", height: "100px" }} >
<Image alt="" style={{ width: "200px", height: "100px" }} src={image}
preview={{
visible: previewImage === image,
onVisibleChange: (visible) =>
setPreviewImage(visible ? image || "" : "")
}} >
</Image>
<Button
type="text"
danger
icon={<DeleteOutlined className="text-red" />}
onClick={() => image && setImageList(imageList.filter((_, i) => i !== index))}
style={{
position: "absolute", // 绝对定位
top: "0", // 顶部对齐
right: "0", // 右侧对齐
zIndex: 1, // 确保按钮在图片上方
padding: "4px", // 调整按钮内边距
backgroundColor: "rgba(255, 255, 255, 0.2)", // 半透明背景
borderRadius: "50%", // 圆形按钮
}}
/>
</div>
)
})}
</div>
<div className="flex">
<AvatarUploader showCover={false} successText={'轮播图上传成功'} onChange={(value) => {
console.log(value);
setImageList([...imageList, value])
}}></AvatarUploader>
</div>
</>;
}
export default MultiAvatarUploader;

View File

@ -1,173 +0,0 @@
import React, { useState } from 'react';
import { Upload, Modal, message } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { useTusUpload } from "@web/src/hooks/useTusUpload";
import toast from "react-hot-toast";
export interface MultiImageUploadProps {
value?: string;
placeholder?: string;
className?: string;
onChange?: (value: string) => void;
compressed?: boolean;
style?: React.CSSProperties; // 添加style属性
}
interface UploadFile {
name: string;
progress: number;
status: "uploading" | "done" | "error";
url?: string;
fileKey?: string;
uid: string;
}
const MultiImageUpload: React.FC<MultiImageUploadProps> = ({
value,
onChange,
compressed = false,
className,
placeholder = "点击上传",
style, // 解构style属性
}) => {
const [fileList, setFileList] = useState<UploadFile[] | null>(null); // 存储已上传的文件列表
const [previewVisible, setPreviewVisible] = useState(false); // 控制预览模态框的显示
const [previewImage, setPreviewImage] = useState(''); // 当前预览的图片URL
const { handleFileUpload, uploadProgress } = useTusUpload();
const [compressedUrl, setCompressedUrl] = useState<string>(value || "");
const [url, setUrl] = useState<string>(value || "");
const [uploading, setUploading] = useState(false);
// 处理文件上传前的校验
const beforeUpload = (file) => {
const isImage = file.type.startsWith('image/');
if (!isImage) {
message.error('只能上传图片文件!');
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('图片大小不能超过10MB');
}
return isImage && isLt10M;
};
// 处理文件列表变化
const handleChange = async ({ fileList }) => {
//setFileList(fileList);
console.log(fileList);
const imageUrls = fileList.map(file => {
return URL.createObjectURL(file.originFileObj)
});
console.log("imageUrls", imageUrls);
const newFileList = fileList.map(file => {
return {
name: file.name,
progress: 0,
status: "uploading",
//uid: file.uid,
fileKey: `${file.name}-${Date.now()}`,
//url: file.url,
}
});
console.log("newFileList", newFileList);
setUploading(true);
try {
const resFileList = newFileList.map(async (file, index) => {
const uploadedUrl = await new Promise<string>((resolve, reject) => {
handleFileUpload(
fileList[index].originFileObj,
(result) => {
() => {
return {
...newFileList[index],
progress: 100,
status: "done",
fileId: result.fileId,
url: result.url,
compressedUrl: result.compressedUrl,
}
}
// setFile((prev) => ({
// ...prev!,
// progress: 100,
// status: "done",
// fileId: result.fileId,
// url: result.url,
// compressedUrl: result.compressedUrl,
// }));
setUrl(result.url);
setCompressedUrl(result.compressedUrl);
// 直接使用 result 中的最新值
resolve(compressed ? result.compressedUrl : result.url);
},
(error) => {
reject(error);
},
newFileList[index]?.fileKey
);
});
})
// await new Promise((resolve) => setTimeout(resolve,4999)); // 方法1使用 await 暂停执行
// 使用 resolved 的最新值调用 onChange
// 强制刷新 Avatar 组件
// setAvatarKey((prev) => prev + 1); // 修改 key 强制重新挂载
onChange?.(resFileList);
console.log(resFileList);
toast.success("图片上传成功");
} catch (error) {
console.error("上传错误:", error);
toast.error("图片上传失败");
// setFile((prev) => ({ ...prev!, status: "error" }));
} finally {
setUploading(false);
}
};uploadProgress
// 处理预览
const handlePreview = async (file) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
setPreviewImage(file.url || file.preview);
setPreviewVisible(true);
};
// 关闭预览模态框
const handleCancel = () => setPreviewVisible(false);
// 将文件转换为Base64格式用于预览
const getBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
};
return (
<div>
<Upload.Dragger
listType="picture-card" // 卡片样式
fileList={fileList} // 已上传的文件列表
beforeUpload={beforeUpload} // 上传前的校验
onChange={handleChange} // 文件列表变化时的回调
onPreview={handlePreview} // 预览回调
multiple // 支持多选
// style={{ width: '200px' }}
>
<p className="ant-upload-drag-icon">
</p>
<p className="ant-upload-text"></p>
<p className="ant-upload-hint"></p>
</Upload.Dragger>
<Modal visible={previewVisible} footer={null} onCancel={handleCancel}>
<img alt="预览" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
);
};
export default MultiImageUpload;

View File

@ -1,18 +0,0 @@
import AvatarUploader from "./AvatarUploader"
interface TestUploaderProps {
value?: string[],
onChange?: (value: string[]) => void
}
export function TestUploader({
value,
onChange,
}: TestUploaderProps) {
return <>
<AvatarUploader></AvatarUploader>
</>
}