mirror of
https://github.com/MrXujiang/h5-Dooring.git
synced 2026-03-23 22:12:30 +00:00
commit
d5c4c0d59c
@ -88,8 +88,8 @@
|
||||
"@types/node": "^14.6.2",
|
||||
"@types/react-color": "^3.0.4",
|
||||
"@types/redux-logger": "^3.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "2.x",
|
||||
"@typescript-eslint/parser": "2.x",
|
||||
"@typescript-eslint/eslint-plugin": "4.1.1",
|
||||
"@typescript-eslint/parser": "4.1.1",
|
||||
"babel-eslint": "10.x",
|
||||
"babel-plugin-import": "^1.13.0",
|
||||
"eslint": "6.x",
|
||||
|
||||
@ -7,4 +7,4 @@
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
import React, { memo, PropsWithChildren } from 'react';
|
||||
import { Carousel } from 'zarm';
|
||||
import styles from './index.less';
|
||||
import { CarouselConfigType } from '../DynamicEngine/schema';
|
||||
import { ICarouselConfig } from './schema';
|
||||
|
||||
interface CarouselTypes extends CarouselConfigType {
|
||||
interface CarouselTypes extends ICarouselConfig {
|
||||
isTpl: boolean;
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ const XCarousel = memo((props: PropsWithChildren<CarouselTypes>) => {
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', overflow: 'hidden' }}>
|
||||
{isTpl ? (
|
||||
98
src/components/BasicShop/BasicComponents/Carousel/schema.ts
Normal file
98
src/components/BasicShop/BasicComponents/Carousel/schema.ts
Normal file
@ -0,0 +1,98 @@
|
||||
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;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Carousel',
|
||||
h: 82,
|
||||
};
|
||||
export default template;
|
||||
21
src/components/BasicShop/BasicComponents/Footer/index.tsx
Normal file
21
src/components/BasicShop/BasicComponents/Footer/index.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import React, { memo } from 'react';
|
||||
import { IFooterConfig } from './schema';
|
||||
const Footer = memo((props: IFooterConfig) => {
|
||||
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;
|
||||
87
src/components/BasicShop/BasicComponents/Footer/schema.ts
Normal file
87
src/components/BasicShop/BasicComponents/Footer/schema.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import {
|
||||
IColorConfigType,
|
||||
INumberConfigType,
|
||||
ISelectConfigType,
|
||||
ITextConfigType,
|
||||
TColorDefaultType,
|
||||
TNumberDefaultType,
|
||||
TSelectDefaultType,
|
||||
TTextDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TfooterSelectKeyType = 'left' | 'center' | 'right';
|
||||
|
||||
export type TFooterEditData = Array<
|
||||
IColorConfigType | INumberConfigType | ITextConfigType | ISelectConfigType<TfooterSelectKeyType>
|
||||
>;
|
||||
export interface IFooterConfig {
|
||||
bgColor: TColorDefaultType;
|
||||
text: TTextDefaultType;
|
||||
color: TColorDefaultType;
|
||||
align: TSelectDefaultType<TfooterSelectKeyType>;
|
||||
fontSize: TNumberDefaultType;
|
||||
height: TNumberDefaultType;
|
||||
}
|
||||
|
||||
export interface IFooterSchema {
|
||||
editData: TFooterEditData;
|
||||
config: IFooterConfig;
|
||||
}
|
||||
|
||||
const Footer: IFooterSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'bgColor',
|
||||
name: '背景色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'height',
|
||||
name: '高度',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'text',
|
||||
name: '文字',
|
||||
type: 'Text',
|
||||
},
|
||||
{
|
||||
key: 'fontSize',
|
||||
name: '字体大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'color',
|
||||
name: '文字颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'align',
|
||||
name: '对齐方式',
|
||||
type: 'Select',
|
||||
range: [
|
||||
{
|
||||
key: 'left',
|
||||
text: '左对齐',
|
||||
},
|
||||
{
|
||||
key: 'center',
|
||||
text: '居中对齐',
|
||||
},
|
||||
{
|
||||
key: 'right',
|
||||
text: '右对齐',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
config: {
|
||||
bgColor: 'rgba(0,0,0,1)',
|
||||
text: '页脚Footer',
|
||||
color: 'rgba(255,255,255,1)',
|
||||
align: 'center',
|
||||
fontSize: 16,
|
||||
height: 48,
|
||||
},
|
||||
};
|
||||
export default Footer;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Footer',
|
||||
h: 24,
|
||||
};
|
||||
export default template;
|
||||
@ -1,9 +1,23 @@
|
||||
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渲染性能
|
||||
const BaseForm = {
|
||||
Text: props => {
|
||||
|
||||
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}>
|
||||
@ -11,7 +25,7 @@ const BaseForm = {
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
Textarea: props => {
|
||||
Textarea: (props: baseFormTextAreaTpl & { onChange: (v: string | undefined) => void }) => {
|
||||
const { label, placeholder, onChange } = props;
|
||||
return (
|
||||
<Cell title={label}>
|
||||
@ -26,22 +40,15 @@ const BaseForm = {
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
Number: props => {
|
||||
Number: (props: baseFormNumberTpl & { onChange: (v: string | undefined | number) => void }) => {
|
||||
const { label, placeholder, onChange } = props;
|
||||
return (
|
||||
<Cell title={label}>
|
||||
<Input
|
||||
type="number"
|
||||
rows={3}
|
||||
autoHeight
|
||||
showLength
|
||||
placeholder={placeholder}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<Input type="number" placeholder={placeholder} onChange={onChange} />
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
MyRadio: props => {
|
||||
MyRadio: (props: baseFormMyRadioTpl & { onChange: (v: string | undefined | number) => void }) => {
|
||||
const { label, options, onChange } = props;
|
||||
return (
|
||||
<div className={styles.radioWrap}>
|
||||
@ -60,7 +67,7 @@ const BaseForm = {
|
||||
</div>
|
||||
);
|
||||
},
|
||||
Date: props => {
|
||||
Date: (props: baseFormDateTpl & { onChange: (v: Date) => void }) => {
|
||||
const { label, placeholder, onChange } = props;
|
||||
return (
|
||||
<Cell title={label}>
|
||||
@ -74,7 +81,9 @@ const BaseForm = {
|
||||
</Cell>
|
||||
);
|
||||
},
|
||||
MySelect: props => {
|
||||
MySelect: (
|
||||
props: baseFormMySelectTpl & { onChange: ((v: Record<string, any>) => void) | undefined },
|
||||
) => {
|
||||
const { label, options, onChange } = props;
|
||||
return (
|
||||
<Cell title={label}>
|
||||
@ -1,30 +1,22 @@
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { Button } from 'zarm';
|
||||
import BaseForm from './BaseForm';
|
||||
import req from 'utils/req';
|
||||
import styles from './index.less';
|
||||
import { IFormConfig } from './schema';
|
||||
|
||||
function unParams(params = '?a=1&b=2&c=3') {
|
||||
let obj = {};
|
||||
params &&
|
||||
params.replace(/((\w*)=([\.a-z0-9A-Z]*)?)?/g, (m, a, b, c) => {
|
||||
if (b || c) obj[b] = c;
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
const FormComponent = props => {
|
||||
const FormComponent = (props: IFormConfig) => {
|
||||
const { title, bgColor, fontSize, titColor, btnColor, btnTextColor, api, formControls } = props;
|
||||
|
||||
const formData = {};
|
||||
|
||||
const handleChange = (item, v) => {
|
||||
if (item.options) {
|
||||
formData[item.label] = v[0].label;
|
||||
return;
|
||||
}
|
||||
formData[item.label] = v;
|
||||
};
|
||||
|
||||
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, {
|
||||
@ -38,7 +30,6 @@ const FormComponent = props => {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.formWrap} style={{ backgroundColor: bgColor }}>
|
||||
{title && (
|
||||
108
src/components/BasicShop/BasicComponents/Form/schema.ts
Normal file
108
src/components/BasicShop/BasicComponents/Form/schema.ts
Normal file
@ -0,0 +1,108 @@
|
||||
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;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Form',
|
||||
h: 172,
|
||||
};
|
||||
export default template;
|
||||
@ -21,23 +21,3 @@
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/components/BasicShop/BasicComponents/Header/index.tsx
Normal file
20
src/components/BasicShop/BasicComponents/Header/index.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { memo } from 'react';
|
||||
import styles from './index.less';
|
||||
import React from 'react';
|
||||
import { IHeaderConfig } from './schema';
|
||||
|
||||
const Header = memo((props: IHeaderConfig) => {
|
||||
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;
|
||||
81
src/components/BasicShop/BasicComponents/Header/schema.ts
Normal file
81
src/components/BasicShop/BasicComponents/Header/schema.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import {
|
||||
IColorConfigType,
|
||||
INumberConfigType,
|
||||
ITextConfigType,
|
||||
IUploadConfigType,
|
||||
TColorDefaultType,
|
||||
TNumberDefaultType,
|
||||
TTextDefaultType,
|
||||
TUploadDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type THeaderEditData = Array<
|
||||
IColorConfigType | INumberConfigType | IUploadConfigType | ITextConfigType
|
||||
>;
|
||||
export interface IHeaderConfig {
|
||||
bgColor: TColorDefaultType;
|
||||
logo: TUploadDefaultType;
|
||||
logoText: TTextDefaultType;
|
||||
fontSize: TNumberDefaultType;
|
||||
color: TColorDefaultType;
|
||||
height: TNumberDefaultType;
|
||||
}
|
||||
|
||||
export interface IHeaderSchema {
|
||||
editData: THeaderEditData;
|
||||
config: IHeaderConfig;
|
||||
}
|
||||
|
||||
const Header: IHeaderSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'bgColor',
|
||||
name: '背景色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'height',
|
||||
name: '高度',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'logo',
|
||||
name: 'logo',
|
||||
type: 'Upload',
|
||||
isCrop: true,
|
||||
cropRate: 1000 / 618,
|
||||
},
|
||||
{
|
||||
key: 'logoText',
|
||||
name: 'logo文字',
|
||||
type: 'Text',
|
||||
},
|
||||
{
|
||||
key: 'color',
|
||||
name: '文字颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'fontSize',
|
||||
name: '文字大小',
|
||||
type: 'Number',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
bgColor: 'rgba(0,0,0,1)',
|
||||
logo: [
|
||||
{
|
||||
uid: '001',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'http://io.nainor.com/uploads/3_1740be8a482.png',
|
||||
},
|
||||
],
|
||||
logoText: '页头Header',
|
||||
fontSize: 20,
|
||||
color: 'rgba(255,255,255,1)',
|
||||
height: 50,
|
||||
},
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Header',
|
||||
h: 28,
|
||||
};
|
||||
export default template;
|
||||
@ -2,13 +2,11 @@ import React, { memo } from 'react';
|
||||
import * as Icon from '@ant-design/icons';
|
||||
import IconImg from 'assets/icon.png';
|
||||
import { AntdIconProps } from '@ant-design/icons/lib/components/AntdIcon';
|
||||
import { IconConfigType } from '../DynamicEngine/schema';
|
||||
import { AntdIconType } from './icon';
|
||||
import { IIconConfig } from './schema';
|
||||
|
||||
interface IconType extends Omit<IconConfigType, 'spin' | 'color'> {
|
||||
interface IconType extends IIconConfig {
|
||||
isTpl?: boolean;
|
||||
spin?: IconConfigType['spin'];
|
||||
color?: IconConfigType['color'];
|
||||
}
|
||||
const XIcon = memo((props: IconType) => {
|
||||
const { color, size, type, spin, isTpl } = props;
|
||||
144
src/components/BasicShop/BasicComponents/Icon/schema.ts
Normal file
144
src/components/BasicShop/BasicComponents/Icon/schema.ts
Normal file
@ -0,0 +1,144 @@
|
||||
import {
|
||||
ICardPickerConfigType,
|
||||
IColorConfigType,
|
||||
INumberConfigType,
|
||||
ISwitchConfigType,
|
||||
TCardPickerDefaultType,
|
||||
TColorDefaultType,
|
||||
TNumberDefaultType,
|
||||
TSwitchDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TIconEditData = Array<
|
||||
IColorConfigType | INumberConfigType | ISwitchConfigType | ICardPickerConfigType<IconTypes>
|
||||
>;
|
||||
export interface IIconConfig {
|
||||
color: TColorDefaultType;
|
||||
size: TNumberDefaultType;
|
||||
spin: TSwitchDefaultType;
|
||||
type: TCardPickerDefaultType<IconTypes>;
|
||||
}
|
||||
|
||||
export interface IIconSchema {
|
||||
editData: TIconEditData;
|
||||
config: IIconConfig;
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
const Icon: IIconSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'color',
|
||||
name: '颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'size',
|
||||
name: '大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'spin',
|
||||
name: '旋转动画',
|
||||
type: 'Switch',
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
name: '图标类型',
|
||||
type: 'CardPicker',
|
||||
icons: [
|
||||
'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',
|
||||
],
|
||||
},
|
||||
],
|
||||
config: {
|
||||
color: 'rgba(74,144,226,1)',
|
||||
size: 36,
|
||||
spin: false,
|
||||
type: 'CarTwoTone',
|
||||
},
|
||||
};
|
||||
|
||||
export default Icon;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Icon',
|
||||
h: 23,
|
||||
};
|
||||
export default template;
|
||||
12
src/components/BasicShop/BasicComponents/Image/index.tsx
Normal file
12
src/components/BasicShop/BasicComponents/Image/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React, { memo } from 'react';
|
||||
import { IImageConfig } from './schema';
|
||||
const Image = memo((props: IImageConfig) => {
|
||||
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;
|
||||
46
src/components/BasicShop/BasicComponents/Image/schema.ts
Normal file
46
src/components/BasicShop/BasicComponents/Image/schema.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import {
|
||||
INumberConfigType,
|
||||
IUploadConfigType,
|
||||
TNumberDefaultType,
|
||||
TUploadDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TImageEditData = Array<IUploadConfigType | INumberConfigType>;
|
||||
export interface IImageConfig {
|
||||
imgUrl: TUploadDefaultType;
|
||||
round: TNumberDefaultType;
|
||||
}
|
||||
|
||||
export interface IImageSchema {
|
||||
editData: TImageEditData;
|
||||
config: IImageConfig;
|
||||
}
|
||||
|
||||
const Image: IImageSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'imgUrl',
|
||||
name: '上传',
|
||||
type: 'Upload',
|
||||
isCrop: false,
|
||||
},
|
||||
{
|
||||
key: 'round',
|
||||
name: '圆角',
|
||||
type: 'Number',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
imgUrl: [
|
||||
{
|
||||
uid: '001',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'http://io.nainor.com/uploads/4_1740bf4535c.png',
|
||||
},
|
||||
],
|
||||
round: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export default Image;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Image',
|
||||
h: 188,
|
||||
};
|
||||
export default template;
|
||||
19
src/components/BasicShop/BasicComponents/List/index.less
Normal file
19
src/components/BasicShop/BasicComponents/List/index.less
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/components/BasicShop/BasicComponents/List/index.tsx
Normal file
51
src/components/BasicShop/BasicComponents/List/index.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React, { memo } from 'react';
|
||||
import styles from './index.less';
|
||||
import { IListConfig } from './schema';
|
||||
const List = memo((props: IListConfig) => {
|
||||
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: parseFloat(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;
|
||||
116
src/components/BasicShop/BasicComponents/List/schema.ts
Normal file
116
src/components/BasicShop/BasicComponents/List/schema.ts
Normal file
@ -0,0 +1,116 @@
|
||||
import {
|
||||
IColorConfigType,
|
||||
IDataListConfigType,
|
||||
INumberConfigType,
|
||||
ISelectConfigType,
|
||||
TColorDefaultType,
|
||||
TDataListDefaultType,
|
||||
TNumberDefaultType,
|
||||
TSelectDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
export type TListSelectKeyType = '60' | '80' | '100' | '120' | '150';
|
||||
export type TListEditData = Array<
|
||||
IColorConfigType | IDataListConfigType | INumberConfigType | ISelectConfigType<TListSelectKeyType>
|
||||
>;
|
||||
export interface IListConfig {
|
||||
sourceData: TDataListDefaultType;
|
||||
round: TNumberDefaultType;
|
||||
imgSize: TSelectDefaultType<TListSelectKeyType>;
|
||||
fontSize: TNumberDefaultType;
|
||||
color: TColorDefaultType;
|
||||
}
|
||||
|
||||
export interface IListSchema {
|
||||
editData: TListEditData;
|
||||
config: IListConfig;
|
||||
}
|
||||
|
||||
const List: IListSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'sourceData',
|
||||
name: '数据源',
|
||||
type: 'DataList',
|
||||
},
|
||||
{
|
||||
key: 'round',
|
||||
name: '圆角',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'imgSize',
|
||||
name: '图片大小',
|
||||
type: 'Select',
|
||||
range: [
|
||||
{
|
||||
key: '60',
|
||||
text: '60 x 60',
|
||||
},
|
||||
{
|
||||
key: '80',
|
||||
text: '80 x 80',
|
||||
},
|
||||
{
|
||||
key: '100',
|
||||
text: '100 x 100',
|
||||
},
|
||||
{
|
||||
key: '120',
|
||||
text: '120 x 120',
|
||||
},
|
||||
{
|
||||
key: '150',
|
||||
text: '150 x 150',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'fontSize',
|
||||
name: '文字大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'color',
|
||||
name: '文字颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
sourceData: [
|
||||
{
|
||||
id: '1',
|
||||
title: '趣谈小课',
|
||||
desc: '致力于打造优质小课程',
|
||||
link: 'xxxxx',
|
||||
imgUrl: [
|
||||
{
|
||||
uid: '001',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'http://io.nainor.com/uploads/1_1740c6fbcd9.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '趣谈小课',
|
||||
desc: '致力于打造优质小课程',
|
||||
link: 'xxxxx',
|
||||
imgUrl: [
|
||||
{
|
||||
uid: '002',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'http://io.nainor.com/uploads/1_1740c6fbcd9.png',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
round: 0,
|
||||
imgSize: '80',
|
||||
fontSize: 16,
|
||||
color: 'rgba(153,153,153,1)',
|
||||
},
|
||||
};
|
||||
|
||||
export default List;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'List',
|
||||
h: 110,
|
||||
};
|
||||
export default template;
|
||||
15
src/components/BasicShop/BasicComponents/LongText/index.tsx
Normal file
15
src/components/BasicShop/BasicComponents/LongText/index.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React, { memo } from 'react';
|
||||
import styles from './index.less';
|
||||
import { ILongTextConfig } from './schema';
|
||||
const LongText = memo((props: ILongTextConfig) => {
|
||||
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;
|
||||
92
src/components/BasicShop/BasicComponents/LongText/schema.ts
Normal file
92
src/components/BasicShop/BasicComponents/LongText/schema.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import {
|
||||
IColorConfigType,
|
||||
INumberConfigType,
|
||||
ISelectConfigType,
|
||||
ITextAreaConfigType,
|
||||
TColorDefaultType,
|
||||
TNumberDefaultType,
|
||||
TSelectDefaultType,
|
||||
TTextAreaDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
export type TLongTextSelectKeyType = 'left' | 'center' | 'right';
|
||||
|
||||
export type TLongTextEditData = Array<
|
||||
| ITextAreaConfigType
|
||||
| IColorConfigType
|
||||
| INumberConfigType
|
||||
| ISelectConfigType<TLongTextSelectKeyType>
|
||||
>;
|
||||
export interface ILongTextConfig {
|
||||
text: TTextAreaDefaultType;
|
||||
color: TColorDefaultType;
|
||||
fontSize: TNumberDefaultType;
|
||||
indent: TNumberDefaultType;
|
||||
lineHeight: TNumberDefaultType;
|
||||
textAlign: TSelectDefaultType<TLongTextSelectKeyType>;
|
||||
}
|
||||
|
||||
export interface ILongTextSchema {
|
||||
editData: TLongTextEditData;
|
||||
config: ILongTextConfig;
|
||||
}
|
||||
|
||||
const LongText: ILongTextSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'text',
|
||||
name: '文字',
|
||||
type: 'TextArea',
|
||||
},
|
||||
{
|
||||
key: 'color',
|
||||
name: '标题颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'fontSize',
|
||||
name: '字体大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'indent',
|
||||
name: '首行缩进',
|
||||
type: 'Number',
|
||||
range: [0, 100],
|
||||
},
|
||||
{
|
||||
key: 'textAlign',
|
||||
name: '对齐方式',
|
||||
type: 'Select',
|
||||
range: [
|
||||
{
|
||||
key: 'left',
|
||||
text: '左对齐',
|
||||
},
|
||||
{
|
||||
key: 'center',
|
||||
text: '居中对齐',
|
||||
},
|
||||
{
|
||||
key: 'right',
|
||||
text: '右对齐',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'lineHeight',
|
||||
name: '行高',
|
||||
type: 'Number',
|
||||
step: 0.1,
|
||||
},
|
||||
],
|
||||
config: {
|
||||
text: '我是长文本有一段故事,dooring可视化编辑器无限可能,赶快来体验吧,骚年们,奥利给~',
|
||||
color: 'rgba(60,60,60,1)',
|
||||
fontSize: 14,
|
||||
indent: 20,
|
||||
lineHeight: 1.8,
|
||||
textAlign: 'left',
|
||||
},
|
||||
};
|
||||
|
||||
export default LongText;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'LongText',
|
||||
h: 36,
|
||||
};
|
||||
export default template;
|
||||
13
src/components/BasicShop/BasicComponents/Notice/index.tsx
Normal file
13
src/components/BasicShop/BasicComponents/Notice/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
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;
|
||||
81
src/components/BasicShop/BasicComponents/Notice/schema.ts
Normal file
81
src/components/BasicShop/BasicComponents/Notice/schema.ts
Normal file
@ -0,0 +1,81 @@
|
||||
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;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Notice',
|
||||
h: 20,
|
||||
};
|
||||
export default template;
|
||||
14
src/components/BasicShop/BasicComponents/Qrcode/index.tsx
Normal file
14
src/components/BasicShop/BasicComponents/Qrcode/index.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import React, { memo } from 'react';
|
||||
import { IQrcodeConfig } from './schema';
|
||||
|
||||
const Qrcode = memo((props: IQrcodeConfig) => {
|
||||
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;
|
||||
67
src/components/BasicShop/BasicComponents/Qrcode/schema.ts
Normal file
67
src/components/BasicShop/BasicComponents/Qrcode/schema.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import {
|
||||
IColorConfigType,
|
||||
INumberConfigType,
|
||||
ITextConfigType,
|
||||
IUploadConfigType,
|
||||
TColorDefaultType,
|
||||
TNumberDefaultType,
|
||||
TTextDefaultType,
|
||||
TUploadDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TQrcodeEditData = Array<
|
||||
IUploadConfigType | ITextConfigType | IColorConfigType | INumberConfigType
|
||||
>;
|
||||
export interface IQrcodeConfig {
|
||||
qrcode: TUploadDefaultType;
|
||||
text: TTextDefaultType;
|
||||
color: TColorDefaultType;
|
||||
fontSize: TNumberDefaultType;
|
||||
}
|
||||
|
||||
export interface IQrcodeSchema {
|
||||
editData: TQrcodeEditData;
|
||||
config: IQrcodeConfig;
|
||||
}
|
||||
|
||||
const Qrcode: IQrcodeSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'qrcode',
|
||||
name: '二维码',
|
||||
type: 'Upload',
|
||||
isCrop: true,
|
||||
cropRate: 1,
|
||||
},
|
||||
{
|
||||
key: 'text',
|
||||
name: '文字',
|
||||
type: 'Text',
|
||||
},
|
||||
{
|
||||
key: 'color',
|
||||
name: '文字颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'fontSize',
|
||||
name: '文字大小',
|
||||
type: 'Number',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
qrcode: [
|
||||
{
|
||||
uid: '001',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'http://io.nainor.com/uploads/code_173e1705e0c.png',
|
||||
},
|
||||
],
|
||||
text: '二维码',
|
||||
color: 'rgba(153,153,153,1)',
|
||||
fontSize: 14,
|
||||
},
|
||||
};
|
||||
|
||||
export default Qrcode;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Qrcode',
|
||||
h: 150,
|
||||
};
|
||||
export default template;
|
||||
@ -24,4 +24,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Tabs } from 'zarm';
|
||||
import styles from './index.less';
|
||||
import { TabConfigType } from '../DynamicEngine/schema';
|
||||
import { ITabConfig } from './schema';
|
||||
|
||||
const { Panel } = Tabs;
|
||||
|
||||
const XTab = (props: TabConfigType) => {
|
||||
const XTab = (props: ITabConfig) => {
|
||||
const { tabs = ['分类一', '分类二'], activeColor, color, fontSize, sourceData } = props;
|
||||
|
||||
const tabWrapRef = useRef<HTMLDivElement>(null);
|
||||
@ -22,7 +22,7 @@ const XTab = (props: TabConfigType) => {
|
||||
return (
|
||||
<div className={styles.tabWrap} ref={tabWrapRef}>
|
||||
<Tabs
|
||||
canSwipe
|
||||
scrollThreshold={3}
|
||||
onChange={i => {
|
||||
console.log(i);
|
||||
}}
|
||||
118
src/components/BasicShop/BasicComponents/Tab/schema.ts
Normal file
118
src/components/BasicShop/BasicComponents/Tab/schema.ts
Normal file
@ -0,0 +1,118 @@
|
||||
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;
|
||||
5
src/components/BasicShop/BasicComponents/Tab/template.ts
Normal file
5
src/components/BasicShop/BasicComponents/Tab/template.ts
Normal file
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Tab',
|
||||
h: 130,
|
||||
};
|
||||
export default template;
|
||||
13
src/components/BasicShop/BasicComponents/Text/index.tsx
Normal file
13
src/components/BasicShop/BasicComponents/Text/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import React, { memo } from 'react';
|
||||
import styles from './index.less';
|
||||
import { ITextConfig } from './schema';
|
||||
|
||||
const Text = memo((props: ITextConfig) => {
|
||||
const { align, text, fontSize, color, lineHeight } = props;
|
||||
return (
|
||||
<div className={styles.textWrap} style={{ color, textAlign: align, fontSize, lineHeight }}>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
export default Text;
|
||||
78
src/components/BasicShop/BasicComponents/Text/schema.ts
Normal file
78
src/components/BasicShop/BasicComponents/Text/schema.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import {
|
||||
IColorConfigType,
|
||||
INumberConfigType,
|
||||
ISelectConfigType,
|
||||
ITextConfigType,
|
||||
TColorDefaultType,
|
||||
TNumberDefaultType,
|
||||
TSelectDefaultType,
|
||||
TTextDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TTextSelectKeyType = 'left' | 'right' | 'center';
|
||||
export type TTextEditData = Array<
|
||||
ITextConfigType | IColorConfigType | INumberConfigType | ISelectConfigType<TTextSelectKeyType>
|
||||
>;
|
||||
export interface ITextConfig {
|
||||
text: TTextDefaultType;
|
||||
color: TColorDefaultType;
|
||||
fontSize: TNumberDefaultType;
|
||||
align: TSelectDefaultType<TTextSelectKeyType>;
|
||||
lineHeight: TNumberDefaultType;
|
||||
}
|
||||
|
||||
export interface ITextSchema {
|
||||
editData: TTextEditData;
|
||||
config: ITextConfig;
|
||||
}
|
||||
const Text: ITextSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'text',
|
||||
name: '文字',
|
||||
type: 'Text',
|
||||
},
|
||||
{
|
||||
key: 'color',
|
||||
name: '标题颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'fontSize',
|
||||
name: '字体大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'align',
|
||||
name: '对齐方式',
|
||||
type: 'Select',
|
||||
range: [
|
||||
{
|
||||
key: 'left',
|
||||
text: '左对齐',
|
||||
},
|
||||
{
|
||||
key: 'center',
|
||||
text: '居中对齐',
|
||||
},
|
||||
{
|
||||
key: 'right',
|
||||
text: '右对齐',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'lineHeight',
|
||||
name: '行高',
|
||||
type: 'Number',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
text: '我是文本',
|
||||
color: 'rgba(60,60,60,1)',
|
||||
fontSize: 18,
|
||||
align: 'center',
|
||||
lineHeight: 2,
|
||||
},
|
||||
};
|
||||
export default Text;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Text',
|
||||
h: 20,
|
||||
};
|
||||
export default template;
|
||||
28
src/components/BasicShop/BasicComponents/schema.ts
Normal file
28
src/components/BasicShop/BasicComponents/schema.ts
Normal file
@ -0,0 +1,28 @@
|
||||
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;
|
||||
32
src/components/BasicShop/BasicComponents/template.ts
Normal file
32
src/components/BasicShop/BasicComponents/template.ts
Normal file
@ -0,0 +1,32 @@
|
||||
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,9 +1,9 @@
|
||||
import React, { memo } from 'react';
|
||||
import { Player, BigPlayButton } from 'video-react';
|
||||
import './index.css';
|
||||
import { VideoConfigType } from '../DynamicEngine/schema';
|
||||
import { IVideoConfig } from './schema';
|
||||
|
||||
const VideoPlayer = memo((props: VideoConfigType) => {
|
||||
const VideoPlayer = memo((props: IVideoConfig) => {
|
||||
const { poster, url } = props;
|
||||
return (
|
||||
<div>
|
||||
45
src/components/BasicShop/MediaComponents/Video/schema.ts
Normal file
45
src/components/BasicShop/MediaComponents/Video/schema.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import {
|
||||
ITextConfigType,
|
||||
IUploadConfigType,
|
||||
TTextDefaultType,
|
||||
TUploadDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TVideoEditData = Array<IUploadConfigType | ITextConfigType>;
|
||||
export interface IVideoConfig {
|
||||
poster: TUploadDefaultType;
|
||||
url: TTextDefaultType;
|
||||
}
|
||||
|
||||
export interface IVideoSchema {
|
||||
editData: TVideoEditData;
|
||||
config: IVideoConfig;
|
||||
}
|
||||
|
||||
const Video: IVideoSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'poster',
|
||||
name: '视频封面',
|
||||
type: 'Upload',
|
||||
},
|
||||
{
|
||||
key: 'url',
|
||||
name: '视频链接',
|
||||
type: 'Text',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
poster: [
|
||||
{
|
||||
uid: '001',
|
||||
name: 'image.png',
|
||||
status: 'done',
|
||||
url: 'http://io.nainor.com/uploads/1_1740c6fbcd9.png',
|
||||
},
|
||||
],
|
||||
url: '',
|
||||
},
|
||||
};
|
||||
|
||||
export default Video;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Video',
|
||||
h: 107,
|
||||
};
|
||||
export default template;
|
||||
6
src/components/BasicShop/MediaComponents/schema.ts
Normal file
6
src/components/BasicShop/MediaComponents/schema.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import Video from './Video/schema';
|
||||
|
||||
const mediaSchema = {
|
||||
Video,
|
||||
};
|
||||
export default mediaSchema;
|
||||
9
src/components/BasicShop/MediaComponents/template.ts
Normal file
9
src/components/BasicShop/MediaComponents/template.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import Video from './Video/template';
|
||||
|
||||
const mediaTemplate = [Video];
|
||||
|
||||
const MediaTemplate = mediaTemplate.map(v => {
|
||||
return { ...v, category: 'media' };
|
||||
});
|
||||
|
||||
export default MediaTemplate;
|
||||
@ -1,25 +1,16 @@
|
||||
import { Chart } from '@antv/f2';
|
||||
import React, { memo, PropsWithChildren, useEffect, useRef } from 'react';
|
||||
import React, { memo, useEffect, useRef } from 'react';
|
||||
// import { uuid } from 'utils/tool';
|
||||
import ChartImg from '@/assets/chart.png';
|
||||
|
||||
import styles from './index.less';
|
||||
import { IChartConfig } from './schema';
|
||||
|
||||
type DataItem = {
|
||||
name: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
interface XChartProps {
|
||||
interface XChartProps extends IChartConfig {
|
||||
isTpl: boolean;
|
||||
title: string;
|
||||
color: string;
|
||||
size: number;
|
||||
paddingTop: number;
|
||||
data: Array<DataItem>;
|
||||
}
|
||||
|
||||
const XChart = (props: PropsWithChildren<XChartProps>) => {
|
||||
const XChart = (props: XChartProps) => {
|
||||
const { isTpl, data, color, size, paddingTop, title } = props;
|
||||
const chartRef = useRef(null);
|
||||
useEffect(() => {
|
||||
@ -44,7 +35,7 @@ const XChart = (props: PropsWithChildren<XChartProps>) => {
|
||||
// Step 4: 渲染图表
|
||||
chart.render();
|
||||
}
|
||||
}, []);
|
||||
}, [data, isTpl]);
|
||||
return (
|
||||
<div className={styles.chartWrap}>
|
||||
<div className={styles.chartTitle} style={{ color, fontSize: size, paddingTop }}>
|
||||
78
src/components/BasicShop/VisualComponents/Chart/schema.ts
Normal file
78
src/components/BasicShop/VisualComponents/Chart/schema.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import {
|
||||
IColorConfigType,
|
||||
INumberConfigType,
|
||||
ITableConfigType,
|
||||
ITextConfigType,
|
||||
TColorDefaultType,
|
||||
TNumberDefaultType,
|
||||
TTableDefaultType,
|
||||
TTextDefaultType,
|
||||
} from '@/components/PanelComponents/FormEditor/types';
|
||||
|
||||
export type TChartEditData = Array<
|
||||
ITextConfigType | INumberConfigType | IColorConfigType | ITableConfigType
|
||||
>;
|
||||
export interface IChartConfig {
|
||||
title: TTextDefaultType;
|
||||
size: TNumberDefaultType;
|
||||
color: TColorDefaultType;
|
||||
paddingTop: TNumberDefaultType;
|
||||
data: TTableDefaultType;
|
||||
}
|
||||
|
||||
export interface IChartSchema {
|
||||
editData: TChartEditData;
|
||||
config: IChartConfig;
|
||||
}
|
||||
|
||||
const Chart: IChartSchema = {
|
||||
editData: [
|
||||
{
|
||||
key: 'title',
|
||||
name: '标题',
|
||||
type: 'Text',
|
||||
},
|
||||
{
|
||||
key: 'size',
|
||||
name: '标题大小',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'color',
|
||||
name: '标题颜色',
|
||||
type: 'Color',
|
||||
},
|
||||
{
|
||||
key: 'paddingTop',
|
||||
name: '上边距',
|
||||
type: 'Number',
|
||||
},
|
||||
{
|
||||
key: 'data',
|
||||
name: '数据源',
|
||||
type: 'Table',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
title: '柱状图',
|
||||
size: 14,
|
||||
color: 'rgba(0,0,0,1)',
|
||||
paddingTop: 10,
|
||||
data: [
|
||||
{
|
||||
name: 'A',
|
||||
value: 20,
|
||||
},
|
||||
{
|
||||
name: 'B',
|
||||
value: 60,
|
||||
},
|
||||
{
|
||||
name: 'C',
|
||||
value: 20,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default Chart;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'Chart',
|
||||
h: 102,
|
||||
};
|
||||
export default template;
|
||||
@ -0,0 +1,21 @@
|
||||
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;
|
||||
@ -0,0 +1,95 @@
|
||||
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;
|
||||
@ -0,0 +1,5 @@
|
||||
const template = {
|
||||
type: 'XProgress',
|
||||
h: 102,
|
||||
};
|
||||
export default template;
|
||||
8
src/components/BasicShop/VisualComponents/schema.ts
Normal file
8
src/components/BasicShop/VisualComponents/schema.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import Chart from './Chart/schema';
|
||||
import XProgress from './XProgress/schema';
|
||||
|
||||
const visualSchema = {
|
||||
Chart,
|
||||
XProgress,
|
||||
};
|
||||
export default visualSchema;
|
||||
9
src/components/BasicShop/VisualComponents/template.ts
Normal file
9
src/components/BasicShop/VisualComponents/template.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import Chart from './Chart/template';
|
||||
import XProgress from './XProgress/template';
|
||||
|
||||
const visualTemplate = [Chart, XProgress];
|
||||
|
||||
const VisualTemplate = visualTemplate.map(v => {
|
||||
return { ...v, category: 'visual' };
|
||||
});
|
||||
export default VisualTemplate;
|
||||
11
src/components/BasicShop/schema.ts
Normal file
11
src/components/BasicShop/schema.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import BasicSchema from './BasicComponents/schema';
|
||||
import MediaSchema from './MediaComponents/schema';
|
||||
import VisualSchema from './VisualComponents/schema';
|
||||
|
||||
const schema = {
|
||||
...BasicSchema,
|
||||
...MediaSchema,
|
||||
...VisualSchema,
|
||||
};
|
||||
|
||||
export default schema;
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
@ -16,6 +16,45 @@ export default function Calibration(props: CalibrationTypes) {
|
||||
const [calibrationLength, setCalibration] = useState<calibrationTypes>({ width: 0, height: 0 });
|
||||
const calibrationRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const generateElement = useCallback(
|
||||
(item?: boolean, num?: number) => {
|
||||
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') {
|
||||
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 (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);
|
||||
}
|
||||
},
|
||||
[direction],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (calibrationRef.current) {
|
||||
let calibration = calibrationRef.current.getBoundingClientRect();
|
||||
@ -29,43 +68,7 @@ export default function Calibration(props: CalibrationTypes) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [direction]);
|
||||
|
||||
const generateElement = (item?: boolean, num?: number) => {
|
||||
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') {
|
||||
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 (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);
|
||||
}
|
||||
};
|
||||
}, [direction, generateElement]);
|
||||
|
||||
useEffect(() => {
|
||||
if (calibrationRef.current) {
|
||||
|
||||
@ -1,160 +0,0 @@
|
||||
import { memo } from 'react';
|
||||
import { NoticeBar, Progress } from 'zarm';
|
||||
import styles from './components.less';
|
||||
import React from 'react';
|
||||
import {
|
||||
HeaderConfigType,
|
||||
TextConfigType,
|
||||
LongTextConfigType,
|
||||
NoticeConfigType,
|
||||
QRCodeConfigType,
|
||||
FooterConfigType,
|
||||
ImageConfigType,
|
||||
ListConfigType,
|
||||
XProgressConfigType,
|
||||
} from './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>
|
||||
);
|
||||
});
|
||||
|
||||
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>
|
||||
);
|
||||
});
|
||||
|
||||
const Notice = memo((props: NoticeConfigType) => {
|
||||
const { text, speed, theme, link, isClose = false } = props;
|
||||
return (
|
||||
<NoticeBar theme={theme === 'default' ? undefined : theme} closable={isClose} speed={speed}>
|
||||
<a style={{ color: 'inherit' }}>{text}</a>
|
||||
</NoticeBar>
|
||||
);
|
||||
});
|
||||
|
||||
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>
|
||||
);
|
||||
});
|
||||
|
||||
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>
|
||||
);
|
||||
});
|
||||
|
||||
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>
|
||||
);
|
||||
});
|
||||
|
||||
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>
|
||||
);
|
||||
});
|
||||
|
||||
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>
|
||||
);
|
||||
});
|
||||
|
||||
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 { Header, Text, LongText, Notice, Qrcode, Footer, Image, List, XProgress };
|
||||
@ -1,17 +0,0 @@
|
||||
import { BasicTemplateItem } from './schema';
|
||||
|
||||
export type GraphTplKeyType = 'XProgress' | 'Chart';
|
||||
export type GraphTplType = Array<BasicTemplateItem<GraphTplKeyType>>;
|
||||
|
||||
const graphTpl: GraphTplType = [
|
||||
{
|
||||
type: 'XProgress',
|
||||
h: 102,
|
||||
},
|
||||
{
|
||||
type: 'Chart',
|
||||
h: 102,
|
||||
},
|
||||
];
|
||||
|
||||
export default graphTpl;
|
||||
@ -3,22 +3,23 @@ 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) => {
|
||||
const { config, isTpl } = props;
|
||||
return <Component {...config} isTpl={isTpl} />;
|
||||
@ -35,14 +36,17 @@ type DynamicType = {
|
||||
isTpl: boolean;
|
||||
config: { [key: string]: any };
|
||||
type: AllTemplateType;
|
||||
componentsType: componentsType;
|
||||
category: componentsType;
|
||||
};
|
||||
const DynamicEngine = memo((props: DynamicType) => {
|
||||
const { type, config, isTpl } = props;
|
||||
const { type, config, category } = props;
|
||||
const Dynamic = useMemo(() => {
|
||||
return (DynamicFunc(type) as unknown) as FC<DynamicType>;
|
||||
return (DynamicFunc(type, category) 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 {...props} />;
|
||||
});
|
||||
|
||||
export default DynamicEngine;
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
import { BasicTemplateItem } from './schema';
|
||||
|
||||
export type MediaTplKeyType = 'Video';
|
||||
export type MediaTplType = Array<BasicTemplateItem<MediaTplKeyType>>;
|
||||
|
||||
const mediaTpl: MediaTplType = [
|
||||
{
|
||||
type: 'Video',
|
||||
h: 107,
|
||||
},
|
||||
];
|
||||
|
||||
export default mediaTpl;
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,65 +0,0 @@
|
||||
import { BasicTemplateItem } from './schema';
|
||||
|
||||
export type TemplateKeyType =
|
||||
| 'Text'
|
||||
| 'LongText'
|
||||
| 'Carousel'
|
||||
| 'Tab'
|
||||
| 'Notice'
|
||||
| 'Qrcode'
|
||||
| 'Icon'
|
||||
| 'Image'
|
||||
| 'Header'
|
||||
| 'List'
|
||||
| 'Footer';
|
||||
|
||||
export type TemplateType = Array<BasicTemplateItem<TemplateKeyType>>;
|
||||
|
||||
const template: TemplateType = [
|
||||
{
|
||||
type: 'Text',
|
||||
h: 20,
|
||||
},
|
||||
{
|
||||
type: 'LongText',
|
||||
h: 36,
|
||||
},
|
||||
{
|
||||
type: 'Carousel',
|
||||
h: 82,
|
||||
},
|
||||
{
|
||||
type: 'Tab',
|
||||
h: 130,
|
||||
},
|
||||
{
|
||||
type: 'Notice',
|
||||
h: 20,
|
||||
},
|
||||
{
|
||||
type: 'Qrcode',
|
||||
h: 150,
|
||||
},
|
||||
{
|
||||
type: 'Icon',
|
||||
h: 23,
|
||||
},
|
||||
{
|
||||
type: 'Image',
|
||||
h: 188,
|
||||
},
|
||||
{
|
||||
type: 'Header',
|
||||
h: 28,
|
||||
},
|
||||
{
|
||||
type: 'List',
|
||||
h: 110,
|
||||
},
|
||||
{
|
||||
type: 'Footer',
|
||||
h: 24,
|
||||
},
|
||||
];
|
||||
|
||||
export default template;
|
||||
@ -1,12 +1,16 @@
|
||||
import React from 'react';
|
||||
import React, { ErrorInfo, PropsWithChildren } from 'react';
|
||||
|
||||
class ErrorBoundary extends React.Component<any, any> {
|
||||
constructor(props: any) {
|
||||
interface ErrorBoundaryState {
|
||||
hasError: boolean;
|
||||
}
|
||||
|
||||
class ErrorBoundary extends React.Component<PropsWithChildren<{}>, ErrorBoundaryState> {
|
||||
constructor(props: PropsWithChildren<{}>) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
componentDidCatch(error: any, info: any) {
|
||||
componentDidCatch(_error: Error, _info: ErrorInfo) {
|
||||
// Display fallback UI
|
||||
this.setState({ hasError: true });
|
||||
// You can also log the error to an error reporting service
|
||||
|
||||
@ -1,104 +0,0 @@
|
||||
import React, { memo, useEffect } from 'react';
|
||||
import { Form, Select, Input, Modal } from 'antd';
|
||||
|
||||
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 && (
|
||||
<Modal
|
||||
title="编辑表单组件"
|
||||
visible={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={onCancel}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
name={`formItem_editor_modal`}
|
||||
{...formItemLayout}
|
||||
onFinish={onFinish}
|
||||
initialValues={item}
|
||||
>
|
||||
{
|
||||
<Form.Item label="类型" name="type" hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
}
|
||||
{!!item.label && (
|
||||
<Form.Item
|
||||
label="字段名"
|
||||
name="label"
|
||||
rules={[{ required: true, message: '请输入字段名!' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{!!item.placeholder && (
|
||||
<Form.Item label="提示文本" name="placeholder">
|
||||
<Input placeholder="请输入提示文本" />
|
||||
</Form.Item>
|
||||
)}
|
||||
{!!item.options && (
|
||||
<Form.Item
|
||||
label="选项源"
|
||||
name="options"
|
||||
rules={[{ required: true, message: '选项不能为空!' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请输入"
|
||||
mode="tags"
|
||||
labelInValue
|
||||
maxTagCount={39}
|
||||
maxTagTextLength={16}
|
||||
>
|
||||
{item.options.map((v, i) => {
|
||||
return (
|
||||
<Option value={v.value} key={i}>
|
||||
{v.label}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(EditorModal);
|
||||
@ -1,19 +1,18 @@
|
||||
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 React from 'react';
|
||||
import { IconTypes } from '@/components/BasicShop/BasicComponents/Icon/schema';
|
||||
import { ICardPickerConfigType } from '../FormEditor/types';
|
||||
|
||||
interface CardPickerType {
|
||||
type: IconTypes;
|
||||
icons: Array<IconTypes>;
|
||||
interface CardPickerType extends Omit<ICardPickerConfigType<IconTypes>, 'type' | 'key' | 'name'> {
|
||||
onChange?: (v: string) => void;
|
||||
type: IconTypes;
|
||||
}
|
||||
|
||||
export default memo((props: CardPickerType) => {
|
||||
const { type, icons, onChange } = props;
|
||||
console.log(type);
|
||||
const [selected, setSelected] = useState<IconTypes>(type);
|
||||
|
||||
const handlePicker = (v: IconTypes) => {
|
||||
@ -37,7 +36,7 @@ export default memo((props: CardPickerType) => {
|
||||
onClick={() => handlePicker(item)}
|
||||
key={i}
|
||||
>
|
||||
<Icon type={item} size={20} />
|
||||
<Icon type={item} size={20} color={'#4091f7'} spin={false} />
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
0
src/components/PanelComponents/Color/index.less
Normal file
0
src/components/PanelComponents/Color/index.less
Normal 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> {
|
||||
@ -1,13 +1,10 @@
|
||||
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 { Store } from 'antd/lib/form/interface';
|
||||
|
||||
import { TDataListDefaultTypeItem } from '../FormEditor/types';
|
||||
// import styles from './index.less';
|
||||
const normFile = (e: any) => {
|
||||
console.log('Upload event:', e);
|
||||
console.log('ffffffffff'); //待修改?
|
||||
if (Array.isArray(e)) {
|
||||
return e;
|
||||
}
|
||||
@ -24,7 +21,7 @@ const formItemLayout = {
|
||||
export type EditorModalProps = {
|
||||
visible: boolean;
|
||||
onCancel: ((e: React.MouseEvent<HTMLElement, MouseEvent>) => void) | undefined;
|
||||
item?: BasicDataSource;
|
||||
item?: TDataListDefaultTypeItem;
|
||||
onSave: Function;
|
||||
};
|
||||
|
||||
@ -39,4 +39,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 { TDataListDefaultType, TDataListDefaultTypeItem } from '../FormEditor/types';
|
||||
|
||||
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`函数定义
|
||||
@ -69,11 +66,10 @@ function ListItem(props: ListItemProps) {
|
||||
);
|
||||
}
|
||||
|
||||
type DndItemProps = BasicDataSource & {
|
||||
type DndItemProps = TDataListDefaultTypeItem & {
|
||||
onDel: Function;
|
||||
onEdit: Function;
|
||||
key: number;
|
||||
id: string;
|
||||
find: Function;
|
||||
move: Function;
|
||||
type?: number;
|
||||
@ -131,8 +127,8 @@ const DndItem = DropTarget(
|
||||
)(DragSource(type, dragSpec, dragCollect)(ListItem));
|
||||
|
||||
export type DataListMemo = {
|
||||
onChange?: (v: BasicDataSource[]) => void;
|
||||
value?: BasicDataSource[];
|
||||
onChange?: (v: TDataListDefaultType) => void;
|
||||
value?: TDataListDefaultType;
|
||||
};
|
||||
|
||||
export type DataListType = DataListMemo & {
|
||||
@ -143,7 +139,7 @@ const List = function(props: DataListType) {
|
||||
const { onChange, value, connectDropTarget } = props;
|
||||
const [list, setList] = useState(value);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [curItem, setCurItem] = useState<BasicDataSource>();
|
||||
const [curItem, setCurItem] = useState<TDataListDefaultTypeItem>();
|
||||
|
||||
const handleDel = (id: string) => {
|
||||
if (value && onChange) {
|
||||
@ -176,13 +172,13 @@ const List = function(props: DataListType) {
|
||||
setVisible(false);
|
||||
}, []);
|
||||
|
||||
const handleEdit = useCallback((item: BasicDataSource) => {
|
||||
const handleEdit = useCallback((item: TDataListDefaultTypeItem) => {
|
||||
setVisible(true);
|
||||
setCurItem(item);
|
||||
}, []);
|
||||
|
||||
const handleSave = useCallback(
|
||||
(item: BasicDataSource) => {
|
||||
(item: TDataListDefaultTypeItem) => {
|
||||
setVisible(false);
|
||||
if (onChange) {
|
||||
onChange(list!.map(p => (p.id === item.id ? item : p)));
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import React, { memo, useEffect } from 'react';
|
||||
import { Form, Select, InputNumber, Input, Switch, Radio, Button } from 'antd';
|
||||
import Upload from '../Upload';
|
||||
import DataList from '../DataList';
|
||||
@ -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 FormItems from '../FormItems';
|
||||
// import styles from './index.less';
|
||||
const normFile = (e: any) => {
|
||||
console.log('Upload event:', e);
|
||||
@ -36,6 +36,7 @@ interface FormEditorProps {
|
||||
|
||||
const FormEditor = (props: FormEditorProps) => {
|
||||
const { config, defaultValue, onSave, onDel, uid } = props;
|
||||
console.log(defaultValue, config);
|
||||
const onFinish = (values: Store) => {
|
||||
onSave && onSave(values);
|
||||
};
|
||||
@ -50,8 +51,7 @@ const FormEditor = (props: FormEditorProps) => {
|
||||
return () => {
|
||||
form.resetFields();
|
||||
};
|
||||
}, [defaultValue]);
|
||||
|
||||
}, [defaultValue, form]);
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
@ -96,7 +96,7 @@ const FormEditor = (props: FormEditorProps) => {
|
||||
{item.type === 'Select' && (
|
||||
<Form.Item label={item.name} name={item.key}>
|
||||
<Select placeholder="请选择">
|
||||
{item.range.map((v: BasicRangeType<string>, i: number) => {
|
||||
{item.range.map((v: any, i: number) => {
|
||||
return (
|
||||
<Option value={v.key} key={i}>
|
||||
{v.text}
|
||||
@ -109,7 +109,7 @@ const FormEditor = (props: FormEditorProps) => {
|
||||
{item.type === 'Radio' && (
|
||||
<Form.Item label={item.name} name={item.key}>
|
||||
<Radio.Group>
|
||||
{item.range.map((v: BasicRangeType<string>, i: number) => {
|
||||
{item.range.map((v: any, i: number) => {
|
||||
return (
|
||||
<Radio value={v.key} key={i}>
|
||||
{v.text}
|
||||
@ -136,10 +136,7 @@ const FormEditor = (props: FormEditorProps) => {
|
||||
)}
|
||||
{item.type === 'CardPicker' && (
|
||||
<Form.Item label={item.name} name={item.key} valuePropName="type">
|
||||
<CardPicker
|
||||
icons={item.icons}
|
||||
type={defaultValue['type'] as IconSchema['config']['type']}
|
||||
/>
|
||||
<CardPicker icons={item.icons} type={defaultValue['type']} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{item.type === 'Table' && (
|
||||
@ -147,6 +144,11 @@ const FormEditor = (props: FormEditorProps) => {
|
||||
<Table data={item.data} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{item.type === 'FormItems' && (
|
||||
<Form.Item label={item.name} name={item.key} valuePropName="formList">
|
||||
<FormItems data={item.data} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
199
src/components/PanelComponents/FormEditor/types.ts
Normal file
199
src/components/PanelComponents/FormEditor/types.ts
Normal file
@ -0,0 +1,199 @@
|
||||
////////////////////
|
||||
export interface IUploadConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'Upload';
|
||||
isCrop?: boolean;
|
||||
cropRate?: number;
|
||||
}
|
||||
|
||||
export type TUploadDefaultType = Array<{
|
||||
uid: string;
|
||||
name: string;
|
||||
status: string;
|
||||
url: string;
|
||||
}>;
|
||||
/////////////////
|
||||
export interface ITextConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'Text';
|
||||
}
|
||||
export type TTextDefaultType = string;
|
||||
////////////////////////
|
||||
export interface ITextAreaConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'TextArea';
|
||||
}
|
||||
export type TTextAreaDefaultType = string;
|
||||
////////////////////////////
|
||||
export interface INumberConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'Number';
|
||||
range?: [number, number];
|
||||
step?: number;
|
||||
}
|
||||
|
||||
export type TNumberDefaultType = number;
|
||||
|
||||
///////////////////
|
||||
export interface IDataListConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'DataList';
|
||||
}
|
||||
|
||||
export type TDataListDefaultTypeItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
desc: string;
|
||||
link: string;
|
||||
type?: number;
|
||||
imgUrl: TUploadDefaultType;
|
||||
};
|
||||
|
||||
export type TDataListDefaultType = Array<TDataListDefaultTypeItem>;
|
||||
|
||||
////////////////////
|
||||
export interface IColorConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'Color';
|
||||
}
|
||||
|
||||
export type TColorDefaultType = string;
|
||||
|
||||
export interface IMutiTextConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'MutiText';
|
||||
}
|
||||
|
||||
export type TMutiTextDefaultType = Array<string>;
|
||||
|
||||
/////////////////////////////////
|
||||
export interface ISelectConfigType<KeyType> {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'Select';
|
||||
range: Array<{
|
||||
key: KeyType;
|
||||
text: string;
|
||||
}>;
|
||||
}
|
||||
export type TSelectDefaultType<KeyType> = KeyType;
|
||||
|
||||
/////////////////////////
|
||||
export interface IRadioConfigType<KeyType> {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'Radio';
|
||||
range: Array<{
|
||||
key: KeyType;
|
||||
text: string;
|
||||
}>;
|
||||
}
|
||||
export type TRadioDefaultType<KeyType> = KeyType;
|
||||
|
||||
///////////////
|
||||
|
||||
export interface ISwitchConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'Switch';
|
||||
}
|
||||
export type TSwitchDefaultType = boolean;
|
||||
|
||||
/////////////////////////////
|
||||
export interface ICardPickerConfigType<T> {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'CardPicker';
|
||||
icons: Array<T>;
|
||||
}
|
||||
export type TCardPickerDefaultType<T> = T;
|
||||
|
||||
/////////////
|
||||
|
||||
export interface ITableConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'Table';
|
||||
}
|
||||
export type TTableDefaultType = Array<{
|
||||
name: string;
|
||||
value: number;
|
||||
}>;
|
||||
|
||||
//////////////////
|
||||
export interface IFormItemsConfigType {
|
||||
key: string;
|
||||
name: string;
|
||||
type: 'FormItems';
|
||||
}
|
||||
|
||||
//0---------baseform
|
||||
export type baseFormOptionsType = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type baseFormTextTpl = {
|
||||
id: string;
|
||||
type: 'Text';
|
||||
label: string;
|
||||
placeholder: string;
|
||||
};
|
||||
export type baseFormNumberTpl = {
|
||||
id: string;
|
||||
type: 'Number';
|
||||
label: string;
|
||||
placeholder: string;
|
||||
};
|
||||
|
||||
export type baseFormTextAreaTpl = {
|
||||
id: string;
|
||||
type: 'Textarea';
|
||||
label: string;
|
||||
placeholder: string;
|
||||
};
|
||||
|
||||
export type baseFormMyRadioTpl = {
|
||||
id: string;
|
||||
type: 'MyRadio';
|
||||
label: string;
|
||||
options: baseFormOptionsType[];
|
||||
};
|
||||
|
||||
export type baseFormMySelectTpl = {
|
||||
id: string;
|
||||
type: 'MySelect';
|
||||
label: string;
|
||||
options: baseFormOptionsType[];
|
||||
};
|
||||
|
||||
export type baseFormDateTpl = {
|
||||
id: string;
|
||||
type: 'Date';
|
||||
label: string;
|
||||
placeholder: string;
|
||||
};
|
||||
|
||||
export type baseFormUnion =
|
||||
| baseFormTextTpl
|
||||
| baseFormNumberTpl
|
||||
| baseFormTextAreaTpl
|
||||
| baseFormMyRadioTpl
|
||||
| baseFormMySelectTpl
|
||||
| baseFormDateTpl;
|
||||
export type baseFormUnionType =
|
||||
| baseFormTextTpl['type']
|
||||
| baseFormNumberTpl['type']
|
||||
| baseFormTextAreaTpl['type']
|
||||
| baseFormMyRadioTpl['type']
|
||||
| baseFormMySelectTpl['type']
|
||||
| baseFormDateTpl['type'];
|
||||
|
||||
export type TFormItemsDefaultType = Array<baseFormUnion>;
|
||||
114
src/components/PanelComponents/FormItems/EditorModal.tsx
Normal file
114
src/components/PanelComponents/FormItems/EditorModal.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
import React, { FC, memo, useEffect } from 'react';
|
||||
import { Form, Select, Input, Modal } from 'antd';
|
||||
import { baseFormOptionsType, baseFormUnion } from '../FormEditor/types';
|
||||
import { Store } from 'antd/lib/form/interface';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 14 },
|
||||
};
|
||||
|
||||
interface EditorModalProps {
|
||||
item: any;
|
||||
onSave: (data: any) => void;
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const EditorModal: FC<EditorModalProps> = props => {
|
||||
const { item, onSave, visible, onCancel } = props;
|
||||
|
||||
const onFinish = (values: any) => {
|
||||
onSave && onSave(values);
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then(values => {
|
||||
values.id = item.id;
|
||||
onSave && onSave(values);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
form.resetFields();
|
||||
};
|
||||
}, [form, item]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!!item && (
|
||||
<Modal
|
||||
title="编辑表单组件"
|
||||
visible={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={onCancel}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
name={`formItem_editor_modal`}
|
||||
{...formItemLayout}
|
||||
onFinish={onFinish}
|
||||
initialValues={item}
|
||||
>
|
||||
{
|
||||
<Form.Item label="类型" name="type" hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
}
|
||||
{!!item.label && (
|
||||
<Form.Item
|
||||
label="字段名"
|
||||
name="label"
|
||||
rules={[{ required: true, message: '请输入字段名!' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{!!item.placeholder && (
|
||||
<Form.Item label="提示文本" name="placeholder">
|
||||
<Input placeholder="请输入提示文本" />
|
||||
</Form.Item>
|
||||
)}
|
||||
{!!item.options && (
|
||||
<Form.Item
|
||||
label="选项源"
|
||||
name="options"
|
||||
rules={[{ required: true, message: '选项不能为空!' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请输入"
|
||||
mode="tags"
|
||||
labelInValue
|
||||
maxTagCount={39}
|
||||
maxTagTextLength={16}
|
||||
>
|
||||
{item.options.map((v: baseFormOptionsType, i: number) => {
|
||||
return (
|
||||
<Option value={v.value} key={i}>
|
||||
{v.label}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(EditorModal);
|
||||
@ -1,14 +1,13 @@
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import BaseForm from './BaseForm';
|
||||
import React, { memo, useState } from 'react';
|
||||
import BaseForm from '../../BasicShop/BasicComponents/Form/BaseForm';
|
||||
import EditorModal from './EditorModal';
|
||||
|
||||
import { EditOutlined, MinusCircleOutlined } from '@ant-design/icons';
|
||||
import styles from './formItems.less';
|
||||
import { baseFormUnion, TFormItemsDefaultType } from '../FormEditor/types';
|
||||
|
||||
// import { Popconfirm } from 'antd';
|
||||
|
||||
import styles from './formItems.less';
|
||||
|
||||
const formTpl = [
|
||||
const formTpl: TFormItemsDefaultType = [
|
||||
{
|
||||
id: '1',
|
||||
type: 'Text',
|
||||
@ -50,28 +49,35 @@ const formTpl = [
|
||||
id: '6',
|
||||
type: 'Date',
|
||||
label: '日期框',
|
||||
placeholder: '',
|
||||
},
|
||||
];
|
||||
|
||||
const FormItems = props => {
|
||||
const { formList, onChange } = props;
|
||||
const [formData, setFormData] = useState(formList || []);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [curItem, setCurItem] = useState();
|
||||
interface FormItemsProps {
|
||||
formList?: TFormItemsDefaultType;
|
||||
onChange?: (v: TFormItemsDefaultType) => void;
|
||||
data: any;
|
||||
}
|
||||
|
||||
const handleAddItem = item => {
|
||||
const FormItems = (props: FormItemsProps) => {
|
||||
const { formList, onChange } = props;
|
||||
const [formData, setFormData] = useState<TFormItemsDefaultType>(formList || []);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [curItem, setCurItem] = useState<baseFormUnion>();
|
||||
|
||||
const handleAddItem = (item: baseFormUnion) => {
|
||||
let tpl = formTpl.find(v => v.type === item.type);
|
||||
let newData = [...formData, tpl];
|
||||
let newData = [...formData, tpl!];
|
||||
setFormData(newData);
|
||||
onChange && onChange(newData);
|
||||
};
|
||||
|
||||
const handleEditItem = item => {
|
||||
const handleEditItem = (item: baseFormUnion) => {
|
||||
setVisible(true);
|
||||
setCurItem(item);
|
||||
};
|
||||
|
||||
const handleDelItem = item => {
|
||||
const handleDelItem = (item: baseFormUnion) => {
|
||||
let newData = formData.filter(v => v.type !== item.type);
|
||||
setFormData(newData);
|
||||
onChange && onChange(newData);
|
||||
@ -81,7 +87,7 @@ const FormItems = props => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const handleSaveItem = data => {
|
||||
const handleSaveItem = (data: baseFormUnion) => {
|
||||
let newData = formData.map(v => (v.type === data.type ? data : v));
|
||||
setFormData(newData);
|
||||
onChange && onChange(newData);
|
||||
@ -90,7 +96,7 @@ const FormItems = props => {
|
||||
return (
|
||||
<div className={styles.formItemWrap}>
|
||||
<div className={styles.editForm}>
|
||||
{formData.map((item, i) => {
|
||||
{formData.map((item: baseFormUnion, i: number) => {
|
||||
let FormItem = BaseForm[item.type];
|
||||
return (
|
||||
<div className={styles.formItem} key={i}>
|
||||
2
src/components/PanelComponents/FormItems/index.tsx
Normal file
2
src/components/PanelComponents/FormItems/index.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
import FormItems from './FormItems';
|
||||
export default FormItems;
|
||||
@ -8,4 +8,4 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,11 +2,11 @@ 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 { TMutiTextDefaultType } from '../FormEditor/types';
|
||||
|
||||
type MultiTextProps = {
|
||||
onChange?: (v: TabConfigType['tabs']) => void;
|
||||
value?: TabConfigType['tabs'];
|
||||
onChange?: (v: TMutiTextDefaultType) => void;
|
||||
value?: TMutiTextDefaultType;
|
||||
};
|
||||
|
||||
export default memo(function MutiText(props: MultiTextProps) {
|
||||
@ -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,14 @@ 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>
|
||||
<Button type="link">删除</Button>
|
||||
</Popconfirm>
|
||||
) : null,
|
||||
},
|
||||
];
|
||||
|
||||
const dataSource = props.data.map((item, i: number) => ({ key: i + '', ...item }));
|
||||
const dataSource =
|
||||
props.data && props.data.map((item: any, i: number) => ({ key: i + '', ...item }));
|
||||
|
||||
this.state = {
|
||||
dataSource: dataSource,
|
||||
@ -156,7 +172,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 +190,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 +212,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;
|
||||
}
|
||||
@ -6,4 +6,4 @@
|
||||
:global(.ant-upload-select-picture-card .ant-upload-text) {
|
||||
margin-top: 8px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
@ -4,9 +4,7 @@ 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';
|
||||
|
||||
import { isDev } from '@/utils/tool';
|
||||
function getBase64(file: File | Blob) {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
@ -2,9 +2,10 @@ import React, { memo } from 'react';
|
||||
import { Button, Popover } from 'antd';
|
||||
import styles from './index.less';
|
||||
|
||||
///这组件写的有问题 popover会重定位
|
||||
const content = (
|
||||
<div className={styles.imgWrap}>
|
||||
<img src="http://io.nainor.com/uploads/WechatIMG2_1742b586c3d.jpeg" />
|
||||
<img src="http://io.nainor.com/uploads/WechatIMG2_1742b586c3d.jpeg" alt="sponsorship" />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@ -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: /(.*?)作者是谁(.*?)/,
|
||||
|
||||
@ -13,11 +13,11 @@ import SourceBox from './SourceBox';
|
||||
import TargetBox from './TargetBox';
|
||||
import Calibration from 'components/Calibration';
|
||||
import DynamicEngine from 'components/DynamicEngine';
|
||||
import FormEditor from 'components/FormEditor';
|
||||
import template from 'components/DynamicEngine/template';
|
||||
import mediaTpl from 'components/DynamicEngine/mediaTpl';
|
||||
import graphTpl from 'components/DynamicEngine/graphTpl';
|
||||
import schema from 'components/DynamicEngine/schema';
|
||||
import FormEditor from 'components/PanelComponents/FormEditor';
|
||||
import template from 'components/BasicShop/BasicComponents/template';
|
||||
import mediaTpl from 'components/BasicShop/MediaComponents/template';
|
||||
import graphTpl from 'components/BasicShop/VisualComponents/template';
|
||||
import schema from 'components/BasicShop/schema';
|
||||
import { ActionCreators } from 'redux-undo';
|
||||
|
||||
import styles from './index.less';
|
||||
@ -63,7 +63,6 @@ const Container = props => {
|
||||
setScale(prev => (prev <= 0.5 ? 0.5 : prev - 0.1));
|
||||
}
|
||||
};
|
||||
|
||||
const handleFormSave = data => {
|
||||
dispatch({
|
||||
type: 'editorModal/modPointData',
|
||||
@ -90,7 +89,7 @@ const Container = props => {
|
||||
useEffect(() => {
|
||||
if (window.innerWidth < 1024) {
|
||||
props.history.push('/mobileTip');
|
||||
}
|
||||
} //待修改
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@ -135,23 +134,40 @@ const Container = props => {
|
||||
<div className={styles.componentList}>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<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} />
|
||||
</TargetBox>
|
||||
))}
|
||||
{template.map((value, i) => {
|
||||
return (
|
||||
<TargetBox item={value} key={i} canvasId={canvasId}>
|
||||
<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>
|
||||
|
||||
@ -79,7 +79,6 @@ const SourceBox = memo(props => {
|
||||
window.clearTimeout(timer);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const opacity = isOver ? 0.7 : 1;
|
||||
const backgroundColor = isOver ? 1 : 0.7;
|
||||
return (
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import React, { useMemo, memo } from 'react';
|
||||
import { useDrag } from 'react-dnd';
|
||||
import schema from 'components/DynamicEngine/schema';
|
||||
|
||||
import schema from 'components/BasicShop/schema';
|
||||
import styles from './index.less';
|
||||
|
||||
const TargetBox = memo(props => {
|
||||
@ -12,6 +11,7 @@ const TargetBox = memo(props => {
|
||||
config: schema[item.type].config,
|
||||
h: item.h,
|
||||
editableEl: schema[item.type].editData,
|
||||
category: item.category,
|
||||
},
|
||||
collect: monitor => ({
|
||||
isDragging: monitor.isDragging(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user