From 316344e5a4de8959838fbc1adb12fddac59b6951 Mon Sep 17 00:00:00 2001 From: yehuozhili Date: Fri, 4 Sep 2020 16:37:28 +0800 Subject: [PATCH] connect components --- package.json | 2 + src/components/BackTop/index.tsx | 2 +- src/components/Calibration/index.tsx | 132 +-- src/components/CardPicker/index.tsx | 7 +- src/components/Carousel/index.tsx | 1 - src/components/Color/index.js | 73 -- src/components/Color/index.tsx | 83 ++ src/components/DataList/editorModal.js | 96 --- src/components/DataList/editorModal.tsx | 123 +++ src/components/DataList/index.js | 187 ----- src/components/DataList/index.tsx | 250 ++++++ src/components/DynamicEngine/components.js | 137 ---- src/components/DynamicEngine/components.tsx | 147 ++++ .../DynamicEngine/{index.js => index.tsx} | 24 +- src/components/DynamicEngine/schema.ts | 14 + src/components/FormEditor/index.tsx | 41 +- src/components/LoadingCp/index.js | 11 - src/components/LoadingCp/index.tsx | 20 + src/components/MutiText/index.js | 70 -- src/components/MutiText/index.tsx | 80 ++ src/components/Tab/index.js | 68 -- src/components/Tab/index.tsx | 64 ++ src/components/Upload/{index.js => index.tsx} | 79 +- src/components/Video/index.js | 17 - src/components/Video/index.tsx | 21 + src/components/Zan/{index.js => index.tsx} | 2 +- src/layouts/test.js | 150 ---- src/pages/editor/Container.js | 1 - src/pages/login/index.tsx | 36 +- src/pages/mobileTip.tsx | 2 +- src/typings.d.ts | 3 + src/utils/tool.js | 38 - src/utils/tool.ts | 40 + src/video-react.d.ts | 771 ++++++++++++++++++ yarn.lock | 20 + 35 files changed, 1828 insertions(+), 984 deletions(-) delete mode 100644 src/components/Color/index.js create mode 100644 src/components/Color/index.tsx delete mode 100644 src/components/DataList/editorModal.js create mode 100644 src/components/DataList/editorModal.tsx delete mode 100644 src/components/DataList/index.js create mode 100644 src/components/DataList/index.tsx delete mode 100644 src/components/DynamicEngine/components.js create mode 100644 src/components/DynamicEngine/components.tsx rename src/components/DynamicEngine/{index.js => index.tsx} (58%) delete mode 100644 src/components/LoadingCp/index.js create mode 100644 src/components/LoadingCp/index.tsx delete mode 100644 src/components/MutiText/index.js create mode 100644 src/components/MutiText/index.tsx delete mode 100644 src/components/Tab/index.js create mode 100644 src/components/Tab/index.tsx rename src/components/Upload/{index.js => index.tsx} (55%) delete mode 100644 src/components/Video/index.js create mode 100644 src/components/Video/index.tsx rename src/components/Zan/{index.js => index.tsx} (93%) delete mode 100644 src/layouts/test.js delete mode 100644 src/utils/tool.js create mode 100644 src/utils/tool.ts create mode 100644 src/video-react.d.ts diff --git a/package.json b/package.json index 90c1eb1..cf8a4d6 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,8 @@ }, "license": "MIT", "devDependencies": { + "@types/classnames": "^2.2.10", + "@types/react-color": "^3.0.4", "@typescript-eslint/eslint-plugin": "2.x", "@typescript-eslint/parser": "2.x", "babel-eslint": "10.x", diff --git a/src/components/BackTop/index.tsx b/src/components/BackTop/index.tsx index 689ab63..64126f0 100644 --- a/src/components/BackTop/index.tsx +++ b/src/components/BackTop/index.tsx @@ -1,6 +1,6 @@ import { memo } from 'react'; import { BackToTop, Icon } from 'zarm'; - +import React from 'react'; const themeObj = { simple: { bgColor: '#fff', color: '#999' }, black: { bgColor: '#000', color: '#fff' }, diff --git a/src/components/Calibration/index.tsx b/src/components/Calibration/index.tsx index d71a3f4..d15a38c 100644 --- a/src/components/Calibration/index.tsx +++ b/src/components/Calibration/index.tsx @@ -17,81 +17,87 @@ export default function Calibration(props: CalibrationTypes) { const calibrationRef = useRef(null); useEffect(() => { - let calibration = calibrationRef.current.getBoundingClientRect(); - setCalibration({ width: calibration.width, height: calibration.height }); - let length = direction === 'up' ? calibration.width : calibration.height; - for (let i = 0; i < length / 5; i++) { - if (i % 10 === 0) { - generateElement(true, i); - } else { - generateElement(); + if (calibrationRef.current) { + let calibration = calibrationRef.current.getBoundingClientRect(); + setCalibration({ width: calibration.width, height: calibration.height }); + let length = direction === 'up' ? calibration.width : calibration.height; + for (let i = 0; i < length / 5; i++) { + if (i % 10 === 0) { + generateElement(true, i); + } else { + generateElement(); + } } } }, [direction]); const generateElement = (item?: boolean, num?: number) => { - let createSpan = document.createElement('div'); - createSpan.className = 'calibrationLine'; - createSpan.style.backgroundColor = '#ccc'; - calibrationRef.current.style.display = 'flex'; - calibrationRef.current.style.justifyContent = 'space-between'; - if (direction === 'up') { - calibrationRef.current.style.marginLeft = '50px'; - createSpan.style.width = '1px'; - createSpan.style.height = '6px'; - createSpan.style.display = 'inline-block'; - } else { - calibrationRef.current.style.flexDirection = 'column'; - createSpan.style.height = '1px'; - createSpan.style.width = '6px'; - } - if (item) { - let createSpanContent = document.createElement('span'); + if (calibrationRef.current) { + let createSpan = document.createElement('div'); + createSpan.className = 'calibrationLine'; + createSpan.style.backgroundColor = '#ccc'; + calibrationRef.current.style.display = 'flex'; + calibrationRef.current.style.justifyContent = 'space-between'; if (direction === 'up') { - createSpan.style.height = '12px'; - createSpanContent.style.transform = 'translate3d(-4px, 20px, 0px)'; - createSpan.style.transform = 'translateY(0px)'; + calibrationRef.current.style.marginLeft = '50px'; + createSpan.style.width = '1px'; + createSpan.style.height = '6px'; + createSpan.style.display = 'inline-block'; } else { - createSpan.style.width = '12px'; - createSpanContent.style.paddingLeft = '20px'; + calibrationRef.current.style.flexDirection = 'column'; + createSpan.style.height = '1px'; + createSpan.style.width = '6px'; } - createSpanContent.style.display = 'block'; - createSpanContent.className = 'calibrationNumber'; - createSpanContent.innerHTML = num * 5 + ''; - createSpan.appendChild(createSpanContent); + if (item) { + let createSpanContent = document.createElement('span'); + if (direction === 'up') { + createSpan.style.height = '12px'; + createSpanContent.style.transform = 'translate3d(-4px, 20px, 0px)'; + createSpan.style.transform = 'translateY(0px)'; + } else { + createSpan.style.width = '12px'; + createSpanContent.style.paddingLeft = '20px'; + } + createSpanContent.style.display = 'block'; + createSpanContent.className = 'calibrationNumber'; + createSpanContent.innerHTML = num! * 5 + ''; + createSpan.appendChild(createSpanContent); + } + calibrationRef.current.appendChild(createSpan); } - calibrationRef.current.appendChild(createSpan); }; useEffect(() => { - let width = calibrationLength.width - ? calibrationLength.width - : calibrationRef.current.getBoundingClientRect().width; - let height = calibrationLength.height - ? calibrationLength.height - : calibrationRef.current.getBoundingClientRect().height; - let arr = [...calibrationRef.current.querySelectorAll('.calibrationLine')]; - if (arr.length) { - if (direction === 'up') { - calibrationRef.current.style.width = parseFloat(multiple.toFixed(1)) * width + 'px'; - arr.forEach(el => { - let dom = [...el.querySelectorAll('.calibrationNumber')][0] as HTMLElement; - if (dom) { - dom.style.transform = `translate3d(-4px, 16px, 0px) scale(${(multiple + 0.1).toFixed( - 1, - )})`; - } - }); - } else { - calibrationRef.current.style.height = parseFloat(multiple.toFixed(1)) * height + 'px'; - arr.forEach(el => { - let dom = [...el.querySelectorAll('.calibrationNumber')][0] as HTMLElement; - if (dom) { - dom.style.transform = `translate3d(-4px, -8px, 0px) scale(${(multiple + 0.1).toFixed( - 1, - )})`; - } - }); + if (calibrationRef.current) { + let width = calibrationLength.width + ? calibrationLength.width + : calibrationRef.current.getBoundingClientRect().width; + let height = calibrationLength.height + ? calibrationLength.height + : calibrationRef.current.getBoundingClientRect().height; + let arr = [...calibrationRef.current.querySelectorAll('.calibrationLine')]; + if (arr.length) { + if (direction === 'up') { + calibrationRef.current.style.width = parseFloat(multiple.toFixed(1)) * width + 'px'; + arr.forEach(el => { + let dom = [...el.querySelectorAll('.calibrationNumber')][0] as HTMLElement; + if (dom) { + dom.style.transform = `translate3d(-4px, 16px, 0px) scale(${(multiple + 0.1).toFixed( + 1, + )})`; + } + }); + } else { + calibrationRef.current.style.height = parseFloat(multiple.toFixed(1)) * height + 'px'; + arr.forEach(el => { + let dom = [...el.querySelectorAll('.calibrationNumber')][0] as HTMLElement; + if (dom) { + dom.style.transform = `translate3d(-4px, -8px, 0px) scale(${(multiple + 0.1).toFixed( + 1, + )})`; + } + }); + } } } }, [calibrationLength.height, calibrationLength.width, direction, multiple]); diff --git a/src/components/CardPicker/index.tsx b/src/components/CardPicker/index.tsx index 042c879..26b2191 100644 --- a/src/components/CardPicker/index.tsx +++ b/src/components/CardPicker/index.tsx @@ -3,16 +3,17 @@ import classnames from 'classnames'; import Icon from '../Icon'; import styles from './index.less'; import { IconTypes } from '../DynamicEngine/schema'; +import React from 'react'; interface CardPickerType { - type?: IconTypes; + type: IconTypes; icons: Array; onChange?: (v: string) => void; } export default memo((props: CardPickerType) => { const { type, icons, onChange } = props; - + console.log(type); const [selected, setSelected] = useState(type); const handlePicker = (v: IconTypes) => { @@ -33,7 +34,7 @@ export default memo((props: CardPickerType) => { return ( handlePicker(item)} key={i} > diff --git a/src/components/Carousel/index.tsx b/src/components/Carousel/index.tsx index 0f529c9..bfd6eb8 100644 --- a/src/components/Carousel/index.tsx +++ b/src/components/Carousel/index.tsx @@ -9,7 +9,6 @@ interface CarouselTypes extends CarouselConfigType { const XCarousel = memo((props: PropsWithChildren) => { const { direction, swipeable, autoPlay, isTpl, imgList, tplImg } = props; - console.log(direction); const contentRender = () => { return imgList.map((item, i) => { return ( diff --git a/src/components/Color/index.js b/src/components/Color/index.js deleted file mode 100644 index d83542a..0000000 --- a/src/components/Color/index.js +++ /dev/null @@ -1,73 +0,0 @@ - -import React from 'react' -import { SketchPicker } from 'react-color' -import { rgba2Obj } from '@/utils/tool' -// import styles from './index.less' - -class colorPicker extends React.Component { - state = { - displayColorPicker: false, - color: rgba2Obj(this.props.value), - }; - - handleClick = () => { - this.setState({ displayColorPicker: !this.state.displayColorPicker }) - }; - - handleClose = () => { - this.setState({ displayColorPicker: false }) - }; - - handleChange = (color) => { - this.setState({ color: color.rgb }) - this.props.onChange && this.props.onChange(`rgba(${color.rgb.r},${color.rgb.g},${color.rgb.b},${color.rgb.a})`) - }; - - render() { - return ( -
-
-
-
- { this.state.displayColorPicker ? -
-
- -
: null } - -
- ) - } -} - -export default colorPicker \ No newline at end of file diff --git a/src/components/Color/index.tsx b/src/components/Color/index.tsx new file mode 100644 index 0000000..8f64749 --- /dev/null +++ b/src/components/Color/index.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { SketchPicker, ColorResult } from 'react-color'; +import { rgba2Obj } from '@/utils/tool'; +// import styles from './index.less' + +interface ColorProps { + value?: string; + id?: string; + onChange?: (v: string) => void; +} + +class colorPicker extends React.Component { + state = { + displayColorPicker: false, + color: rgba2Obj(this.props.value), + }; + + handleClick = () => { + this.setState({ displayColorPicker: !this.state.displayColorPicker }); + }; + + handleClose = () => { + this.setState({ displayColorPicker: false }); + }; + + handleChange = (color: ColorResult) => { + this.setState({ color: color.rgb }); + this.props.onChange && + this.props.onChange(`rgba(${color.rgb.r},${color.rgb.g},${color.rgb.b},${color.rgb.a})`); + }; + + render() { + return ( +
+
+
+
+ {this.state.displayColorPicker ? ( + +
+ +
+
+ + ) : null} +
+ ); + } +} + +export default colorPicker; diff --git a/src/components/DataList/editorModal.js b/src/components/DataList/editorModal.js deleted file mode 100644 index d2fe21f..0000000 --- a/src/components/DataList/editorModal.js +++ /dev/null @@ -1,96 +0,0 @@ -import React, { memo, useEffect } from 'react'; -import { - Form, - Select, - Input, - Modal -} from 'antd'; -import Upload from '@/components/Upload'; - -// import styles from './index.less'; -const normFile = e => { - console.log('Upload event:', e); - if (Array.isArray(e)) { - return e; - } - return e && e.fileList; -}; - -const { Option } = Select; - -const formItemLayout = { - labelCol: { span: 6 }, - wrapperCol: { span: 14 }, -}; - -const EditorModal = (props) => { - const { item, onSave, visible, onCancel } = props - const onFinish = values => { - onSave && onSave(values) - } - const handleOk = () => { - form.validateFields().then(values => { - console.log(values) - values.id = item.id - onSave && onSave(values) - }).catch(err => { - console.log(err) - }) - } - - const [form] = Form.useForm() - - useEffect(() => { - return () => { - form.resetFields() - } - }, [item]) - - return !!item && ( - -
- - - - - - - - - - { - !!window['currentCates'] && - - - - } - - - - -
- -
- ) -} - -export default memo(EditorModal) \ No newline at end of file diff --git a/src/components/DataList/editorModal.tsx b/src/components/DataList/editorModal.tsx new file mode 100644 index 0000000..d0fe5f4 --- /dev/null +++ b/src/components/DataList/editorModal.tsx @@ -0,0 +1,123 @@ +import React, { memo, useEffect, FC } from 'react'; +import { Form, Select, Input, Modal } from 'antd'; +import Upload from '@/components/Upload'; +import { BasicDataSource } from '../DynamicEngine/schema'; +import { Store } from 'antd/lib/form/interface'; + +// import styles from './index.less'; +const normFile = (e: any) => { + console.log('Upload event:', e); + console.log('ffffffffff'); //待修改? + if (Array.isArray(e)) { + return e; + } + return e && e.fileList; +}; + +const { Option } = Select; + +const formItemLayout = { + labelCol: { span: 6 }, + wrapperCol: { span: 14 }, +}; + +export type EditorModalProps = { + visible: boolean; + onCancel: ((e: React.MouseEvent) => void) | undefined; + item?: BasicDataSource; + onSave: Function; +}; + +const EditorModal: FC = props => { + const { item, onSave, visible, onCancel } = props; + const onFinish = (values: Store) => { + onSave && onSave(values); + }; + const handleOk = () => { + form + .validateFields() + .then(values => { + console.log(values); + if (item) { + values.id = item.id; + onSave && onSave(values); + } + }) + .catch(err => { + console.log(err); + }); + }; + + const [form] = Form.useForm(); + + useEffect(() => { + return () => { + form.resetFields(); + }; + }, [item]); + + return ( + <> + {!!item && ( + +
+ + + + + + + + + + {!!window['currentCates'] && ( + + + + )} + + + + +
+
+ )} + + ); +}; + +export default memo(EditorModal); diff --git a/src/components/DataList/index.js b/src/components/DataList/index.js deleted file mode 100644 index 69742c8..0000000 --- a/src/components/DataList/index.js +++ /dev/null @@ -1,187 +0,0 @@ -import React, { memo, useState, useEffect, useCallback } from 'react' -import { - EditOutlined, - MinusCircleOutlined, - MenuOutlined -} from '@ant-design/icons'; -import { Button } from 'antd'; -import { DragSource, DropTarget, DndProvider } from "react-dnd"; -import { HTML5Backend } from "react-dnd-html5-backend"; -import EditorModal from './editorModal'; -import { uuid } from '@/utils/tool'; -import styles from './index.less' - -function ListItem(props) { - const { title, desc, link, imgUrl, type, onDel, onEdit, - // 这些 props 由 React DnD注入,参考`collect`函数定义 - isDragging, connectDragSource, connectDragPreview, connectDropTarget, - // 这些是组件收到的 props - item, style = {}, find, move, change, remove, ...restProps - } = props - const opacity = isDragging ? 0.5 : 1 - return connectDropTarget( // 列表项本身作为 Drop 对象 - connectDragPreview( // 整个列表项作为跟随拖动的影像 -
-
{ title }
-
{ desc }
-
- - - { - connectDragSource( - - ) // 拖动图标作为 Drag 对象 - } - -
-
- ) - ) -} - -const type = "item"; -const dragSpec = { - // 拖动开始时,返回描述 source 数据。后续通过 monitor.getItem() 获得 - beginDrag: props => ({ - id: props.id, - originalIndex: props.find(props.id).index - }), - // 拖动停止时,处理 source 数据 - endDrag(props, monitor) { - const { id: droppedId, originalIndex } = monitor.getItem(); - const didDrop = monitor.didDrop(); - // source 是否已经放置在 target - if (!didDrop) { - return props.move(droppedId, originalIndex); - } - } -} - -const dragCollect = (connect, monitor) => ({ - connectDragSource: connect.dragSource(), // 用于包装需要拖动的组件 - connectDragPreview: connect.dragPreview(), // 用于包装需要拖动跟随预览的组件 - isDragging: monitor.isDragging() // 用于判断是否处于拖动状态 -}) - -const dropSpec = { - canDrop: () => false, // item 不处理 drop - hover(props, monitor) { - const { id: draggedId } = monitor.getItem(); - const { id: overId } = props; - // 如果 source item 与 target item 不同,则交换位置并重新排序 - if (draggedId !== overId) { - const { index: overIndex } = props.find(overId); - props.move(draggedId, overIndex); - } - } -} - -const dropCollect = (connect, monitor) => ({ - connectDropTarget: connect.dropTarget() // 用于包装需接收拖拽的组件 -}) - -const DndItem = DropTarget(type, dropSpec, dropCollect)( - DragSource(type, dragSpec, dragCollect)(ListItem) -) - - - const List = function(props) { - const { onChange, value, connectDropTarget } = props - const [list, setList] = useState(value) - const [visible, setVisible] = useState(false) - const [curItem, setCurItem] = useState() - - const handleDel = (id) => { - let newVal = value.filter(item => id !== item.id) - onChange(newVal) - } - - const find = id => { - const item = list.find(c => `${c.id}` === id); - return { - item, - index: list.indexOf(item) - } - } - - const move = (id, toIndex) => { - const { item, index } = find(id); - const oldList = [...list]; - oldList.splice(index, 1); - oldList.splice(toIndex, 0, item); - if(onChange) { - onChange(oldList) - return - } - setList(oldList) - } - - const handleCancel = useCallback(() => { - setVisible(false) - }, []) - - const handleEdit = useCallback((item) => { - setVisible(true) - setCurItem(item) - }, []) - - const handleSave = useCallback((item) => { - setVisible(false) - console.log(22, list, item) - if(onChange) { - onChange(list.map(p => p.id === item.id ? item : p)) - return - } - setList(prev => prev.map(p => p.id === item.id ? item : p)) - }, [curItem]) - - const handleAdd = () => { - const item = { - title: '新增项标题', - desc: '新增项描述', - id: uuid(8, 10), - imgUrl: [], - link: '' - } - if(onChange) { - onChange([...list, item]) - return - } - setList([...list, item]) - } - - useEffect(() => { - setList(value) - }, [value]) - - return connectDropTarget( -
- { - !!(list && list.length) && list.map((item, i) => - - ) - } -
- -
- ) -} - -const DndList = DropTarget(type, {}, connect => ({ - connectDropTarget: connect.dropTarget() -}))(List) - -// 将 HTMLBackend 作为参数传给 DragDropContext -export default memo((props) => { - return - - -}) \ No newline at end of file diff --git a/src/components/DataList/index.tsx b/src/components/DataList/index.tsx new file mode 100644 index 0000000..64e571a --- /dev/null +++ b/src/components/DataList/index.tsx @@ -0,0 +1,250 @@ +import React, { memo, useState, useEffect, useCallback } from 'react'; +import { EditOutlined, MinusCircleOutlined, MenuOutlined } from '@ant-design/icons'; +import { Button } from 'antd'; +import { + DragSource, + DropTarget, + DndProvider, + ConnectDropTarget, + DragSourceSpec, + DropTargetConnector, + DragSourceMonitor, + DragSourceConnector, + DropTargetSpec, + ConnectDragSource, + ConnectDragPreview, +} from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; +import EditorModal from './editorModal'; +import { uuid } from '@/utils/tool'; +import styles from './index.less'; +import { BasicDataSource } from '../DynamicEngine/schema'; + +type ListItemProps = DndItemProps & { + isDragging: boolean; + connectDragSource: ConnectDragSource; + connectDragPreview: ConnectDragPreview; + connectDropTarget: ConnectDropTarget; +}; + +function ListItem(props: ListItemProps) { + const { + title, + desc, + link, + imgUrl, + type, + onDel, + onEdit, + // 这些 props 由 React DnD注入,参考`collect`函数定义 + isDragging, + connectDragSource, + connectDragPreview, + connectDropTarget, + } = props; + const opacity = isDragging ? 0.5 : 1; + return connectDropTarget( + // 列表项本身作为 Drop 对象 + connectDragPreview( + // 整个列表项作为跟随拖动的影像 +
+
{title}
+
{desc}
+
+ onEdit()}> + + + onDel()}> + + + {connectDragSource( + + + , + ) // 拖动图标作为 Drag 对象 + } +
+
, + ), + ); +} + +type DndItemProps = BasicDataSource & { + onDel: Function; + onEdit: Function; + key: number; + id: string; + find: Function; + move: Function; + type?: number; +}; + +const type = 'item'; +type DragObject = { + id: string; + originalIndex: number; +}; +const dragSpec: DragSourceSpec = { + // 拖动开始时,返回描述 source 数据。后续通过 monitor.getItem() 获得 + beginDrag: props => ({ + id: props.id, + originalIndex: props.find(props.id).index, + }), + // 拖动停止时,处理 source 数据 + endDrag(props, monitor) { + const { id: droppedId, originalIndex } = monitor.getItem(); + const didDrop = monitor.didDrop(); + // source 是否已经放置在 target + if (!didDrop) { + return props.move(droppedId, originalIndex); + } + }, +}; + +const dragCollect = (connect: DragSourceConnector, monitor: DragSourceMonitor) => ({ + connectDragSource: connect.dragSource(), // 用于包装需要拖动的组件 + connectDragPreview: connect.dragPreview(), // 用于包装需要拖动跟随预览的组件 + isDragging: monitor.isDragging(), // 用于判断是否处于拖动状态 +}); + +const dropSpec: DropTargetSpec = { + canDrop: () => false, // item 不处理 drop + hover(props, monitor) { + const { id: draggedId } = monitor.getItem(); + const { id: overId } = props; + // 如果 source item 与 target item 不同,则交换位置并重新排序 + if (draggedId !== overId) { + const { index: overIndex } = props.find(overId); + props.move(draggedId, overIndex); + } + }, +}; + +const dropCollect = (connect: DropTargetConnector) => ({ + connectDropTarget: connect.dropTarget(), // 用于包装需接收拖拽的组件 +}); + +const DndItem = DropTarget( + type, + dropSpec, + dropCollect, +)(DragSource(type, dragSpec, dragCollect)(ListItem)); + +export type DataListMemo = { + onChange?: (v: BasicDataSource[]) => void; + value?: BasicDataSource[]; +}; + +export type DataListType = DataListMemo & { + connectDropTarget: ConnectDropTarget; +}; + +const List = function(props: DataListType) { + const { onChange, value, connectDropTarget } = props; + const [list, setList] = useState(value); + const [visible, setVisible] = useState(false); + const [curItem, setCurItem] = useState(); + + const handleDel = (id: string) => { + if (value && onChange) { + let newVal = value.filter(item => id !== item.id); + onChange(newVal); + } + }; + + const find = (id: string) => { + const item = list!.find(c => `${c.id}` === id)!; + return { + item, + index: list!.indexOf(item!), + }; + }; + + const move = (id: string, toIndex: number) => { + const { item, index } = find(id); + const oldList = [...list!]; + oldList.splice(index, 1); + oldList.splice(toIndex, 0, item); + if (onChange) { + onChange(oldList); + return; + } + setList(oldList); + }; + + const handleCancel = useCallback(() => { + setVisible(false); + }, []); + + const handleEdit = useCallback((item: BasicDataSource) => { + setVisible(true); + setCurItem(item); + }, []); + + const handleSave = useCallback( + (item: BasicDataSource) => { + setVisible(false); + if (onChange) { + onChange(list!.map(p => (p.id === item.id ? item : p))); + return; + } + setList(prev => prev!.map(p => (p.id === item.id ? item : p))); + }, + [curItem], + ); + + const handleAdd = () => { + const item = { + title: '新增项标题', + desc: '新增项描述', + id: uuid(8, 10), + imgUrl: [], + link: '', + }; + if (onChange) { + onChange([...list!, item]); + return; + } + setList([...list!, item]); + }; + + useEffect(() => { + setList(value); + }, [value]); + + return connectDropTarget( +
+ {!!(list && list.length) && + list.map((item, i) => ( + handleDel(item.id)} + onEdit={() => handleEdit(item)} + key={i} + id={`${item.id}`} + find={find} + move={move} + /> + ))} +
+ +
+ +
, + ); +}; + +const DndList = DropTarget(type, {}, connect => ({ + connectDropTarget: connect.dropTarget(), +}))(List); + +// 将 HTMLBackend 作为参数传给 DragDropContext +export default memo((props: DataListMemo) => { + return ( + + + + ); +}); diff --git a/src/components/DynamicEngine/components.js b/src/components/DynamicEngine/components.js deleted file mode 100644 index 8c29c07..0000000 --- a/src/components/DynamicEngine/components.js +++ /dev/null @@ -1,137 +0,0 @@ -import { memo } from 'react' -import { NoticeBar, Progress } from 'zarm' -import styles from './components.less' - -const Header = memo((props) => { - const { - bgColor, - logo, - logoText, - fontSize, - color - } = props - return
-
- {logoText} -
-
{ logoText }
-
-}) - -const Text = memo((props) => { - const { - align, - text, - fontSize, - color, - lineHeight - } = props - return
- { text } -
-}) - -const Notice = memo((props) => { - const { - text, - speed, - theme, - link, - isClose = false - } = props - return { text } -}) - -const Qrcode = memo((props) => { - const { - qrcode, - text, - color, - fontSize = 14 - } = props - return
- {text} -
{ text }
-
-}) - -const Footer = memo((props) => { - const { - bgColor, - text, - color, - align, - fontSize, - height - } = props - return
{ text }
-}) - -const Image = memo((props) => { - const { - imgUrl, - round = 0 - } = props - return
- -
-}) - -const List = memo((props) => { - const { - round, - sourceData, - imgSize, - fontSize, - color - } = props - return
-
- { - sourceData.map((item, i) => { - return - }) - } -
-
-}) - -const XProgress = memo((props) => { - const { - theme, - size, - shape, - percent, - strokeWidth - } = props - return
- -
-}) - -export { - Header, - Text, - Notice, - Qrcode, - Footer, - Image, - List, - XProgress -} \ No newline at end of file diff --git a/src/components/DynamicEngine/components.tsx b/src/components/DynamicEngine/components.tsx new file mode 100644 index 0000000..03581b9 --- /dev/null +++ b/src/components/DynamicEngine/components.tsx @@ -0,0 +1,147 @@ +import { memo } from 'react'; +import { NoticeBar, Progress } from 'zarm'; +import styles from './components.less'; +import React from 'react'; +import { + HeaderConfigType, + TextConfigType, + NoticeConfigType, + QRCodeConfigType, + FooterConfigType, + ImageConfigType, + ListConfigType, + XProgressConfigType, +} from './schema'; + +const Header = memo((props: HeaderConfigType) => { + const { bgColor, logo, logoText, fontSize, color } = props; + return ( +
+
+ {logoText} +
+
+ {logoText} +
+
+ ); +}); + +const Text = memo((props: TextConfigType) => { + const { align, text, fontSize, color, lineHeight } = props; + return ( +
+ {text} +
+ ); +}); + +const Notice = memo((props: NoticeConfigType) => { + const { text, speed, theme, link, isClose = false } = props; + return ( + + {text} + + ); +}); + +const Qrcode = memo((props: QRCodeConfigType) => { + const { qrcode, text, color, fontSize = 14 } = props; + return ( +
+ {text} +
{text}
+
+ ); +}); + +const Footer = memo((props: FooterConfigType) => { + const { bgColor, text, color, align, fontSize, height } = props; + return ( +
+ {text} +
+ ); +}); + +const Image = memo((props: ImageConfigType) => { + const { imgUrl, round = 0 } = props; + return ( +
+ +
+ ); +}); + +const List = memo((props: ListConfigType) => { + const { round, sourceData, imgSize, fontSize, color } = props; + return ( +
+
+ {sourceData.map((item, i) => { + return ( + + ); + })} +
+
+ ); +}); + +const XProgress = memo((props: XProgressConfigType) => { + const { theme, size, shape, percent, strokeWidth } = props; + return ( +
+ +
+ ); +}); + +export { Header, Text, Notice, Qrcode, Footer, Image, List, XProgress }; diff --git a/src/components/DynamicEngine/index.js b/src/components/DynamicEngine/index.tsx similarity index 58% rename from src/components/DynamicEngine/index.js rename to src/components/DynamicEngine/index.tsx index 187b75a..ddfc243 100644 --- a/src/components/DynamicEngine/index.js +++ b/src/components/DynamicEngine/index.tsx @@ -1,22 +1,25 @@ import { dynamic } from 'umi'; import Loading from '../LoadingCp'; -import { useMemo, memo } from 'react'; - +import { useMemo, memo, FC } from 'react'; +import React from 'react'; +import { UnionData, AllTemplateType } from './schema'; const needList = ['Tab', 'Carousel', 'Upload', 'Video', 'Icon']; -const DynamicFunc = type => +const DynamicFunc = (type: AllTemplateType) => dynamic({ loader: async function() { - let Component; + let Component: FC<{ isTpl: boolean }>; if (needList.includes(type)) { const { default: Graph } = await import(`@/components/${type}`); Component = Graph; } else { - const Components = await import(`@/components/DynamicEngine/components`); + const Components = ((await import(`@/components/DynamicEngine/components`)) as unknown) as { + [key: string]: FC; + }; Component = Components[type]; } - return props => { + return (props: DynamicType) => { const { config, isTpl } = props; return ; }; @@ -28,10 +31,15 @@ const DynamicFunc = type => ), }); -const DynamicEngine = memo(props => { +type DynamicType = { + isTpl: boolean; + config: UnionData<'config'>; + type: AllTemplateType; +}; +const DynamicEngine = memo((props: DynamicType) => { const { type, config, isTpl } = props; const Dynamic = useMemo(() => { - return DynamicFunc(type); + return (DynamicFunc(type) as unknown) as FC; // eslint-disable-next-line react-hooks/exhaustive-deps }, [type, config]); return ; diff --git a/src/components/DynamicEngine/schema.ts b/src/components/DynamicEngine/schema.ts index 95a189a..b0fcee5 100644 --- a/src/components/DynamicEngine/schema.ts +++ b/src/components/DynamicEngine/schema.ts @@ -472,6 +472,20 @@ export interface SchemaType extends SchemaImplement { XProgress: XProgressSchema; } +export type UnionData = + | CarouselSchema[T] + | TextSchema[T] + | TabSchema[T] + | NoticeSchema[T] + | QRCodeSchema[T] + | FooterSchema[T] + | ImageSchema[T] + | HeaderSchema[T] + | ListSchema[T] + | IconSchema[T] + | VideoSchema[T] + | XProgressSchema[T]; + const schema: SchemaType = { Carousel: { editData: [ diff --git a/src/components/FormEditor/index.tsx b/src/components/FormEditor/index.tsx index 3ba70dc..3bda828 100644 --- a/src/components/FormEditor/index.tsx +++ b/src/components/FormEditor/index.tsx @@ -1,15 +1,17 @@ import React, { memo, useState, useEffect } from 'react'; import { Form, Select, InputNumber, Input, Switch, Radio, Button } from 'antd'; -import Upload from '@/components/Upload'; -import DataList from '@/components/DataList'; -import MutiText from '@/components/MutiText'; -import Color from '@/components/Color'; -import CardPicker from '@/components/CardPicker'; - +import Upload from '../Upload'; +import DataList from '../DataList'; +import MutiText from '../MutiText'; +import Color from '../Color'; +import CardPicker from '../CardPicker'; +import { Store } from 'antd/lib/form/interface'; +import { UnionData, BasicRangeType, IconSchema } from '../DynamicEngine/schema'; // import styles from './index.less'; -const normFile = e => { +const normFile = (e: any) => { console.log('Upload event:', e); if (Array.isArray(e)) { + //待修改 return e; } return e && e.fileList; @@ -22,10 +24,18 @@ const formItemLayout = { wrapperCol: { span: 16 }, }; -const FormEditor = props => { - const { config, defaultValue, onSave, onDel, uid } = props; +interface FormEditorProps { + uid: string; + onSave: Function; + onDel: Function; + defaultValue: { [key: string]: any }; + config: Array; +} - const onFinish = values => { +const FormEditor = (props: FormEditorProps) => { + const { config, defaultValue, onSave, onDel, uid } = props; + console.log(config, defaultValue, uid); + const onFinish = (values: Store) => { onSave && onSave(values); }; @@ -80,7 +90,7 @@ const FormEditor = props => { {item.type === 'Select' && ( - - - - -
- }) : -
- -
- } - { - valueList.length < 3 && -
- -
- } - -
-}) \ No newline at end of file diff --git a/src/components/MutiText/index.tsx b/src/components/MutiText/index.tsx new file mode 100644 index 0000000..f423440 --- /dev/null +++ b/src/components/MutiText/index.tsx @@ -0,0 +1,80 @@ +import React, { memo, useState, useEffect } from 'react'; +import { Input, Button, Popconfirm } from 'antd'; +import { MinusCircleOutlined } from '@ant-design/icons'; +import styles from './index.less'; +import { TabConfigType } from '../DynamicEngine/schema'; + +type MultiTextProps = { + onChange?: (v: TabConfigType['tabs']) => void; + value?: TabConfigType['tabs']; +}; + +export default memo(function MutiText(props: MultiTextProps) { + const { value, onChange } = props; + const [valueList, setValueList] = useState(value || []); + const handleAdd = () => { + setValueList(prev => { + return [...prev, '新增项']; + }); + }; + + const handleDel = (index: number) => { + setValueList(prev => { + let newList = prev.filter((_item, i) => i !== index); + onChange && onChange(newList); + return newList; + }); + }; + + const handleChange = (index: number, e: React.ChangeEvent) => { + const { value } = e.target; + setValueList(prev => { + let newList = prev.map((item, i) => (i === index ? value : item)); + onChange && onChange(newList); + return newList; + }); + }; + + useEffect(() => { + window['currentCates'] = valueList; + return () => { + window['currentCates'] = null; + }; + }, [valueList]); + + return ( +
+ {valueList.length ? ( + valueList.map((item, i) => { + return ( +
+ handleChange(i, e)} /> + handleDel(i)} + placement="leftTop" + okText="确定" + cancelText="取消" + > + + + + +
+ ); + }) + ) : ( +
+ +
+ )} + {valueList.length < 3 && ( +
+ +
+ )} +
+ ); +}); diff --git a/src/components/Tab/index.js b/src/components/Tab/index.js deleted file mode 100644 index 47fc561..0000000 --- a/src/components/Tab/index.js +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import { Tabs } from 'zarm'; -import styles from './index.less'; - -const { Panel } = Tabs; - -const XTab = (props) => { - const { - tabs = ['分类一', '分类二'], - activeColor, - color, - fontSize, - sourceData = [ - { - "title": "趣谈小课", - "desc": "致力于打造优质小课程", - "link": "xxxxx", - "type": 0, - "imgUrl": "http://io.nainor.com/uploads/01_173e15d3493.png" - }, - { - "title": "趣谈小课", - "desc": "致力于打造优质小课程", - "link": "xxxxx", - "type": 1, - "imgUrl": "http://io.nainor.com/uploads/01_173e15d3493.png" - }, - { - "title": "趣谈小课", - "desc": "致力于打造优质小课程", - "link": "xxxxx", - "type": 0, - "imgUrl": "http://io.nainor.com/uploads/01_173e15d3493.png" - } - ] - } = props - - const tabWrapRef = useRef(null) - - useEffect(() => { - tabWrapRef.current.querySelector('.za-tabs__line').style.backgroundColor = activeColor - }, [activeColor]) - - return
- { console.log(i); }}> - { - tabs.map((item, i) => { - return -
- { - sourceData.filter(item => item.type === i).map((item, i) => { - return - }) - } -
-
- }) - } -
-
-}; - -export default XTab; \ No newline at end of file diff --git a/src/components/Tab/index.tsx b/src/components/Tab/index.tsx new file mode 100644 index 0000000..94ab138 --- /dev/null +++ b/src/components/Tab/index.tsx @@ -0,0 +1,64 @@ +import React, { useEffect, useRef } from 'react'; +import { Tabs } from 'zarm'; +import styles from './index.less'; +import { TabConfigType } from '../DynamicEngine/schema'; + +const { Panel } = Tabs; + +const XTab = (props: TabConfigType) => { + const { tabs = ['分类一', '分类二'], activeColor, color, fontSize, sourceData } = props; + + const tabWrapRef = useRef(null); + + useEffect(() => { + if (tabWrapRef.current) { + let res = tabWrapRef.current.querySelector('.za-tabs__line') as HTMLElement; + if (res) { + res.style.backgroundColor = activeColor; + } + } + }, [activeColor]); + + return ( +
+ { + console.log(i); + }} + > + {tabs.map((item, i) => { + return ( + +
+ {sourceData + .filter(item => item.type === i) + .map((item, i) => { + return ( + + ); + })} +
+
+ ); + })} +
+
+ ); +}; + +export default XTab; diff --git a/src/components/Upload/index.js b/src/components/Upload/index.tsx similarity index 55% rename from src/components/Upload/index.js rename to src/components/Upload/index.tsx index f8c04af..bed62d7 100644 --- a/src/components/Upload/index.js +++ b/src/components/Upload/index.tsx @@ -3,54 +3,69 @@ import { Upload, Modal, message } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; import ImgCrop from 'antd-img-crop'; import styles from './index.less'; +import { UploadFile, UploadChangeParam, RcFile } from 'antd/lib/upload/interface'; -const isDev = process.env.NODE_ENV === 'development' +const isDev = process.env.NODE_ENV === 'development'; -function getBase64(file) { - return new Promise((resolve, reject) => { +function getBase64(file: File | Blob) { + return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); - reader.onload = () => resolve(reader.result); + reader.onload = () => resolve(reader.result as string); reader.onerror = error => reject(error); }); } +interface PicturesWallType { + fileList?: UploadFile[]; + action?: string; + headers?: any; + withCredentials?: boolean; + maxLen?: number; + onChange?: (v: any) => void; + cropRate?: boolean; + isCrop?: boolean; +} -class PicturesWall extends React.Component { +class PicturesWall extends React.Component { state = { previewVisible: false, previewImage: '', previewTitle: '', - fileList: this.props.fileList || [] + fileList: this.props.fileList || [], }; handleCancel = () => this.setState({ previewVisible: false }); - handlePreview = async file => { + handlePreview = async (file: UploadFile) => { if (!file.url && !file.preview) { - file.preview = await getBase64(file.originFileObj); + file.preview = await getBase64(file.originFileObj!); } this.setState({ previewImage: file.url || file.preview, previewVisible: true, - previewTitle: file.name || file.url.substring(file.url.lastIndexOf('/') + 1), - }) - } + previewTitle: file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1), + }); + }; - handleChange = ({ file, fileList }) => { - this.setState({ fileList }) - if(file.status === 'done') { + handleChange = ({ file, fileList }: UploadChangeParam>) => { + this.setState({ fileList }); + if (file.status === 'done') { const files = fileList.map(item => { - const { uid, name, status } = item - const url = item.url || item.response.result.url - return { uid, name, status, url } - }) - this.props.onChange && this.props.onChange(files) + const { uid, name, status } = item; + const url = item.url || item.response.result.url; + return { uid, name, status, url }; + }); + this.props.onChange && this.props.onChange(files); } - } + }; - handleBeforeUpload = (file) => { - const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg' || file.type === 'image/gif'; + handleBeforeUpload = (file: RcFile) => { + const isJpgOrPng = + file.type === 'image/jpeg' || + file.type === 'image/png' || + file.type === 'image/jpg' || + file.type === 'image/gif'; if (!isJpgOrPng) { message.error('只能上传格式为jpeg/png/gif的图片'); } @@ -59,7 +74,7 @@ class PicturesWall extends React.Component { message.error('图片必须小于2MB!'); } return isJpgOrPng && isLt2M; - } + }; render() { const { previewVisible, previewImage, fileList, previewTitle } = this.state; @@ -68,8 +83,8 @@ class PicturesWall extends React.Component { action = isDev ? 'http://192.168.1.6:3000/api/xxx' : 'http://xxxx', headers, withCredentials = true, - maxLen = 1 - } = this.props + maxLen = 1, + } = this.props; const uploadButton = (
@@ -79,7 +94,13 @@ class PicturesWall extends React.Component { ); return ( - + @@ -111,4 +132,4 @@ class PicturesWall extends React.Component { } } -export default PicturesWall \ No newline at end of file +export default PicturesWall; diff --git a/src/components/Video/index.js b/src/components/Video/index.js deleted file mode 100644 index 59f88a2..0000000 --- a/src/components/Video/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import { memo } from 'react' -import { Player, BigPlayButton } from 'video-react' -import './index.css' - -const VideoPlayer = memo((props) => { - const { - poster, - url - } = props - return
- - - -
-}) - -export default VideoPlayer \ No newline at end of file diff --git a/src/components/Video/index.tsx b/src/components/Video/index.tsx new file mode 100644 index 0000000..c9424dc --- /dev/null +++ b/src/components/Video/index.tsx @@ -0,0 +1,21 @@ +import React, { memo } from 'react'; +import { Player, BigPlayButton } from 'video-react'; +import './index.css'; +import { VideoConfigType } from '../DynamicEngine/schema'; + +const VideoPlayer = memo((props: VideoConfigType) => { + const { poster, url } = props; + return ( +
+ + + +
+ ); +}); + +export default VideoPlayer; diff --git a/src/components/Zan/index.js b/src/components/Zan/index.tsx similarity index 93% rename from src/components/Zan/index.js rename to src/components/Zan/index.tsx index 94fb76c..f7e55cd 100644 --- a/src/components/Zan/index.js +++ b/src/components/Zan/index.tsx @@ -1,4 +1,4 @@ -import { memo } from 'react'; +import React, { memo } from 'react'; import { Button, Popover } from 'antd'; import styles from './index.less'; diff --git a/src/layouts/test.js b/src/layouts/test.js deleted file mode 100644 index b58cc9a..0000000 --- a/src/layouts/test.js +++ /dev/null @@ -1,150 +0,0 @@ -import React, { useState } from "react"; -import { DragSource, DropTarget, DragDropContext } from "react-dnd"; -import HTML5Backend from "react-dnd-html5-backend"; -import { faTrashAlt, faArrowsAlt } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import classnames from "classnames"; - -function Item(props) { - const { - // 这些 props 由 React DnD注入,参考`collect`函数定义 - isDragging, connectDragSource, connectDragPreview, connectDropTarget, - // 这些是组件收到的 props - item, style = {}, find, move, change, remove, ...restProps - } = props; - const opacity = isDragging ? 0.5 : 1; - const onRemove = event => { - event.stopPropagation(); - remove(item); - } - return connectDropTarget( // 列表项本身作为 Drop 对象 - connectDragPreview( // 整个列表项作为跟随拖动的影像 -
-

{item.title || "任务标题"}

-
    - { - connectDragSource( -
  • - -
  • - ) // 拖动图标作为 Drag 对象 - } -
  • - -
  • -
-
- ) - ); -} - -const type = "item"; -const dragSpec = { - // 拖动开始时,返回描述 source 数据。后续通过 monitor.getItem() 获得 - beginDrag: props => ({ - id: props.id, - originalIndex: props.find(props.id).index - }), - // 拖动停止时,处理 source 数据 - endDrag(props, monitor) { - const { id: droppedId, originalIndex } = monitor.getItem(); - const didDrop = monitor.didDrop(); - // source 是否已经放置在 target - if (!didDrop) { - return props.move(droppedId, originalIndex); - } - return props.change(droppedId, originalIndex); - } -}; -const dragCollect = (connect, monitor) => ({ - connectDragSource: connect.dragSource(), // 用于包装需要拖动的组件 - connectDragPreview: connect.dragPreview(), // 用于包装需要拖动跟随预览的组件 - isDragging: monitor.isDragging() // 用于判断是否处于拖动状态 -}); -const dropSpec = { - canDrop: () => false, // item 不处理 drop - hover(props, monitor) { - const { id: draggedId } = monitor.getItem(); - const { id: overId } = props; - // 如果 source item 与 target item 不同,则交换位置并重新排序 - if (draggedId !== overId) { - const { index: overIndex } = props.find(overId); - props.move(draggedId, overIndex); - } - } -}; -const dropCollect = (connect, monitor) => ({ - connectDropTarget: connect.dropTarget() // 用于包装需接收拖拽的组件 -}); - -const DndItem = DropTarget(type, dropSpec, dropCollect)( - DragSource(type, dragSpec, dragCollect)(Item) -); - -function List(props) { - let { list: propsList, activeItem, connectDropTarget } = props; - propsList = propsList.map(item => { - const isActive = activeItem.id === item.id; - item = isActive ? activeItem : item; - item.active = isActive; - return item; - }); - const [list, setList] = useState(propsList); - const find = id => { - const item = list.find(c => `${c.id}` === id); - return { - item, - index: list.indexOf(item) - }; - }; - const move = (id, toIndex) => { - const { item, index } = find(id); - list.splice(index, 1); - list.splice(toIndex, 0, item); - setList([...list]); - }; - const change = (id, fromIndex) => { - const { index: toIndex } = find(id); - props.onDropEnd(list, fromIndex, toIndex); - }; - const remove = item => { - const newList = list.filter(it => it.id !== item.id); - setList(newList); - props.onDelete(newList); - }; - const onClick = event => { - const { id } = event.currentTarget; - const { item } = find(id); - props.onClick(item); - }; - - return connectDropTarget( -
    - {list.map((item, index) => ( -
  • -
    {index + 1}
    - -
  • - ))} -
- ); -} - -const DndList = DropTarget(type, {}, connect => ({ - connectDropTarget: connect.dropTarget() -}))(List); - -// 将 HTMLBackend 作为参数传给 DragDropContext -export default DragDropContext(HTML5Backend)(DndList); \ No newline at end of file diff --git a/src/pages/editor/Container.js b/src/pages/editor/Container.js index 9b4e19b..5a1cd6f 100644 --- a/src/pages/editor/Container.js +++ b/src/pages/editor/Container.js @@ -27,7 +27,6 @@ const Container = memo(props => { const [scaleNum, setScale] = useState(1); const { pointData, curPoint, dispatch } = props; - // 指定画布的id let canvasId = 'js_canvas'; diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 81d4963..be98af7 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -1,8 +1,10 @@ -import { Form, Input, Button, Checkbox } from 'antd'; +import { Form, Input, Button } from 'antd'; import http from '@/utils/req'; import { history } from 'umi'; import styles from './index.less'; - +import React from 'react'; +import { ValidateErrorEntity, Store } from 'rc-field-form/lib/interface'; +import { RouteComponentProps } from 'react-router-dom'; const layout = { labelCol: { span: 6 }, wrapperCol: { span: 16 }, @@ -11,16 +13,18 @@ const tailLayout = { wrapperCol: { offset: 6, span: 16 }, }; -const Login = (props) => { - const onFinish = values => { - http.post('/login', {...values}).then(res => { - localStorage.setItem('token', res.token) - localStorage.setItem('user', values.username) - history.push('/') - }) +const Login = (props: RouteComponentProps) => { + const onFinish = (values: Store) => { + http + .post('/login', { ...values }) + .then(res => { + localStorage.setItem('token', res.token); + localStorage.setItem('user', values.username); + history.push('/'); + }); }; - const onFinishFailed = errorInfo => { + const onFinishFailed = (errorInfo: ValidateErrorEntity) => { console.log('Failed:', errorInfo); }; @@ -34,7 +38,10 @@ const Login = (props) => { onFinish={onFinish} onFinishFailed={onFinishFailed} > -
Doring开放平台登录
+
+ Doring开放平台 + 登录 +
{ - - +
- ); }; -export default Login +export default Login; diff --git a/src/pages/mobileTip.tsx b/src/pages/mobileTip.tsx index 48c04da..879aad0 100644 --- a/src/pages/mobileTip.tsx +++ b/src/pages/mobileTip.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Result } from 'antd'; -function MobileTip(props) { +function MobileTip() { return (
; +} diff --git a/src/utils/tool.js b/src/utils/tool.js deleted file mode 100644 index 0a6b4ab..0000000 --- a/src/utils/tool.js +++ /dev/null @@ -1,38 +0,0 @@ -// 生成uuid -function uuid(len, radix) { - let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); - let uuid = [], i; - radix = radix || chars.length; - - if (len) { - for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix]; - } else { - let r; - uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; - uuid[14] = '4'; - - for (i = 0; i < 36; i++) { - if (!uuid[i]) { - r = 0 | Math.random()*16; - uuid[i] = chars[(i === 19) ? (r & 0x3) | 0x8 : r]; - } - } - } - - return uuid.join(''); -} - -// 将rgba字符串对象转化为rgba对象 -function rgba2Obj(rgba = '') { - let reg = /rgba\((\d+),(\d+),(\d+),(\d+)\)/g - let rgbaObj = {} - rgba.replace(reg, (m, r, g, b, a) => { - rgbaObj = {r, g, b, a} - }) - return rgbaObj -} - -export { - uuid, - rgba2Obj -} \ No newline at end of file diff --git a/src/utils/tool.ts b/src/utils/tool.ts new file mode 100644 index 0000000..2eef4e2 --- /dev/null +++ b/src/utils/tool.ts @@ -0,0 +1,40 @@ +import { RGBColor } from 'react-color'; + +// 生成uuid +function uuid(len: number, radix: number) { + let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); + let uuid = [], + i; + radix = radix || chars.length; + + if (len) { + for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)]; + } else { + let r; + uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; + uuid[14] = '4'; + + for (i = 0; i < 36; i++) { + if (!uuid[i]) { + r = 0 | (Math.random() * 16); + uuid[i] = chars[i === 19 ? (r & 0x3) | 0x8 : r]; + } + } + } + + return uuid.join(''); +} + +// 将rgba字符串对象转化为rgba对象 +function rgba2Obj(rgba = '') { + let reg = /rgba\((\d+),(\d+),(\d+),(\d+)\)/g; + let rgbaObj: RGBColor = { r: 0, g: 0, b: 0, a: 0 }; + + rgba.replace(reg, (_m, r, g, b, a) => { + rgbaObj = { r, g, b, a }; + return rgba; + }); + return rgbaObj; +} + +export { uuid, rgba2Obj }; diff --git a/src/video-react.d.ts b/src/video-react.d.ts new file mode 100644 index 0000000..d869123 --- /dev/null +++ b/src/video-react.d.ts @@ -0,0 +1,771 @@ +declare module 'video-react' { + type PreloadType = 'auto' | 'metadata' | 'none'; + + interface PlayerPropsType { + children?: any; + + width?: string | number; + height?: string | number; + fluid?: boolean; // = true; + muted?: boolean; // = false; + playsInline?: boolean; // = false; + aspectRatio?: string; // = 'auto'; + className?: string; + videoId?: string; + + startTime?: number; + loop?: boolean; + autoPlay?: boolean; + src?: string; + poster?: string; + preload?: PreloadType; // = 'auto'; + + onLoadStart?: ReactEventHandler; + onWaiting?: ReactEventHandler; + onCanPlay?: ReactEventHandler; + onCanPlayThrough?: ReactEventHandler; + onPlaying?: ReactEventHandler; + onEnded?: ReactEventHandler; + onSeeking?: ReactEventHandler; + onSeeked?: ReactEventHandler; + onPlay?: ReactEventHandler; + onPause?: ReactEventHandler; + onProgress?: ReactEventHandler; + onDurationChange?: ReactEventHandler; + onError?: ReactEventHandler; + onSuspend?: ReactEventHandler; + onAbort?: ReactEventHandler; + onEmptied?: ReactEventHandler; + onStalled?: ReactEventHandler; + onLoadedMetadata?: ReactEventHandler; + onLoadedData?: ReactEventHandler; + onTimeUpdate?: ReactEventHandler; + onRateChange?: ReactEventHandler; + onVolumeChange?: ReactEventHandler; + + store?: object; + } + + class Player extends React.Component { + readonly video: Video; + + getDefaultChildren(originalChildren): Array; + + getChildren(props): Array; + + setWidthOrHeight(style: object, name: string, value: string | number); + + getStyle(): object; + + // get redux state + // { player, operation } + getState(): object; + + // get playback rate + get playbackRate(): number; + + // set playback rate + // speed of video + set playbackRate(rate: number); + + get muted(): boolean; + + set muted(val: boolean); + + get volume(): number; + + set volume(val: number); + + // video width + get videoWidth(): number; + + // video height + get videoHeight(): number; + + // play the video + play(); + + // pause the video + pause(); + + // Change the video source and re-load the video: + load(); + + // Add a new text track to the video + addTextTrack(kind: TextTrackKind, label?: string, language?: string): TextTrack; + + // Check if your browser can play different types of video: + canPlayType(type: string): CanPlayTypeResult; + + // seek video by time + seek(time: number); + + // jump forward x seconds + forward(seconds: number); + + // jump back x seconds + replay(seconds: number); + + // enter or exist full screen + toggleFullscreen(); + + // subscribe to player state change + subscribeToStateChange(listener: (state: any, prevState: any) => void); + } + + interface VideoPropsType { + actions?: object; + player?: object; + children?: any; + startTime?: number; + loop?: boolean; + muted?: boolean; + autoPlay?: boolean; + playsInline?: boolean; + src?: string; + poster?: string; + className?: string; + preload?: PreloadType; + crossOrigin?: string; + + onLoadStart?: ReactEventHandler; + onWaiting?: ReactEventHandler; + onCanPlay?: ReactEventHandler; + onCanPlayThrough?: ReactEventHandler; + onPlaying?: ReactEventHandler; + onEnded?: ReactEventHandler; + onSeeking?: ReactEventHandler; + onSeeked?: ReactEventHandler; + onPlay?: ReactEventHandler; + onPause?: ReactEventHandler; + onProgress?: ReactEventHandler; + onDurationChange?: ReactEventHandler; + onError?: ReactEventHandler; + onSuspend?: ReactEventHandler; + onAbort?: ReactEventHandler; + onEmptied?: ReactEventHandler; + onStalled?: ReactEventHandler; + onLoadedMetadata?: ReactEventHandler; + onLoadedData?: ReactEventHandler; + onTimeUpdate?: ReactEventHandler; + onRateChange?: ReactEventHandler; + onVolumeChange?: ReactEventHandler; + onResize?: ReactEventHandler; + } + + class Video extends React.Component { + // get all video properties + getProperties(): any; + + // get playback rate + get playbackRate(): number; + + // set playback rate + // speed of video + set playbackRate(rate: number); + + get muted(): boolean; + + set muted(val: boolean); + + get volume(): number; + + set volume(val: number); + + // video width + get videoWidth(): number; + + // video height + get videoHeight(): number; + + // play the video + play(); + + // pause the video + pause(); + + // Change the video source and re-load the video: + load(); + + // Add a new text track to the video + addTextTrack(kind: TextTrackKind, label?: string, language?: string): TextTrack; + + // Check if your browser can play different types of video: + canPlayType(type: string): CanPlayTypeResult; + + // toggle play + togglePlay(); + + // seek video by time + seek(time: number); + + // jump forward x seconds + forward(seconds: number); + + // jump back x seconds + replay(seconds: number); + + // enter or exist full screen + toggleFullscreen(); + } + + interface BigPlayButtonPropsType { + actions?: object; + player?: object; + position?: 'center' | 'left-top'; // = 'left'; + className?: string; + } + + class BigPlayButton extends React.Component {} + + interface LoadingSpinnerPropsType { + player?: object; + className?: string; + } + class LoadingSpinner extends React.Component {} + + interface PosterImagePropsType { + poster?: string; + player?: object; + actions?: object; + className?: string; + } + class PosterImage extends React.Component {} + + interface BezelPropsType { + manager?: object; + className?: string; + } + class Bezel extends React.Component {} + + interface ShortcutPropsType { + clickable?: boolean; // = true; + dblclickable?: boolean; // = true; + manager?: object; + actions?: object; + player?: object; + shortcuts?: Array; + } + class Shortcut extends React.Component {} + + interface ControlBarPropsType { + children?: any; + autoHide?: boolean; // = true; + autoHideTime?: number; // used in Player + disableDefaultControls?: boolean; + disableCompletely?: boolean; // = false; + className?: string; + } + class ControlBar extends React.Component {} + + interface PlayTogglePropsType { + actions?: object; + player?: object; + className?: string; + } + class PlayToggle extends React.Component {} + + type ForwardSecondsType = 5 | 10 | 30; + interface ForwardControlPropsType { + actions?: object; + className?: string; + seconds?: ForwardSecondsType; // = 10; + } + class ForwardControl extends React.Component {} + + interface ReplayControlPropsType { + actions?: object; + className?: string; + seconds?: ForwardSecondsType; // = 10; + } + class ReplayControl extends React.Component {} + + interface FullscreenTogglePropsType { + actions?: object; + player?: object; + className?: string; + } + class FullscreenToggle extends React.Component {} + + interface ProgressControlPropsType { + player?: object; + className?: string; + } + class ProgressControl extends React.Component {} + + interface SeekBarPropsType { + player?: object; + mouseTime?: object; + actions?: object; + className?: string; + } + class SeekBar extends React.Component { + /** + * Get percentage of video played + * + * @return {Number} Percentage played + * @method getPercent + */ + getPercent(): number; + } + + interface SliderPropsType { + className?: string; + onMouseDown?: ReactEventHandler; + onMouseMove?: ReactEventHandler; + stepForward?: Function; + stepBack?: Function; + sliderActive?: ReactEventHandler; + sliderInactive?: ReactEventHandler; + onMouseUp?: ReactEventHandler; + onFocus?: ReactEventHandler; + onBlur?: ReactEventHandler; + onClick?: ReactEventHandler; + getPercent?: () => number; + vertical?: boolean; + children?: ReactNode; + label?: string; + valuenow?: string; + valuetext?: string; + } + class Slider extends React.Component {} + + interface PlayProgressBarPropsType { + currentTime?: number; + duration?: number; + percentage?: string; + className?: string; + } + class PlayProgressBar extends React.Component {} + + interface LoadProgressBarPropsType { + duration?: number; + buffered?: object; + className?: string; + } + const LoadProgressBar: React.FC; + + interface MouseTimeDisplayPropsType { + duration?: number; + mouseTime?: { + time: number; + position: number; + }; + className?: string; + text?: string; + } + const MouseTimeDisplay: React.FC; + + interface RemainingTimeDisplayPropsType { + player?: { + currentTime: number; + duration: number; + }; + className?: string; + } + const RemainingTimeDisplay: React.FC; + + interface CurrentTimeDisplayPropsType { + player?: { + currentTime: number; + duration: number; + }; + className?: string; + } + const CurrentTimeDisplay: React.FC; + + interface DurationDisplayPropsType { + player?: { + duration: number; + }; + className?: string; + } + const DurationDisplay: React.FC; + + interface TimeDividerPropsType { + separator?: string; + className?: string; + } + const TimeDivider: React.FC; + + interface VolumeMenuButtonPropsType { + player?: { + volume: number; + muted: boolean; + }; + actions?: object; + vertical?: boolean; + className?: string; + alwaysShowVolume?: boolean; + } + class VolumeMenuButton extends React.Component { + get volumeLevel(): number; + } + + interface PlaybackRateMenuButtonPropsType { + player?: object; + actions?: object; + rates?: Array; // = [2, 1.5, 1.25, 1, 0.5, 0.25]; + className?: string; + } + class PlaybackRateMenuButton extends React.Component {} + + interface ClosedCaptionButtonPropsType { + player?: object; + actions?: object; + className?: string; + offMenuText?: string; // = 'Off'; + showOffMenu?: boolean; // = true; + kinds?: Array; // = ['captions', 'subtitles']; // `kind`s of TextTrack to look for to associate it with this menu. + } + class ClosedCaptionButton extends React.Component {} + + class PlaybackRate extends React.Component {} + + interface MenuButtonPropsType { + inline?: boolean; + items?: Array; + className?: string; + onSelectItem?: ReactEventHandler; + children?: any; + selectedIndex?: number; + } + class MenuButton extends React.Component {} + + namespace playerActions { + type OPERATE = 'video-react/OPERATE'; + type FULLSCREEN_CHANGE = 'video-react/FULLSCREEN_CHANGE'; + type PLAYER_ACTIVATE = 'video-react/PLAYER_ACTIVATE'; + type USER_ACTIVATE = 'video-react/USER_ACTIVATE'; + + function handleFullscreenChange( + isFullscreen: boolean, + ): { + type: FULLSCREEN_CHANGE; + isFullscreen; + }; + + function activate( + activity, + ): { + type: PLAYER_ACTIVATE; + activity; + }; + + function userActivate( + activity, + ): { + type: USER_ACTIVATE; + activity; + }; + + function play(operation: { + action: 'play'; + source: string; + }): { + type: OPERATE; + operation; + }; + + function pause(operation: { + action: 'pause'; + source: string; + }): { + type: OPERATE; + operation; + }; + + function togglePlay(operation?: { + action: 'toggle-play'; + source: string; + }): { + type: OPERATE; + operation; + }; + + // seek video by time + function seek( + time: number, + operation?: { + action: 'seek'; + source: string; + }, + ): { + type: OPERATE; + operation; + }; + + // jump forward x seconds + function forward( + seconds: number, + operation?: { + action: string; + source: string; + }, + ): { + type: OPERATE; + operation; + }; + + // jump back x seconds + function replay( + seconds: number, + operation?: { + action: string; + source: string; + }, + ): { + type: OPERATE; + operation; + }; + + function changeRate( + rate: number, + operation?: { + action: 'change-rate'; + source: string; + }, + ): { + type: OPERATE; + operation; + }; + + function changeVolume( + volume: number, + operation?: { + action: 'change-volume'; + source: string; + }, + ): { + type: OPERATE; + operation; + }; + + function mute( + muted: boolean, + operation?: { + action: 'muted' | 'unmuted'; + source: string; + }, + ): { + type: OPERATE; + operation; + }; + + function toggleFullscreen(player): { type: string; [key: string]: any }; + } + + namespace videoActions { + type LOAD_START = 'video-react/LOAD_START'; + type CAN_PLAY = 'video-react/CAN_PLAY'; + type WAITING = 'video-react/WAITING'; + type CAN_PLAY_THROUGH = 'video-react/CAN_PLAY_THROUGH'; + type PLAYING = 'video-react/PLAYING'; + type PLAY = 'video-react/PLAY'; + type PAUSE = 'video-react/PAUSE'; + type END = 'video-react/END'; + type SEEKING = 'video-react/SEEKING'; + type SEEKED = 'video-react/SEEKED'; + type SEEKING_TIME = 'video-react/SEEKING_TIME'; + type END_SEEKING = 'video-react/END_SEEKING'; + type DURATION_CHANGE = 'video-react/DURATION_CHANGE'; + type TIME_UPDATE = 'video-react/TIME_UPDATE'; + type VOLUME_CHANGE = 'video-react/VOLUME_CHANGE'; + type PROGRESS_CHANGE = 'video-react/PROGRESS_CHANGE'; + type RATE_CHANGE = 'video-react/RATE_CHANGE'; + type SUSPEND = 'video-react/SUSPEND'; + type ABORT = 'video-react/ABORT'; + type EMPTIED = 'video-react/EMPTIED'; + type STALLED = 'video-react/STALLED'; + type LOADED_META_DATA = 'video-react/LOADED_META_DATA'; + type LOADED_DATA = 'video-react/LOADED_DATA'; + type RESIZE = 'video-react/RESIZE'; + type ERROR = 'video-react/ERROR'; + type ACTIVATE_TEXT_TRACK = 'video-react/ACTIVATE_TEXT_TRACK'; + + function handleLoadStart( + videoProps, + ): { + type: LOAD_START; + videoProps; + }; + + function handleCanPlay( + videoProps, + ): { + type: CAN_PLAY; + videoProps; + }; + + function handleWaiting( + videoProps, + ): { + type: WAITING; + videoProps; + }; + + function handleCanPlayThrough( + videoProps, + ): { + type: CAN_PLAY_THROUGH; + videoProps; + }; + + function handlePlaying( + videoProps, + ): { + type: PLAYING; + videoProps; + }; + + function handlePlay( + videoProps, + ): { + type: PLAY; + videoProps; + }; + + function handlePause( + videoProps, + ): { + type: PAUSE; + videoProps; + }; + + function handleEnd( + videoProps, + ): { + type: END; + videoProps; + }; + + function handleSeeking( + videoProps, + ): { + type: SEEKING; + videoProps; + }; + + function handleSeeked( + videoProps, + ): { + type: SEEKED; + videoProps; + }; + + function handleDurationChange( + videoProps, + ): { + type: DURATION_CHANGE; + videoProps; + }; + + function handleTimeUpdate( + videoProps, + ): { + type: TIME_UPDATE; + videoProps; + }; + + function handleVolumeChange( + videoProps, + ): { + type: VOLUME_CHANGE; + videoProps; + }; + + function handleProgressChange( + videoProps, + ): { + type: PROGRESS_CHANGE; + videoProps; + }; + + function handleRateChange( + videoProps, + ): { + type: RATE_CHANGE; + videoProps; + }; + + function handleSuspend( + videoProps, + ): { + type: SUSPEND; + videoProps; + }; + + function handleAbort( + videoProps, + ): { + type: ABORT; + videoProps; + }; + + function handleEmptied( + videoProps, + ): { + type: EMPTIED; + videoProps; + }; + + function handleStalled( + videoProps, + ): { + type: STALLED; + videoProps; + }; + + function handleLoadedMetaData( + videoProps, + ): { + type: LOADED_META_DATA; + videoProps; + }; + + function handleLoadedData( + videoProps, + ): { + type: LOADED_DATA; + videoProps; + }; + + function handleResize( + videoProps, + ): { + type: RESIZE; + videoProps; + }; + + function handleError( + videoProps, + ): { + type: ERROR; + videoProps; + }; + + function handleSeekingTime( + time, + ): { + type: SEEKING_TIME; + time; + }; + + function handleEndSeeking( + time, + ): { + type: END_SEEKING; + time; + }; + + function activateTextTrack( + textTrack, + ): { + type: ACTIVATE_TEXT_TRACK; + textTrack; + }; + } + + function playerReducer(state: any, action: any); + function operationReducer(state: any, action: any); +} diff --git a/yarn.lock b/yarn.lock index 7089a02..3df331d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1830,6 +1830,11 @@ dependencies: "@types/node" "*" +"@types/classnames@^2.2.10": + version "2.2.10" + resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999" + integrity sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ== + "@types/color-convert@*": version "1.9.0" resolved "https://registry.npm.taobao.org/@types/color-convert/download/@types/color-convert-1.9.0.tgz#bfa8203e41e7c65471e9841d7e306a7cd8b5172d" @@ -2126,6 +2131,14 @@ resolved "https://registry.npm.taobao.org/@types/range-parser/download/@types/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha1-fuMwunyq+5gJC+zoal7kQRWQTCw= +"@types/react-color@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-3.0.4.tgz#c63daf012ad067ac0127bdd86725f079d02082bd" + integrity sha512-EswbYJDF1kkrx93/YU+BbBtb46CCtDMvTiGmcOa/c5PETnwTiSWoseJ1oSWeRl/4rUXkhME9bVURvvPg0W5YQw== + dependencies: + "@types/react" "*" + "@types/reactcss" "*" + "@types/react-dom@^16.9.8": version "16.9.8" resolved "https://registry.npm.taobao.org/@types/react-dom/download/@types/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423" @@ -2184,6 +2197,13 @@ "@types/prop-types" "*" csstype "^3.0.2" +"@types/reactcss@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/reactcss/-/reactcss-1.2.3.tgz#af28ae11bbb277978b99d04d1eedfd068ca71834" + integrity sha512-d2gQQ0IL6hXLnoRfVYZukQNWHuVsE75DzFTLPUuyyEhJS8G2VvlE+qfQQ91SJjaMqlURRCNIsX7Jcsw6cEuJlA== + dependencies: + "@types/react" "*" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npm.taobao.org/@types/resolve/download/@types/resolve-1.17.1.tgz?cache=0&sync_timestamp=1596840738717&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fresolve%2Fdownload%2F%40types%2Fresolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"