refactor update folder

This commit is contained in:
yehuozhili 2020-09-16 16:52:11 +08:00
parent c81e15efaa
commit dae99ae103
51 changed files with 422 additions and 56 deletions

View File

@ -1,7 +1,7 @@
import { CarouselConfigType } from '@/components/DynamicEngine/schema';
import React, { memo, PropsWithChildren } from 'react';
import { Carousel } from 'zarm';
import styles from './index.less';
import { CarouselConfigType } from '../DynamicEngine/schema';
interface CarouselTypes extends CarouselConfigType {
isTpl: boolean;

View File

@ -0,0 +1,71 @@
const Carousel = {
editData: [
{
key: 'direction',
name: '方向',
type: 'Radio',
range: [
{
key: 'down',
text: '从上到下',
},
{
key: 'left',
text: '从左到右',
},
],
},
{
key: 'swipeable',
name: '是否可拖拽',
type: 'Switch',
},
{
key: 'autoPlay',
name: '是否自动播放',
type: 'Switch',
},
{
key: 'imgList',
name: '图片列表',
type: 'DataList',
},
],
config: {
direction: 'left',
swipeable: false,
autoPlay: false,
imgList: [
{
id: '1',
title: '趣谈小课1',
desc: '致力于打造优质小课程',
link: 'xxxxx',
imgUrl: [
{
uid: '001',
name: 'image.png',
status: 'done',
url: 'http://io.nainor.com/uploads/1_1740bd7c3dc.png',
},
],
},
{
id: '2',
title: '趣谈小课1',
desc: '致力于打造优质小课程',
link: 'xxxxx',
imgUrl: [
{
uid: '001',
name: 'image.png',
status: 'done',
url: 'http://io.nainor.com/uploads/2_1740bd8d525.png',
},
],
},
],
tplImg: 'http://io.nainor.com/uploads/carousal_17442e1420f.png',
},
};
export default Carousel;

View File

@ -0,0 +1,21 @@
import { FooterConfigType } from '@/components/DynamicEngine/schema';
import React, { memo } from 'react';
const Footer = memo((props: FooterConfigType) => {
const { bgColor, text, color, align, fontSize, height } = props;
return (
<footer
style={{
backgroundColor: bgColor,
color,
fontSize,
textAlign: align,
height,
lineHeight: height + 'px',
}}
>
{text}
</footer>
);
});
export default Footer;

View File

@ -1,6 +1,6 @@
import { Input, Cell, DateSelect, Radio, Select } from 'zarm';
import styles from './baseForm.less';
import React from 'react';
// 维护表单控件, 提高form渲染性能
const BaseForm = {
Text: props => {

View File

@ -0,0 +1,23 @@
.header {
box-sizing: content-box;
padding: 3px 12px;
display: flex;
align-items: center;
height: 50px;
background-color: #000;
.logo {
margin-right: 10px;
max-width: 160px;
max-height: 46px;
height: 46px;
overflow: hidden;
img {
height: 100%;
object-fit: contain;
}
}
.title {
font-size: 20px;
color: #fff;
}
}

View File

@ -0,0 +1,20 @@
import { memo } from 'react';
import styles from './index.less';
import React from 'react';
import { HeaderConfigType } from '@/components/DynamicEngine/schema';
const Header = memo((props: HeaderConfigType) => {
const { bgColor, logo, logoText, fontSize, color } = props;
return (
<header className={styles.header} style={{ backgroundColor: bgColor }}>
<div className={styles.logo}>
<img src={logo && logo[0].url} alt={logoText} />
</div>
<div className={styles.title} style={{ fontSize, color }}>
{logoText}
</div>
</header>
);
});
export default Header;

View File

@ -0,0 +1,42 @@
export type IconTypes =
| 'AccountBookTwoTone'
| 'AlertTwoTone'
| 'ApiTwoTone'
| 'AppstoreTwoTone'
| 'AudioTwoTone'
| 'BankTwoTone'
| 'BellTwoTone'
| 'BookTwoTone'
| 'BugTwoTone'
| 'BuildTwoTone'
| 'BulbTwoTone'
| 'CalculatorTwoTone'
| 'CalendarTwoTone'
| 'CameraTwoTone'
| 'CarTwoTone'
| 'CarryOutTwoTone'
| 'CiCircleTwoTone'
| 'CloudTwoTone'
| 'CodeTwoTone'
| 'CrownTwoTone'
| 'CustomerServiceTwoTone'
| 'DollarCircleTwoTone'
| 'EnvironmentTwoTone'
| 'ExperimentTwoTone'
| 'FireTwoTone'
| 'GiftTwoTone'
| 'InsuranceTwoTone'
| 'LikeTwoTone'
| 'LockTwoTone'
| 'MailTwoTone'
| 'MessageTwoTone'
| 'PhoneTwoTone'
| 'PictureTwoTone'
| 'PlaySquareTwoTone'
| 'RedEnvelopeTwoTone'
| 'ShopTwoTone'
| 'TrademarkCircleTwoTone'
| 'StarTwoTone'
| 'SafetyCertificateTwoTone'
| 'SettingTwoTone'
| 'RocketTwoTone';

View File

@ -0,0 +1,12 @@
import { ImageConfigType } from '@/components/DynamicEngine/schema';
import React, { memo } from 'react';
const Image = memo((props: ImageConfigType) => {
const { imgUrl, round = 0 } = props;
return (
<div style={{ borderRadius: round, width: '100%', textAlign: 'center', overflow: 'hidden' }}>
<img src={imgUrl && imgUrl[0].url} alt="" style={{ width: '100%' }} />
</div>
);
});
export default Image;

View File

@ -0,0 +1,19 @@
.list {
margin: 20px auto;
width: 94%;
.sourceList {
.sourceItem {
display: flex;
align-items: center;
margin-bottom: 16px;
.imgWrap {
}
.content {
margin-left: 12px;
.tit {
line-height: 2;
}
}
}
}
}

View File

@ -0,0 +1,51 @@
import { ListConfigType } from '@/components/DynamicEngine/schema';
import React, { memo } from 'react';
import styles from './index.less';
const List = memo((props: ListConfigType) => {
const { round, sourceData, imgSize, fontSize, color } = props;
return (
<div className={styles.list}>
<div className={styles.sourceList}>
{sourceData.map((item, i) => {
return (
<div className={styles.sourceItem} key={i}>
<div className={styles.imgWrap}>
<img
src={
item.imgUrl[0]
? item.imgUrl[0].url
: 'http://io.nainor.com/uploads/01_173e15d3493.png'
}
alt={item.desc}
style={{
width: imgSize,
height: imgSize,
objectFit: 'cover',
borderRadius: round,
}}
/>
</div>
<div className={styles.content}>
<a
className={styles.tit}
style={{ fontSize, color }}
href={item.link ? item.link : '#'}
>
{item.title}
<div
className={styles.desc}
style={{ fontSize: fontSize * 0.8, color: 'rgba(0,0,0, .3)' }}
>
{item.desc}
</div>
</a>
</div>
</div>
);
})}
</div>
</div>
);
});
export default List;

View File

@ -0,0 +1,15 @@
import { LongTextConfigType } from '@/components/DynamicEngine/schema';
import React, { memo } from 'react';
import styles from './index.less';
const LongText = memo((props: LongTextConfigType) => {
const { text, fontSize, color, indent, lineHeight, textAlign } = props;
return (
<div
className={styles.textWrap}
style={{ color, textIndent: indent + 'px', fontSize, lineHeight, textAlign }}
>
{text}
</div>
);
});
export default LongText;

View File

@ -0,0 +1,13 @@
import { NoticeBar } from 'zarm';
import React, { memo } from 'react';
import { NoticeConfigType } from '@/components/DynamicEngine/schema';
const Notice = memo((props: NoticeConfigType) => {
const { text, speed, theme, isClose = false } = props;
return (
<NoticeBar theme={theme === 'default' ? undefined : theme} closable={isClose} speed={speed}>
<span style={{ color: 'inherit' }}>{text}</span>
</NoticeBar>
);
});
export default Notice;

View File

@ -0,0 +1,14 @@
import { QRCodeConfigType } from '@/components/DynamicEngine/schema';
import React, { memo } from 'react';
const Qrcode = memo((props: QRCodeConfigType) => {
const { qrcode, text, color, fontSize = 14 } = props;
return (
<div style={{ width: '240px', margin: '16px auto' }}>
<img src={qrcode && qrcode[0].url} alt={text} style={{ width: '100%' }} />
<div style={{ textAlign: 'center', color, fontSize, paddingTop: '8px' }}>{text}</div>
</div>
);
});
export default Qrcode;

View File

@ -1,7 +1,7 @@
import { TabConfigType } from '@/components/DynamicEngine/schema';
import React, { useEffect, useRef } from 'react';
import { Tabs } from 'zarm';
import styles from './index.less';
import { TabConfigType } from '../DynamicEngine/schema';
const { Panel } = Tabs;
@ -22,7 +22,7 @@ const XTab = (props: TabConfigType) => {
return (
<div className={styles.tabWrap} ref={tabWrapRef}>
<Tabs
canSwipe
scrollThreshold={3}
onChange={i => {
console.log(i);
}}

View File

@ -0,0 +1,13 @@
import { TextConfigType } from '@/components/DynamicEngine/schema';
import React, { memo } from 'react';
import styles from './index.less';
const Text = memo((props: TextConfigType) => {
const { align, text, fontSize, color, lineHeight } = props;
return (
<div className={styles.textWrap} style={{ color, textAlign: align, fontSize, lineHeight }}>
{text}
</div>
);
});
export default Text;

View File

@ -1,7 +1,7 @@
import { VideoConfigType } from '@/components/DynamicEngine/schema';
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;

View File

@ -0,0 +1,21 @@
import { XProgressConfigType } from '@/components/DynamicEngine/schema';
import React, { memo } from 'react';
import { Progress } from 'zarm';
import styles from './index.less';
const XProgress = memo((props: XProgressConfigType) => {
const { theme, size, shape, percent, strokeWidth } = props;
return (
<div className={styles.textWrap} style={{ textAlign: 'center' }}>
<Progress
shape={shape}
size={size}
percent={percent}
theme={theme}
strokeWidth={strokeWidth}
/>
</div>
);
});
export default XProgress;

View File

@ -3,20 +3,22 @@ import Loading from '../LoadingCp';
import { useMemo, memo, FC } from 'react';
import React from 'react';
import { AllTemplateType } from './schema';
const needList = ['Tab', 'Carousel', 'Upload', 'Video', 'Icon', 'Chart'];
const DynamicFunc = (type: AllTemplateType) =>
export type componentsType = 'media' | 'base' | 'visible';
const DynamicFunc = (type: AllTemplateType, componentsType: componentsType) =>
dynamic({
loader: async function() {
let Component: FC<{ isTpl: boolean }>;
if (needList.includes(type)) {
const { default: Graph } = await import(`@/components/${type}`);
if (componentsType === 'base') {
const { default: Graph } = await import(`@/components/BasicShop/BasicComponents/${type}`);
Component = Graph;
} else if (componentsType === 'media') {
const { default: Graph } = await import(`@/components/BasicShop/MediaComponents/${type}`);
Component = Graph;
} else {
const Components = ((await import(`@/components/DynamicEngine/components`)) as unknown) as {
[key: string]: FC;
};
Component = Components[type];
const { default: Graph } = await import(`@/components/BasicShop/VisualComponents/${type}`);
Component = Graph;
}
return (props: DynamicType) => {
@ -35,14 +37,15 @@ type DynamicType = {
isTpl: boolean;
config: { [key: string]: any };
type: AllTemplateType;
componentsType: componentsType;
};
const DynamicEngine = memo((props: DynamicType) => {
const { type, config, isTpl } = props;
const { type, config, isTpl, componentsType } = props;
const Dynamic = useMemo(() => {
return (DynamicFunc(type) as unknown) as FC<DynamicType>;
return (DynamicFunc(type, componentsType) as unknown) as FC<DynamicType>;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [type, config]);
return <Dynamic type={type} config={config} isTpl={isTpl} />;
return <Dynamic type={type} config={config} isTpl={isTpl} componentsType={componentsType} />;
});
export default DynamicEngine;

View File

@ -1,8 +1,8 @@
import { useState, useEffect, memo } from 'react';
import classnames from 'classnames';
import Icon from '../Icon/';
import Icon from '../../BasicShop/BasicComponents/Icon';
import styles from './index.less';
import { IconTypes } from '../DynamicEngine/schema';
import { IconTypes } from '../../DynamicEngine/schema';
import React from 'react';
interface CardPickerType {

View File

@ -3,10 +3,12 @@ import { SketchPicker, ColorResult } from 'react-color';
import { rgba2Obj } from '@/utils/tool';
// import styles from './index.less'
export type ColorConfigType = string;
//value 初始值传来onchange item给的回调
interface ColorProps {
value?: string;
id?: string;
onChange?: (v: string) => void;
value?: ColorConfigType;
onChange?: (v: ColorConfigType) => void;
}
class colorPicker extends React.Component<ColorProps> {

View File

@ -1,7 +1,7 @@
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 Upload from '../Upload';
import { BasicDataSource } from '../../DynamicEngine/schema';
import { Store } from 'antd/lib/form/interface';
// import styles from './index.less';

View File

@ -18,7 +18,7 @@ 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';
import { BasicDataSource } from '../../DynamicEngine/schema';
type ListItemProps = DndItemProps & {
isDragging: boolean;
@ -31,9 +31,6 @@ function ListItem(props: ListItemProps) {
const {
title,
desc,
link,
imgUrl,
type,
onDel,
onEdit,
// 这些 props 由 React DnD注入参考`collect`函数定义
@ -73,7 +70,6 @@ type DndItemProps = BasicDataSource & {
onDel: Function;
onEdit: Function;
key: number;
id: string;
find: Function;
move: Function;
type?: number;

View File

@ -7,7 +7,7 @@ import Color from '../Color';
import CardPicker from '../CardPicker';
import Table from '../Table';
import { Store } from 'antd/lib/form/interface';
import { BasicRangeType, IconSchema } from '../DynamicEngine/schema';
import { BasicRangeType, IconSchema } from '../../DynamicEngine/schema';
// import styles from './index.less';
const normFile = (e: any) => {
console.log('Upload event:', e);

View File

@ -2,7 +2,7 @@ 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';
import { TabConfigType } from '../../DynamicEngine/schema';
type MultiTextProps = {
onChange?: (v: TabConfigType['tabs']) => void;

View File

@ -1,9 +1,10 @@
import React, { useContext, useState, useEffect, useRef, memo } from 'react';
import React, { useContext, useState, useEffect, useRef, memo, RefObject } from 'react';
import { Table, Input, Button, Popconfirm, Form, Modal } from 'antd';
// 下方样式主要为全局样式,暂时不可删
import styles from './index.less';
import { ColumnsType } from 'antd/lib/table';
const EditableContext = React.createContext<any>();
const EditableContext = React.createContext<any>(null);
interface Item {
key: string;
@ -32,7 +33,7 @@ interface EditableCellProps {
editable: boolean;
children: React.ReactNode;
dataIndex: string;
record: Item;
record: any;
handleSave: (record: Item) => void;
}
@ -46,12 +47,12 @@ const EditableCell: React.FC<EditableCellProps> = ({
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef();
const inputRef = useRef<HTMLInputElement>(null);
const form = useContext(EditableContext);
useEffect(() => {
if (editing) {
inputRef.current.focus();
inputRef.current?.focus();
}
}, [editing]);
@ -60,10 +61,9 @@ const EditableCell: React.FC<EditableCellProps> = ({
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const save = async e => {
const save = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleSave({ ...record, ...values });
} catch (errInfo) {
@ -85,7 +85,11 @@ const EditableCell: React.FC<EditableCellProps> = ({
},
]}
>
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
<Input
ref={(inputRef as unknown) as () => RefObject<HTMLInputElement>}
onPressEnter={save}
onBlur={save}
/>
</Form.Item>
) : (
<div className="editable-cell-value-wrap" style={{ paddingRight: 24 }} onClick={toggleEdit}>
@ -97,8 +101,19 @@ const EditableCell: React.FC<EditableCellProps> = ({
return <td {...restProps}>{childNode}</td>;
};
class EditableTable extends React.Component {
constructor(props) {
class EditableTable extends React.Component<any, any> {
columns: (
| { title: string; dataIndex: string; width: string; editable: boolean; render?: undefined }
| {
title: string;
dataIndex: string;
render: (text: string, record: any) => JSX.Element | null;
width?: undefined;
editable?: undefined;
}
)[];
constructor(props: any) {
super(props);
this.columns = [
{
@ -119,13 +134,13 @@ class EditableTable extends React.Component {
render: (text: string, record) =>
this.state.dataSource.length >= 1 ? (
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
<a></a>
<span></span>
</Popconfirm>
) : null,
},
];
const dataSource = props.data.map((item, i: number) => ({ key: i + '', ...item }));
const dataSource = props.data.map((item: any, i: number) => ({ key: i + '', ...item }));
this.state = {
dataSource: dataSource,
@ -156,7 +171,7 @@ class EditableTable extends React.Component {
this.props.onChange && this.props.onChange(newDataSource);
};
handleSave = row => {
handleSave = (row: any) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
@ -174,14 +189,14 @@ class EditableTable extends React.Component {
});
};
handleOk = e => {
handleOk = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
console.log(e);
this.setState({
visible: false,
});
};
handleCancel = e => {
handleCancel = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
console.log(e);
this.setState({
visible: false,
@ -196,7 +211,7 @@ class EditableTable extends React.Component {
cell: EditableCell,
},
};
const columns = this.columns.map(col => {
const columns: ColumnsType<any> = this.columns.map(col => {
if (!col.editable) {
return col;
}

View File

@ -6,4 +6,4 @@
:global(.ant-upload-select-picture-card .ant-upload-text) {
margin-top: 8px;
color: #666;
}
}

View File

@ -14,7 +14,7 @@ library.push(
text: (
<div>
<a href="https://github.com/MrXujiang">@徐小夕</a>
<a href="https://github.com/yehuozhili/learnsinglespa">@yehuozhili</a>
<a href="https://github.com/yehuozhili">@yehuozhili</a>
</div>
),
useReg: /(.*?)作者是谁(.*?)/,

View File

@ -13,7 +13,7 @@ import SourceBox from './SourceBox';
import TargetBox from './TargetBox';
import Calibration from 'components/Calibration';
import DynamicEngine from 'components/DynamicEngine';
import FormEditor from 'components/FormEditor';
import FormEditor from 'components/PanelComponents/FormEditor';
import template from 'components/DynamicEngine/template';
import mediaTpl from 'components/DynamicEngine/mediaTpl';
import graphTpl from 'components/DynamicEngine/graphTpl';
@ -137,21 +137,36 @@ const Container = props => {
<TabPane tab={generateHeader('base', '基础组件')} key="1">
{template.map((value, i) => (
<TargetBox item={value} key={i} canvasId={canvasId}>
<DynamicEngine {...value} config={schema[value.type].config} isTpl={true} />
<DynamicEngine
{...value}
config={schema[value.type].config}
componentsType="base"
isTpl={true}
/>
</TargetBox>
))}
</TabPane>
<TabPane tab={generateHeader('media', '媒体组件')} key="2">
{mediaTpl.map((value, i) => (
<TargetBox item={value} key={i} canvasId={canvasId}>
<DynamicEngine {...value} config={schema[value.type].config} isTpl={true} />
<DynamicEngine
{...value}
config={schema[value.type].config}
componentsType="media"
isTpl={true}
/>
</TargetBox>
))}
</TabPane>
<TabPane tab={generateHeader('visible', '可视化组件')} key="3">
{graphTpl.map((value, i) => (
<TargetBox item={value} key={i} canvasId={canvasId}>
<DynamicEngine {...value} config={schema[value.type].config} isTpl={true} />
<DynamicEngine
{...value}
config={schema[value.type].config}
componentsType="visible"
isTpl={true}
/>
</TargetBox>
))}
</TabPane>