|
@@ -0,0 +1,212 @@
|
|
|
+import { dataURLtoFile } from '@/utils/index';
|
|
|
+import { useDebounce } from 'ahooks';
|
|
|
+import { Button, Col, Form, InputNumber, Modal, Row } from 'antd';
|
|
|
+import { RcFile } from 'antd/lib/upload';
|
|
|
+import 'cropperjs/dist/cropper.css';
|
|
|
+import React, { useEffect, useRef, useState } from 'react';
|
|
|
+import Cropper from 'react-cropper';
|
|
|
+
|
|
|
+interface Props {
|
|
|
+ size?: { width: number; height: number };
|
|
|
+ isEdit?: boolean;
|
|
|
+ file?: RcFile;
|
|
|
+ visible?: boolean;
|
|
|
+ onChange?: (fileList: any[], file: any) => void;
|
|
|
+ onClose?: () => void;
|
|
|
+}
|
|
|
+
|
|
|
+const CropperImg: React.FC<Props> = (props) => {
|
|
|
+ const { size, isEdit, file, visible, onChange, onClose } = props;
|
|
|
+ const [image, setImage] = useState(''); // 初始图片
|
|
|
+ const cropperRef = useRef<any>(null); // 使用 useRef 来获取实例
|
|
|
+ const [dragMode] = useState<'none' | 'crop' | 'move'>('none');
|
|
|
+ const [imgSize, setImageSize] = useState<{ width: number; height: number }>({
|
|
|
+ width: 0,
|
|
|
+ height: 0,
|
|
|
+ });
|
|
|
+ const [detail, setDetail] = useState<{ width: number; height: number; rotate: number }>({
|
|
|
+ width: 0,
|
|
|
+ height: 0,
|
|
|
+ rotate: 0,
|
|
|
+ }); // 设置数据
|
|
|
+
|
|
|
+ /** 获取宽高 image */
|
|
|
+ useEffect(() => {
|
|
|
+ if (file) {
|
|
|
+ let img: any = new Image();
|
|
|
+ let _URL = window.URL || window.webkitURL;
|
|
|
+ img.onload = function (e: any) {
|
|
|
+ const reader = new FileReader();
|
|
|
+ if (!isEdit) {
|
|
|
+ // 可更改
|
|
|
+ let sizeWidth = size?.width || this.width,
|
|
|
+ sizeHeight = size?.height || this.height;
|
|
|
+ let width = sizeWidth > this.width ? this.width : sizeWidth;
|
|
|
+ let height = sizeHeight > this.height ? this.height : sizeHeight;
|
|
|
+ setImageSize({ width: width, height: height });
|
|
|
+ setDetail({ ...detail, width: width, height: height });
|
|
|
+ } else {
|
|
|
+ // 不可更改
|
|
|
+ setImageSize({ width: this.width, height: this.height });
|
|
|
+ if (size?.width && size?.height) {
|
|
|
+ setDetail({ ...detail, width: size?.width, height: size?.height });
|
|
|
+ } else {
|
|
|
+ setDetail({ ...detail, width: this.width, height: this.height });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ reader.onload = async () => {
|
|
|
+ setImage(reader.result as any);
|
|
|
+ };
|
|
|
+ reader.readAsDataURL(file);
|
|
|
+ };
|
|
|
+ img.src = _URL.createObjectURL(file);
|
|
|
+ }
|
|
|
+ }, [size, isEdit]);
|
|
|
+
|
|
|
+ const debouncedValue = useDebounce(detail, { wait: 500 });
|
|
|
+
|
|
|
+ /** 数值变化重置实例 */
|
|
|
+ useEffect(() => {
|
|
|
+ if (cropperRef.current && debouncedValue?.width) {
|
|
|
+ console.log(cropperRef.current?.cropper, debouncedValue);
|
|
|
+ cropperRef.current?.cropper.setData(debouncedValue);
|
|
|
+ }
|
|
|
+ }, [debouncedValue]);
|
|
|
+
|
|
|
+ /** 初始化 */
|
|
|
+ useEffect(() => {
|
|
|
+ if (visible && cropperRef.current) {
|
|
|
+ setTimeout(() => {
|
|
|
+ cropperRef.current?.cropper.reset();
|
|
|
+ }, 200);
|
|
|
+ }
|
|
|
+ }, [visible]);
|
|
|
+
|
|
|
+ /** 关闭 */
|
|
|
+ const cancel = () => {
|
|
|
+ onClose && onClose();
|
|
|
+ setImage('');
|
|
|
+ };
|
|
|
+
|
|
|
+ const getCropData = async () => {
|
|
|
+ if (cropperRef.current) {
|
|
|
+ let newFile = await dataURLtoFile(
|
|
|
+ cropperRef.current?.cropper.getCroppedCanvas().toDataURL('image/jpeg'),
|
|
|
+ file?.name.split('.')[0] + '.jpg',
|
|
|
+ );
|
|
|
+ onChange &&
|
|
|
+ onChange(
|
|
|
+ [
|
|
|
+ {
|
|
|
+ lastModified: newFile.lastModified,
|
|
|
+ name: newFile.name,
|
|
|
+ percent: 0,
|
|
|
+ size: newFile.size,
|
|
|
+ thumbUrl: cropperRef.current?.cropper.getCroppedCanvas().toDataURL('image/jpeg'),
|
|
|
+ type: newFile.type,
|
|
|
+ originFileObj: newFile,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ newFile,
|
|
|
+ );
|
|
|
+ setImage('');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Modal
|
|
|
+ open={visible}
|
|
|
+ onCancel={cancel}
|
|
|
+ width={1000}
|
|
|
+ footer={
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center' }}>
|
|
|
+ <Button onClick={cancel}>取消</Button>
|
|
|
+ <Button type="primary" onClick={getCropData}>
|
|
|
+ 确定
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ >
|
|
|
+ <div style={{ width: '100%' }}>
|
|
|
+ <Row>
|
|
|
+ <Col span={16}>
|
|
|
+ <Cropper
|
|
|
+ ref={cropperRef} // 绑定 ref
|
|
|
+ style={{
|
|
|
+ height: 450,
|
|
|
+ width: 600,
|
|
|
+ backgroundImage: `url(${require('../../../public/init.png')})`,
|
|
|
+ border: '1px solid #efefef',
|
|
|
+ }}
|
|
|
+ initialAspectRatio={1}
|
|
|
+ preview=".img-preview"
|
|
|
+ src={image}
|
|
|
+ viewMode={1}
|
|
|
+ dragMode={dragMode}
|
|
|
+ guides={true}
|
|
|
+ rotatable={true}
|
|
|
+ minCropBoxHeight={10}
|
|
|
+ minCropBoxWidth={10}
|
|
|
+ background={false}
|
|
|
+ responsive={true}
|
|
|
+ autoCropArea={1}
|
|
|
+ cropBoxResizable={false}
|
|
|
+ zoomable={false}
|
|
|
+ movable={false}
|
|
|
+ // onInitialized={(instance: any) => {
|
|
|
+ // console.log('instance--->', instance);
|
|
|
+ // cropperRef.current = instance;
|
|
|
+ // instance.zoomTo(0.6);
|
|
|
+ // }}
|
|
|
+ crop={() => {}}
|
|
|
+ />
|
|
|
+ </Col>
|
|
|
+ <Col span={8}>
|
|
|
+ <Row gutter={[10, 10]}>
|
|
|
+ <Col span={24} style={{ height: 275 }}>
|
|
|
+ <div className="box" style={{ width: '100%', float: 'right', overflow: 'hidden' }}>
|
|
|
+ <h1 style={{ marginTop: 0 }}>预览</h1>
|
|
|
+ <div
|
|
|
+ className="img-preview"
|
|
|
+ style={{ width: '100%', float: 'left', height: '200px', overflow: 'hidden' }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </Col>
|
|
|
+ <Col>
|
|
|
+ <Form>
|
|
|
+ <Form.Item label="裁剪框宽" style={{ marginBottom: 10 }}>
|
|
|
+ <InputNumber
|
|
|
+ style={{ width: 200 }}
|
|
|
+ value={detail.width}
|
|
|
+ min={1}
|
|
|
+ max={imgSize.width}
|
|
|
+ disabled={isEdit}
|
|
|
+ onChange={(e) => {
|
|
|
+ setDetail({ ...detail, width: e || 0 });
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item label="裁剪框高" style={{ marginBottom: 0 }}>
|
|
|
+ <InputNumber
|
|
|
+ style={{ width: 200 }}
|
|
|
+ value={detail.height}
|
|
|
+ min={1}
|
|
|
+ max={imgSize.height}
|
|
|
+ disabled={isEdit}
|
|
|
+ onChange={(e) => {
|
|
|
+ setDetail({ ...detail, height: e || 0 });
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ </Col>
|
|
|
+ </Row>
|
|
|
+ </div>
|
|
|
+ </Modal>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default React.memo(CropperImg);
|