You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
6.5 KiB
JavaScript
264 lines
6.5 KiB
JavaScript
import { useEffect, useState } from "react";
|
|
import { Upload, List, Button, Space,Popconfirm } from "antd";
|
|
import { UploadOutlined, FileTextOutlined,DeleteOutlined } from "@ant-design/icons";
|
|
import { Image } from "antd";
|
|
import { fetchJSON } from "@/utils/request";
|
|
import { HT3_HOST } from "@/config";
|
|
|
|
// 加密函数
|
|
const simple_encrypt = text => {
|
|
const key = "TPDa*UU8h5%!zS";
|
|
let encrypted = [];
|
|
let keyIndex = 0;
|
|
for (let i = 0; i < text.length; i++) {
|
|
const charCode = text.charCodeAt(i);
|
|
const keyCharCode = key.charCodeAt(keyIndex);
|
|
const encryptedChar = charCode ^ keyCharCode;
|
|
encrypted.push(encryptedChar);
|
|
keyIndex = (keyIndex + 1) % key.length;
|
|
}
|
|
return encrypted.map(byte => byte.toString(16).padStart(2, "0")).join("");
|
|
};
|
|
|
|
// 获取图片列表
|
|
const getImageList = async key => {
|
|
try {
|
|
const { errcode, result } = await fetchJSON(`${HT3_HOST}/oss/list_unique_key?key=${key}`);
|
|
if (errcode === 0) {
|
|
return result
|
|
.map(file => ({
|
|
key: file.key,
|
|
encrypt_key: file.encrypt_key,
|
|
size: file.size,
|
|
status: "done",
|
|
url: file.url,
|
|
last_modified: file.last_modified,
|
|
}))
|
|
.sort((a, b) => {
|
|
const dateA = new Date(a.last_modified);
|
|
const dateB = new Date(b.last_modified);
|
|
return dateA - dateB;
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error("获取图片列表失败", error);
|
|
}
|
|
return [];
|
|
};
|
|
|
|
// 删除图片
|
|
const deleteImage = async key => {
|
|
try {
|
|
const { errcode } = await fetchJSON(`${HT3_HOST}/oss/delete_unique_key?key=${key}`, {
|
|
method: "GET",
|
|
});
|
|
return errcode === 0;
|
|
} catch (error) {
|
|
console.error("删除图片失败", error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// 获取上传签名
|
|
const getSignature = async (file, key, onSuccess, onError) => {
|
|
try {
|
|
const { errcode, result } = await fetchJSON(`${HT3_HOST}/oss/signature_unique_key?key=${key}&filename=${file.name}`);
|
|
if (errcode === 0) {
|
|
const { method, host, signed_headers } = result;
|
|
const response = await fetch(host, {
|
|
method,
|
|
headers: signed_headers,
|
|
body: file,
|
|
});
|
|
if (response.ok) {
|
|
onSuccess(response, file);
|
|
} else {
|
|
onError(new Error("图片上传失败"));
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("获取签名失败:", error);
|
|
onError(error);
|
|
}
|
|
};
|
|
|
|
export const ImageUploader = props => {
|
|
const [fileList, setFileList] = useState([]);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [previewOpen, setPreviewOpen] = useState(false);
|
|
const [previewImage, setPreviewImage] = useState("");
|
|
const key = simple_encrypt(props.osskey);
|
|
|
|
// 组件挂载时获取图片列表
|
|
useEffect(() => {
|
|
const loadImages = async () => {
|
|
setIsLoading(true);
|
|
const images = await getImageList(key);
|
|
setFileList(images);
|
|
if (props.onChange) {
|
|
//作用是回调函数,给外面的组件传递数据
|
|
props.onChange(images);
|
|
}
|
|
setIsLoading(false);
|
|
};
|
|
|
|
if (key) {
|
|
loadImages();
|
|
}
|
|
}, [key]);
|
|
|
|
// 处理删除操作
|
|
const handleDelete = async file => {
|
|
const success = await deleteImage(file.encrypt_key);
|
|
if (success) {
|
|
const newImages = fileList.filter(item => item.encrypt_key !== file.encrypt_key);
|
|
if (props.onChange) {
|
|
props.onChange(newImages);
|
|
}
|
|
setFileList(newImages);
|
|
//console.log("删除成功");
|
|
} else {
|
|
//console.error("删除失败");
|
|
}
|
|
};
|
|
|
|
// 处理上传操作
|
|
const handleChange = ({ fileList: newFileList }) => {
|
|
setFileList(newFileList);
|
|
};
|
|
|
|
const handleUploadFile = ({ file, onProgress, onSuccess, onError }) => {
|
|
getSignature(
|
|
file,
|
|
key,
|
|
(response, file) => {
|
|
getImageList(key).then(newImages => {
|
|
if (props.onChange) {
|
|
props.onChange(newImages);
|
|
}
|
|
setFileList(prevList => {
|
|
// 找到当前正在上传的文件并移除
|
|
const index = prevList.findIndex(item => item.status === "uploading");
|
|
if (index !== -1) {
|
|
const newPrevList = [...prevList];
|
|
newPrevList.splice(index, 1);
|
|
// 找出新增元素
|
|
const newItems = newImages.filter(newItem => !newPrevList.some(prevItem => prevItem.key === newItem.key));
|
|
// 将新增元素添加到过滤后的列表末尾
|
|
return [...newPrevList, ...newItems];
|
|
}
|
|
|
|
return prevList;
|
|
});
|
|
});
|
|
},
|
|
onError
|
|
);
|
|
};
|
|
|
|
const handlePreview = file => {
|
|
if (!file.url) {
|
|
return;
|
|
}
|
|
setPreviewImage(file.url);
|
|
setPreviewOpen(true);
|
|
};
|
|
|
|
const handleRemove = () => {
|
|
return false;
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Upload
|
|
customRequest={handleUploadFile}
|
|
multiple={true}
|
|
onRemove={handleRemove}
|
|
listType="picture-card"
|
|
fileList={fileList}
|
|
onPreview={handlePreview}
|
|
onChange={handleChange}
|
|
showUploadList={{
|
|
showDownloadIcon: true,
|
|
showRemoveIcon: true,
|
|
removeIcon: file => {
|
|
return (
|
|
<Popconfirm
|
|
title="删除"
|
|
description="是否要删除文件?"
|
|
onConfirm={() => {
|
|
handleDelete(file);
|
|
}}
|
|
onCancel={() => setFileList([...fileList])}
|
|
okText="是"
|
|
cancelText="否">
|
|
<DeleteOutlined />
|
|
</Popconfirm>
|
|
);
|
|
},
|
|
}}>
|
|
<div>
|
|
<UploadOutlined />
|
|
<div style={{ marginTop: 8 }}>Upload</div>
|
|
</div>
|
|
</Upload>
|
|
<List loading={isLoading} dataSource={fileList} />
|
|
{previewImage && (
|
|
<Image
|
|
wrapperStyle={{ display: "none" }}
|
|
preview={{
|
|
visible: previewOpen,
|
|
onVisibleChange: visible => setPreviewOpen(visible),
|
|
afterOpenChange: visible => !visible && setPreviewImage(""),
|
|
}}
|
|
src={previewImage}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export const ImageViewer = props => {
|
|
const [fileList, setFileList] = useState([]);
|
|
const key = simple_encrypt(props.osskey);
|
|
|
|
// 组件挂载时获取图片列表
|
|
useEffect(() => {
|
|
const loadImages = async () => {
|
|
const images = await getImageList(key);
|
|
setFileList(images);
|
|
if (props.onChange) {
|
|
//作用是回调函数,给外面的组件传递数据
|
|
props.onChange(images);
|
|
}
|
|
};
|
|
|
|
if (key) {
|
|
loadImages();
|
|
}
|
|
}, [key]);
|
|
|
|
return (
|
|
<>
|
|
<Image.PreviewGroup>
|
|
<Space>
|
|
{fileList &&
|
|
fileList.map(item => {
|
|
return item.key.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/i) ? (
|
|
<Image key={item.encrypt_key} width={200} src={item.url} />
|
|
) : (
|
|
<a key={item.encrypt_key} href={item.url} download>
|
|
<Button type="primary" icon={<FileTextOutlined />} size="large" title={item.key.replace(/^.*[\\/]/, '')}>
|
|
...{item.key.slice(-10)}
|
|
</Button>
|
|
</a>
|
|
);
|
|
})}
|
|
</Space>
|
|
</Image.PreviewGroup>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ImageUploader;
|