|
|
@@ -1,38 +1,217 @@ |
|
|
|
import React, { useState } from 'react'; |
|
|
|
import React, { useState, useRef } from 'react'; |
|
|
|
import { Anchor, Image, Card, List, Button, Upload, Radio } from 'antd'; |
|
|
|
import { useLocation } from 'react-router-dom'; |
|
|
|
import type { UploadProps, RadioChangeEvent } from 'antd'; |
|
|
|
import { CropperRef, Cropper } from 'react-advanced-cropper'; |
|
|
|
import 'react-advanced-cropper/dist/style.css' |
|
|
|
import 'react-advanced-cropper/dist/style.css'; |
|
|
|
import { ImageStencil } from "./components/ImageStencil"; |
|
|
|
import { DeleteOutlined, EditOutlined, UploadOutlined } from '@ant-design/icons'; |
|
|
|
import cn from 'classnames'; |
|
|
|
import './index.css' |
|
|
|
import property from '../../../../../../mock/propertyById.json' |
|
|
|
|
|
|
|
interface SampleProperty { |
|
|
|
id: number; |
|
|
|
prototypeName: string; |
|
|
|
categoryName: string; |
|
|
|
prototypeImgs: PropertyImage[]; |
|
|
|
} |
|
|
|
|
|
|
|
interface PropertyImage { |
|
|
|
id: number; |
|
|
|
imgUrl: string; |
|
|
|
imgType: number; |
|
|
|
imgName: string; |
|
|
|
locations: ImageLocation[]; |
|
|
|
} |
|
|
|
|
|
|
|
interface ImageLocation { |
|
|
|
id: number; |
|
|
|
prototypeImgId: number; |
|
|
|
clipHeight: number; |
|
|
|
clipWidth: number; |
|
|
|
clipX: number; |
|
|
|
clipY: number; |
|
|
|
cropBoxJson: string; |
|
|
|
} |
|
|
|
|
|
|
|
const options = [ |
|
|
|
{ label: 'Free', value: 0 }, |
|
|
|
{ label: '1:1', value: 1 }, |
|
|
|
{ label: '2:3', value: 2/3 }, |
|
|
|
{ label: '4:3', value: 4/3 }, |
|
|
|
]; |
|
|
|
|
|
|
|
export default () => { |
|
|
|
const { search } = useLocation() |
|
|
|
const [image] = useState( |
|
|
|
const [image, setImage] = useState( |
|
|
|
'https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A053.jpg', |
|
|
|
); |
|
|
|
const [styleValue, setStyleValue] = useState<number>(0); |
|
|
|
const [coordinateInfo, setCoordinateInfo] = useState(''); |
|
|
|
|
|
|
|
const [selectImageId, setSelectImageId] = useState<number>(); |
|
|
|
const cropperRef = useRef<CropperRef>(null); |
|
|
|
|
|
|
|
const sample: SampleProperty = property; |
|
|
|
|
|
|
|
const onChange = (cropper: CropperRef) => { |
|
|
|
console.log(cropper.getCoordinates(), cropper.getCanvas()); |
|
|
|
const coordinates = cropper.getCoordinates(); |
|
|
|
setCoordinateInfo(`X轴: ${coordinates?.left}px; Y轴: ${coordinates?.top}px; 宽度: ${coordinates?.width}px; 高度: ${coordinates?.height}px;`) |
|
|
|
}; |
|
|
|
|
|
|
|
const props: UploadProps = { |
|
|
|
name: 'file', |
|
|
|
action: 'https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188', |
|
|
|
headers: { |
|
|
|
authorization: 'authorization-text', |
|
|
|
}, |
|
|
|
onChange(info) { |
|
|
|
if (info.file.status !== 'uploading') { |
|
|
|
console.log(info.file, info.fileList); |
|
|
|
} |
|
|
|
if (info.file.status === 'done') { |
|
|
|
//message.success(`${info.file.name} file uploaded successfully`); |
|
|
|
} else if (info.file.status === 'error') { |
|
|
|
//message.error(`${info.file.name} file upload failed.`); |
|
|
|
} |
|
|
|
}, |
|
|
|
}; |
|
|
|
|
|
|
|
const onSelectChange = (value: PropertyImage) => { |
|
|
|
setImage(value.imgUrl); |
|
|
|
setSelectImageId(value.id); |
|
|
|
} |
|
|
|
|
|
|
|
const renderListItem = (item: PropertyImage) => { |
|
|
|
return (<List.Item> |
|
|
|
<Card |
|
|
|
hoverable |
|
|
|
bordered |
|
|
|
style={selectImageId === item.id ? { width: 110, borderColor: "#7c4dff" } : { width: 110 }} |
|
|
|
cover={ |
|
|
|
<img alt={item.imgName} |
|
|
|
className='p-1' |
|
|
|
src={item.imgUrl} |
|
|
|
onClick={() => { |
|
|
|
onSelectChange(item) |
|
|
|
}} |
|
|
|
/> |
|
|
|
} |
|
|
|
actions={[ |
|
|
|
<EditOutlined key="edit" />, |
|
|
|
<DeleteOutlined key="delete" />, |
|
|
|
]} |
|
|
|
> |
|
|
|
</Card> |
|
|
|
</List.Item>) |
|
|
|
} |
|
|
|
|
|
|
|
const onStyleChanged = ({ target: { value } }: RadioChangeEvent) => { |
|
|
|
setStyleValue(value); |
|
|
|
} |
|
|
|
|
|
|
|
return ( |
|
|
|
<div> |
|
|
|
<Cropper |
|
|
|
src={image} |
|
|
|
className={cn('cropper', `w-[${600}px]`, `h-[${600}px]`)} |
|
|
|
onChange={onChange} |
|
|
|
stencilProps={{ |
|
|
|
patternUrl: 'https://test.vogocm.com:9696/image/material/20230413162155A010.png' |
|
|
|
}} |
|
|
|
stencilComponent={ImageStencil} |
|
|
|
defaultCoordinates={{ |
|
|
|
left: 300, |
|
|
|
top: 300, |
|
|
|
width: 100, |
|
|
|
height: 100, |
|
|
|
}} |
|
|
|
/> |
|
|
|
<div className='flex'> |
|
|
|
<Card className='flex-none mb-[10px] w-[270px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]'> |
|
|
|
<div style={{ padding: '20px' }}> |
|
|
|
<Anchor |
|
|
|
direction="horizontal" |
|
|
|
items={[ |
|
|
|
{ |
|
|
|
key: 'part-1', |
|
|
|
href: '#part-1', |
|
|
|
title: '主图', |
|
|
|
}, |
|
|
|
{ |
|
|
|
key: 'part-2', |
|
|
|
href: '#part-2', |
|
|
|
title: '属性图', |
|
|
|
} |
|
|
|
]} |
|
|
|
/> |
|
|
|
</div> |
|
|
|
<div > |
|
|
|
<div > |
|
|
|
<div id="part-1" > |
|
|
|
<List dataSource={sample.prototypeImgs.filter(it => it.imgType === 2)} |
|
|
|
grid={{ |
|
|
|
gutter: 2, |
|
|
|
column: 2 |
|
|
|
}} |
|
|
|
header={( |
|
|
|
<div className='flex justify-center'> |
|
|
|
<Upload {...props}> |
|
|
|
<Button icon={<UploadOutlined />}>点击上传主图</Button> |
|
|
|
</Upload> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
renderItem={(item) => (renderListItem(item))} |
|
|
|
|
|
|
|
/> |
|
|
|
</div> |
|
|
|
<div id="part-2"> |
|
|
|
<List |
|
|
|
dataSource={sample.prototypeImgs.filter(it => it.imgType === 1)} |
|
|
|
grid={{ |
|
|
|
gutter: 1, |
|
|
|
column: 2, |
|
|
|
}} |
|
|
|
header={( |
|
|
|
<div className='flex justify-center'> |
|
|
|
<Upload {...props}> |
|
|
|
<Button icon={<UploadOutlined />}>点击上传属性图</Button> |
|
|
|
</Upload> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
renderItem={(item) => (renderListItem(item))} |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</Card> |
|
|
|
|
|
|
|
<Card className='flex-auto mx-[10px] mb-[10px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]'> |
|
|
|
<div className='flex flex-col justify-center'> |
|
|
|
<div className='flex justify-center text-3xl p-5 font-extrabold'>工作台</div> |
|
|
|
<div className='flex justify-center'> |
|
|
|
<Cropper |
|
|
|
ref={cropperRef} |
|
|
|
src={image} |
|
|
|
className={cn('cropper', `w-[600px]`, `h-[${600}px]`)} |
|
|
|
onChange={onChange} |
|
|
|
stencilProps={{ |
|
|
|
patternUrl: 'https://test.vogocm.com:9696/image/material/20230413162155A010.png' |
|
|
|
}} |
|
|
|
aspectRatio={() => { |
|
|
|
return styleValue |
|
|
|
}} |
|
|
|
stencilComponent={ImageStencil} |
|
|
|
defaultCoordinates={{ |
|
|
|
left: 300, |
|
|
|
top: 300, |
|
|
|
width: 100, |
|
|
|
height: 100, |
|
|
|
}} |
|
|
|
/> |
|
|
|
</div> |
|
|
|
<div className='flex justify-center p-5'> |
|
|
|
{coordinateInfo} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</Card> |
|
|
|
|
|
|
|
<Card className='flex-none mb-[10px] w-[270px] dark:bg-[rgb(33,41,70)] bg-white roundle-lg px[12px]'> |
|
|
|
<div className='flex justify-center pt-2'> |
|
|
|
<Radio.Group options={options} |
|
|
|
onChange={onStyleChanged} |
|
|
|
value={styleValue} |
|
|
|
optionType="button" |
|
|
|
buttonStyle="solid"> |
|
|
|
|
|
|
|
</Radio.Group> |
|
|
|
</div> |
|
|
|
</Card> |
|
|
|
</div> |
|
|
|
) |
|
|
|
} |