From 15105c1efba1ce92be12f2f638af1ec76a3ba491 Mon Sep 17 00:00:00 2001 From: chenos Date: Fri, 13 Aug 2021 23:34:17 +0800 Subject: [PATCH] feat: improve the upload component --- packages/client/package.json | 1 + .../client/src/schemas/upload/demos/demo1.tsx | 33 +++ .../client/src/schemas/upload/demos/demo2.tsx | 39 +++ packages/client/src/schemas/upload/index.md | 57 +--- packages/client/src/schemas/upload/index.tsx | 250 +++++++++++++++++- yarn.lock | 20 +- 6 files changed, 343 insertions(+), 57 deletions(-) create mode 100644 packages/client/src/schemas/upload/demos/demo1.tsx create mode 100644 packages/client/src/schemas/upload/demos/demo2.tsx diff --git a/packages/client/package.json b/packages/client/package.json index ded5d7cb3..2ff46bfda 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -40,6 +40,7 @@ "react-dnd-touch-backend": "^14.0.0", "react-helmet": "^6.1.0", "react-hooks-global-state": "^1.0.1", + "react-image-lightbox": "^5.1.4", "react-modal-hook": "^3.0.0", "umi-request": "^1.3.5" } diff --git a/packages/client/src/schemas/upload/demos/demo1.tsx b/packages/client/src/schemas/upload/demos/demo1.tsx new file mode 100644 index 000000000..bd26170c4 --- /dev/null +++ b/packages/client/src/schemas/upload/demos/demo1.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { SchemaRenderer } from '../../'; + +const schema = { + type: 'object', + properties: { + input: { + type: 'object', + title: `编辑模式`, + 'x-decorator': 'FormItem', + 'x-component': 'Upload.File', + 'x-reactions': { + target: 'read', + fulfill: { + state: { + value: '{{$self.value}}', + }, + }, + }, + }, + read: { + type: 'object', + title: `阅读模式`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'Upload.File', + }, + }, +}; + +export default () => { + return ; +}; diff --git a/packages/client/src/schemas/upload/demos/demo2.tsx b/packages/client/src/schemas/upload/demos/demo2.tsx new file mode 100644 index 000000000..b124edc03 --- /dev/null +++ b/packages/client/src/schemas/upload/demos/demo2.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { SchemaRenderer } from '../../'; + +const schema = { + type: 'object', + properties: { + input: { + type: 'object', + title: `编辑模式`, + 'x-decorator': 'FormItem', + 'x-component': 'Upload.File', + 'x-component-props': { + multiple: true, + }, + 'x-reactions': { + target: 'read', + fulfill: { + state: { + value: '{{$self.value}}', + }, + }, + }, + }, + read: { + type: 'object', + title: `阅读模式`, + 'x-read-pretty': true, + 'x-decorator': 'FormItem', + 'x-component': 'Upload.File', + 'x-component-props': { + multiple: true, + }, + }, + }, +}; + +export default () => { + return ; +}; diff --git a/packages/client/src/schemas/upload/index.md b/packages/client/src/schemas/upload/index.md index da15e9419..5ec39718d 100644 --- a/packages/client/src/schemas/upload/index.md +++ b/packages/client/src/schemas/upload/index.md @@ -23,59 +23,10 @@ group: ## Examples -### 上传 +### 单文件上传 -```tsx -import React from 'react'; -import { Button } from 'antd' -import { UploadOutlined, InboxOutlined } from '@ant-design/icons' -import { SchemaRenderer } from '../'; -import Upload from './'; + -const NormalUpload = (props) => { - return ( - - - - ) -} +### 多文件上传 -const schema = { - type: 'object', - properties: { - input: { - type: 'string', - title: `编辑模式`, - 'x-decorator': 'FormItem', - 'x-component': 'NormalUpload', - 'x-reactions': { - target: 'read', - fulfill: { - state: { - value: '{{$self.value}}', - }, - }, - }, - }, - read: { - type: 'string', - title: `阅读模式`, - 'x-read-pretty': true, - 'x-decorator': 'FormItem', - 'x-component': 'NormalUpload', - }, - } -}; - -export default () => { - return ( - - ); -}; -``` + diff --git a/packages/client/src/schemas/upload/index.tsx b/packages/client/src/schemas/upload/index.tsx index 9226463f0..fa0a116c9 100644 --- a/packages/client/src/schemas/upload/index.tsx +++ b/packages/client/src/schemas/upload/index.tsx @@ -1,6 +1,12 @@ import React, { useEffect } from 'react'; -import { connect, mapProps, mapReadPretty, useField } from '@formily/react'; -import { Upload as AntdUpload } from 'antd'; +import { + connect, + mapProps, + mapReadPretty, + observer, + useField, +} from '@formily/react'; +import { Button, Progress, Upload as AntdUpload } from 'antd'; import { UploadChangeParam, UploadProps as AntdUploadProps, @@ -11,6 +17,12 @@ import { UploadFile } from 'antd/lib/upload/interface'; import { isArr, toArr } from '@formily/shared'; import { UPLOAD_PLACEHOLDER } from './placeholder'; import { usePrefixCls } from '@formily/antd/esm/__builtins__'; +import { useState } from 'react'; +import Lightbox from 'react-image-lightbox'; +import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app +import { useMap } from 'ahooks'; +import DeleteOutlined from '@ant-design/icons/DeleteOutlined'; +import UploadOutlined from '@ant-design/icons/UploadOutlined'; type UploadProps = Omit & { onChange?: (fileList: UploadFile[]) => void; @@ -24,6 +36,7 @@ type DraggerProps = Omit & { type ComposedUpload = React.FC & { Dragger?: React.FC; + File?: React.FC; }; type IUploadProps = { @@ -168,7 +181,15 @@ export const Upload: ComposedUpload = connect( const field = useField(); console.log('field.value', field.value); return (field.value || []).map((item) => ( -
{item.url ? {item.name} : {item.name}}
+
+ {item.url ? ( + + {item.name} + + ) : ( + {item.name} + )} +
)); }), ); @@ -186,6 +207,229 @@ const Dragger = connect( }), ); +function toItem(file) { + if (file?.response?.data) { + file = file.response.data; + } + return { + ...file, + id: file.id || file.uid, + title: file.title || file.name, + imageUrl: getImageByUrl(file.url, { + exclude: ['.png', '.jpg', '.jpeg', '.gif'], + }), + }; +} + +function toMap(fileList: any) { + if (!fileList) { + return []; + } + if (typeof fileList !== 'object') { + return []; + } + if (Object.keys(fileList).length === 0) { + return []; + } + let list = fileList; + if (!Array.isArray(fileList) && typeof fileList === 'object') { + list = [fileList]; + } + return list.map((item) => { + return [item.id || item.uid, toItem(item)]; + }); +} + +const toImages = (fileList) => { + if (!fileList) { + return []; + } + if (typeof fileList !== 'object') { + return []; + } + if (Object.keys(fileList).length === 0) { + return []; + } + let list = fileList; + if (!Array.isArray(fileList) && typeof fileList === 'object') { + list = [fileList]; + } + return list.map((item) => { + return { + ...item, + title: item.title || item.name, + imageUrl: getImageByUrl(item.url, { + exclude: ['.png', '.jpg', '.jpeg', '.gif'], + }), + }; + }); +}; + +Upload.File = connect( + (props: UploadProps & { value?: any }) => { + const { + multiple, + listType = 'picture-card', + value, + onChange, + ...others + } = props; + const [map, { set, setAll, remove }] = useMap(toMap(value)); + const images = toImages(value); + const [photoIndex, setPhotoIndex] = useState(0); + const [visible, setVisible] = useState(false); + useEffect(() => { + setAll(toMap(value)); + }, [value]); + console.log('value', value); + return ( +
+ { + console.log({ multiple, file }); + if (multiple) { + set(file.uid, toItem(file)); + } else { + setAll([[file.uid, toItem(file)]]); + } + if (file.status === 'done') { + if (multiple) { + onChange([...toArr(value), file?.response?.data]); + } else { + onChange(file?.response?.data); + } + } + }} + showUploadList={false} + > + + + {[...map.entries()].map( + ([key, item]) => + item.id && ( + + ), + )} + {visible && ( + setVisible(false)} + onMovePrevRequest={() => + setPhotoIndex((photoIndex + images.length - 1) % images.length) + } + onMoveNextRequest={() => + setPhotoIndex((photoIndex + 1) % images.length) + } + imageTitle={images[photoIndex]?.title} + // toolbarButtons={[ + //
下载
, + // ]} + // imageCaption={'xxx'} + /> + )} +
+ ); + }, + mapReadPretty( + observer((props) => { + const field = useField(); + const images = toImages(field.value); + const [photoIndex, setPhotoIndex] = useState(0); + const [visible, setVisible] = useState(false); + console.log('field.value', field.value, images); + return ( +
+ {images.map((item) => ( + + ))} + {visible && ( + setVisible(false)} + onMovePrevRequest={() => + setPhotoIndex((photoIndex + images.length - 1) % images.length) + } + onMoveNextRequest={() => + setPhotoIndex((photoIndex + 1) % images.length) + } + imageTitle={images[photoIndex]?.title} + // toolbarButtons={[ + //
下载
, + // ]} + // imageCaption={'xxx'} + /> + )} +
+ ); + }), + ), +); + Upload.Dragger = Dragger; export default Upload; diff --git a/yarn.lock b/yarn.lock index 63dc52e41..bb9a2ba56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16108,6 +16108,14 @@ react-hooks-global-state@^1.0.1: resolved "https://registry.npmjs.org/react-hooks-global-state/-/react-hooks-global-state-1.0.1.tgz#3e4e1009f3e8a6fd001cd507808b4f1b289e9b27" integrity sha512-GFe/7KOdf8toByd8eF5LBhogMn65XSyghIzInsnA1e8vqIxGdT2Ohs2Ohdclp8CX0QU9xNj72ZIt+WD/SQEh9Q== +react-image-lightbox@^5.1.4: + version "5.1.4" + resolved "https://registry.npmjs.org/react-image-lightbox/-/react-image-lightbox-5.1.4.tgz#5b847dcb79e9efdf9d7cd5621a92e0f156d2cf30" + integrity sha512-kTiAODz091bgT7SlWNHab0LSMZAPJtlNWDGKv7pLlLY1krmf7FuG1zxE0wyPpeA8gPdwfr3cu6sPwZRqWsc3Eg== + dependencies: + prop-types "^15.7.2" + react-modal "^3.11.1" + react-intl@3.12.1: version "3.12.1" resolved "https://registry.npmjs.org/react-intl/-/react-intl-3.12.1.tgz#e9a783ea20302e9da25e4eda59e5593a43d2ec80" @@ -16136,7 +16144,7 @@ react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react- resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -16146,6 +16154,16 @@ react-modal-hook@^3.0.0: resolved "https://registry.npmjs.org/react-modal-hook/-/react-modal-hook-3.0.0.tgz#24b2ff2acf288ef25c11e4fb11b329458a823ea3" integrity sha512-mNkJwgEtOoIabuILlWnGAto993WVkimZASBWk/rAGZ6tNIqjx4faQtNdr/X31vw+QGfKhspXc4vPi1jbfkk2Yg== +react-modal@^3.11.1: + version "3.14.3" + resolved "https://registry.npmjs.org/react-modal/-/react-modal-3.14.3.tgz#7eb7c5ec85523e5843e2d4737cc17fc3f6aeb1c0" + integrity sha512-+C2KODVKyu20zHXPJxfOOcf571L1u/EpFlH+oS/3YDn8rgVE51QZuxuuIwabJ8ZFnOEHaD+r6XNjqwtxZnXO0g== + dependencies: + exenv "^1.2.0" + prop-types "^15.7.2" + react-lifecycles-compat "^3.0.0" + warning "^4.0.3" + react-native-swipeout@^2.2.2: version "2.3.6" resolved "https://registry.npmjs.org/react-native-swipeout/-/react-native-swipeout-2.3.6.tgz#47dac8a835825cf3f2eef9e495574a3d9ab6d3fa"