完成图片压缩功能
This commit is contained in:
29
package-lock.json
generated
29
package-lock.json
generated
@@ -11,6 +11,7 @@
|
|||||||
"antd": "^5.19.3",
|
"antd": "^5.19.3",
|
||||||
"apexcharts": "^3.41.0",
|
"apexcharts": "^3.41.0",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
|
"compressorjs": "^1.2.1",
|
||||||
"dicebear": "^9.2.1",
|
"dicebear": "^9.2.1",
|
||||||
"flatpickr": "^4.6.13",
|
"flatpickr": "^4.6.13",
|
||||||
"headlessui": "^0.0.0",
|
"headlessui": "^0.0.0",
|
||||||
@@ -2770,6 +2771,12 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/blueimp-canvas-to-blob": {
|
||||||
|
"version": "3.29.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz",
|
||||||
|
"integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/boxen": {
|
"node_modules/boxen": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/boxen/-/boxen-7.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/boxen/-/boxen-7.1.1.tgz",
|
||||||
@@ -3264,6 +3271,16 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/compressorjs": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/compressorjs/-/compressorjs-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"blueimp-canvas-to-blob": "^3.29.0",
|
||||||
|
"is-blob": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/compute-gcd": {
|
"node_modules/compute-gcd": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/compute-gcd/-/compute-gcd-1.2.1.tgz",
|
"resolved": "https://registry.npmmirror.com/compute-gcd/-/compute-gcd-1.2.1.tgz",
|
||||||
@@ -4230,6 +4247,18 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-blob": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/is-blob/-/is-blob-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-ci": {
|
"node_modules/is-ci": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-3.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-3.0.1.tgz",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"antd": "^5.19.3",
|
"antd": "^5.19.3",
|
||||||
"apexcharts": "^3.41.0",
|
"apexcharts": "^3.41.0",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
|
"compressorjs": "^1.2.1",
|
||||||
"dicebear": "^9.2.1",
|
"dicebear": "^9.2.1",
|
||||||
"flatpickr": "^4.6.13",
|
"flatpickr": "^4.6.13",
|
||||||
"headlessui": "^0.0.0",
|
"headlessui": "^0.0.0",
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { FileDir } from '@/types/app/file';
|
import { FileDir } from '@/types/app/file';
|
||||||
import { InboxOutlined } from '@ant-design/icons';
|
import { InboxOutlined } from '@ant-design/icons';
|
||||||
import type { UploadProps } from 'antd';
|
import type { UploadProps } from 'antd';
|
||||||
import { message, Modal, Upload } from 'antd';
|
import { message, Modal, Radio, Select, Upload } from 'antd';
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores';
|
||||||
import { baseURL } from '@/utils/request'
|
import { baseURL } from '@/utils/request';
|
||||||
|
import Compressor from 'compressorjs';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
const { Dragger } = Upload;
|
const { Dragger } = Upload;
|
||||||
|
|
||||||
@@ -15,7 +17,9 @@ interface UploadFileProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default ({ dir, open, onCancel, onSuccess }: UploadFileProps) => {
|
export default ({ dir, open, onCancel, onSuccess }: UploadFileProps) => {
|
||||||
const store = useUserStore()
|
const store = useUserStore();
|
||||||
|
const [quality, setQuality] = useState(1000);
|
||||||
|
const [isCompressionUpload, setIsCompressionUpload] = useState(false);
|
||||||
|
|
||||||
const uploadProps: UploadProps = {
|
const uploadProps: UploadProps = {
|
||||||
name: 'files',
|
name: 'files',
|
||||||
@@ -25,28 +29,80 @@ export default ({ dir, open, onCancel, onSuccess }: UploadFileProps) => {
|
|||||||
headers: {
|
headers: {
|
||||||
"Authorization": `Bearer ${store.token}`
|
"Authorization": `Bearer ${store.token}`
|
||||||
},
|
},
|
||||||
|
// 上传之前触发
|
||||||
|
beforeUpload: async (file) => {
|
||||||
|
if (quality === 1000) return file
|
||||||
|
|
||||||
|
// 对图片进行压缩处理
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
new Compressor(file, {
|
||||||
|
quality,
|
||||||
|
success: (file) => {
|
||||||
|
resolve(file);
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
reject(err);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
},
|
||||||
onChange(info) {
|
onChange(info) {
|
||||||
const { status } = info.file;
|
const { status } = info.file;
|
||||||
|
|
||||||
let res;
|
let res;
|
||||||
if (status !== 'uploading') {
|
if (status !== 'uploading') {
|
||||||
res = info?.file?.response
|
res = info?.file?.response;
|
||||||
|
|
||||||
if (res.code === 400) return message.error(res.message)
|
if (res?.code === 400) return message.error(res.message);
|
||||||
}
|
}
|
||||||
if (status === 'done') {
|
if (status === 'done') {
|
||||||
message.success(`文件上传成功`);
|
message.success(`文件上传成功`);
|
||||||
onSuccess()
|
onSuccess();
|
||||||
} else if (status === 'error') {
|
} else if (status === 'error') {
|
||||||
message.error(`文件上传失败:${res.message}`);
|
message.error(`文件上传失败:${res?.message}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
className: "py-4"
|
className: "py-4"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 初始化操作
|
||||||
|
const onCloseModel = () => {
|
||||||
|
setIsCompressionUpload(false);
|
||||||
|
setQuality(1000);
|
||||||
|
onCancel();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal title="文件上传" open={open} onCancel={onCancel} footer={null}>
|
<Modal title="文件上传" open={open} onCancel={onCloseModel} footer={null}>
|
||||||
|
<div className='my-4'>
|
||||||
|
<Radio.Group defaultValue={0} onChange={(e) => setIsCompressionUpload(e.target.value ? true : false)}>
|
||||||
|
<Radio value={0}>无损上传</Radio>
|
||||||
|
<Radio value={1}>压缩上传</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
|
||||||
|
{
|
||||||
|
isCompressionUpload && <Select
|
||||||
|
onChange={setQuality}
|
||||||
|
options={[
|
||||||
|
{ value: NaN, label: '自适应压缩(推荐)' },
|
||||||
|
{ value: 1, label: '轻量压缩' },
|
||||||
|
{ value: 0.9, label: '0.9' },
|
||||||
|
{ value: 0.8, label: '0.8' },
|
||||||
|
{ value: 0.7, label: '0.7' },
|
||||||
|
{ value: 0.6, label: '0.6' },
|
||||||
|
{ value: 0.5, label: '0.5' },
|
||||||
|
{ value: 0.4, label: '0.4' },
|
||||||
|
{ value: 0.3, label: '0.3' },
|
||||||
|
{ value: 0.2, label: '0.2' },
|
||||||
|
{ value: 0.1, label: '0.1' },
|
||||||
|
]}
|
||||||
|
placeholder="请选择图片压缩质量"
|
||||||
|
className='min-w-44'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<Dragger {...uploadProps}>
|
<Dragger {...uploadProps}>
|
||||||
<p className="ant-upload-drag-icon">
|
<p className="ant-upload-drag-icon">
|
||||||
<InboxOutlined />
|
<InboxOutlined />
|
||||||
@@ -56,5 +112,5 @@ export default ({ dir, open, onCancel, onSuccess }: UploadFileProps) => {
|
|||||||
</Dragger>
|
</Dragger>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { getSwiperListAPI, addSwiperDataAPI, editSwiperDataAPI, delSwiperDataAPI
|
|||||||
import { Swiper } from '@/types/app/swiper';
|
import { Swiper } from '@/types/app/swiper';
|
||||||
import Title from '@/components/Title';
|
import Title from '@/components/Title';
|
||||||
import { ColumnsType } from 'antd/es/table';
|
import { ColumnsType } from 'antd/es/table';
|
||||||
|
import { CloudUploadOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
const SwiperPage = () => {
|
const SwiperPage = () => {
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
@@ -14,13 +15,13 @@ const SwiperPage = () => {
|
|||||||
const [tab, setTab] = useState<string>('list');
|
const [tab, setTab] = useState<string>('list');
|
||||||
|
|
||||||
const columns: ColumnsType<Swiper> = [
|
const columns: ColumnsType<Swiper> = [
|
||||||
{ title: 'ID', dataIndex: 'id', key: 'id' },
|
{ title: 'ID', dataIndex: 'id', key: 'id', align: 'center' },
|
||||||
{
|
{
|
||||||
title: '图片', dataIndex: 'image', key: 'image', width: 200,
|
title: '图片', dataIndex: 'image', key: 'image', width: 200,
|
||||||
render: (text: string) => <img src={text} alt="swiper" className="w-full rounded cursor-pointer" onClick={() => { setViewImage(text); setIsModelOpen(true) }} />
|
render: (text: string) => <img src={text} alt="swiper" className="w-full rounded cursor-pointer" onClick={() => { setViewImage(text); setIsModelOpen(true) }} />
|
||||||
},
|
},
|
||||||
{ title: '标题', dataIndex: 'title', key: 'title' },
|
{ title: '标题', dataIndex: 'title', key: 'title' },
|
||||||
{ title: '描述', dataIndex: 'description', key: 'description' },
|
{ title: '描述', dataIndex: 'description', key: 'description', width: 500, },
|
||||||
{
|
{
|
||||||
title: '操作', key: 'action', align: 'center',
|
title: '操作', key: 'action', align: 'center',
|
||||||
render: (text: string, record: Swiper) => (
|
render: (text: string, record: Swiper) => (
|
||||||
@@ -84,6 +85,11 @@ const SwiperPage = () => {
|
|||||||
setSwiper({} as Swiper);
|
setSwiper({} as Swiper);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 文件上传
|
||||||
|
const UploadBtn = () => (
|
||||||
|
<CloudUploadOutlined className='text-xl cursor-pointer'/>
|
||||||
|
)
|
||||||
|
|
||||||
const tabItems = [
|
const tabItems = [
|
||||||
{
|
{
|
||||||
label: '轮播图列表',
|
label: '轮播图列表',
|
||||||
@@ -131,7 +137,7 @@ const SwiperPage = () => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label="图片" name="image" rules={[{ required: true, message: '轮播图不能为空' }]}>
|
<Form.Item label="图片" name="image" rules={[{ required: true, message: '轮播图不能为空' }]}>
|
||||||
<Input placeholder="https://blog.liuyuyang.net/swiper.jpg" />
|
<Input placeholder="https://blog.liuyuyang.net/swiper.jpg" addonAfter={<UploadBtn />} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ export const getListAPI = <T>(api: string) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user