mirror of
https://github.com/MrXujiang/h5-Dooring.git
synced 2026-03-10 09:55:53 +00:00
🔨 代码部分重构,渲染层和数据层分离,核心组件放在core文件夹
This commit is contained in:
parent
a1516cfffa
commit
5a8d62d98a
@ -1,36 +0,0 @@
|
||||
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' },
|
||||
danger: { bgColor: '#ff5050', color: '#fff' },
|
||||
primary: { bgColor: '#00bc71', color: '#fff' },
|
||||
blue: { bgColor: '#06c', color: '#fff' },
|
||||
};
|
||||
const BackTop = memo((props: { theme: keyof typeof themeObj }) => {
|
||||
const { theme = 'simple' } = props;
|
||||
|
||||
return (
|
||||
<BackToTop>
|
||||
<div
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
lineHeight: '48px',
|
||||
textAlign: 'center',
|
||||
backgroundColor: themeObj[theme].bgColor,
|
||||
color: themeObj[theme].color,
|
||||
fontSize: 20,
|
||||
borderRadius: 30,
|
||||
boxShadow: '0 2px 10px 0 rgba(0, 0, 0, 0.2)',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<Icon type="arrow-top" />
|
||||
</div>
|
||||
</BackToTop>
|
||||
);
|
||||
});
|
||||
|
||||
export default BackTop;
|
||||
@ -7,7 +7,7 @@ import {
|
||||
TDataListDefaultType,
|
||||
TNumberDefaultType,
|
||||
TSelectDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
} from '@/core/FormComponents/types';
|
||||
import { baseConfig, baseDefault, ICommonBaseType } from '../../common';
|
||||
export type TListSelectKeyType = '60' | '80' | '100' | '120' | '150';
|
||||
export type TListEditData = Array<
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
TNumberDefaultType,
|
||||
TSelectDefaultType,
|
||||
TTextAreaDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
} from '@/core/FormComponents/types';
|
||||
export type TLongTextSelectKeyType = 'left' | 'center' | 'right';
|
||||
|
||||
export type TLongTextEditData = Array<
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
TNumberDefaultType,
|
||||
TSelectDefaultType,
|
||||
TTextDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
} from '@/core/FormComponents/types';
|
||||
|
||||
export type TTextSelectKeyType = 'left' | 'right' | 'center';
|
||||
export type TTextEditData = Array<
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
import { NoticeBar } from 'zarm';
|
||||
import React, { memo } from 'react';
|
||||
import { INoticeConfig } from './schema';
|
||||
const Notice = memo((props: INoticeConfig) => {
|
||||
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;
|
||||
@ -1,81 +0,0 @@
|
||||
import {
|
||||
INumberConfigType,
|
||||
ISelectConfigType,
|
||||
ISwitchConfigType,
|
||||
ITextConfigType,
|
||||
TNumberDefaultType,
|
||||
TSelectDefaultType,
|
||||
TSwitchDefaultType,
|
||||
TTextDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TNoticeSelectKeyType = 'default' | 'warning' | 'primary' | 'success' | 'danger';
|
||||
export type TNoticeEditData = Array<
|
||||
ITextConfigType | INumberConfigType | ISelectConfigType<TNoticeSelectKeyType> | ISwitchConfigType
|
||||
>;
|
||||
export interface INoticeConfig {
|
||||
text: TTextDefaultType;
|
||||
speed: TNumberDefaultType;
|
||||
theme: TSelectDefaultType<TNoticeSelectKeyType>;
|
||||
isClose: TSwitchDefaultType;
|
||||
}
|
||||
|
||||
export interface INoticeSchema {
|
||||
editData: TNoticeEditData;
|
||||
config: INoticeConfig;
|
||||
}
|
||||
|
||||
const Notice: INoticeSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'text',
|
||||
name: '文本',
|
||||
type: 'Text',
|
||||
},
|
||||
{
|
||||
key: 'speed',
|
||||
name: '滚动速度',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'theme',
|
||||
name: '主题',
|
||||
type: 'Select',
|
||||
range: [
|
||||
{
|
||||
key: 'default',
|
||||
text: '默认',
|
||||
},
|
||||
{
|
||||
key: 'warning',
|
||||
text: '警告',
|
||||
},
|
||||
{
|
||||
key: 'primary',
|
||||
text: '主要',
|
||||
},
|
||||
{
|
||||
key: 'success',
|
||||
text: '成功',
|
||||
},
|
||||
{
|
||||
key: 'danger',
|
||||
text: '危险',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'isClose',
|
||||
name: '是否可关闭',
|
||||
type: 'Switch',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
text: '通知栏: 趣谈前端上线啦',
|
||||
speed: 50,
|
||||
theme: 'warning',
|
||||
isClose: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default Notice;
|
||||
@ -1,5 +0,0 @@
|
||||
const template = {
|
||||
type: 'Notice',
|
||||
h: 20,
|
||||
};
|
||||
export default template;
|
||||
@ -1,27 +0,0 @@
|
||||
.tabWrap {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
.content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.item {
|
||||
padding: 20px 20px 0;
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
.imgWrap {
|
||||
display: inline-block;
|
||||
width: 80%;
|
||||
img {
|
||||
border-radius: 6px;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
}
|
||||
.title {
|
||||
line-height: 2.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Tabs } from 'zarm';
|
||||
import styles from './index.less';
|
||||
import { ITabConfig } from './schema';
|
||||
|
||||
const { Panel } = Tabs;
|
||||
|
||||
const XTab = (props: ITabConfig) => {
|
||||
const { tabs = ['分类一', '分类二'], activeColor, color, fontSize, sourceData } = props;
|
||||
|
||||
const tabWrapRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (tabWrapRef.current) {
|
||||
let res = tabWrapRef.current.querySelector('.za-tabs__line') as HTMLElement;
|
||||
if (res) {
|
||||
res.style.backgroundColor = activeColor;
|
||||
}
|
||||
}
|
||||
}, [activeColor]);
|
||||
|
||||
return (
|
||||
<div className={styles.tabWrap} ref={tabWrapRef}>
|
||||
<Tabs scrollThreshold={3}>
|
||||
{tabs.map((item, i) => {
|
||||
return (
|
||||
<Panel title={item} key={i}>
|
||||
<div className={styles.content}>
|
||||
{sourceData
|
||||
.filter(item => item.type === i)
|
||||
.map((item, i) => {
|
||||
return (
|
||||
<div className={styles.item} key={i}>
|
||||
<a className={styles.imgWrap} href={item.link} title={item.desc}>
|
||||
<img
|
||||
src={
|
||||
item.imgUrl[0]
|
||||
? item.imgUrl[0].url
|
||||
: 'http://io.nainor.com/uploads/01_173e15d3493.png'
|
||||
}
|
||||
alt={item.title}
|
||||
/>
|
||||
<div className={styles.title} style={{ fontSize, color }}>
|
||||
{item.title}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Panel>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default XTab;
|
||||
@ -1,118 +0,0 @@
|
||||
import {
|
||||
IColorConfigType,
|
||||
IDataListConfigType,
|
||||
IMutiTextConfigType,
|
||||
INumberConfigType,
|
||||
TColorDefaultType,
|
||||
TDataListDefaultType,
|
||||
TMutiTextDefaultType,
|
||||
TNumberDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TTabEditData = Array<
|
||||
IMutiTextConfigType | IColorConfigType | INumberConfigType | IDataListConfigType
|
||||
>;
|
||||
export interface ITabConfig {
|
||||
tabs: TMutiTextDefaultType;
|
||||
color: TColorDefaultType;
|
||||
activeColor: TColorDefaultType;
|
||||
fontSize: TNumberDefaultType;
|
||||
imgSize: TNumberDefaultType;
|
||||
sourceData: TDataListDefaultType;
|
||||
}
|
||||
|
||||
export interface ITabSchema {
|
||||
editData: TTabEditData;
|
||||
config: ITabConfig;
|
||||
}
|
||||
|
||||
const Tab: ITabSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'tabs',
|
||||
name: '项目类别',
|
||||
type: 'MutiText',
|
||||
},
|
||||
{
|
||||
key: 'activeColor',
|
||||
name: '激活颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'color',
|
||||
name: '文字颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'fontSize',
|
||||
name: '文字大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'imgSize',
|
||||
name: '图片大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'sourceData',
|
||||
name: '数据源',
|
||||
type: 'DataList',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
tabs: ['类别一', '类别二'],
|
||||
color: 'rgba(153,153,153,1)',
|
||||
activeColor: 'rgba(0,102,204,1)',
|
||||
fontSize: 16,
|
||||
imgSize: 100,
|
||||
sourceData: [
|
||||
{
|
||||
id: '1',
|
||||
title: '趣谈小课1',
|
||||
desc: '致力于打造优质小课程',
|
||||
link: 'xxxxx',
|
||||
type: 0,
|
||||
imgUrl: [
|
||||
{
|
||||
uid: '001',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'http://io.nainor.com/uploads/1_1740c6fbcd9.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '趣谈小课2',
|
||||
desc: '致力于打造优质小课程',
|
||||
link: 'xxxxx',
|
||||
type: 0,
|
||||
imgUrl: [
|
||||
{
|
||||
uid: '001',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'http://io.nainor.com/uploads/2_1740c7033a9.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: '趣谈小课3',
|
||||
desc: '致力于打造优质小课程',
|
||||
link: 'xxxxx',
|
||||
type: 1,
|
||||
imgUrl: [
|
||||
{
|
||||
uid: '001',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'http://io.nainor.com/uploads/1_1740c6fbcd9.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default Tab;
|
||||
@ -1,5 +0,0 @@
|
||||
const template = {
|
||||
type: 'Tab',
|
||||
h: 130,
|
||||
};
|
||||
export default template;
|
||||
@ -1,28 +0,0 @@
|
||||
import Carousel from './Carousel/schema';
|
||||
import Footer from './Footer/schema';
|
||||
import Form from './Form/schema';
|
||||
import Header from './Header/schema';
|
||||
import Icon from './Icon/schema';
|
||||
import Image from './Image/schema';
|
||||
import List from './List/schema';
|
||||
import LongText from './LongText/schema';
|
||||
import Notice from './Notice/schema';
|
||||
import Qrcode from './Qrcode/schema';
|
||||
import Tab from './Tab/schema';
|
||||
import Text from './Text/schema';
|
||||
|
||||
const basicSchema = {
|
||||
Carousel,
|
||||
Footer,
|
||||
Form,
|
||||
Header,
|
||||
Icon,
|
||||
Image,
|
||||
List,
|
||||
LongText,
|
||||
Notice,
|
||||
Qrcode,
|
||||
Tab,
|
||||
Text,
|
||||
};
|
||||
export default basicSchema;
|
||||
@ -1,32 +0,0 @@
|
||||
import Carousel from './Carousel/template';
|
||||
import Footer from './Footer/template';
|
||||
import Form from './Form/template';
|
||||
import Header from './Header/template';
|
||||
import Icon from './Icon/template';
|
||||
import Image from './Image/template';
|
||||
import List from './List/template';
|
||||
import LongText from './LongText/template';
|
||||
import Notice from './Notice/template';
|
||||
import Qrcode from './Qrcode/template';
|
||||
import Tab from './Tab/template';
|
||||
import Text from './Text/template';
|
||||
|
||||
const basicTemplate = [
|
||||
Carousel,
|
||||
Footer,
|
||||
Form,
|
||||
Header,
|
||||
Icon,
|
||||
Image,
|
||||
List,
|
||||
LongText,
|
||||
Notice,
|
||||
Qrcode,
|
||||
Tab,
|
||||
Text,
|
||||
];
|
||||
const BasicTemplate = basicTemplate.map(v => {
|
||||
return { ...v, category: 'base' };
|
||||
});
|
||||
|
||||
export default BasicTemplate;
|
||||
@ -1,10 +0,0 @@
|
||||
.carousel__item__pic {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
max-height: 220px;
|
||||
overflow: hidden;
|
||||
vertical-align: top;
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
import React, { memo, PropsWithChildren } from 'react';
|
||||
import { Carousel } from 'zarm';
|
||||
import styles from './index.less';
|
||||
import { ICarouselConfig } from './schema';
|
||||
|
||||
interface CarouselTypes extends ICarouselConfig {
|
||||
isTpl: boolean;
|
||||
}
|
||||
|
||||
const XCarousel = memo((props: PropsWithChildren<CarouselTypes>) => {
|
||||
const { direction, swipeable, autoPlay, isTpl, imgList, tplImg } = props;
|
||||
const contentRender = () => {
|
||||
return imgList.map((item, i) => {
|
||||
return (
|
||||
<div className={styles.carousel__item__pic} key={+i}>
|
||||
<img src={item.imgUrl.length > 0 ? item.imgUrl[0].url : ''} alt="" />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
return (
|
||||
<div style={{ width: '100%', overflow: 'hidden' }}>
|
||||
{isTpl ? (
|
||||
<div className={styles.carousel__item__pic}>
|
||||
<img src={tplImg} alt="" />
|
||||
</div>
|
||||
) : (
|
||||
<Carousel
|
||||
onChange={index => {
|
||||
// console.log(`onChange: ${index}`);
|
||||
}}
|
||||
direction={direction}
|
||||
swipeable={swipeable}
|
||||
autoPlay={autoPlay}
|
||||
loop
|
||||
>
|
||||
{contentRender()}
|
||||
</Carousel>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default XCarousel;
|
||||
@ -1,98 +0,0 @@
|
||||
import {
|
||||
IDataListConfigType,
|
||||
IRadioConfigType,
|
||||
ISwitchConfigType,
|
||||
TDataListDefaultType,
|
||||
TRadioDefaultType,
|
||||
TSwitchDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type CarouselDirectionKeyType = 'down' | 'left';
|
||||
|
||||
export type TCarouselEditData = Array<
|
||||
IRadioConfigType<CarouselDirectionKeyType> | ISwitchConfigType | IDataListConfigType
|
||||
>;
|
||||
export interface ICarouselConfig {
|
||||
direction: TRadioDefaultType<CarouselDirectionKeyType>;
|
||||
swipeable: TSwitchDefaultType;
|
||||
autoPlay: TSwitchDefaultType;
|
||||
imgList: TDataListDefaultType;
|
||||
tplImg: string;
|
||||
}
|
||||
|
||||
export interface ICarouselSchema {
|
||||
editData: TCarouselEditData;
|
||||
config: ICarouselConfig;
|
||||
}
|
||||
|
||||
const Carousel: ICarouselSchema = {
|
||||
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;
|
||||
@ -1,5 +0,0 @@
|
||||
const template = {
|
||||
type: 'Carousel',
|
||||
h: 82,
|
||||
};
|
||||
export default template;
|
||||
@ -1,96 +0,0 @@
|
||||
import { Input, Cell, DateSelect, Radio, Select } from 'zarm';
|
||||
import styles from './baseForm.less';
|
||||
import React from 'react';
|
||||
import {
|
||||
baseFormDateTpl,
|
||||
baseFormMyRadioTpl,
|
||||
baseFormMySelectTpl,
|
||||
baseFormNumberTpl,
|
||||
baseFormTextAreaTpl,
|
||||
baseFormTextTpl,
|
||||
baseFormUnionType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
// 维护表单控件, 提高form渲染性能
|
||||
|
||||
type TBaseForm = {
|
||||
[key in baseFormUnionType]: any;
|
||||
};
|
||||
|
||||
const BaseForm: TBaseForm = {
|
||||
Text: (props: baseFormTextTpl & { onChange: (v: string | undefined) => void }) => {
|
||||
const { label, placeholder, onChange } = props;
|
||||
return (
|
||||
<Cell title={label}>
|
||||
<Input clearable type="text" placeholder={placeholder} onChange={onChange} />
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
Textarea: (props: baseFormTextAreaTpl & { onChange: (v: string | undefined) => void }) => {
|
||||
const { label, placeholder, onChange } = props;
|
||||
return (
|
||||
<Cell title={label}>
|
||||
<Input
|
||||
type="text"
|
||||
rows={3}
|
||||
autoHeight
|
||||
showLength
|
||||
placeholder={placeholder}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
Number: (props: baseFormNumberTpl & { onChange: (v: string | undefined | number) => void }) => {
|
||||
const { label, placeholder, onChange } = props;
|
||||
return (
|
||||
<Cell title={label}>
|
||||
<Input type="number" placeholder={placeholder} onChange={onChange} />
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
MyRadio: (props: baseFormMyRadioTpl & { onChange: (v: string | undefined | number) => void }) => {
|
||||
const { label, options, onChange } = props;
|
||||
return (
|
||||
<div className={styles.radioWrap}>
|
||||
<div className={styles.radioTitle}>{label}</div>
|
||||
<Cell>
|
||||
<Radio.Group onChange={onChange}>
|
||||
{options.map((item, i) => {
|
||||
return (
|
||||
<Radio value={item.value} key={i} className={styles.radioItem}>
|
||||
{item.label}
|
||||
</Radio>
|
||||
);
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Cell>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Date: (props: baseFormDateTpl & { onChange: (v: Date) => void }) => {
|
||||
const { label, placeholder, onChange } = props;
|
||||
return (
|
||||
<Cell title={label}>
|
||||
<DateSelect
|
||||
placeholder={placeholder}
|
||||
mode="date"
|
||||
min="1949-05-15"
|
||||
max="2100-05-15"
|
||||
onOk={onChange}
|
||||
/>
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
MySelect: (
|
||||
props: baseFormMySelectTpl & { onChange: ((v: Record<string, any>) => void) | undefined },
|
||||
) => {
|
||||
const { label, options, onChange } = props;
|
||||
return (
|
||||
<Cell title={label}>
|
||||
<Select dataSource={options} onOk={onChange} />
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default BaseForm;
|
||||
@ -1,10 +0,0 @@
|
||||
.radioWrap {
|
||||
margin-bottom: 10px;
|
||||
.radioTitle {
|
||||
padding: 19px 14px;
|
||||
// font-size: 15px;
|
||||
}
|
||||
.radioItem {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
.formWrap {
|
||||
margin: 10px;
|
||||
padding: 20px 16px;
|
||||
border-radius: 6px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 6px #f0f0f0;
|
||||
.title {
|
||||
padding-bottom: 20px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
}
|
||||
.formContent {
|
||||
}
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { Button } from 'zarm';
|
||||
import BaseForm from './BaseForm';
|
||||
import styles from './index.less';
|
||||
import { IFormConfig } from './schema';
|
||||
|
||||
const FormComponent = (props: IFormConfig) => {
|
||||
const { title, bgColor, fontSize, titColor, btnColor, btnTextColor, api, formControls } = props;
|
||||
const formData: Record<string, any> = {};
|
||||
const handleChange = useCallback(
|
||||
(item, v) => {
|
||||
if (item.options) {
|
||||
formData[item.label] = v[0].label;
|
||||
return;
|
||||
}
|
||||
formData[item.label] = v;
|
||||
},
|
||||
[formData],
|
||||
);
|
||||
const handleSubmit = () => {
|
||||
if (api) {
|
||||
fetch(api, {
|
||||
body: JSON.stringify(formData),
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
});
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={styles.formWrap} style={{ backgroundColor: bgColor }}>
|
||||
{title && (
|
||||
<div className={styles.title} style={{ fontSize, color: titColor }}>
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.formContent}>
|
||||
{formControls.map(item => {
|
||||
const FormItem = BaseForm[item.type];
|
||||
return <FormItem onChange={handleChange.bind(this, item)} {...item} key={item.id} />;
|
||||
})}
|
||||
<div style={{ textAlign: 'center', padding: '16px 0' }}>
|
||||
<Button
|
||||
theme="primary"
|
||||
size="sm"
|
||||
block
|
||||
onClick={handleSubmit}
|
||||
style={{ backgroundColor: btnColor, borderColor: btnColor, color: btnTextColor }}
|
||||
>
|
||||
提交
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(FormComponent);
|
||||
@ -1,108 +0,0 @@
|
||||
import {
|
||||
IColorConfigType,
|
||||
IFormItemsConfigType,
|
||||
INumberConfigType,
|
||||
ITextConfigType,
|
||||
TColorDefaultType,
|
||||
TFormItemsDefaultType,
|
||||
TNumberDefaultType,
|
||||
TTextDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TFormEditData = Array<
|
||||
ITextConfigType | INumberConfigType | IColorConfigType | ITextConfigType | IFormItemsConfigType
|
||||
>;
|
||||
export interface IFormConfig {
|
||||
title: TTextDefaultType;
|
||||
fontSize: TNumberDefaultType;
|
||||
titColor: TColorDefaultType;
|
||||
bgColor: TColorDefaultType;
|
||||
btnColor: TColorDefaultType;
|
||||
btnTextColor: TColorDefaultType;
|
||||
api: TTextDefaultType;
|
||||
formControls: TFormItemsDefaultType;
|
||||
}
|
||||
|
||||
export interface IFormSchema {
|
||||
editData: TFormEditData;
|
||||
config: IFormConfig;
|
||||
}
|
||||
|
||||
const Form: IFormSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'title',
|
||||
name: '标题',
|
||||
type: 'Text',
|
||||
},
|
||||
{
|
||||
key: 'fontSize',
|
||||
name: '标题大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'titColor',
|
||||
name: '标题颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'bgColor',
|
||||
name: '背景色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'btnColor',
|
||||
name: '按钮颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'btnTextColor',
|
||||
name: '按钮文本颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'api',
|
||||
name: '表单Api地址',
|
||||
type: 'Text',
|
||||
},
|
||||
{
|
||||
key: 'formControls',
|
||||
name: '表单控件',
|
||||
type: 'FormItems',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
title: '表单定制组件',
|
||||
fontSize: 18,
|
||||
titColor: 'rgba(60,60,60,1)',
|
||||
bgColor: 'rgba(255,255,255,1)',
|
||||
btnColor: 'rgba(129,173,173,1)',
|
||||
btnTextColor: 'rgba(255,255,255,1)',
|
||||
api: '',
|
||||
formControls: [
|
||||
{
|
||||
id: '1',
|
||||
type: 'Text',
|
||||
label: '姓名',
|
||||
placeholder: '请输入姓名',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: 'Number',
|
||||
label: '年龄',
|
||||
placeholder: ' 请输入年龄',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
type: 'MySelect',
|
||||
label: '爱好',
|
||||
options: [
|
||||
{ label: '篮球', value: '1' },
|
||||
{ label: '乒乓球', value: '2' },
|
||||
{ label: '健身', value: '3' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
export default Form;
|
||||
@ -1,5 +0,0 @@
|
||||
const template = {
|
||||
type: 'Form',
|
||||
h: 172,
|
||||
};
|
||||
export default template;
|
||||
@ -1,21 +0,0 @@
|
||||
import React, { memo } from 'react';
|
||||
import { Progress } from 'zarm';
|
||||
import styles from './index.less';
|
||||
import { IXProgressConfig } from './schema';
|
||||
|
||||
const XProgress = memo((props: IXProgressConfig) => {
|
||||
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;
|
||||
@ -1,95 +0,0 @@
|
||||
import {
|
||||
INumberConfigType,
|
||||
IRadioConfigType,
|
||||
ISelectConfigType,
|
||||
TNumberDefaultType,
|
||||
TRadioDefaultType,
|
||||
TSelectDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
export type TXProgressSelectKeyType = 'success' | 'warning' | 'danger';
|
||||
export type TXProgressRadiotKeyType = 'circle' | 'line' | 'semi-circle';
|
||||
export type TXProgressEditData = Array<
|
||||
| ISelectConfigType<TXProgressSelectKeyType>
|
||||
| IRadioConfigType<TXProgressRadiotKeyType>
|
||||
| INumberConfigType
|
||||
>;
|
||||
export interface IXProgressConfig {
|
||||
theme: TSelectDefaultType<TXProgressSelectKeyType>;
|
||||
shape: TRadioDefaultType<TXProgressRadiotKeyType>;
|
||||
size: TNumberDefaultType;
|
||||
percent: TNumberDefaultType;
|
||||
strokeWidth: TNumberDefaultType;
|
||||
}
|
||||
|
||||
export interface IXProgressSchema {
|
||||
editData: TXProgressEditData;
|
||||
config: IXProgressConfig;
|
||||
}
|
||||
|
||||
const XProgress: IXProgressSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'theme',
|
||||
name: '主题',
|
||||
type: 'Select',
|
||||
range: [
|
||||
{
|
||||
key: 'success',
|
||||
text: '成功',
|
||||
},
|
||||
{
|
||||
key: 'warning',
|
||||
text: '警告',
|
||||
},
|
||||
{
|
||||
key: 'danger',
|
||||
text: '危险',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'shape',
|
||||
name: '形状',
|
||||
type: 'Radio',
|
||||
range: [
|
||||
{
|
||||
key: 'circle',
|
||||
text: '圆形',
|
||||
},
|
||||
{
|
||||
key: 'line',
|
||||
text: '线形',
|
||||
},
|
||||
{
|
||||
key: 'semi-circle',
|
||||
text: '半圆形',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'size',
|
||||
name: '大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'percent',
|
||||
name: '进度值',
|
||||
type: 'Number',
|
||||
range: [0, 100],
|
||||
},
|
||||
{
|
||||
key: 'strokeWidth',
|
||||
name: '线条粗细',
|
||||
type: 'Number',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
theme: 'success',
|
||||
shape: 'circle',
|
||||
size: 200,
|
||||
percent: 30,
|
||||
strokeWidth: 10,
|
||||
},
|
||||
};
|
||||
|
||||
export default XProgress;
|
||||
@ -1,5 +0,0 @@
|
||||
const template = {
|
||||
type: 'XProgress',
|
||||
h: 102,
|
||||
};
|
||||
export default template;
|
||||
BIN
src/components/DynamicEngine/.DS_Store
vendored
BIN
src/components/DynamicEngine/.DS_Store
vendored
Binary file not shown.
@ -1,9 +1,8 @@
|
||||
import { dynamic } from 'umi';
|
||||
import Loading from '../LoadingCp';
|
||||
import Loading from '../components/LoadingCp';
|
||||
import { useMemo, memo, FC, useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { dooringContext, dooringContextType } from '@/layouts';
|
||||
// import { AllTemplateType } from './schema';
|
||||
|
||||
export type componentsType = 'media' | 'base' | 'visible';
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { useState, useEffect, memo } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import Icon from '../../BasicShop/BasicComponents/Icon';
|
||||
import Icon from '@/components/BasicShop/BasicComponents/Icon';
|
||||
import styles from './index.less';
|
||||
import React from 'react';
|
||||
import { IconTypes } from '@/components/BasicShop/BasicComponents/Icon/schema';
|
||||
import { ICardPickerConfigType } from '../FormEditor/types';
|
||||
import { ICardPickerConfigType } from '../types';
|
||||
|
||||
interface CardPickerType extends Omit<ICardPickerConfigType<IconTypes>, 'type' | 'key' | 'name'> {
|
||||
onChange?: (v: string) => void;
|
||||
@ -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 { TDataListDefaultType, TDataListDefaultTypeItem } from '../FormEditor/types';
|
||||
import { TDataListDefaultType, TDataListDefaultTypeItem } from '../types';
|
||||
|
||||
type ListItemProps = DndItemProps & {
|
||||
isDragging: boolean;
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { FC, memo, useEffect } from 'react';
|
||||
import { Form, Select, Input, Modal, Button, InputNumber } from 'antd';
|
||||
import { baseFormOptionsType } from '../FormEditor/types';
|
||||
import { baseFormOptionsType } from '../types';
|
||||
import Color from '../Color';
|
||||
|
||||
const { Option } = Select;
|
||||
@ -1,10 +1,10 @@
|
||||
import React, { memo, RefObject, useCallback, useEffect, useState } from 'react';
|
||||
import BaseForm from '../../BasicShop/BasicComponents/Form/BaseForm';
|
||||
import BasePopoverForm from '../../BasicShop/BasicComponents/Form/BasePopoverForm';
|
||||
import BaseForm from '@/components/BasicShop/BasicComponents/Form/BaseForm';
|
||||
import BasePopoverForm from '@/components/BasicShop/BasicComponents/Form/BasePopoverForm';
|
||||
import EditorModal from './EditorModal';
|
||||
import { MinusCircleFilled, EditFilled, PlusOutlined } from '@ant-design/icons';
|
||||
import styles from './formItems.less';
|
||||
import { baseFormUnion, TFormItemsDefaultType } from '../FormEditor/types';
|
||||
import { baseFormUnion, TFormItemsDefaultType } from '../types';
|
||||
import { uuid } from '@/utils/tool';
|
||||
import { Button } from 'antd';
|
||||
import MyPopover from 'yh-react-popover';
|
||||
@ -2,7 +2,7 @@ import React, { memo, useEffect } from 'react';
|
||||
import { Input, Button, Popconfirm } from 'antd';
|
||||
import { MinusCircleFilled } from '@ant-design/icons';
|
||||
import styles from './index.less';
|
||||
import { TMutiTextDefaultType } from '../FormEditor/types';
|
||||
import { TMutiTextDefaultType } from '../types';
|
||||
|
||||
type MultiTextProps = {
|
||||
onChange?: (v: TMutiTextDefaultType) => void;
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import { InputNumber } from 'antd';
|
||||
import styles from './index.less';
|
||||
import { TPosDefaultType, TPosItem } from '../FormEditor/types';
|
||||
import { TPosDefaultType, TPosItem } from '../types';
|
||||
|
||||
type PosProps = {
|
||||
value?: TPosDefaultType;
|
||||
@ -27,11 +27,10 @@ const controls = [
|
||||
];
|
||||
|
||||
export default memo(function XEditor(props: any) {
|
||||
const [editorState, setEditorState] = useState(BraftEditor.createEditorState());
|
||||
|
||||
const { value, onChange } = props;
|
||||
const [editorState, setEditorState] = useState(BraftEditor.createEditorState(value));
|
||||
|
||||
const myUploadFn = param => {
|
||||
const myUploadFn = (param: any) => {
|
||||
const fd = new FormData();
|
||||
fd.append('file', param.file);
|
||||
|
||||
@ -46,8 +45,7 @@ export default memo(function XEditor(props: any) {
|
||||
param.progress((event.loaded / event.total) * 100);
|
||||
},
|
||||
})
|
||||
.then(res => {
|
||||
console.log(res);
|
||||
.then((res: any) => {
|
||||
// 上传成功后调用param.success并传入上传后的文件地址
|
||||
param.success({
|
||||
url: res.url,
|
||||
@ -1,16 +1,15 @@
|
||||
import React, { memo, RefObject, useEffect } from 'react';
|
||||
import { Form, Select, InputNumber, Input, Switch, Radio, Button } from 'antd';
|
||||
import Upload from '../Upload';
|
||||
import DataList from '../DataList';
|
||||
import MutiText from '../MutiText';
|
||||
import Color from '../Color';
|
||||
import CardPicker from '../CardPicker';
|
||||
import Table from '../Table';
|
||||
import Pos from '../Pos';
|
||||
import { Form, Select, InputNumber, Input, Switch, Radio } from 'antd';
|
||||
import Upload from './FormComponents/Upload';
|
||||
import DataList from './FormComponents/DataList';
|
||||
import MutiText from './FormComponents/MutiText';
|
||||
import Color from './FormComponents/Color';
|
||||
import CardPicker from './FormComponents/CardPicker';
|
||||
import Table from './FormComponents/Table';
|
||||
import Pos from './FormComponents/Pos';
|
||||
import { Store } from 'antd/lib/form/interface';
|
||||
import RichText from '../XEditor';
|
||||
import FormItems from '../FormItems';
|
||||
// import styles from './index.less';
|
||||
import RichText from './FormComponents/XEditor';
|
||||
import FormItems from './FormComponents/FormItems';
|
||||
const normFile = (e: any) => {
|
||||
console.log('Upload event:', e);
|
||||
if (Array.isArray(e)) {
|
||||
50
src/core/ViewRender.tsx
Normal file
50
src/core/ViewRender.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import React, { memo } from 'react';
|
||||
import GridLayout, { ItemCallback } from 'react-grid-layout';
|
||||
import DynamicEngine from '@/core/DynamicEngine';
|
||||
import styles from './viewRender.less';
|
||||
|
||||
interface PointDataItem {
|
||||
id: string;
|
||||
item: Record<string, any>;
|
||||
point: Record<string, any>;
|
||||
}
|
||||
|
||||
interface ViewProps {
|
||||
pointData: Array<PointDataItem>;
|
||||
pageData?: any;
|
||||
width?: number;
|
||||
dragStop?: ItemCallback;
|
||||
onDragStart?: ItemCallback;
|
||||
onResizeStop?: ItemCallback;
|
||||
}
|
||||
|
||||
const ViewRender = memo((props: ViewProps) => {
|
||||
const { pointData, pageData, width, dragStop, onDragStart, onResizeStop } = props;
|
||||
|
||||
return (
|
||||
<GridLayout
|
||||
cols={24}
|
||||
rowHeight={2}
|
||||
width={width}
|
||||
margin={[0, 0]}
|
||||
onDragStop={dragStop}
|
||||
onDragStart={onDragStart}
|
||||
onResizeStop={onResizeStop}
|
||||
style={{
|
||||
backgroundColor: pageData && pageData.bgColor,
|
||||
backgroundImage:
|
||||
pageData && pageData.bgImage ? `url(${pageData.bgImage[0].url})` : 'initial',
|
||||
backgroundSize: 'contain',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
>
|
||||
{pointData.map((value: PointDataItem) => (
|
||||
<div key={value.id} data-grid={value.point} className={styles.dragItem}>
|
||||
<DynamicEngine {...(value.item as any)} isTpl={false} />
|
||||
</div>
|
||||
))}
|
||||
</GridLayout>
|
||||
);
|
||||
});
|
||||
|
||||
export default ViewRender;
|
||||
14
src/core/viewRender.less
Normal file
14
src/core/viewRender.less
Normal file
@ -0,0 +1,14 @@
|
||||
.dragItem {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
border: 2px solid transparent;
|
||||
cursor: move;
|
||||
&:hover {
|
||||
border: 2px solid #06c;
|
||||
}
|
||||
:global(a) {
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useMemo, useCallback, useContext, useRef } from 'react';
|
||||
import { Result, Tabs, Button } from 'antd';
|
||||
import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
|
||||
import { Result, Tabs } from 'antd';
|
||||
import {
|
||||
PieChartOutlined,
|
||||
PlayCircleOutlined,
|
||||
@ -13,8 +13,8 @@ import CanvasControl from './components/CanvasControl';
|
||||
import SourceBox from './SourceBox';
|
||||
import TargetBox from './TargetBox';
|
||||
import Calibration from 'components/Calibration';
|
||||
import DynamicEngine, { componentsType } from 'components/DynamicEngine';
|
||||
import FormEditor from 'components/PanelComponents/FormEditor';
|
||||
import DynamicEngine, { componentsType } from '@/core/DynamicEngine';
|
||||
import FormRender from '@/core/FormRender';
|
||||
|
||||
import template from 'components/BasicShop/BasicComponents/template';
|
||||
import mediaTpl from 'components/BasicShop/MediaComponents/template';
|
||||
@ -22,7 +22,6 @@ import graphTpl from 'components/BasicShop/VisualComponents/template';
|
||||
|
||||
import schemaH5 from 'components/BasicShop/schema';
|
||||
import { ActionCreators, StateWithHistory } from 'redux-undo';
|
||||
import { dooringContext } from '@/layouts';
|
||||
import { throttle } from '@/utils/tool';
|
||||
|
||||
import styles from './index.less';
|
||||
@ -53,7 +52,6 @@ const Container = (props: {
|
||||
setRightColla(c);
|
||||
};
|
||||
}, []);
|
||||
const context = useContext(dooringContext);
|
||||
const curPoint = pstate ? pstate.curPoint : {};
|
||||
|
||||
// 指定画布的id
|
||||
@ -91,48 +89,26 @@ const Container = (props: {
|
||||
}, []);
|
||||
|
||||
const handleFormSave = useMemo(() => {
|
||||
if (context.theme === 'h5') {
|
||||
return (data: any) => {
|
||||
dispatch({
|
||||
type: 'editorModal/modPointData',
|
||||
payload: { ...curPoint, item: { ...curPoint.item, config: data } },
|
||||
});
|
||||
};
|
||||
} else {
|
||||
return (data: any) => {
|
||||
dispatch({
|
||||
type: 'editorPcModal/modPointData',
|
||||
payload: { ...curPoint, item: { ...curPoint.item, config: data } },
|
||||
});
|
||||
};
|
||||
}
|
||||
}, [context.theme, curPoint, dispatch]);
|
||||
return (data: any) => {
|
||||
dispatch({
|
||||
type: 'editorModal/modPointData',
|
||||
payload: { ...curPoint, item: { ...curPoint.item, config: data } },
|
||||
});
|
||||
};
|
||||
}, [curPoint, dispatch]);
|
||||
|
||||
const clearData = useCallback(() => {
|
||||
if (context.theme === 'h5') {
|
||||
dispatch({ type: 'editorModal/clearAll' });
|
||||
} else {
|
||||
dispatch({ type: 'editorPcModal/clearAll' });
|
||||
}
|
||||
}, [context.theme, dispatch]);
|
||||
dispatch({ type: 'editorModal/clearAll' });
|
||||
}, [dispatch]);
|
||||
|
||||
const handleDel = useMemo(() => {
|
||||
if (context.theme === 'h5') {
|
||||
return (id: any) => {
|
||||
dispatch({
|
||||
type: 'editorModal/delPointData',
|
||||
payload: { id },
|
||||
});
|
||||
};
|
||||
} else {
|
||||
return (id: any) => {
|
||||
dispatch({
|
||||
type: 'editorPcModal/delPointData',
|
||||
payload: { id },
|
||||
});
|
||||
};
|
||||
}
|
||||
}, [context.theme, dispatch]);
|
||||
return (id: any) => {
|
||||
dispatch({
|
||||
type: 'editorModal/delPointData',
|
||||
payload: { id },
|
||||
});
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
const redohandler = useMemo(() => {
|
||||
return () => {
|
||||
@ -184,81 +160,35 @@ const Container = (props: {
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const renderRight = useMemo(() => {
|
||||
if (context.theme === 'h5') {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={styles.attrSetting}
|
||||
style={{
|
||||
transition: 'all ease-in-out 0.5s',
|
||||
transform: rightColla ? 'translate(100%,0)' : 'translate(0,0)',
|
||||
}}
|
||||
>
|
||||
{pointData.length && curPoint ? (
|
||||
<>
|
||||
<div className={styles.tit}>属性设置</div>
|
||||
<FormEditor
|
||||
config={curPoint.item.editableEl}
|
||||
uid={curPoint.id}
|
||||
defaultValue={curPoint.item.config}
|
||||
onSave={handleFormSave}
|
||||
onDel={handleDel}
|
||||
rightPannelRef={ref}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div style={{ paddingTop: '100px' }}>
|
||||
<Result
|
||||
status="404"
|
||||
title="还没有数据哦"
|
||||
subTitle="赶快拖拽组件来生成你的H5页面吧~"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
className={styles.attrSetting}
|
||||
style={{
|
||||
transition: 'all ease-in-out 0.5s',
|
||||
transform: rightColla ? 'translate(100%,0)' : 'translate(0,0)',
|
||||
}}
|
||||
>
|
||||
{cpointData.length && curPoint ? (
|
||||
<>
|
||||
<div className={styles.tit}>属性设置</div>
|
||||
<FormEditor
|
||||
config={curPoint.item.editableEl}
|
||||
uid={curPoint.id}
|
||||
defaultValue={curPoint.item.config}
|
||||
onSave={handleFormSave}
|
||||
onDel={handleDel}
|
||||
rightPannelRef={ref}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div style={{ paddingTop: '100px' }}>
|
||||
<Result
|
||||
status="404"
|
||||
title="还没有数据哦"
|
||||
subTitle="赶快拖拽组件来生成你的H5页面吧~"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}, [
|
||||
context.theme,
|
||||
cpointData.length,
|
||||
curPoint,
|
||||
handleDel,
|
||||
handleFormSave,
|
||||
pointData.length,
|
||||
rightColla,
|
||||
]);
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={styles.attrSetting}
|
||||
style={{
|
||||
transition: 'all ease-in-out 0.5s',
|
||||
transform: rightColla ? 'translate(100%,0)' : 'translate(0,0)',
|
||||
}}
|
||||
>
|
||||
{pointData.length && curPoint ? (
|
||||
<>
|
||||
<div className={styles.tit}>属性设置</div>
|
||||
<FormRender
|
||||
config={curPoint.item.editableEl}
|
||||
uid={curPoint.id}
|
||||
defaultValue={curPoint.item.config}
|
||||
onSave={handleFormSave}
|
||||
onDel={handleDel}
|
||||
rightPannelRef={ref}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div style={{ paddingTop: '100px' }}>
|
||||
<Result status="404" title="还没有数据哦" subTitle="赶快拖拽组件来生成你的H5页面吧~" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}, [cpointData.length, curPoint, handleDel, handleFormSave, pointData.length, rightColla]);
|
||||
|
||||
const tabRender = useMemo(() => {
|
||||
if (collapsed) {
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import React, { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useDrop } from 'react-dnd';
|
||||
import Draggable, { DraggableData, DraggableEvent } from 'react-draggable';
|
||||
import GridLayout, { ItemCallback } from 'react-grid-layout';
|
||||
import { ItemCallback } from 'react-grid-layout';
|
||||
import { connect } from 'dva';
|
||||
import DynamicEngine from 'components/DynamicEngine';
|
||||
import ViewRender from '@/core/ViewRender';
|
||||
import styles from './index.less';
|
||||
import { uuid } from '@/utils/tool';
|
||||
import { Dispatch } from 'umi';
|
||||
import { StateWithHistory } from 'redux-undo';
|
||||
import { Menu, Item, MenuProvider } from 'react-contexify';
|
||||
import 'react-contexify/dist/ReactContexify.min.css';
|
||||
import { dooringContext } from '@/layouts';
|
||||
interface SourceBoxProps {
|
||||
pstate: { pointData: { id: string; item: any; point: any; isMenu?: any }[]; curPoint: any };
|
||||
cstate: { pointData: { id: string; item: any; point: any }[]; curPoint: any };
|
||||
@ -29,15 +28,12 @@ interface SourceBoxProps {
|
||||
|
||||
const SourceBox = memo((props: SourceBoxProps) => {
|
||||
const { pstate, scaleNum, canvasId, allType, dispatch, dragState, setDragState, cstate } = props;
|
||||
const context = useContext(dooringContext);
|
||||
|
||||
let pointData = pstate ? pstate.pointData : [];
|
||||
const cpointData = cstate ? cstate.pointData : [];
|
||||
|
||||
const [canvasRect, setCanvasRect] = useState<number[]>([]);
|
||||
const [isShowTip, setIsShowTip] = useState(true);
|
||||
// const [clonePointData, setPointData] = useState(pointData);
|
||||
// const [isMenu, setIsMenu] = useState(false);
|
||||
const [{ isOver }, drop] = useDrop({
|
||||
accept: allType,
|
||||
drop: (item: { h: number; type: string; x: number }, monitor) => {
|
||||
@ -51,34 +47,15 @@ const SourceBox = memo((props: SourceBoxProps) => {
|
||||
w = item.type === 'Icon' ? 3 : col;
|
||||
// 转换成网格规则的坐标和大小
|
||||
let gridY = Math.ceil(y / cellHeight);
|
||||
if (context.theme === 'h5') {
|
||||
dispatch({
|
||||
type: 'editorModal/addPointData',
|
||||
payload: {
|
||||
id: uuid(6, 10),
|
||||
item,
|
||||
point: { i: `x-${pointData.length}`, x: 0, y: gridY, w, h: item.h, isBounded: true },
|
||||
status: 'inToCanvas',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: 'editorPcModal/addPointData',
|
||||
payload: {
|
||||
id: uuid(6, 10),
|
||||
item,
|
||||
point: {
|
||||
i: `x-${cpointData.length}`,
|
||||
x: item.x || 0,
|
||||
y: gridY,
|
||||
w,
|
||||
h: item.h,
|
||||
isBounded: true,
|
||||
},
|
||||
status: 'inToCanvas',
|
||||
},
|
||||
});
|
||||
}
|
||||
dispatch({
|
||||
type: 'editorModal/addPointData',
|
||||
payload: {
|
||||
id: uuid(6, 10),
|
||||
item,
|
||||
point: { i: `x-${pointData.length}`, x: 0, y: gridY, w, h: item.h, isBounded: true },
|
||||
status: 'inToCanvas',
|
||||
},
|
||||
});
|
||||
},
|
||||
collect: monitor => ({
|
||||
isOver: monitor.isOver(),
|
||||
@ -94,21 +71,8 @@ const SourceBox = memo((props: SourceBoxProps) => {
|
||||
type: 'editorModal/modPointData',
|
||||
payload: { ...curPointData, point: newItem, status: 'inToCanvas' },
|
||||
});
|
||||
// if (context.theme === 'h5') {
|
||||
// const curPointData = pointData.filter(item => item.id === newItem.i)[0];
|
||||
// dispatch({
|
||||
// type: 'editorModal/modPointData',
|
||||
// payload: { ...curPointData, point: newItem, status: 'inToCanvas' },
|
||||
// });
|
||||
// } else {
|
||||
// const curPointData = cpointData.filter(item => item.id === newItem.i)[0];
|
||||
// dispatch({
|
||||
// type: 'editorPcModal/modPointData',
|
||||
// payload: { ...curPointData, point: newItem, status: 'inToCanvas' },
|
||||
// });
|
||||
// }
|
||||
};
|
||||
}, [context.theme, cpointData, dispatch, pointData]);
|
||||
}, [cpointData, dispatch, pointData]);
|
||||
|
||||
const onDragStart: ItemCallback = useMemo(() => {
|
||||
return (layout, oldItem, newItem, placeholder, e, element) => {
|
||||
@ -117,19 +81,6 @@ const SourceBox = memo((props: SourceBoxProps) => {
|
||||
type: 'editorModal/modPointData',
|
||||
payload: { ...curPointData, status: 'inToCanvas' },
|
||||
});
|
||||
// if (context.theme === 'h5') {
|
||||
// const curPointData = pointData.filter(item => item.id === newItem.i)[0];
|
||||
// dispatch({
|
||||
// type: 'editorModal/modPointData',
|
||||
// payload: { ...curPointData, status: 'inToCanvas' },
|
||||
// });
|
||||
// } else {
|
||||
// const curPointData = cpointData.filter(item => item.id === newItem.i)[0];
|
||||
// dispatch({
|
||||
// type: 'editorPcModal/modPointData',
|
||||
// payload: { ...curPointData, status: 'inToCanvas' },
|
||||
// });
|
||||
// }
|
||||
};
|
||||
}, [dispatch, pointData]);
|
||||
|
||||
@ -140,32 +91,9 @@ const SourceBox = memo((props: SourceBoxProps) => {
|
||||
type: 'editorModal/modPointData',
|
||||
payload: { ...curPointData, point: newItem, status: 'inToCanvas' },
|
||||
});
|
||||
// if (context.theme === 'h5') {
|
||||
// const curPointData = pointData.filter(item => item.id === newItem.i)[0];
|
||||
// dispatch({
|
||||
// type: 'editorModal/modPointData',
|
||||
// payload: { ...curPointData, point: newItem, status: 'inToCanvas' },
|
||||
// });
|
||||
// } else {
|
||||
// const curPointData = cpointData.filter(item => item.id === newItem.i)[0];
|
||||
// dispatch({
|
||||
// type: 'editorPcModal/modPointData',
|
||||
// payload: { ...curPointData, point: newItem, status: 'inToCanvas' },
|
||||
// });
|
||||
// }
|
||||
};
|
||||
}, [dispatch, pointData]);
|
||||
|
||||
const initSelect: any = (data: any = []) => {
|
||||
return (
|
||||
data &&
|
||||
data.map((itemData: any) => ({
|
||||
...itemData,
|
||||
isMenu: false,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
const handleContextMenuDel = () => {
|
||||
if (pstate.curPoint) {
|
||||
dispatch({
|
||||
@ -245,26 +173,13 @@ const SourceBox = memo((props: SourceBoxProps) => {
|
||||
ref={drop}
|
||||
>
|
||||
{pointData.length > 0 ? (
|
||||
<GridLayout
|
||||
className={styles.layout}
|
||||
cols={24}
|
||||
rowHeight={2}
|
||||
<ViewRender
|
||||
pointData={pointData}
|
||||
width={canvasRect[0] || 0}
|
||||
margin={[0, 0]}
|
||||
onDragStop={dragStop}
|
||||
dragStop={dragStop}
|
||||
onDragStart={onDragStart}
|
||||
onResizeStop={onResizeStop}
|
||||
>
|
||||
{pointData.map(value => (
|
||||
<div
|
||||
className={value.isMenu ? styles.selected : styles.dragItem}
|
||||
key={value.id}
|
||||
data-grid={value.point}
|
||||
>
|
||||
<DynamicEngine {...value.item} isTpl={false} />
|
||||
</div>
|
||||
))}
|
||||
</GridLayout>
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -132,7 +132,6 @@
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
border: 2px solid transparent;
|
||||
cursor: move;
|
||||
:global(a) {
|
||||
display: block;
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import React, { CSSProperties, memo, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import GridLayout from 'react-grid-layout';
|
||||
import DynamicEngine from 'components/DynamicEngine';
|
||||
import ViewRender from '@/core/ViewRender';
|
||||
import domtoimage from 'dom-to-image';
|
||||
import req from '@/utils/req';
|
||||
import styles from './index.less';
|
||||
import { useGetScrollBarWidth } from '@/utils/tool';
|
||||
import { LocationDescriptorObject } from 'history-with-query';
|
||||
|
||||
@ -123,25 +121,7 @@ const PreviewPage = memo((props: PreviewPageProps) => {
|
||||
}
|
||||
>
|
||||
<div ref={refImgDom}>
|
||||
<GridLayout
|
||||
className={styles.layout}
|
||||
cols={24}
|
||||
rowHeight={2}
|
||||
width={vw > 800 ? 375 : vw}
|
||||
margin={[0, 0]}
|
||||
style={{
|
||||
backgroundColor: pageData.bgColor,
|
||||
backgroundImage: pageData.bgImage ? `url(${pageData.bgImage[0].url})` : 'initial',
|
||||
backgroundSize: 'contain',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
}}
|
||||
>
|
||||
{pointData.map((value: PointDataItem) => (
|
||||
<div className={styles.dragItem} key={value.id} data-grid={value.point}>
|
||||
<DynamicEngine {...(value.item as any)} />
|
||||
</div>
|
||||
))}
|
||||
</GridLayout>
|
||||
<ViewRender pageData={pageData} pointData={pointData} width={vw > 800 ? 375 : vw} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user