├── .env ├── src ├── components │ ├── PanelComponents │ │ ├── Color │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── FormEditor │ │ │ └── index.less │ │ ├── FormItems │ │ │ ├── index.tsx │ │ │ ├── formItems.less │ │ │ └── EditorModal.tsx │ │ ├── XEditor │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── Pos │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── MutiText │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── CardPicker │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── InteractionData │ │ │ └── index.less │ │ ├── Table │ │ │ └── index.less │ │ ├── DataList │ │ │ ├── index.less │ │ │ └── editorModal.tsx │ │ └── Upload │ │ │ └── index.less │ ├── BasicShop │ │ ├── BasicComponents │ │ │ ├── Text │ │ │ │ ├── index.less │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Divider │ │ │ │ ├── index.less │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── LongText │ │ │ │ ├── index.less │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Card │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Form │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── baseForm.less │ │ │ │ ├── index.tsx │ │ │ │ ├── BasePopoverForm.tsx │ │ │ │ └── schema.ts │ │ │ ├── Icon │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Image │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── List │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Tab │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── WhiteTpl │ │ │ │ ├── index.less │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Footer │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Header │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Notice │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Qrcode │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Search │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── XButton │ │ │ │ ├── template.ts │ │ │ │ ├── Modal.tsx │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Carousel │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── VisualComponents │ │ │ ├── XProgress │ │ │ │ ├── index.less │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Area │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── schema.ts │ │ │ │ └── index.tsx │ │ │ ├── Line │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── schema.ts │ │ │ │ └── index.tsx │ │ │ ├── Pie │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── schema.ts │ │ │ │ └── index.tsx │ │ │ ├── Chart │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── Funnel │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── schema.ts │ │ │ │ └── index.tsx │ │ │ ├── Radar │ │ │ │ ├── template.ts │ │ │ │ ├── index.less │ │ │ │ ├── schema.ts │ │ │ │ └── index.tsx │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── MediaComponents │ │ │ ├── Audio │ │ │ │ ├── index.less │ │ │ │ ├── template.ts │ │ │ │ ├── schema.ts │ │ │ │ └── index.tsx │ │ │ ├── Video │ │ │ │ ├── template.ts │ │ │ │ ├── index.tsx │ │ │ │ └── schema.ts │ │ │ ├── schema.ts │ │ │ └── template.ts │ │ ├── schema.ts │ │ └── common.ts │ ├── Zan │ │ ├── index.less │ │ └── index.tsx │ ├── ModalTpl │ │ ├── cate.js │ │ ├── index.less │ │ └── index.js │ ├── Calibration │ │ └── index.less │ ├── LoadingCp │ │ └── index.tsx │ ├── ErrorBundaries │ │ └── index.tsx │ ├── BackTop │ │ └── index.js │ └── DynamicEngine │ │ └── index.tsx ├── assets │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── osc.png │ ├── pie.png │ ├── 01-轮播.png │ ├── 02-页脚.png │ ├── 03-表单.png │ ├── 04-页头.png │ ├── 05-图标.png │ ├── 07-列表.png │ ├── 08-长文本.png │ ├── 09-通知.png │ ├── 10-二维码.png │ ├── 11-切换页.png │ ├── 12-文本.png │ ├── 13-空白.png │ ├── 14-视频.png │ ├── 15-进度.png │ ├── area.png │ ├── audio.mp3 │ ├── chart.png │ ├── code.png │ ├── icon.png │ ├── leida.png │ ├── line.png │ ├── logo.ico │ ├── logo.png │ ├── loudou.png │ ├── search.png │ ├── 06-图片组件.png │ ├── 17-table.png │ ├── card@2x.png │ ├── drivide.png │ ├── login_bg.png │ ├── music@2x.png │ └── 16-botton.png ├── pages │ ├── editor │ │ ├── services │ │ │ └── editorService.js │ │ ├── index.js │ │ ├── components │ │ │ ├── CanvasControl │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── AvatorGroup │ │ │ │ └── index.tsx │ │ │ └── Header │ │ │ │ └── index.less │ │ └── TargetBox.js │ ├── mobileTip.js │ ├── help │ │ ├── index.less │ │ └── index.tsx │ ├── __tests__ │ │ └── index.test.js │ └── document.ejs ├── layouts │ ├── __tests__ │ │ └── index.test.js │ └── index.tsx ├── app.tsx ├── utils │ ├── req.ts │ └── tool.ts └── global.css ├── public └── iphone.png ├── .prettierignore ├── .prettierrc ├── .editorconfig ├── .gitignore ├── typings.d.ts ├── tsconfig.json ├── server.js ├── .umirc.ts ├── README.md └── package.json /.env: -------------------------------------------------------------------------------- 1 | PORT=8000 -------------------------------------------------------------------------------- /src/components/PanelComponents/Color/index.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/PanelComponents/FormEditor/index.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Text/index.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Divider/index.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/LongText/index.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/XProgress/index.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/1.png -------------------------------------------------------------------------------- /src/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/2.png -------------------------------------------------------------------------------- /src/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/3.png -------------------------------------------------------------------------------- /src/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/4.png -------------------------------------------------------------------------------- /src/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/5.png -------------------------------------------------------------------------------- /src/assets/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/6.png -------------------------------------------------------------------------------- /public/iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/public/iphone.png -------------------------------------------------------------------------------- /src/assets/osc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/osc.png -------------------------------------------------------------------------------- /src/assets/pie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/pie.png -------------------------------------------------------------------------------- /src/assets/01-轮播.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/01-轮播.png -------------------------------------------------------------------------------- /src/assets/02-页脚.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/02-页脚.png -------------------------------------------------------------------------------- /src/assets/03-表单.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/03-表单.png -------------------------------------------------------------------------------- /src/assets/04-页头.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/04-页头.png -------------------------------------------------------------------------------- /src/assets/05-图标.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/05-图标.png -------------------------------------------------------------------------------- /src/assets/07-列表.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/07-列表.png -------------------------------------------------------------------------------- /src/assets/08-长文本.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/08-长文本.png -------------------------------------------------------------------------------- /src/assets/09-通知.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/09-通知.png -------------------------------------------------------------------------------- /src/assets/10-二维码.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/10-二维码.png -------------------------------------------------------------------------------- /src/assets/11-切换页.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/11-切换页.png -------------------------------------------------------------------------------- /src/assets/12-文本.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/12-文本.png -------------------------------------------------------------------------------- /src/assets/13-空白.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/13-空白.png -------------------------------------------------------------------------------- /src/assets/14-视频.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/14-视频.png -------------------------------------------------------------------------------- /src/assets/15-进度.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/15-进度.png -------------------------------------------------------------------------------- /src/assets/area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/area.png -------------------------------------------------------------------------------- /src/assets/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/audio.mp3 -------------------------------------------------------------------------------- /src/assets/chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/chart.png -------------------------------------------------------------------------------- /src/assets/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/code.png -------------------------------------------------------------------------------- /src/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/icon.png -------------------------------------------------------------------------------- /src/assets/leida.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/leida.png -------------------------------------------------------------------------------- /src/assets/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/line.png -------------------------------------------------------------------------------- /src/assets/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/logo.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/loudou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/loudou.png -------------------------------------------------------------------------------- /src/assets/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/search.png -------------------------------------------------------------------------------- /src/assets/06-图片组件.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/06-图片组件.png -------------------------------------------------------------------------------- /src/assets/17-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/17-table.png -------------------------------------------------------------------------------- /src/assets/card@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/card@2x.png -------------------------------------------------------------------------------- /src/assets/drivide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/drivide.png -------------------------------------------------------------------------------- /src/assets/login_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/login_bg.png -------------------------------------------------------------------------------- /src/assets/music@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/music@2x.png -------------------------------------------------------------------------------- /src/assets/16-botton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/pc-Dooring/HEAD/src/assets/16-botton.png -------------------------------------------------------------------------------- /src/components/PanelComponents/FormItems/index.tsx: -------------------------------------------------------------------------------- 1 | import FormItems from './FormItems'; 2 | export default FormItems; 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.md 2 | **/*.svg 3 | **/*.ejs 4 | **/*.html 5 | package.json 6 | .umi 7 | .umi-production 8 | .umi-test 9 | -------------------------------------------------------------------------------- /src/components/BasicShop/MediaComponents/Audio/index.less: -------------------------------------------------------------------------------- 1 | .audioWrap { 2 | height: 100%; 3 | display: flex; 4 | align-items: center; 5 | } -------------------------------------------------------------------------------- /src/components/PanelComponents/XEditor/index.less: -------------------------------------------------------------------------------- 1 | .avatarUploader > :global(.ant-upload) { 2 | width: 128px; 3 | height: 128px; 4 | } -------------------------------------------------------------------------------- /src/components/Zan/index.less: -------------------------------------------------------------------------------- 1 | .takeCat { 2 | display: inline-block; 3 | } 4 | .imgWrap { 5 | width: 160px; 6 | img { 7 | width: 100%; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Card/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Card', 3 | h: 162, 4 | displayName: '卡片组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Form/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Form', 3 | h: 172, 4 | displayName: '表单组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Icon/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Icon', 3 | h: 23, 4 | displayName: '图标组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Image/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Image', 3 | h: 80, 4 | displayName: '图片组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/List/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'List', 3 | h: 110, 4 | displayName: '列表组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Tab/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Tab', 3 | h: 130, 4 | displayName: '切换页组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Text/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Text', 3 | h: 20, 4 | displayName: '文本组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/WhiteTpl/index.less: -------------------------------------------------------------------------------- 1 | .whiteTpl { 2 | text-align: center; 3 | .title { 4 | font-size: 14px; 5 | color: #fff; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/components/BasicShop/MediaComponents/Audio/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Audio', 3 | h: 36, 4 | displayName: '音频组件', 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/MediaComponents/Video/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Video', 3 | h: 107, 4 | displayName: '视频组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Area/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Area', 3 | h: 142, 4 | displayName: '面积图组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Line/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Line', 3 | h: 142, 4 | displayName: '折线图组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Pie/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Pie', 3 | h: 142, 4 | displayName: '饼图组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Divider/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Divider', 3 | h: 26, 4 | displayName: '分割线组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Footer/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Footer', 3 | h: 24, 4 | displayName: '页脚组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Header/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Header', 3 | h: 28, 4 | displayName: '页头组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Notice/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Notice', 3 | h: 20, 4 | displayName: '通知组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Qrcode/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Qrcode', 3 | h: 150, 4 | displayName: '二维码组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Search/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Search', 3 | h: 28, 4 | displayName: '搜索组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/WhiteTpl/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'WhiteTpl', 3 | h: 20, 4 | displayName: '空白组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/XButton/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type:'XButton', 3 | h: 23, 4 | displayName: '按钮组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Chart/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Chart', 3 | h: 142, 4 | displayName: '柱状图组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Funnel/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Funnel', 3 | h: 142, 4 | displayName: '漏斗图组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Radar/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Radar', 3 | h: 142, 4 | displayName: '雷达图组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/pages/editor/services/editorService.js: -------------------------------------------------------------------------------- 1 | import req from '@/utils/req' 2 | 3 | export function getTemplate(data) { 4 | return req('/test', { method: 'GET', params: data }) 5 | } -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Carousel/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'Carousel', 3 | h: 150, 4 | displayName: '轮播图组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/LongText/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'LongText', 3 | h: 36, 4 | displayName: '长文本组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/XProgress/template.ts: -------------------------------------------------------------------------------- 1 | const template = { 2 | type: 'XProgress', 3 | h: 120, 4 | displayName: '进度条组件' 5 | }; 6 | export default template; 7 | -------------------------------------------------------------------------------- /src/components/PanelComponents/Pos/index.less: -------------------------------------------------------------------------------- 1 | .posIpt { 2 | display: flex; 3 | .posItem { 4 | margin-right: 10px; 5 | span { 6 | margin-right: 3px; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/BasicShop/MediaComponents/schema.ts: -------------------------------------------------------------------------------- 1 | import Video from './Video/schema'; 2 | import Audio from './Audio/schema'; 3 | 4 | const mediaSchema = { 5 | Video, 6 | Audio 7 | }; 8 | export default mediaSchema; 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 80, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { "parser": "json" } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Carousel/index.less: -------------------------------------------------------------------------------- 1 | .carousel__item__pic { 2 | display: inline-block; 3 | width: 100%; 4 | max-height: 100%; 5 | overflow: hidden; 6 | vertical-align: top; 7 | img { 8 | width: 100%; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/components/PanelComponents/MutiText/index.less: -------------------------------------------------------------------------------- 1 | .mutiText { 2 | .iptWrap { 3 | margin-bottom: 12px; 4 | display: flex; 5 | .delBtn { 6 | font-size: 18px; 7 | margin-left: 12px; 8 | cursor: pointer; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Area/index.less: -------------------------------------------------------------------------------- 1 | .chartWrap { 2 | position: relative; 3 | width: 100%; 4 | .chartTitle { 5 | text-align: center; 6 | } 7 | img { 8 | width: 100%; 9 | } 10 | canvas { 11 | width: 100%; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Chart/index.less: -------------------------------------------------------------------------------- 1 | .chartWrap { 2 | position: relative; 3 | width: 100%; 4 | .chartTitle { 5 | text-align: center; 6 | } 7 | img { 8 | width: 100%; 9 | } 10 | canvas { 11 | width: 100%; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Funnel/index.less: -------------------------------------------------------------------------------- 1 | .chartWrap { 2 | position: relative; 3 | width: 100%; 4 | .chartTitle { 5 | text-align: center; 6 | } 7 | img { 8 | width: 100%; 9 | } 10 | canvas { 11 | width: 100%; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Line/index.less: -------------------------------------------------------------------------------- 1 | .chartWrap { 2 | position: relative; 3 | width: 100%; 4 | .chartTitle { 5 | text-align: center; 6 | } 7 | img { 8 | width: 100%; 9 | } 10 | canvas { 11 | width: 100%; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Pie/index.less: -------------------------------------------------------------------------------- 1 | .chartWrap { 2 | position: relative; 3 | width: 100%; 4 | .chartTitle { 5 | text-align: center; 6 | } 7 | img { 8 | width: 100%; 9 | } 10 | canvas { 11 | width: 100%; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Radar/index.less: -------------------------------------------------------------------------------- 1 | .chartWrap { 2 | position: relative; 3 | width: 100%; 4 | .chartTitle { 5 | text-align: center; 6 | } 7 | img { 8 | width: 100%; 9 | } 10 | canvas { 11 | width: 100%; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/ModalTpl/cate.js: -------------------------------------------------------------------------------- 1 | const cateTpls = [ 2 | "教育/培训", 3 | "餐饮美食", 4 | "电商", 5 | "美容美妆", 6 | "金融理财", 7 | "IT互联", 8 | "医疗医药", 9 | "酒店旅游", 10 | "运动健身", 11 | "地产家居", 12 | "工业制造", 13 | "其他", 14 | ] 15 | 16 | export default cateTpls -------------------------------------------------------------------------------- /src/components/Calibration/index.less: -------------------------------------------------------------------------------- 1 | .calibration { 2 | width: calc(200% - 50px); 3 | height: 200%; 4 | position: relative; 5 | white-space: nowrap; 6 | pointer-events: none; 7 | user-select: none; 8 | :global(.calibrationNumber) { 9 | font-size: 12px; 10 | color: #888; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Form/index.less: -------------------------------------------------------------------------------- 1 | .formWrap { 2 | margin: 10px; 3 | padding: 20px 16px; 4 | border-radius: 6px; 5 | background-color: #fff; 6 | box-shadow: 0 2px 6px #f0f0f0; 7 | .title { 8 | padding-bottom: 20px; 9 | text-align: center; 10 | font-size: 18px; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /src/components/BasicShop/MediaComponents/template.ts: -------------------------------------------------------------------------------- 1 | import Video from './Video/template'; 2 | import Audio from './Audio/template'; 3 | 4 | const mediaTemplate = [Video, Audio]; 5 | 6 | const MediaTemplate = mediaTemplate.map(v => { 7 | return { ...v, category: 'media' }; 8 | }); 9 | 10 | export default MediaTemplate; 11 | -------------------------------------------------------------------------------- /src/components/BasicShop/schema.ts: -------------------------------------------------------------------------------- 1 | import BasicSchema from './BasicComponents/schema'; 2 | import MediaSchema from './MediaComponents/schema'; 3 | import VisualSchema from './VisualComponents/schema'; 4 | 5 | const schema = { 6 | ...BasicSchema, 7 | ...MediaSchema, 8 | ...VisualSchema, 9 | }; 10 | 11 | export default schema; 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /npm-debug.log* 6 | /yarn-error.log 7 | /yarn.lock 8 | /package-lock.json 9 | 10 | # production 11 | /dist 12 | 13 | # misc 14 | .DS_Store 15 | 16 | # umi 17 | /src/.umi 18 | /src/.umi-production 19 | /src/.umi-test 20 | /.env.local 21 | -------------------------------------------------------------------------------- /src/pages/mobileTip.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Result } from 'antd'; 3 | 4 | function MobileTip(props) { 5 | return ( 6 |
7 | 12 |
13 | ); 14 | } 15 | 16 | export default MobileTip; 17 | -------------------------------------------------------------------------------- /src/components/PanelComponents/CardPicker/index.less: -------------------------------------------------------------------------------- 1 | .pickerWrap { 2 | display: flex; 3 | flex-wrap: wrap; 4 | .picker { 5 | display: inline-block; 6 | padding: 10px; 7 | border: 2px solid transparent; 8 | cursor: pointer; 9 | &:hover { 10 | border-color: #4091f7; 11 | } 12 | &.selected { 13 | border-color: #4091f7; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/List/index.less: -------------------------------------------------------------------------------- 1 | .list { 2 | margin: 20px auto; 3 | width: 94%; 4 | .sourceList { 5 | .sourceItem { 6 | display: flex; 7 | align-items: center; 8 | margin-bottom: 16px; 9 | .imgWrap { 10 | } 11 | .content { 12 | margin-left: 12px; 13 | .tit { 14 | line-height: 2; 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css'; 2 | declare module '*.less'; 3 | declare module '*.png'; 4 | declare module '*.mp3'; 5 | declare module '*.svg' { 6 | export function ReactComponent(props: React.SVGProps): React.ReactElement 7 | const url: string 8 | export default url 9 | } 10 | declare module 'dom-to-image' { 11 | const domtoimage:any 12 | export default domtoimage 13 | } 14 | 15 | declare var getFaceUrl:any; 16 | 17 | -------------------------------------------------------------------------------- /src/pages/help/index.less: -------------------------------------------------------------------------------- 1 | .helpWrap { 2 | width: 880px; 3 | height: 100vh; 4 | overflow: auto; 5 | margin: 0 auto; 6 | padding-top: 20px; 7 | background-color: #fff; 8 | h2 { 9 | text-align: center; 10 | } 11 | .helpItem { 12 | border-bottom: 1px dashed #ccc; 13 | margin-bottom: 20px; 14 | h3 { 15 | margin-bottom: 10px; 16 | } 17 | .imgWrap { 18 | img { 19 | width: 100%; 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/components/PanelComponents/InteractionData/index.less: -------------------------------------------------------------------------------- 1 | .selectBox { 2 | display: flex; 3 | align-items: center; 4 | } 5 | .formLabel { 6 | display: inline-block; 7 | width: 100px; 8 | } 9 | .contentBox { 10 | margin-top: 20px; 11 | max-height: 480px; 12 | overflow: auto; 13 | .formItem { 14 | display: flex; 15 | align-items: center; 16 | margin-bottom: 20px; 17 | } 18 | } 19 | .codeWrap { 20 | width: 100%; 21 | } -------------------------------------------------------------------------------- /src/pages/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | import Index from '..'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | 5 | describe('Page: index', () => { 6 | it('Render correctly', () => { 7 | const wrapper = renderer.create(); 8 | expect(wrapper.root.children.length).toBe(1); 9 | const outerLayer = wrapper.root.children[0]; 10 | expect(outerLayer.type).toBe('div'); 11 | expect(outerLayer.children.length).toBe(2); 12 | 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/schema.ts: -------------------------------------------------------------------------------- 1 | import Chart from './Chart/schema'; 2 | import Line from './Line/schema'; 3 | import Pie from './Pie/schema'; 4 | import Area from './Area/schema'; 5 | import Radar from './Radar/schema'; 6 | import Funnel from './Funnel/schema'; 7 | import XProgress from './XProgress/schema'; 8 | 9 | 10 | const visualSchema = { 11 | Chart, 12 | Line, 13 | Pie, 14 | Area, 15 | Radar, 16 | Funnel, 17 | XProgress, 18 | }; 19 | 20 | export default visualSchema; 21 | -------------------------------------------------------------------------------- /src/pages/editor/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { HTML5Backend } from 'react-dnd-html5-backend'; 4 | import { DndProvider } from 'react-dnd'; 5 | 6 | import Container from './Container' 7 | 8 | import styles from './index.less'; 9 | 10 | function BasicLayout(props) { 11 | return ( 12 |
13 | 14 | 15 | 16 |
17 | ); 18 | } 19 | 20 | export default BasicLayout; 21 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Header/index.less: -------------------------------------------------------------------------------- 1 | .header { 2 | box-sizing: content-box; 3 | padding: 3px 12px; 4 | display: flex; 5 | align-items: center; 6 | height: 50px; 7 | background-color: #000; 8 | .logo { 9 | margin-right: 10px; 10 | max-width: 160px; 11 | max-height: 46px; 12 | height: 46px; 13 | overflow: hidden; 14 | img { 15 | height: 100%; 16 | object-fit: contain; 17 | } 18 | } 19 | .title { 20 | font-size: 20px; 21 | color: #fff; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Search/index.less: -------------------------------------------------------------------------------- 1 | .header { 2 | box-sizing: content-box; 3 | padding: 3px 12px; 4 | display: flex; 5 | align-items: center; 6 | height: 50px; 7 | background-color: #000; 8 | .logo { 9 | margin-right: 10px; 10 | max-width: 160px; 11 | max-height: 46px; 12 | height: 46px; 13 | overflow: hidden; 14 | img { 15 | height: 100%; 16 | object-fit: contain; 17 | } 18 | } 19 | .title { 20 | font-size: 20px; 21 | color: #fff; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/LoadingCp/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => ( 4 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ); 21 | -------------------------------------------------------------------------------- /src/layouts/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | import BasicLayout from '..'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | describe('Layout: BasicLayout', () => { 5 | it('Render correctly', () => { 6 | const wrapper = renderer.create(); 7 | expect(wrapper.root.children.length).toBe(1); 8 | const outerLayer = wrapper.root.children[0]; 9 | expect(outerLayer.type).toBe('div'); 10 | const title = outerLayer.children[0]; 11 | expect(title.type).toBe('h1'); 12 | expect(title.children[0]).toBe('Yay! Welcome to umi!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/components/PanelComponents/Table/index.less: -------------------------------------------------------------------------------- 1 | :global(.editable-cell) { 2 | position: relative; 3 | } 4 | 5 | :global(.editable-cell-value-wrap) { 6 | padding: 5px 12px; 7 | cursor: pointer; 8 | } 9 | 10 | :global(.editable-row) { 11 | &:hover :global(.editable-cell-value-wrap) { 12 | border: 1px solid #d9d9d9; 13 | border-radius: 4px; 14 | padding: 4px 11px; 15 | } 16 | } 17 | 18 | :global([data-theme='dark']) { 19 | :global(.editable-row) { 20 | &:hover { 21 | :global(.editable-cell-value-wrap) { 22 | border: 1px solid #434343; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/template.ts: -------------------------------------------------------------------------------- 1 | import Chart from './Chart/template'; 2 | import Line from './Line/template'; 3 | import Pie from './Pie/template'; 4 | import Area from './Area/template'; 5 | import XProgress from './XProgress/template'; 6 | import Radar from './Radar/template'; 7 | import Funnel from './Funnel/template'; 8 | 9 | const visualTemplate = [ 10 | Chart, 11 | Line, 12 | Pie, 13 | Area, 14 | Radar, 15 | XProgress, 16 | Funnel 17 | ]; 18 | 19 | const VisualTemplate = visualTemplate.map(v => { 20 | return { ...v, category: 'visual' }; 21 | }); 22 | export default VisualTemplate; 23 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Tab/index.less: -------------------------------------------------------------------------------- 1 | .tabWrap { 2 | padding-top: 16px; 3 | padding-bottom: 16px; 4 | .content { 5 | display: flex; 6 | flex-wrap: wrap; 7 | .item { 8 | padding: 20px 20px 0; 9 | width: 50%; 10 | text-align: center; 11 | justify-content: center; 12 | .imgWrap { 13 | display: inline-block; 14 | width: 80%; 15 | img { 16 | border-radius: 6px; 17 | width: 120px; 18 | height: 120px; 19 | object-fit: cover; 20 | } 21 | .title { 22 | line-height: 2.4; 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "importHelpers": true, 7 | "jsx": "react", 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "baseUrl": "./", 11 | "strict": true, 12 | "paths": { 13 | "@/*": ["src/*"], 14 | "@@/*": ["src/.umi/*"], 15 | "components/*": ["src/components/*"], 16 | "utils/*": ["src/utils/*"], 17 | "assets/*": ["src/assets/*"] 18 | }, 19 | "allowSyntheticDefaultImports": true 20 | }, 21 | "include": [ 22 | "mock/**/*", 23 | "src/**/*", 24 | "config/**/*", 25 | ".umirc.ts", 26 | "typings.d.ts" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Zan/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { Button, Popover } from 'antd'; 3 | import styles from './index.less'; 4 | 5 | ///这组件写的有问题 popover会重定位 6 | const content = ( 7 |
8 | sponsorship 9 |
10 | ); 11 | 12 | export default memo(function ZanPao() { 13 | return ( 14 |
15 | 16 | 19 | 20 |
21 | ); 22 | }); 23 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Notice/index.tsx: -------------------------------------------------------------------------------- 1 | import { NoticeBar } from 'zarm'; 2 | import React, { memo } from 'react'; 3 | import { INoticeConfig } from './schema'; 4 | import logo from '@/assets/09-通知.png'; 5 | interface NoticeType extends INoticeConfig { 6 | isTpl?: boolean; 7 | } 8 | const Notice = memo((props:NoticeType) => { 9 | const { text, speed, theme, isTpl, isClose = false } = props; 10 | return isTpl ? ( 11 |
12 | 13 |
14 | ) : ( 15 | 16 | {text} 17 | 18 | ); 19 | }); 20 | 21 | export default Notice; 22 | -------------------------------------------------------------------------------- /src/app.tsx: -------------------------------------------------------------------------------- 1 | import { createLogger } from 'redux-logger'; 2 | import { message } from 'antd'; 3 | import undoable, { StateWithHistory } from 'redux-undo'; 4 | import { Reducer, AnyAction } from 'redux'; 5 | 6 | export const dva = { 7 | config: { 8 | onAction: createLogger(), 9 | onError(e: Error) { 10 | message.error(e.message, 3); 11 | }, 12 | onReducer: (reducer: Reducer) => { 13 | let undoReducer = undoable(reducer); 14 | return function(state: StateWithHistory, action: AnyAction) { 15 | let newState = undoReducer(state, action); 16 | let router = newState.present.router ? newState.present.router : newState.present.routing; 17 | return { ...newState, router: router }; 18 | }; 19 | }, 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Form/baseForm.less: -------------------------------------------------------------------------------- 1 | .radioWrap { 2 | display: flex; 3 | align-items: flex-start; 4 | padding-bottom: 16px; 5 | .radioTitle { 6 | flex-shrink: 0; 7 | padding-right: 16px; 8 | display: inline-block; 9 | min-width: 60px; 10 | width: 100px; 11 | max-width: 100px; 12 | overflow: hidden; 13 | white-space: nowrap; 14 | text-overflow: ellipsis; 15 | text-align: right; 16 | } 17 | } 18 | 19 | .formItem { 20 | display: flex; 21 | align-items: center; 22 | padding-bottom: 16px; 23 | .formLabel { 24 | padding-right: 16px; 25 | flex-shrink: 0; 26 | min-width: 60px; 27 | width: 100px; 28 | max-width: 100px; 29 | overflow: hidden; 30 | white-space: nowrap; 31 | text-overflow: ellipsis; 32 | text-align: right; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Qrcode/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { IQrcodeConfig } from './schema'; 3 | import logo from '@/assets/10-二维码.png'; 4 | 5 | interface QrcodeType extends IQrcodeConfig { 6 | isTpl?: boolean; 7 | } 8 | 9 | const Qrcode = memo((props: QrcodeType) => { 10 | const { qrcode, text, color, isTpl, fontSize = 14 } = props; 11 | return isTpl ? ( 12 |
13 | 14 |
15 | ) : ( 16 |
17 | {text} 18 |
{text}
19 |
20 | ); 21 | }); 22 | 23 | export default Qrcode; 24 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { IFooterConfig } from './schema'; 3 | import logo from '@/assets/02-页脚.png'; 4 | 5 | interface FooterPropTypes extends IFooterConfig { 6 | isTpl: boolean; 7 | } 8 | 9 | const Footer = memo((props: FooterPropTypes) => { 10 | const { bgColor, text, color, align, fontSize, height } = props; 11 | return props.isTpl ? ( 12 |
13 | 14 |
15 | ) : ( 16 |
26 | {text} 27 |
28 | ); 29 | }); 30 | 31 | export default Footer; 32 | -------------------------------------------------------------------------------- /src/pages/editor/components/CanvasControl/index.less: -------------------------------------------------------------------------------- 1 | .sliderWrap { 2 | position: absolute; 3 | display: inline-block; 4 | right: 46px; 5 | bottom: 100px; 6 | height: 32px; 7 | line-height: 30px; 8 | background: #ffffff; 9 | box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.1); 10 | border-radius: 4px; 11 | color: #4a4a4a; 12 | .slider { 13 | position: absolute; 14 | right: 30px; 15 | bottom: 95px; 16 | font-size: 14px; 17 | } 18 | .sliderBtn { 19 | cursor: pointer; 20 | user-select: none; 21 | display: inline; 22 | font-size: 14px; 23 | color: #4a4a4a; 24 | } 25 | span { 26 | margin: 0 8px; 27 | } 28 | .backSize { 29 | cursor: pointer; 30 | color: #4a4a4a; 31 | &::before { 32 | content: '|'; 33 | color: #dbdbdb; 34 | margin-right: 7px; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/WhiteTpl/index.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import styles from './index.less'; 3 | import React from 'react'; 4 | import { IWhiteTplConfig } from './schema'; 5 | import logo from '@/assets/13-空白.png'; 6 | 7 | interface IProps extends IWhiteTplConfig { 8 | isTpl: boolean; 9 | } 10 | 11 | const WhiteTpl = memo((props: IProps) => { 12 | const { bgColor, text, fontSize, color, height, isTpl } = props; 13 | 14 | return isTpl ? ( 15 |
16 | 17 |
18 | ) : ( 19 |
20 |
21 | { isTpl ? '空白模版' : text } 22 |
23 |
24 | ); 25 | }); 26 | 27 | export default WhiteTpl; 28 | -------------------------------------------------------------------------------- /src/components/BasicShop/MediaComponents/Audio/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ITextConfigType, 3 | INumberConfigType, 4 | TTextDefaultType, 5 | TNumberDefaultType, 6 | } from '@/components/PanelComponents/FormEditor/types'; 7 | 8 | export type TAudioEditData = Array; 9 | export interface IAudioConfig { 10 | height: TNumberDefaultType; 11 | url: TTextDefaultType; 12 | } 13 | 14 | export interface IAudioSchema { 15 | editData: TAudioEditData; 16 | config: IAudioConfig; 17 | } 18 | 19 | const Audio: IAudioSchema = { 20 | editData: [ 21 | { 22 | key: 'height', 23 | name: '音频高度', 24 | type: 'Number', 25 | }, 26 | { 27 | key: 'url', 28 | name: '音频链接', 29 | type: 'Text', 30 | }, 31 | ], 32 | config: { 33 | height: 32, 34 | url: 'http://h5.dooring.cn/audio.mp3', 35 | }, 36 | }; 37 | 38 | export default Audio; 39 | -------------------------------------------------------------------------------- /src/components/ErrorBundaries/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { ErrorInfo, PropsWithChildren } from 'react'; 2 | 3 | interface ErrorBoundaryState { 4 | hasError: boolean; 5 | } 6 | 7 | class ErrorBoundary extends React.Component, ErrorBoundaryState> { 8 | constructor(props: PropsWithChildren<{}>) { 9 | super(props); 10 | this.state = { hasError: false }; 11 | } 12 | 13 | componentDidCatch(_error: Error, _info: ErrorInfo) { 14 | // Display fallback UI 15 | this.setState({ hasError: true }); 16 | // You can also log the error to an error reporting service 17 | //logErrorToMyService(error, info); 18 | } 19 | 20 | render() { 21 | if (this.state.hasError) { 22 | // You can render any custom fallback UI 23 | return

Something went wrong.

; 24 | } 25 | return this.props.children; 26 | } 27 | } 28 | 29 | export default ErrorBoundary; 30 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/XProgress/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { Progress } from 'zarm'; 3 | import styles from './index.less'; 4 | import { IXProgressConfig } from './schema'; 5 | import logo from '@/assets/15-进度.png'; 6 | 7 | interface IProps extends IXProgressConfig { 8 | isTpl: boolean; 9 | } 10 | 11 | const XProgress = memo((props: IProps) => { 12 | const { theme, size, shape, percent, strokeWidth, isTpl } = props; 13 | return isTpl ? ( 14 |
15 | 16 |
17 | ) : ( 18 |
19 | 26 |
27 | ); 28 | }); 29 | 30 | export default XProgress; 31 | -------------------------------------------------------------------------------- /src/components/PanelComponents/DataList/index.less: -------------------------------------------------------------------------------- 1 | .dataList { 2 | padding: 6px 10px; 3 | border: 1px solid #f0f0f0; 4 | } 5 | .listItem { 6 | position: relative; 7 | padding-bottom: 6px; 8 | margin-bottom: 6px; 9 | border-bottom: 1px solid #f0f0f0; 10 | &:hover { 11 | .actionBar { 12 | display: block; 13 | } 14 | } 15 | &:last-child { 16 | border-bottom: none; 17 | margin-bottom: 0; 18 | } 19 | .tit { 20 | font-weight: bold; 21 | } 22 | .desc { 23 | font-size: 12px; 24 | line-height: 2.4; 25 | color: #ccc; 26 | } 27 | .actionBar { 28 | position: absolute; 29 | right: 0; 30 | top: 50%; 31 | transform: translateY(-50%); 32 | display: none; 33 | background: #fff; 34 | box-shadow: -20px 0 10px 10px #fff; 35 | .action { 36 | margin-right: 18px; 37 | cursor: pointer; 38 | &:last-child { 39 | cursor: move; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import styles from './index.less'; 3 | import React from 'react'; 4 | import logos from '@/assets/04-页头.png'; 5 | import { IHeaderConfig } from './schema'; 6 | 7 | interface HeaderPropTypes extends IHeaderConfig { 8 | isTpl: boolean; 9 | } 10 | 11 | const Header = memo((props: HeaderPropTypes) => { 12 | const { bgColor, logo, logoText, fontSize, color } = props; 13 | return props.isTpl ? ( 14 |
15 | 16 |
17 | ) : ( 18 |
19 |
20 | {logoText} 21 |
22 |
23 | {logoText} 24 |
25 |
26 | ); 27 | }); 28 | 29 | export default Header; 30 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Divider/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import styles from './index.less'; 3 | import { Divider } from 'antd'; 4 | import { ITextConfig } from './schema'; 5 | import logo from '@/assets/drivide.png'; 6 | 7 | interface TextType extends ITextConfig { 8 | isTpl?: boolean; 9 | } 10 | 11 | const Text = memo((props: TextType) => { 12 | const { 13 | text, 14 | color, 15 | dashed, 16 | orientation, 17 | type, 18 | isTpl 19 | } = props; 20 | 21 | return isTpl ? ( 22 |
23 | 24 |
25 | ) : ( 26 |
29 | 30 | { 31 | !!text && { text } 32 | } 33 | 34 |
35 | ); 36 | }); 37 | export default Text; 38 | -------------------------------------------------------------------------------- /src/pages/document.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | H5-Dooring | 一款强大的H5编辑器 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Icon/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import * as Icon from '@ant-design/icons'; 3 | import IconImg from 'assets/icon.png'; 4 | import { AntdIconProps } from '@ant-design/icons/lib/components/AntdIcon'; 5 | import { AntdIconType } from './icon'; 6 | import { IIconConfig } from './schema'; 7 | import logo from '@/assets/05-图标.png'; 8 | 9 | interface IconType extends IIconConfig { 10 | isTpl?: boolean; 11 | } 12 | const XIcon = memo((props: IconType) => { 13 | const { color, size, type, spin, isTpl } = props; 14 | 15 | const MyIcon: React.ForwardRefExoticComponent & 16 | React.RefAttributes> = Icon[type]; 17 | 18 | return isTpl ? ( 19 |
20 | 21 |
22 | ) : ( 23 | 24 | ); 25 | }); 26 | 27 | export default XIcon; 28 | -------------------------------------------------------------------------------- /src/components/BasicShop/MediaComponents/Audio/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import ReactAudioPlayer from 'react-audio-player'; 3 | import styles from './index.less'; 4 | import { IAudioConfig } from './schema'; 5 | import logo from '@/assets/music@2x.png'; 6 | 7 | const AudioPlayer = memo((props: IAudioConfig & { isTpl: boolean }) => { 8 | const { height, url, isTpl } = props; 9 | return ( 10 | <> 11 | {isTpl ? ( 12 |
13 | h5-dooring音频播放组件 14 |
15 | ) : ( 16 |
17 | 26 |
27 | )} 28 | 29 | ); 30 | }); 31 | 32 | export default AudioPlayer; 33 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Search/index.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import styles from './index.less'; 3 | import React from 'react'; 4 | import { Input } from 'antd'; 5 | import logo from '@/assets/search.png'; 6 | import { IHeaderConfig } from './schema'; 7 | 8 | const { Search } = Input; 9 | 10 | interface HeaderPropTypes extends IHeaderConfig { 11 | isTpl: boolean; 12 | } 13 | 14 | const Header = memo((props: HeaderPropTypes) => { 15 | const { placeholder, enterButton, size } = props; 16 | return props.isTpl ? ( 17 |
18 | 19 |
20 | ) : ( 21 |
22 | {}} 26 | style={{ width: '100%' }} 27 | enterButton={!!enterButton} 28 | size={size} 29 | /> 30 |
31 | ); 32 | }); 33 | 34 | export default Header; 35 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/LongText/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import styles from './index.less'; 3 | import { ILongTextConfig } from './schema'; 4 | import logo from '@/assets/08-长文本.png'; 5 | 6 | interface LongTextType extends ILongTextConfig { 7 | isTpl?: boolean; 8 | } 9 | 10 | const LongText = memo((props: LongTextType) => { 11 | const { text, fontSize, color, indent, lineHeight, textAlign, bgColor, padding, radius } = props; 12 | return props.isTpl ? ( 13 |
14 | 15 |
16 | ) : ( 17 |
30 | {text} 31 |
32 | ); 33 | }); 34 | export default LongText; 35 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/XButton/Modal.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, ReactNode } from 'react' 2 | import ReactDOM from 'react-dom' 3 | import styles from './index.less' 4 | 5 | interface IProps { 6 | visible: boolean; 7 | title: string; 8 | onClose: () => void; 9 | children: ReactNode; 10 | } 11 | 12 | const Modal = (props: IProps) => { 13 | const { visible, title, children, onClose } = props; 14 | 15 | const childDom =
16 |
17 |
18 | x 19 |
{ title }
20 |
{ children }
21 |
22 |
; 23 | 24 | return visible ? ReactDOM.createPortal(childDom, document.body) : null 25 | } 26 | 27 | export default memo(Modal) -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/schema.ts: -------------------------------------------------------------------------------- 1 | import Carousel from './Carousel/schema'; 2 | import Footer from './Footer/schema'; 3 | import Form from './Form/schema'; 4 | import Header from './Header/schema'; 5 | import WhiteTpl from './WhiteTpl/schema'; 6 | import Icon from './Icon/schema'; 7 | import Image from './Image/schema'; 8 | import List from './List/schema'; 9 | import LongText from './LongText/schema'; 10 | import Notice from './Notice/schema'; 11 | import Qrcode from './Qrcode/schema'; 12 | import Tab from './Tab/schema'; 13 | import Text from './Text/schema'; 14 | import XButton from './XButton/schema'; 15 | import Card from './Card/schema'; 16 | import Search from './Search/schema'; 17 | import Divider from './Divider/schema'; 18 | 19 | const basicSchema = { 20 | Carousel, 21 | Footer, 22 | Form, 23 | Header, 24 | Icon, 25 | Image, 26 | List, 27 | LongText, 28 | Notice, 29 | Qrcode, 30 | Tab, 31 | Text, 32 | XButton, 33 | WhiteTpl, 34 | Card, 35 | Search, 36 | Divider 37 | }; 38 | export default basicSchema; 39 | -------------------------------------------------------------------------------- /src/components/BackTop/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { BackToTop, Icon } from 'zarm' 3 | 4 | const themeObj = { 5 | simple: { bgColor: '#fff', color: '#999' }, 6 | black: { bgColor: '#000', color: '#fff' }, 7 | danger: { bgColor: '#ff5050', color: '#fff' }, 8 | primary: { bgColor: '#00bc71', color: '#fff' }, 9 | blue: { bgColor: '#06c', color: '#fff' } 10 | } 11 | const BackTop = memo((props) => { 12 | const { 13 | theme = 'simple' 14 | } = props 15 | 16 | return 17 |
29 | 30 |
31 |
32 | }) 33 | 34 | export default BackTop -------------------------------------------------------------------------------- /src/components/ModalTpl/index.less: -------------------------------------------------------------------------------- 1 | .tplWrap { 2 | display: flex; 3 | flex-wrap: wrap; 4 | max-height: 520px; 5 | overflow: auto; 6 | .tpl { 7 | position: relative; 8 | box-sizing: border-box; 9 | width: 210px; 10 | height: 240px; 11 | overflow: hidden; 12 | margin-right: 20px; 13 | margin-bottom: 20px; 14 | border: 1px solid #f0f0f0; 15 | cursor: pointer; 16 | &:nth-child(3n+3) { 17 | margin-right: 0; 18 | } 19 | &:hover { 20 | img { 21 | transform: translateY(-30%); 22 | opacity: .7; 23 | transition: transform 1s; 24 | } 25 | .btn { 26 | display: block; 27 | } 28 | } 29 | img { 30 | width: 100%; 31 | transition: transform 1s; 32 | } 33 | .btn { 34 | position: absolute; 35 | display: none; 36 | left: 50%; 37 | top: 50%; 38 | transform: translate(-50%, -50%); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/components/PanelComponents/Pos/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useState, useEffect } from 'react'; 2 | import { InputNumber } from 'antd'; 3 | import styles from './index.less'; 4 | import { TPosDefaultType, TPosItem } from '../FormEditor/types'; 5 | 6 | type PosProps = { 7 | value?: TPosDefaultType; 8 | onChange?: (v: TPosItem | string) => void; 9 | }; 10 | 11 | export default memo(function Pos(props: PosProps) { 12 | const { value, onChange } = props; 13 | let _this: typeof Pos = Pos; 14 | 15 | const handleChange = (index: number, v:TPosItem | string) => { 16 | let arr:any = value || []; 17 | arr[index] = v; 18 | onChange && onChange(arr); 19 | }; 20 | 21 | return ( 22 |
23 |
24 | x: 25 | 26 |
27 |
28 | y: 29 | 30 |
31 |
32 | ); 33 | }); 34 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/template.ts: -------------------------------------------------------------------------------- 1 | import Carousel from './Carousel/template'; 2 | import Footer from './Footer/template'; 3 | import Form from './Form/template'; 4 | import Header from './Header/template'; 5 | import Icon from './Icon/template'; 6 | import Image from './Image/template'; 7 | import List from './List/template'; 8 | import LongText from './LongText/template'; 9 | import Notice from './Notice/template'; 10 | import Card from './Card/template'; 11 | import Qrcode from './Qrcode/template'; 12 | import Tab from './Tab/template'; 13 | import Text from './Text/template'; 14 | import XButton from './XButton/template'; 15 | import WhiteTpl from './WhiteTpl/template'; 16 | import Search from './Search/template'; 17 | import Divider from './Divider/template'; 18 | 19 | const basicTemplate = [ 20 | Carousel, 21 | Form, 22 | Header, 23 | Footer, 24 | Icon, 25 | Image, 26 | WhiteTpl, 27 | List, 28 | LongText, 29 | Notice, 30 | Card, 31 | Qrcode, 32 | Tab, 33 | Text, 34 | Search, 35 | XButton, 36 | Divider 37 | ]; 38 | const BasicTemplate = basicTemplate.map(v => { 39 | return { ...v, category: 'base' }; 40 | }); 41 | 42 | export default BasicTemplate; 43 | -------------------------------------------------------------------------------- /src/components/PanelComponents/Upload/index.less: -------------------------------------------------------------------------------- 1 | :global(.ant-upload-select-picture-card i) { 2 | color: #999; 3 | font-size: 32px; 4 | } 5 | 6 | :global(.ant-upload-select-picture-card .ant-upload-text) { 7 | margin-top: 8px; 8 | color: #666; 9 | } 10 | 11 | .avatarUploader { 12 | display: inline-block; 13 | } 14 | 15 | .wallBtn { 16 | position: absolute; 17 | left: 140px; 18 | bottom: 56px; 19 | display: inline-block; 20 | color: #2F54EB; 21 | cursor: pointer; 22 | border-bottom: 1px solid #2F54EB; 23 | } 24 | 25 | .imgBox { 26 | display: flex; 27 | flex-wrap: wrap; 28 | max-height: 520px; 29 | overflow: auto; 30 | .imgItem { 31 | position: relative; 32 | margin-right: 16px; 33 | margin-bottom: 16px; 34 | width: 320px; 35 | max-height: 220px; 36 | overflow: hidden; 37 | cursor: pointer; 38 | img { 39 | width: 100%; 40 | } 41 | &:hover, &.seleted { 42 | .iconBtn { 43 | visibility: visible; 44 | } 45 | } 46 | 47 | .iconBtn { 48 | position: absolute; 49 | visibility: hidden; 50 | top: 6px; 51 | right: 10px; 52 | font-size: 18px; 53 | color: rgb(8, 156, 8); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/components/BasicShop/MediaComponents/Video/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { Player, BigPlayButton } from 'video-react'; 3 | import './index.css'; 4 | import { IVideoConfig } from './schema'; 5 | import logo from '@/assets/14-视频.png'; 6 | import { styles } from 'react-contexify/lib/utils/styles'; 7 | 8 | interface VideoTypes extends IVideoConfig { 9 | isTpl: boolean; 10 | } 11 | 12 | const VideoPlayer = memo((props: VideoTypes) => { 13 | const { poster, url, otherCode, isTpl } = props; 14 | return isTpl ? ( 15 |
16 | 17 |
18 | ) :( 19 |
20 | { 21 | !otherCode ? 26 | 27 | : 28 |
29 | 30 |
31 | } 32 |
33 | ); 34 | }); 35 | 36 | export default VideoPlayer; 37 | -------------------------------------------------------------------------------- /src/pages/editor/components/CanvasControl/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { MouseEvent } from 'react'; 2 | import { ExpandOutlined } from '@ant-design/icons'; 3 | import styles from './index.less'; 4 | 5 | interface CanvasControlProps { 6 | scaleNum: number; 7 | handleSlider: Function; 8 | backSize(event: MouseEvent): void; 9 | } 10 | 11 | const CanvasControl = (props: CanvasControlProps) => { 12 | const { scaleNum, handleSlider, backSize } = props; 13 | return ( 14 |
15 | 22 | + 23 | 24 | {scaleNum * 100}% 25 | 30 | - 31 | 32 | 33 | 34 | 35 |
36 | ); 37 | }; 38 | 39 | export default CanvasControl; 40 | -------------------------------------------------------------------------------- /src/components/BasicShop/MediaComponents/Video/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ITextConfigType, 3 | IUploadConfigType, 4 | TTextDefaultType, 5 | TUploadDefaultType, 6 | TTextAreaDefaultType, 7 | ITextAreaConfigType, 8 | } from '@/components/PanelComponents/FormEditor/types'; 9 | 10 | export type TVideoEditData = Array; 11 | export interface IVideoConfig { 12 | poster: TUploadDefaultType; 13 | url: TTextDefaultType; 14 | otherCode: TTextAreaDefaultType; 15 | } 16 | 17 | export interface IVideoSchema { 18 | editData: TVideoEditData; 19 | config: IVideoConfig; 20 | } 21 | 22 | const Video: IVideoSchema = { 23 | editData: [ 24 | { 25 | key: 'poster', 26 | name: '视频封面', 27 | type: 'Upload', 28 | }, 29 | { 30 | key: 'url', 31 | name: '视频链接', 32 | type: 'Text', 33 | }, 34 | { 35 | key: 'otherCode', 36 | name: '第三方代码', 37 | type: 'TextArea', 38 | }, 39 | ], 40 | config: { 41 | poster: [ 42 | { 43 | uid: '001', 44 | name: 'image.png', 45 | status: 'done', 46 | url: 'http://h5.dooring.cn/uploads/1_1740c6fbcd9.png', 47 | }, 48 | ], 49 | url: '', 50 | otherCode: '' 51 | }, 52 | }; 53 | 54 | export default Video; 55 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Carousel/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, PropsWithChildren } from 'react'; 2 | import { Carousel } from 'antd'; 3 | import styles from './index.less'; 4 | import { ICarouselConfig } from './schema'; 5 | import logo from '@/assets/01-轮播.png'; 6 | 7 | interface CarouselTypes extends ICarouselConfig { 8 | isTpl: boolean; 9 | } 10 | 11 | const XCarousel = memo((props: PropsWithChildren) => { 12 | const { dotPosition, autoPlay, isTpl, imgList, tplImg } = props; 13 | const contentRender = () => { 14 | return imgList.map((item, i) => { 15 | return ( 16 |
17 | 0 ? item.imgUrl[0].url : ''} alt="" /> 18 |
19 | ); 20 | }); 21 | }; 22 | return ( 23 |
24 | {isTpl ? ( 25 |
26 | 27 |
28 | ) : ( 29 | { 31 | // console.log(`onChange: ${index}`); 32 | }} 33 | dotPosition={dotPosition} 34 | autoplay={autoPlay} 35 | > 36 | {contentRender()} 37 | 38 | )} 39 |
40 | ); 41 | }); 42 | 43 | export default XCarousel; 44 | -------------------------------------------------------------------------------- /src/pages/editor/components/AvatorGroup/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useMemo } from 'react'; 2 | import { Avatar } from 'antd'; 3 | 4 | interface IProp { 5 | collapsed: boolean; 6 | list?: Array 7 | } 8 | 9 | const listTpl = [ 10 | 'Walker', 11 | 'Rope', 12 | 'Jerryli', 13 | '道霖', 14 | '秋涛', 15 | '王冰', 16 | '萧枫', 17 | '瑞云', 18 | 'Art', 19 | '刘小灰', 20 | 'Louis' 21 | ] 22 | 23 | const generateRandomColor = () => { 24 | return '#'+('00000'+ (Math.random()*0x1000000<<0).toString(16)).substr(-6); 25 | } 26 | 27 | const AvatorGrounp = (props:IProp) => { 28 | const { collapsed } = props; 29 | 30 | const userList = useMemo(() => { 31 | return listTpl.map(item => ({ n: item, color: generateRandomColor() })) 32 | }, []) 33 | 34 | return 35 | { collapsed && 199+} 36 | { 37 | userList.map((item, i) => { 38 | return { item.n } 39 | }) 40 | } 41 | 199+ 42 | 使用 43 | 44 | } 45 | 46 | export default memo(AvatorGrounp) -------------------------------------------------------------------------------- /src/components/BasicShop/common.ts: -------------------------------------------------------------------------------- 1 | import { INumberConfigType, TNumberDefaultType } from '../PanelComponents/FormEditor/types'; 2 | 3 | ///提取所有公用设置,传来时加到这里,约定公用类型 4 | //公用配置需满足条件,所有组件初始值统一,否则不放公用设置 5 | 6 | export interface ICommonBaseType { 7 | baseTop: TNumberDefaultType; 8 | baseLeft: TNumberDefaultType; 9 | baseRadius: TNumberDefaultType; 10 | baseRotate: TNumberDefaultType; 11 | baseScale: TNumberDefaultType; 12 | baseHeight: TNumberDefaultType; 13 | baseWidth: TNumberDefaultType; 14 | isTpl?: boolean; 15 | } 16 | 17 | export const baseConfig: INumberConfigType[] = [ 18 | { 19 | key: 'baseTop', 20 | name: '纵向位移', 21 | type: 'Number', 22 | }, 23 | { 24 | key: 'baseLeft', 25 | name: '横向位移', 26 | type: 'Number', 27 | }, 28 | { 29 | key: 'baseRadius', 30 | name: '圆角', 31 | type: 'Number', 32 | }, 33 | { 34 | key: 'baseRotate', 35 | name: '旋转', 36 | type: 'Number', 37 | }, 38 | { 39 | key: 'baseScale', 40 | name: '缩放', 41 | type: 'Number', 42 | }, 43 | { 44 | key: 'baseHeight', 45 | name: '容器高度%', 46 | type: 'Number', 47 | }, 48 | { 49 | key: 'baseWidth', 50 | name: '容器宽度%', 51 | type: 'Number', 52 | }, 53 | ]; 54 | 55 | export const baseDefault = { 56 | baseTop: 0, 57 | baseLeft: 0, 58 | baseRadius: 0, 59 | baseRotate: 0, 60 | baseScale: 100, 61 | baseHeight: 100, 62 | baseWidth: 100, 63 | }; 64 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Text/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import styles from './index.less'; 3 | import { Tooltip } from 'antd'; 4 | import { ITextConfig } from './schema'; 5 | import logo from '@/assets/12-文本.png'; 6 | 7 | interface TextType extends ITextConfig { 8 | isTpl?: boolean; 9 | } 10 | 11 | const Text = memo((props: TextType) => { 12 | const { 13 | align, 14 | text, 15 | fontSize, 16 | color, 17 | lineHeight, 18 | isTpl, 19 | link, 20 | fontWeight, 21 | bgColor, 22 | padding, 23 | radius, 24 | textTip, 25 | textTipPosition 26 | } = props; 27 | const toLink = () => { 28 | if(link) { 29 | window.location.href = link.indexOf('http') > -1 ? link : `http://${link}`; 30 | } 31 | } 32 | return isTpl ? ( 33 |
34 | 35 |
36 | ) : ( 37 |
49 | 50 | { text } 51 | 52 |
53 | ); 54 | }); 55 | export default Text; 56 | -------------------------------------------------------------------------------- /src/utils/req.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { message } from 'antd' 3 | 4 | const isDev = process.env.NODE_ENV === 'development' 5 | 6 | const instance = axios.create({ 7 | baseURL: isDev ? 'http://192.168.1.3:3000/api/v0' : '你的服务器地址', 8 | timeout: 10000, 9 | withCredentials: true 10 | }); 11 | 12 | // 添加请求拦截器 13 | instance.interceptors.request.use(function (config) { 14 | // 在发送请求之前做些什么 15 | let n = localStorage.getItem('nickname') 16 | config.headers = { 17 | 'x-requested-with': encodeURIComponent(n ? n : '') 18 | } 19 | return config; 20 | }, function (error) { 21 | // 对请求错误做些什么 22 | return Promise.reject(error); 23 | }); 24 | 25 | // 添加响应拦截器 26 | instance.interceptors.response.use(function (response) { 27 | // 对响应数据做点什么 28 | if(response.headers['x-show-msg'] === 'zxzk_msg_200') { 29 | message.success(response.data.msg, 2); 30 | } 31 | return response.data.result; 32 | }, function (error) { 33 | // 对响应错误做点什么 34 | const { response } = error; 35 | if(response.status === 404) { 36 | message.error('请求资源未发现'); 37 | }else if(response.status === 403) { 38 | message.error(response.data.msg, () => { 39 | window.location.href = '/h5_plus/login' 40 | localStorage.clear() 41 | }); 42 | }else { 43 | message.error(response.data.msg); 44 | } 45 | return Promise.reject(error); 46 | }); 47 | 48 | 49 | 50 | export default instance -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/WhiteTpl/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITextConfigType, 5 | TColorDefaultType, 6 | TNumberDefaultType, 7 | TTextDefaultType 8 | } from '@/components/PanelComponents/FormEditor/types'; 9 | 10 | export type TWhiteTplEditData = Array< 11 | IColorConfigType | INumberConfigType | ITextConfigType 12 | >; 13 | export interface IWhiteTplConfig { 14 | bgColor: TColorDefaultType; 15 | text: TTextDefaultType; 16 | fontSize: TNumberDefaultType; 17 | color: TColorDefaultType; 18 | height: TNumberDefaultType; 19 | } 20 | 21 | export interface IWhiteTplSchema { 22 | editData: TWhiteTplEditData; 23 | config: IWhiteTplConfig; 24 | } 25 | 26 | const WhiteTpl: IWhiteTplSchema = { 27 | editData: [ 28 | { 29 | key: 'bgColor', 30 | name: '背景色', 31 | type: 'Color', 32 | }, 33 | { 34 | key: 'height', 35 | name: '高度', 36 | type: 'Number', 37 | }, 38 | { 39 | key: 'text', 40 | name: '文字', 41 | type: 'Text', 42 | }, 43 | { 44 | key: 'color', 45 | name: '文字颜色', 46 | type: 'Color', 47 | }, 48 | { 49 | key: 'fontSize', 50 | name: '文字大小', 51 | type: 'Number', 52 | }, 53 | ], 54 | config: { 55 | bgColor: 'rgba(255,255,255,1)', 56 | text: '', 57 | fontSize: 16, 58 | color: 'rgba(210,210,210,1)', 59 | height: 30, 60 | }, 61 | }; 62 | 63 | export default WhiteTpl; 64 | -------------------------------------------------------------------------------- /src/components/PanelComponents/CardPicker/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, memo } from 'react'; 2 | import classnames from 'classnames'; 3 | import Icon from '../../BasicShop/BasicComponents/Icon'; 4 | import styles from './index.less'; 5 | import React from 'react'; 6 | import { IconTypes } from '@/components/BasicShop/BasicComponents/Icon/schema'; 7 | import { ICardPickerConfigType } from '../FormEditor/types'; 8 | 9 | interface CardPickerType extends Omit, 'type' | 'key' | 'name'> { 10 | onChange?: (v: string) => void; 11 | type: IconTypes; 12 | } 13 | 14 | export default memo((props: CardPickerType) => { 15 | const { type, icons, onChange } = props; 16 | const [selected, setSelected] = useState(type); 17 | 18 | const handlePicker = (v: IconTypes) => { 19 | if (onChange) { 20 | onChange(v); 21 | return; 22 | } 23 | setSelected(v); 24 | }; 25 | 26 | useEffect(() => { 27 | setSelected(type); 28 | }, [type]); 29 | 30 | return ( 31 |
32 | {icons.map((item, i) => { 33 | return ( 34 | handlePicker(item)} 37 | key={i} 38 | > 39 | 40 | 41 | ); 42 | })} 43 |
44 | ); 45 | }); 46 | -------------------------------------------------------------------------------- /src/pages/help/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import A from '@/assets/1.png'; 3 | import B from '@/assets/2.png'; 4 | import C from '@/assets/3.png'; 5 | import D from '@/assets/4.png'; 6 | import E from '@/assets/5.png'; 7 | import F from '@/assets/6.png'; 8 | import styles from './index.less'; 9 | 10 | const Help = () => { 11 | return ( 12 |
13 |

H5-Dooring使用指南

14 |
15 |

1. 首页功能介绍

16 |
H5编辑器, H5制作, H5设计
17 |
18 |
19 |

2. 客服机器人

20 |
H5编辑器, H5制作, H5设计
21 |
22 |
23 |

3. 编辑器页面使用说明

24 |
H5编辑器, H5制作, H5设计
25 |
26 |
27 |

4. 管理后台入口

28 |
H5编辑器, H5制作, H5设计
29 |
30 |
31 |

5. 页面管理系统使用

32 |
H5编辑器, H5制作, H5设计
33 |
34 |
35 |

6. 页面数据分析, 数据收集

36 |
H5编辑器, H5制作, H5设计
37 |
38 |
39 | ); 40 | }; 41 | 42 | export default Help; 43 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa'); 2 | const { resolve } = require('path'); 3 | const staticServer = require('koa-static'); 4 | const koaBody = require('koa-body'); 5 | const cors = require('koa2-cors'); 6 | const fs = require('fs'); 7 | const logger = require('koa-logger'); 8 | 9 | const app = new Koa(); 10 | 11 | app.use(staticServer(resolve(__dirname, './dist'))); 12 | app.use(koaBody()); 13 | app.use(logger()); 14 | 15 | // 设置跨域 16 | app.use( 17 | cors({ 18 | origin: function(ctx) { 19 | if (ctx.url.indexOf('/dooring') > -1) { 20 | return '*'; // 允许来自所有域名请求 21 | } 22 | return ''; 23 | }, 24 | exposeHeaders: ['WWW-Authenticate', 'Server-Authorization', 'x-test-code'], 25 | maxAge: 5, // 该字段可选,用来指定本次预检请求的有效期,单位为秒 26 | credentials: true, 27 | allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], 28 | allowHeaders: [ 29 | 'Content-Type', 30 | 'Authorization', 31 | 'Accept', 32 | 'x-requested-with', 33 | 'Content-Encoding', 34 | ], 35 | }), 36 | ); 37 | 38 | let htmlStr = ''; 39 | 40 | app.use(async (ctx, next) => { 41 | console.log(ctx.url); 42 | if (ctx.url === '/dooring/render') { 43 | htmlStr = ctx.request.body; 44 | ctx.body = 'success'; 45 | } else if (ctx.url.indexOf('/html') === 0) { 46 | ctx.type = 'html'; 47 | ctx.body = htmlStr; 48 | } else if (/^\/pc_plus/g.test(ctx.path)) { 49 | ctx.type = 'html'; 50 | ctx.body = fs.createReadStream(resolve(__dirname, './dist/pc_plus')); 51 | return; 52 | } 53 | }); 54 | 55 | // 启动的端口号 56 | app.listen(3000); 57 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Chart/index.tsx: -------------------------------------------------------------------------------- 1 | import { Chart } from '@antv/f2'; 2 | import React, { memo, useEffect, useRef } from 'react'; 3 | // import { uuid } from 'utils/tool'; 4 | import ChartImg from '@/assets/chart.png'; 5 | 6 | import styles from './index.less'; 7 | import { IChartConfig } from './schema'; 8 | 9 | interface XChartProps extends IChartConfig { 10 | isTpl: boolean; 11 | } 12 | 13 | const XChart = (props: XChartProps) => { 14 | const { isTpl, data, color, size, paddingTop, title } = props; 15 | const chartRef = useRef(null); 16 | useEffect(() => { 17 | if (!isTpl) { 18 | const chart = new Chart({ 19 | el: chartRef.current || undefined, 20 | pixelRatio: window.devicePixelRatio, // 指定分辨率 21 | }); 22 | 23 | // step 2: 处理数据 24 | const dataX = data.map(item => ({ ...item, value: Number(item.value) })); 25 | 26 | // Step 2: 载入数据源 27 | chart.source(dataX); 28 | 29 | // Step 3:创建图形语法,绘制柱状图,由 genre 和 sold 两个属性决定图形位置,genre 映射至 x 轴,sold 映射至 y 轴 30 | chart 31 | .interval() 32 | .position('name*value') 33 | .color('name'); 34 | 35 | // Step 4: 渲染图表 36 | chart.render(); 37 | } 38 | }, [data, isTpl]); 39 | return ( 40 |
41 |
42 | {title} 43 |
44 | {isTpl ? dooring chart : } 45 |
46 | ); 47 | }; 48 | 49 | export default memo(XChart); 50 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Image/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { IImageConfig } from './schema'; 3 | import logo from '@/assets/06-图片组件.png'; 4 | 5 | interface ImageType extends IImageConfig { 6 | isTpl?: boolean; 7 | } 8 | 9 | const Image = memo((props: ImageType) => { 10 | const { 11 | imgUrl, 12 | round = 0, 13 | translate, 14 | align, 15 | titText, 16 | titFontSize, 17 | titColor, 18 | titFontWeight, 19 | subTitText, 20 | subTitFontSize, 21 | subTitColor, 22 | subTitFontWeight 23 | } = props; 24 | return props.isTpl ? ( 25 |
26 | 27 |
28 | ) : ( 29 |
30 |
40 |
{ titText }
41 |
{ subTitText }
42 |
43 | 44 |
45 | ) 46 | }) 47 | 48 | export default Image; 49 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Qrcode/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITextConfigType, 5 | IUploadConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TTextDefaultType, 9 | TUploadDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TQrcodeEditData = Array< 13 | IUploadConfigType | ITextConfigType | IColorConfigType | INumberConfigType 14 | >; 15 | export interface IQrcodeConfig { 16 | qrcode: TUploadDefaultType; 17 | text: TTextDefaultType; 18 | color: TColorDefaultType; 19 | fontSize: TNumberDefaultType; 20 | } 21 | 22 | export interface IQrcodeSchema { 23 | editData: TQrcodeEditData; 24 | config: IQrcodeConfig; 25 | } 26 | 27 | const Qrcode: IQrcodeSchema = { 28 | editData: [ 29 | { 30 | key: 'qrcode', 31 | name: '二维码', 32 | type: 'Upload', 33 | isCrop: true, 34 | cropRate: 1, 35 | }, 36 | { 37 | key: 'text', 38 | name: '文字', 39 | type: 'Text', 40 | }, 41 | { 42 | key: 'color', 43 | name: '文字颜色', 44 | type: 'Color', 45 | }, 46 | { 47 | key: 'fontSize', 48 | name: '文字大小', 49 | type: 'Number', 50 | }, 51 | ], 52 | config: { 53 | qrcode: [ 54 | { 55 | uid: '001', 56 | name: 'image.png', 57 | status: 'done', 58 | url: 'http://h5.dooring.cn/uploads/code_173e1705e0c.png', 59 | }, 60 | ], 61 | text: '二维码', 62 | color: 'rgba(153,153,153,1)', 63 | fontSize: 14, 64 | }, 65 | }; 66 | 67 | export default Qrcode; 68 | -------------------------------------------------------------------------------- /.umirc.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { defineConfig } from 'umi'; 3 | 4 | export default defineConfig({ 5 | dynamicImport: { 6 | loading: '@/components/LoadingCp', 7 | }, 8 | dva: { 9 | immer: true, 10 | }, 11 | antd: {}, 12 | sass: { 13 | implementation: require('node-sass'), 14 | }, 15 | exportStatic: {}, 16 | externals: { 17 | axios: 'window.axios', 18 | }, 19 | scripts: ['http://h5.dooring.cn/cdn/axios.min.js'], 20 | base: '/pc_plus/', 21 | publicPath: '/pc_plus/', 22 | outputPath: './dist/pc_plus', 23 | routes: [ 24 | { 25 | exact: false, 26 | path: '/', 27 | component: '@/layouts/index', 28 | routes: [ 29 | { 30 | path: '/', 31 | redirect: '/editor', 32 | }, 33 | { 34 | path: '/editor', 35 | component: '../pages/editor', 36 | }, 37 | { 38 | path: '/help', 39 | component: '../pages/help', 40 | }, 41 | { 42 | path: '/mobileTip', 43 | component: '../pages/mobileTip', 44 | }, 45 | { 46 | path: '/preview', 47 | component: '../pages/editor/preview', 48 | }, 49 | ], 50 | }, 51 | ], 52 | theme: { 53 | 'primary-color': '#2F54EB', 54 | // "btn-primary-bg": "#2F54EB" 55 | }, 56 | // extraBabelPlugins: [ 57 | // ['import', { libraryName: "zarm", style: true }], 58 | // ], 59 | alias: { 60 | components: path.resolve(__dirname, 'src/components/'), 61 | utils: path.resolve(__dirname, 'src/utils/'), 62 | assets: path.resolve(__dirname, 'src/assets/'), 63 | }, 64 | }); 65 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/XButton/index.less: -------------------------------------------------------------------------------- 1 | .btn { 2 | display: inline-block; 3 | text-align: center; 4 | padding: 4px 20px; 5 | .text { 6 | display: inline-block; 7 | width: 100%; 8 | } 9 | } 10 | 11 | .modalWrap { 12 | position: fixed; 13 | width: 100vw; 14 | height: 100vh; 15 | left: 0; 16 | top: 0; 17 | z-index: 9999; 18 | .modalMask { 19 | position: absolute; 20 | left: 0; 21 | top: 0; 22 | bottom: 0; 23 | right: 0; 24 | background-color: rgba(0,0,0, .7); 25 | } 26 | .modal { 27 | position: absolute; 28 | left: 50%; 29 | transform: translateX(-50%); 30 | top: 120px; 31 | width: 76%; 32 | max-width: 360px; 33 | max-height: 480px; 34 | padding: 20px; 35 | border-radius: 6px; 36 | background-color: #fff; 37 | .closeBtn { 38 | position: absolute; 39 | font-size: 16px; 40 | right: 10px; 41 | top: 6px; 42 | color: #ccc; 43 | cursor: pointer; 44 | } 45 | .modalTit { 46 | padding: 12px 0; 47 | font-weight: bold; 48 | font-size: 16px; 49 | text-align: center; 50 | } 51 | :global(p) { 52 | margin-bottom: 0; 53 | } 54 | :global(img) { 55 | max-width: 100%; 56 | text-align: center; 57 | } 58 | :global(blockquote) { 59 | margin: 0 0 10px; 60 | padding: 12px 20px; 61 | background-color: #f1f2f3; 62 | border-left: 5px solid #ccc; 63 | color: #666; 64 | font-style: italic; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/XButton/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useState } from 'react'; 2 | import Modal from './Modal'; 3 | import styles from './index.less'; 4 | import { IButtonConfig } from './schema'; 5 | import logo from '@/assets/16-botton.png'; 6 | 7 | interface IProps extends IButtonConfig { 8 | isTpl: boolean; 9 | } 10 | 11 | const XButton = memo((props: IProps) => { 12 | const { 13 | isTpl, 14 | bgColor, 15 | round, 16 | marginTop, 17 | text, 18 | width, 19 | fontSize, 20 | color, 21 | interaction 22 | } = props 23 | 24 | const { type, content, title } = interaction; 25 | 26 | const [visible, setVisible] = useState(false); 27 | 28 | const handleClick = () => { 29 | if(type === 'link') { 30 | window.location.href = content; 31 | }else if(type === 'modal') { 32 | setVisible(true); 33 | }else if(type === 'code') { 34 | eval(content); 35 | } 36 | } 37 | 38 | const handleClose = () => { 39 | setVisible(false); 40 | } 41 | 42 | return isTpl ? ( 43 |
44 | 45 |
46 | ) :
47 |
48 | { text } 49 |
50 | 51 |
52 |
53 |
54 | }) 55 | export default XButton; 56 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Chart/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITableConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TTableDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TChartEditData = Array< 13 | ITextConfigType | INumberConfigType | IColorConfigType | ITableConfigType 14 | >; 15 | export interface IChartConfig { 16 | title: TTextDefaultType; 17 | size: TNumberDefaultType; 18 | color: TColorDefaultType; 19 | paddingTop: TNumberDefaultType; 20 | data: TTableDefaultType; 21 | } 22 | 23 | export interface IChartSchema { 24 | editData: TChartEditData; 25 | config: IChartConfig; 26 | } 27 | 28 | const Chart: IChartSchema = { 29 | editData: [ 30 | { 31 | key: 'title', 32 | name: '标题', 33 | type: 'Text', 34 | }, 35 | { 36 | key: 'size', 37 | name: '标题大小', 38 | type: 'Number', 39 | }, 40 | { 41 | key: 'color', 42 | name: '标题颜色', 43 | type: 'Color', 44 | }, 45 | { 46 | key: 'paddingTop', 47 | name: '上边距', 48 | type: 'Number', 49 | }, 50 | { 51 | key: 'data', 52 | name: '数据源', 53 | type: 'Table', 54 | }, 55 | ], 56 | config: { 57 | title: '柱状图', 58 | size: 14, 59 | color: 'rgba(0,0,0,1)', 60 | paddingTop: 10, 61 | data: [ 62 | { 63 | name: 'A', 64 | value: 20, 65 | }, 66 | { 67 | name: 'B', 68 | value: 60, 69 | }, 70 | { 71 | name: 'C', 72 | value: 20, 73 | }, 74 | ], 75 | }, 76 | }; 77 | 78 | export default Chart; 79 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Pie/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITableConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TTableDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TChartEditData = Array< 13 | ITextConfigType | INumberConfigType | IColorConfigType | ITableConfigType 14 | >; 15 | export interface IChartConfig { 16 | title: TTextDefaultType; 17 | size: TNumberDefaultType; 18 | color: TColorDefaultType; 19 | paddingTop: TNumberDefaultType; 20 | data: TTableDefaultType; 21 | } 22 | 23 | export interface IChartSchema { 24 | editData: TChartEditData; 25 | config: IChartConfig; 26 | } 27 | 28 | const Chart: IChartSchema = { 29 | editData: [ 30 | { 31 | key: 'title', 32 | name: '标题', 33 | type: 'Text', 34 | }, 35 | { 36 | key: 'size', 37 | name: '标题大小', 38 | type: 'Number', 39 | }, 40 | { 41 | key: 'color', 42 | name: '标题颜色', 43 | type: 'Color', 44 | }, 45 | { 46 | key: 'paddingTop', 47 | name: '上边距', 48 | type: 'Number', 49 | }, 50 | { 51 | key: 'data', 52 | name: '数据源', 53 | type: 'Table', 54 | }, 55 | ], 56 | config: { 57 | title: '饼图', 58 | size: 14, 59 | color: 'rgba(0,0,0,1)', 60 | paddingTop: 10, 61 | data: [ 62 | { 63 | name: 'A', 64 | value: 20, 65 | }, 66 | { 67 | name: 'B', 68 | value: 60, 69 | }, 70 | { 71 | name: 'C', 72 | value: 20, 73 | }, 74 | { 75 | name: 'D', 76 | value: 80, 77 | }, 78 | ], 79 | }, 80 | }; 81 | 82 | export default Chart; 83 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Area/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITableConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TTableDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TChartEditData = Array< 13 | ITextConfigType | INumberConfigType | IColorConfigType | ITableConfigType 14 | >; 15 | export interface IChartConfig { 16 | title: TTextDefaultType; 17 | size: TNumberDefaultType; 18 | color: TColorDefaultType; 19 | paddingTop: TNumberDefaultType; 20 | data: TTableDefaultType; 21 | } 22 | 23 | export interface IChartSchema { 24 | editData: TChartEditData; 25 | config: IChartConfig; 26 | } 27 | 28 | const Chart: IChartSchema = { 29 | editData: [ 30 | { 31 | key: 'title', 32 | name: '标题', 33 | type: 'Text', 34 | }, 35 | { 36 | key: 'size', 37 | name: '标题大小', 38 | type: 'Number', 39 | }, 40 | { 41 | key: 'color', 42 | name: '标题颜色', 43 | type: 'Color', 44 | }, 45 | { 46 | key: 'paddingTop', 47 | name: '上边距', 48 | type: 'Number', 49 | }, 50 | { 51 | key: 'data', 52 | name: '数据源', 53 | type: 'Table', 54 | }, 55 | ], 56 | config: { 57 | title: '面积图', 58 | size: 14, 59 | color: 'rgba(0,0,0,1)', 60 | paddingTop: 10, 61 | data: [ 62 | { 63 | name: 'A', 64 | value: 20, 65 | }, 66 | { 67 | name: 'B', 68 | value: 60, 69 | }, 70 | { 71 | name: 'C', 72 | value: 20, 73 | }, 74 | { 75 | name: 'D', 76 | value: 80, 77 | }, 78 | ], 79 | }, 80 | }; 81 | 82 | export default Chart; 83 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Line/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITableConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TTableDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TChartEditData = Array< 13 | ITextConfigType | INumberConfigType | IColorConfigType | ITableConfigType 14 | >; 15 | export interface IChartConfig { 16 | title: TTextDefaultType; 17 | size: TNumberDefaultType; 18 | color: TColorDefaultType; 19 | paddingTop: TNumberDefaultType; 20 | data: TTableDefaultType; 21 | } 22 | 23 | export interface IChartSchema { 24 | editData: TChartEditData; 25 | config: IChartConfig; 26 | } 27 | 28 | const Chart: IChartSchema = { 29 | editData: [ 30 | { 31 | key: 'title', 32 | name: '标题', 33 | type: 'Text', 34 | }, 35 | { 36 | key: 'size', 37 | name: '标题大小', 38 | type: 'Number', 39 | }, 40 | { 41 | key: 'color', 42 | name: '标题颜色', 43 | type: 'Color', 44 | }, 45 | { 46 | key: 'paddingTop', 47 | name: '上边距', 48 | type: 'Number', 49 | }, 50 | { 51 | key: 'data', 52 | name: '数据源', 53 | type: 'Table', 54 | }, 55 | ], 56 | config: { 57 | title: '折线图', 58 | size: 14, 59 | color: 'rgba(0,0,0,1)', 60 | paddingTop: 10, 61 | data: [ 62 | { 63 | name: 'A', 64 | value: 20, 65 | }, 66 | { 67 | name: 'B', 68 | value: 60, 69 | }, 70 | { 71 | name: 'C', 72 | value: 20, 73 | }, 74 | { 75 | name: 'D', 76 | value: 80, 77 | }, 78 | ], 79 | }, 80 | }; 81 | 82 | export default Chart; 83 | -------------------------------------------------------------------------------- /src/pages/editor/TargetBox.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo, memo } from 'react'; 2 | import { useDrag } from 'react-dnd'; 3 | import schema from 'components/BasicShop/schema'; 4 | import styles from './index.less'; 5 | 6 | const TargetBox = memo(props => { 7 | const { item } = props; 8 | 9 | const [{ isDragging }, drag] = useDrag({ 10 | item: { 11 | type: item.type, 12 | config: schema[item.type].config, 13 | h: item.h, 14 | editableEl: schema[item.type].editData, 15 | category: item.category, 16 | }, 17 | collect: monitor => ({ 18 | isDragging: monitor.isDragging(), 19 | }), 20 | }); 21 | 22 | const containerStyle = useMemo( 23 | () => ({ 24 | opacity: isDragging ? 0.4 : 1, 25 | cursor: 'move', 26 | height: '140px', 27 | }), 28 | [isDragging], 29 | ); 30 | 31 | return ( 32 |
33 |
34 |
44 | {props.children} 45 |
46 |
55 | {props.item.displayName} 56 |
57 |
58 |
59 | ); 60 | }); 61 | 62 | export default TargetBox; 63 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Funnel/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITableConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TTableDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TChartEditData = Array< 13 | ITextConfigType | INumberConfigType | IColorConfigType | ITableConfigType 14 | >; 15 | export interface IChartConfig { 16 | title: TTextDefaultType; 17 | size: TNumberDefaultType; 18 | color: TColorDefaultType; 19 | paddingTop: TNumberDefaultType; 20 | data: TTableDefaultType; 21 | } 22 | 23 | export interface IChartSchema { 24 | editData: TChartEditData; 25 | config: IChartConfig; 26 | } 27 | 28 | const Chart: IChartSchema = { 29 | editData: [ 30 | { 31 | key: 'title', 32 | name: '标题', 33 | type: 'Text', 34 | }, 35 | { 36 | key: 'size', 37 | name: '标题大小', 38 | type: 'Number', 39 | }, 40 | { 41 | key: 'color', 42 | name: '标题颜色', 43 | type: 'Color', 44 | }, 45 | { 46 | key: 'paddingTop', 47 | name: '上边距', 48 | type: 'Number', 49 | }, 50 | { 51 | key: 'data', 52 | name: '数据源', 53 | type: 'Table', 54 | }, 55 | ], 56 | config: { 57 | title: '漏斗图', 58 | size: 14, 59 | color: 'rgba(0,0,0,1)', 60 | paddingTop: 10, 61 | data: [ 62 | { 63 | name: 'A', 64 | value: 1, 65 | }, 66 | { 67 | name: 'B', 68 | value: 0.6, 69 | }, 70 | { 71 | name: 'C', 72 | value: 0.4, 73 | }, 74 | { 75 | name: 'D', 76 | value: 0.2, 77 | }, 78 | { 79 | name: 'E', 80 | value: 0.1, 81 | } 82 | ], 83 | }, 84 | }; 85 | 86 | export default Chart; 87 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Card/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { ICardConfig } from './schema'; 3 | import logo from '@/assets/card@2x.png'; 4 | 5 | interface CardType extends ICardConfig { 6 | isTpl?: boolean; 7 | } 8 | 9 | const Card = memo((props: CardType) => { 10 | const { 11 | width, 12 | imgUrl, 13 | title, 14 | link, 15 | desc, 16 | titColor, 17 | titFontSize, 18 | descColor, 19 | descFontSize, 20 | bgColor, 21 | bgPadding, 22 | cardRadius, 23 | isTpl 24 | } = props; 25 | const toLink = () => { 26 | if(link && window.location.href.indexOf('editor') < 0) { 27 | window.location.href = link; 28 | } 29 | } 30 | return isTpl ? ( 31 |
32 | 33 |
34 | ) : ( 35 |
47 | {title} 48 |
56 | { title } 57 |
58 |
67 | { desc } 68 |
69 |
70 | ); 71 | }); 72 | 73 | export default Card; 74 | -------------------------------------------------------------------------------- /src/components/DynamicEngine/index.tsx: -------------------------------------------------------------------------------- 1 | import { dynamic } from 'umi'; 2 | import Loading from '../LoadingCp'; 3 | import { useMemo, memo, FC } from 'react'; 4 | import React from 'react'; 5 | // import { AllTemplateType } from './schema'; 6 | 7 | export type componentsType = 'media' | 'base' | 'visible'; 8 | 9 | const DynamicFunc = (type: any, componentsType: componentsType) => 10 | dynamic({ 11 | loader: async function() { 12 | let Component: FC<{ isTpl: boolean }>; 13 | if (componentsType === 'base') { 14 | const { default: Graph } = await import(`@/components/BasicShop/BasicComponents/${type}`); 15 | Component = Graph; 16 | } else if (componentsType === 'media') { 17 | const { default: Graph } = await import(`@/components/BasicShop/MediaComponents/${type}`); 18 | Component = Graph; 19 | } else { 20 | const { default: Graph } = await import(`@/components/BasicShop/VisualComponents/${type}`); 21 | Component = Graph; 22 | } 23 | return (props: DynamicType) => { 24 | const { config, isTpl } = props; 25 | return ; 26 | }; 27 | }, 28 | loading: () => ( 29 |
30 | 31 |
32 | ), 33 | }); 34 | 35 | type DynamicType = { 36 | isTpl: boolean; 37 | config: { [key: string]: any }; 38 | type: any; 39 | componentsType: componentsType; 40 | category: componentsType; 41 | }; 42 | const DynamicEngine = memo((props: DynamicType) => { 43 | const { type, config, category } = props; 44 | const Dynamic = useMemo(() => { 45 | return (DynamicFunc(type, category) as unknown) as FC; 46 | // eslint-disable-next-line react-hooks/exhaustive-deps 47 | }, [type, config]); 48 | 49 | return ; 50 | }); 51 | 52 | export default DynamicEngine; 53 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Radar/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITableConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TTableDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TChartEditData = Array< 13 | ITextConfigType | INumberConfigType | IColorConfigType | ITableConfigType 14 | >; 15 | export interface IChartConfig { 16 | title: TTextDefaultType; 17 | size: TNumberDefaultType; 18 | color: TColorDefaultType; 19 | paddingTop: TNumberDefaultType; 20 | data: TTableDefaultType; 21 | } 22 | 23 | export interface IChartSchema { 24 | editData: TChartEditData; 25 | config: IChartConfig; 26 | } 27 | 28 | const Chart: IChartSchema = { 29 | editData: [ 30 | { 31 | key: 'title', 32 | name: '标题', 33 | type: 'Text', 34 | }, 35 | { 36 | key: 'size', 37 | name: '标题大小', 38 | type: 'Number', 39 | }, 40 | { 41 | key: 'color', 42 | name: '标题颜色', 43 | type: 'Color', 44 | }, 45 | { 46 | key: 'paddingTop', 47 | name: '上边距', 48 | type: 'Number', 49 | }, 50 | { 51 | key: 'data', 52 | name: '数据源', 53 | type: 'Table', 54 | }, 55 | ], 56 | config: { 57 | title: '雷达图', 58 | size: 14, 59 | color: 'rgba(0,0,0,1)', 60 | paddingTop: 10, 61 | data: [ 62 | { 63 | name: 'A', 64 | value: 80, 65 | }, 66 | { 67 | name: 'B', 68 | value: 90, 69 | }, 70 | { 71 | name: 'C', 72 | value: 60, 73 | }, 74 | { 75 | name: 'D', 76 | value: 80, 77 | }, 78 | { 79 | name: 'E', 80 | value: 80, 81 | }, 82 | { 83 | name: 'F', 84 | value: 90, 85 | }, 86 | ], 87 | }, 88 | }; 89 | 90 | export default Chart; 91 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Search/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITextConfigType, 5 | IUploadConfigType, 6 | IRadioConfigType, 7 | TColorDefaultType, 8 | TNumberDefaultType, 9 | TRadioDefaultType, 10 | TTextDefaultType, 11 | TUploadDefaultType, 12 | } from '@/components/PanelComponents/FormEditor/types'; 13 | 14 | export type ButtonKeyType = 0 | 1; 15 | 16 | export type ButtonSizeType = 'large' | 'middle' | 'small'; 17 | 18 | export type THeaderEditData = Array< 19 | IColorConfigType | INumberConfigType | IUploadConfigType | ITextConfigType | IRadioConfigType | IRadioConfigType 20 | >; 21 | export interface IHeaderConfig { 22 | placeholder: TTextDefaultType; 23 | enterButton: TRadioDefaultType; 24 | size: TRadioDefaultType 25 | } 26 | 27 | export interface IHeaderSchema { 28 | editData: THeaderEditData; 29 | config: IHeaderConfig; 30 | } 31 | 32 | const Header: IHeaderSchema = { 33 | editData: [ 34 | { 35 | key: 'placeholder', 36 | name: '搜索提示文字', 37 | type: 'Text', 38 | }, 39 | { 40 | key: 'enterButton', 41 | name: '按钮样式', 42 | type: 'Radio', 43 | range: [ 44 | { 45 | key: 0, 46 | text: '默认', 47 | }, 48 | { 49 | key: 1, 50 | text: '高亮', 51 | } 52 | ], 53 | }, 54 | { 55 | key: 'size', 56 | name: '搜索框大小', 57 | type: 'Radio', 58 | range: [ 59 | { 60 | key: 'large', 61 | text: '大', 62 | }, 63 | { 64 | key: 'middle', 65 | text: '中', 66 | }, 67 | { 68 | key: 'small', 69 | text: '小', 70 | } 71 | ], 72 | }, 73 | ], 74 | config: { 75 | placeholder: '请输入内容', 76 | enterButton: 0, 77 | size: 'middle' 78 | }, 79 | }; 80 | 81 | export default Header; 82 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Notice/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | INumberConfigType, 3 | ISelectConfigType, 4 | ISwitchConfigType, 5 | ITextConfigType, 6 | TNumberDefaultType, 7 | TSelectDefaultType, 8 | TSwitchDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TNoticeSelectKeyType = 'default' | 'warning' | 'primary' | 'success' | 'danger'; 13 | export type TNoticeEditData = Array< 14 | ITextConfigType | INumberConfigType | ISelectConfigType | ISwitchConfigType 15 | >; 16 | export interface INoticeConfig { 17 | text: TTextDefaultType; 18 | speed: TNumberDefaultType; 19 | theme: TSelectDefaultType; 20 | isClose: TSwitchDefaultType; 21 | } 22 | 23 | export interface INoticeSchema { 24 | editData: TNoticeEditData; 25 | config: INoticeConfig; 26 | } 27 | 28 | const Notice: INoticeSchema = { 29 | editData: [ 30 | { 31 | key: 'text', 32 | name: '文本', 33 | type: 'Text', 34 | }, 35 | { 36 | key: 'speed', 37 | name: '滚动速度', 38 | type: 'Number', 39 | }, 40 | { 41 | key: 'theme', 42 | name: '主题', 43 | type: 'Select', 44 | range: [ 45 | { 46 | key: 'default', 47 | text: '默认', 48 | }, 49 | { 50 | key: 'warning', 51 | text: '警告', 52 | }, 53 | { 54 | key: 'primary', 55 | text: '主要', 56 | }, 57 | { 58 | key: 'success', 59 | text: '成功', 60 | }, 61 | { 62 | key: 'danger', 63 | text: '危险', 64 | }, 65 | ], 66 | }, 67 | { 68 | key: 'isClose', 69 | name: '是否可关闭', 70 | type: 'Switch', 71 | }, 72 | ], 73 | config: { 74 | text: '通知栏: 趣谈前端上线啦', 75 | speed: 50, 76 | theme: 'warning', 77 | isClose: false, 78 | }, 79 | }; 80 | 81 | export default Notice; 82 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Footer/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ISelectConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TSelectDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TfooterSelectKeyType = 'left' | 'center' | 'right'; 13 | 14 | export type TFooterEditData = Array< 15 | IColorConfigType | INumberConfigType | ITextConfigType | ISelectConfigType 16 | >; 17 | export interface IFooterConfig { 18 | bgColor: TColorDefaultType; 19 | text: TTextDefaultType; 20 | color: TColorDefaultType; 21 | align: TSelectDefaultType; 22 | fontSize: TNumberDefaultType; 23 | height: TNumberDefaultType; 24 | } 25 | 26 | export interface IFooterSchema { 27 | editData: TFooterEditData; 28 | config: IFooterConfig; 29 | } 30 | 31 | const Footer: IFooterSchema = { 32 | editData: [ 33 | { 34 | key: 'bgColor', 35 | name: '背景色', 36 | type: 'Color', 37 | }, 38 | { 39 | key: 'height', 40 | name: '高度', 41 | type: 'Number', 42 | }, 43 | { 44 | key: 'text', 45 | name: '文字', 46 | type: 'Text', 47 | }, 48 | { 49 | key: 'fontSize', 50 | name: '字体大小', 51 | type: 'Number', 52 | }, 53 | { 54 | key: 'color', 55 | name: '文字颜色', 56 | type: 'Color', 57 | }, 58 | { 59 | key: 'align', 60 | name: '对齐方式', 61 | type: 'Select', 62 | range: [ 63 | { 64 | key: 'left', 65 | text: '左对齐', 66 | }, 67 | { 68 | key: 'center', 69 | text: '居中对齐', 70 | }, 71 | { 72 | key: 'right', 73 | text: '右对齐', 74 | }, 75 | ], 76 | }, 77 | ], 78 | config: { 79 | bgColor: 'rgba(0,0,0,1)', 80 | text: '页脚Footer', 81 | color: 'rgba(255,255,255,1)', 82 | align: 'center', 83 | fontSize: 16, 84 | height: 48, 85 | }, 86 | }; 87 | export default Footer; 88 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Line/index.tsx: -------------------------------------------------------------------------------- 1 | import { Chart } from '@antv/f2'; 2 | import React, { memo, useEffect, useRef } from 'react'; 3 | // import { uuid } from 'utils/tool'; 4 | import LineImg from '@/assets/line.png'; 5 | 6 | import styles from './index.less'; 7 | import { IChartConfig } from './schema'; 8 | 9 | interface XChartProps extends IChartConfig { 10 | isTpl: boolean; 11 | } 12 | 13 | const XLine = (props: XChartProps) => { 14 | const { isTpl, data, color, size, paddingTop, title } = props; 15 | const chartRef = useRef(null); 16 | useEffect(() => { 17 | if (!isTpl) { 18 | const chart = new Chart({ 19 | el: chartRef.current || undefined, 20 | pixelRatio: window.devicePixelRatio, // 指定分辨率 21 | }); 22 | 23 | // step 2: 处理数据 24 | const dataX = data.map(item => ({ ...item, value: Number(item.value) })); 25 | 26 | // Step 2: 载入数据源 27 | chart.source(dataX, { 28 | value: { 29 | tickCount: 5, 30 | min: 0 31 | } 32 | }); 33 | 34 | chart.tooltip({ 35 | showCrosshairs: true, 36 | showItemMarker: false 37 | }); 38 | 39 | chart.axis('name', { 40 | label: function label(text, index, total) { 41 | const textCfg:any = {}; 42 | if (index === 0) { 43 | textCfg.textAlign = 'left'; 44 | } else if (index === total - 1) { 45 | textCfg.textAlign = 'right'; 46 | } 47 | return textCfg; 48 | } 49 | }); 50 | 51 | chart.line().position('name*value'); 52 | chart.point().position('name*value').style({ 53 | stroke: '#fff', 54 | lineWidth: 1 55 | }); 56 | 57 | chart.render(); 58 | } 59 | }, [data, isTpl]); 60 | return ( 61 |
62 |
63 | {title} 64 |
65 | {isTpl ? dooring chart : } 66 |
67 | ); 68 | }; 69 | 70 | export default memo(XLine); 71 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/List/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import styles from './index.less'; 3 | import { IListConfig } from './schema'; 4 | import logo from '@/assets/07-列表.png'; 5 | 6 | interface ListType extends IListConfig { 7 | isTpl?: boolean; 8 | } 9 | 10 | const List = memo((props: ListType) => { 11 | const { round, sourceData, imgSize, fontSize, color, padding } = props; 12 | return props.isTpl ? ( 13 |
14 | 15 |
16 | ) : ( 17 |
18 |
19 | {sourceData.map((item, i) => { 20 | return ( 21 |
22 |
23 | {item.desc} 37 |
38 | 53 |
54 | ); 55 | })} 56 |
57 |
58 | ); 59 | }); 60 | 61 | export default List; 62 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Header/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITextConfigType, 5 | IUploadConfigType, 6 | IRadioConfigType, 7 | TColorDefaultType, 8 | TNumberDefaultType, 9 | TRadioDefaultType, 10 | TTextDefaultType, 11 | TUploadDefaultType, 12 | } from '@/components/PanelComponents/FormEditor/types'; 13 | 14 | export type HeaderFixedKeyType = 0 | 1; 15 | 16 | export type THeaderEditData = Array< 17 | IColorConfigType | INumberConfigType | IUploadConfigType | ITextConfigType | IRadioConfigType 18 | >; 19 | export interface IHeaderConfig { 20 | bgColor: TColorDefaultType; 21 | logo: TUploadDefaultType; 22 | logoText: TTextDefaultType; 23 | fontSize: TNumberDefaultType; 24 | color: TColorDefaultType; 25 | height: TNumberDefaultType; 26 | // fixed: TRadioDefaultType; 27 | } 28 | 29 | export interface IHeaderSchema { 30 | editData: THeaderEditData; 31 | config: IHeaderConfig; 32 | } 33 | 34 | const Header: IHeaderSchema = { 35 | editData: [ 36 | { 37 | key: 'bgColor', 38 | name: '背景色', 39 | type: 'Color', 40 | }, 41 | { 42 | key: 'height', 43 | name: '高度', 44 | type: 'Number', 45 | }, 46 | { 47 | key: 'logo', 48 | name: 'logo', 49 | type: 'Upload', 50 | isCrop: true, 51 | cropRate: 1000 / 618, 52 | }, 53 | { 54 | key: 'logoText', 55 | name: 'logo文字', 56 | type: 'Text', 57 | }, 58 | { 59 | key: 'color', 60 | name: '文字颜色', 61 | type: 'Color', 62 | }, 63 | { 64 | key: 'fontSize', 65 | name: '文字大小', 66 | type: 'Number', 67 | } 68 | ], 69 | config: { 70 | bgColor: 'rgba(47,84,235,1)', 71 | logo: [ 72 | { 73 | uid: '001', 74 | name: 'image.png', 75 | status: 'done', 76 | url: 'http://h5.dooring.cn/uploads/3_1740be8a482.png', 77 | }, 78 | ], 79 | logoText: '页头Header', 80 | fontSize: 20, 81 | color: 'rgba(255,255,255,1)', 82 | height: 50, 83 | // fixed: 0 84 | }, 85 | }; 86 | 87 | export default Header; 88 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/XButton/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITextConfigType, 5 | TColorDefaultType, 6 | TNumberDefaultType, 7 | TTextDefaultType, 8 | IInteractionConfigType, 9 | TInteractionDefaultType 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TButtonEditData = Array< 13 | ITextConfigType | IColorConfigType | INumberConfigType | IInteractionConfigType 14 | >; 15 | 16 | export interface IButtonConfig { 17 | round: TNumberDefaultType; 18 | text: TTextDefaultType; 19 | bgColor: TColorDefaultType; 20 | color: TColorDefaultType; 21 | fontSize: TNumberDefaultType; 22 | width: TNumberDefaultType; 23 | marginTop: TNumberDefaultType; 24 | interaction: TInteractionDefaultType; 25 | } 26 | 27 | export interface IButtonSchema { 28 | editData: TButtonEditData; 29 | config: IButtonConfig; 30 | } 31 | const Button: IButtonSchema = { 32 | editData: [ 33 | { 34 | "key": "bgColor", 35 | "name": "背景色", 36 | "type": "Color" 37 | }, 38 | { 39 | "key": "width", 40 | "name": "宽度", 41 | "type": "Number" 42 | }, 43 | { 44 | "key": "marginTop", 45 | "name": "上边距", 46 | "type": "Number" 47 | }, 48 | { 49 | "key": "round", 50 | "name": "圆角", 51 | "type": "Number" 52 | }, 53 | { 54 | "key": "text", 55 | "name": "按钮文字", 56 | "type": "Text" 57 | }, 58 | { 59 | "key": "color", 60 | "name": "文字颜色", 61 | "type": "Color" 62 | }, 63 | { 64 | "key": "fontSize", 65 | "name": "文字大小", 66 | "type": "Number" 67 | }, 68 | { 69 | "key": "interaction", 70 | "name": "交互", 71 | "type": "InteractionData" 72 | } 73 | ], 74 | config: { 75 | "bgColor": "rgba(22,40,212,1)", 76 | "width": 190, 77 | "marginTop": 0, 78 | "round": 16, 79 | "text": "按钮", 80 | "fontSize": 15, 81 | "color": "rgba(255,255,255,1)", 82 | "interaction": { 83 | type: "link", 84 | title: "", 85 | content: "" 86 | } 87 | }, 88 | }; 89 | export default Button; 90 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Area/index.tsx: -------------------------------------------------------------------------------- 1 | import { Chart } from '@antv/f2'; 2 | import React, { memo, useEffect, useRef } from 'react'; 3 | // import { uuid } from 'utils/tool'; 4 | import AreaImg from '@/assets/area.png'; 5 | 6 | import styles from './index.less'; 7 | import { IChartConfig } from './schema'; 8 | 9 | interface XChartProps extends IChartConfig { 10 | isTpl: boolean; 11 | } 12 | 13 | const XLine = (props: XChartProps) => { 14 | const { isTpl, data, color, size, paddingTop, title } = props; 15 | const chartRef = useRef(null); 16 | useEffect(() => { 17 | if (!isTpl) { 18 | const chart = new Chart({ 19 | el: chartRef.current || undefined, 20 | pixelRatio: window.devicePixelRatio, // 指定分辨率 21 | }); 22 | 23 | // step 2: 处理数据 24 | const dataX = data.map(item => ({ ...item, value: Number(item.value), a: '1' })); 25 | 26 | // Step 2: 载入数据源 27 | chart.source(dataX, { 28 | percent: { 29 | formatter: function formatter(val) { 30 | return val * 100 + '%'; 31 | } 32 | } 33 | }); 34 | 35 | chart.tooltip({ 36 | showCrosshairs: true 37 | }); 38 | 39 | chart.scale({ 40 | name: { 41 | range: [ 0, 1 ] 42 | }, 43 | value: { 44 | tickCount: 5, 45 | min: 0 46 | } 47 | }); 48 | 49 | chart.axis('name', { 50 | label: function label(text, index, total) { 51 | const textCfg:any = {}; 52 | if (index === 0) { 53 | textCfg.textAlign = 'left'; 54 | } else if (index === total - 1) { 55 | textCfg.textAlign = 'right'; 56 | } 57 | return textCfg; 58 | } 59 | }); 60 | 61 | chart.area().position('name*value'); 62 | chart.line().position('name*value'); 63 | chart.render(); 64 | } 65 | }, [data, isTpl]); 66 | return ( 67 |
68 |
69 | {title} 70 |
71 | {isTpl ? dooring chart : } 72 |
73 | ); 74 | }; 75 | 76 | export default memo(XLine); 77 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Funnel/index.tsx: -------------------------------------------------------------------------------- 1 | import { Chart } from '@antv/f2'; 2 | import React, { memo, useEffect, useRef } from 'react'; 3 | // import { uuid } from 'utils/tool'; 4 | import AreaImg from '@/assets/loudou.png'; 5 | 6 | import styles from './index.less'; 7 | import { IChartConfig } from './schema'; 8 | 9 | const intervalLabel = require('@antv/f2/lib/plugin/interval-label'); 10 | // 第二步:注册插件 Tooltip 11 | Chart.plugins.register(intervalLabel); 12 | 13 | interface XRadarProps extends IChartConfig { 14 | isTpl: boolean; 15 | } 16 | 17 | const XRadar = (props: XRadarProps) => { 18 | const { isTpl, data, color, size, paddingTop, title } = props; 19 | const chartRef = useRef(null); 20 | useEffect(() => { 21 | if (!isTpl) { 22 | const chart = new Chart({ 23 | el: chartRef.current || undefined, 24 | pixelRatio: window.devicePixelRatio, // 指定分辨率 25 | padding: [ 60, 80, 25, 50 ] 26 | }); 27 | 28 | // step 2: 处理数据 29 | const dataX = data.map(item => ({ ...item, value: Number(item.value)})); 30 | 31 | // Step 2: 载入数据源 32 | chart.coord('polar'); 33 | chart.source(dataX); 34 | 35 | chart.axis(false); 36 | chart.coord({ 37 | transposed: true 38 | }); 39 | chart.legend(true); 40 | chart.intervalLabel({ 41 | offsetX: 10, 42 | label: (data, color) => { 43 | return { 44 | text: data.name, 45 | fill: color 46 | }; 47 | }, 48 | guide: data => { 49 | return { 50 | text: (data.value * 100).toFixed(0) + '%', 51 | fill: '#fff' 52 | }; 53 | } 54 | }); 55 | 56 | chart.interval() 57 | .position('name*value') 58 | .color('name', [ '#003366', '#0050B3', '#1890FF', '#40A9FF', '#69C0FF', '#BAE7FF']) 59 | .adjust('symmetric') 60 | .shape('pyramid'); 61 | chart.render(); 62 | } 63 | }, [data, isTpl]); 64 | return ( 65 |
66 |
67 | {title} 68 |
69 | {isTpl ? dooring chart : } 70 |
71 | ); 72 | }; 73 | 74 | export default memo(XRadar); 75 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/XProgress/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | INumberConfigType, 3 | IRadioConfigType, 4 | ISelectConfigType, 5 | TNumberDefaultType, 6 | TRadioDefaultType, 7 | TSelectDefaultType, 8 | } from '@/components/PanelComponents/FormEditor/types'; 9 | export type TXProgressSelectKeyType = 'success' | 'warning' | 'danger'; 10 | export type TXProgressRadiotKeyType = 'circle' | 'line' | 'semi-circle'; 11 | export type TXProgressEditData = Array< 12 | | ISelectConfigType 13 | | IRadioConfigType 14 | | INumberConfigType 15 | >; 16 | export interface IXProgressConfig { 17 | theme: TSelectDefaultType; 18 | shape: TRadioDefaultType; 19 | size: TNumberDefaultType; 20 | percent: TNumberDefaultType; 21 | strokeWidth: TNumberDefaultType; 22 | } 23 | 24 | export interface IXProgressSchema { 25 | editData: TXProgressEditData; 26 | config: IXProgressConfig; 27 | } 28 | 29 | const XProgress: IXProgressSchema = { 30 | editData: [ 31 | { 32 | key: 'theme', 33 | name: '主题', 34 | type: 'Select', 35 | range: [ 36 | { 37 | key: 'success', 38 | text: '成功', 39 | }, 40 | { 41 | key: 'warning', 42 | text: '警告', 43 | }, 44 | { 45 | key: 'danger', 46 | text: '危险', 47 | }, 48 | ], 49 | }, 50 | { 51 | key: 'shape', 52 | name: '形状', 53 | type: 'Radio', 54 | range: [ 55 | { 56 | key: 'circle', 57 | text: '圆形', 58 | }, 59 | { 60 | key: 'line', 61 | text: '线形', 62 | }, 63 | { 64 | key: 'semi-circle', 65 | text: '半圆形', 66 | }, 67 | ], 68 | }, 69 | { 70 | key: 'size', 71 | name: '大小', 72 | type: 'Number', 73 | }, 74 | { 75 | key: 'percent', 76 | name: '进度值', 77 | type: 'Number', 78 | range: [0, 100], 79 | }, 80 | { 81 | key: 'strokeWidth', 82 | name: '线条粗细', 83 | type: 'Number', 84 | }, 85 | ], 86 | config: { 87 | theme: 'success', 88 | shape: 'circle', 89 | size: 200, 90 | percent: 30, 91 | strokeWidth: 10, 92 | }, 93 | }; 94 | 95 | export default XProgress; 96 | -------------------------------------------------------------------------------- /src/components/PanelComponents/FormItems/formItems.less: -------------------------------------------------------------------------------- 1 | .formItemWrap { 2 | .formTitle { 3 | width: 56px; 4 | height: 20px; 5 | font-size: 14px; 6 | font-family: PingFangSC-Medium, PingFang SC; 7 | font-weight: bold; 8 | color: #000000; 9 | line-height: 20px; 10 | } 11 | .editForm { 12 | text-align: left; 13 | width: 251px; 14 | .formItem { 15 | position: relative; 16 | padding-left: 2px; 17 | & > div > div { 18 | width: 92%; 19 | } 20 | .common { 21 | position: absolute; 22 | top: 6px; 23 | box-shadow: 0 0 20px #fff; 24 | .operationBtn { 25 | margin-right: 15px; 26 | display: inline-block; 27 | cursor: pointer; 28 | } 29 | } 30 | .deleteWrap { 31 | .common; 32 | left: 0; 33 | } 34 | .editWrap { 35 | .common; 36 | right: -18px; 37 | } 38 | } 39 | .formAddWrap { 40 | font-size: 14px; 41 | font-weight: 400; 42 | color: #4a4a4a; 43 | line-height: 20px; 44 | background-color: #2f54eb; 45 | } 46 | } 47 | .formAddWrap { 48 | .formTpl { 49 | margin-top: 12px; 50 | border-top: 1px dashed #ccc; 51 | padding-top: 16px; 52 | background-color: #4a4a4a; 53 | .formItem { 54 | button, 55 | [type='button'] { 56 | color: #fff; 57 | background-color: #4a4a4a; 58 | border: 1px solid #fff; 59 | border-radius: 4px 0px 0px 0px; 60 | } 61 | position: relative; 62 | border: 1px solid #ccc; 63 | margin-bottom: 2px; 64 | background-color: #4a4a4a; 65 | cursor: pointer; 66 | .disClick { 67 | pointer-events: none; 68 | color: #fff; 69 | } 70 | &:hover { 71 | border-color: #2f54eb; 72 | .addBtn { 73 | display: inline-block; 74 | } 75 | } 76 | .addBtn { 77 | position: absolute; 78 | right: 0; 79 | top: 50%; 80 | transform: translateY(-50%); 81 | display: none; 82 | padding: 3px 6px; 83 | color: #fff; 84 | border-radius: 3px; 85 | background-color: #2f54eb; 86 | cursor: pointer; 87 | } 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Tab/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import { Tabs } from 'zarm'; 3 | import styles from './index.less'; 4 | import { ITabConfig } from './schema'; 5 | import logo from '@/assets/11-切换页.png'; 6 | 7 | interface TabType extends ITabConfig { 8 | isTpl?: boolean; 9 | } 10 | 11 | const { Panel } = Tabs; 12 | 13 | const XTab = (props: TabType) => { 14 | const { tabs = ['分类一', '分类二'], activeColor, color, fontSize, sourceData, isTpl } = props; 15 | 16 | const tabWrapRef = useRef(null); 17 | 18 | useEffect(() => { 19 | if (tabWrapRef.current) { 20 | let res = tabWrapRef.current.querySelector('.za-tabs__line') as HTMLElement; 21 | if (res) { 22 | res.style.backgroundColor = activeColor; 23 | } 24 | } 25 | }, [activeColor]); 26 | 27 | return isTpl ? ( 28 |
29 | 30 |
31 | ) : ( 32 |
33 | { 36 | console.log(i); 37 | }} 38 | > 39 | {tabs.map((item, i) => { 40 | return ( 41 | 42 |
43 | {sourceData 44 | .filter(item => item.type === i) 45 | .map((item, i) => { 46 | return ( 47 | 62 | ); 63 | })} 64 |
65 |
66 | ); 67 | })} 68 |
69 |
70 | ); 71 | }; 72 | 73 | export default XTab; 74 | -------------------------------------------------------------------------------- /src/components/PanelComponents/XEditor/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, memo } from 'react' 2 | import req from '@/utils/req' 3 | import BraftEditor from 'braft-editor' 4 | import 'braft-editor/dist/index.css' 5 | import styles from './index.less' 6 | 7 | const controls = [ 8 | { 9 | key: 'bold', 10 | text: 加粗 11 | }, 12 | 'undo', 'redo', 'emoji', 'list-ul', 'list-ol', 'blockquote', 'text-align', 13 | 'font-size', 'line-height', 'letter-spacing', 'text-color', 14 | 'italic', 'underline', 'link', 'media' 15 | ] 16 | 17 | 18 | export default memo(function XEditor(props:any) { 19 | const [editorState, setEditorState] = useState(BraftEditor.createEditorState()) 20 | 21 | const { 22 | defaultValue = '', 23 | onSave 24 | } = props 25 | 26 | const myUploadFn = (param) => { 27 | const fd = new FormData() 28 | fd.append('file', param.file) 29 | 30 | req.post('/files/upload/free', fd, { 31 | headers: { 32 | 'x-requested-with': localStorage.getItem('user') || '', 33 | 'authorization': localStorage.getItem('token') || '', 34 | 'Content-Type':'multipart/form-data' 35 | }, 36 | onUploadProgress: function (event) { 37 | // 上传进度发生变化时调用param.progress 38 | console.log(event.loaded / event.total * 100) 39 | param.progress(event.loaded / event.total * 100) 40 | }, 41 | }).then(res => { 42 | console.log(res) 43 | // 上传成功后调用param.success并传入上传后的文件地址 44 | param.success({ 45 | url: res.url, 46 | meta: { 47 | id: Date.now(), 48 | title: res.filename, 49 | alt: '趣谈前端' 50 | } 51 | }) 52 | }).catch(err => { 53 | param.error({ 54 | msg: '上传失败.' 55 | }) 56 | }) 57 | } 58 | 59 | const submitContent = () => { 60 | const htmlContent = editorState.toHTML() 61 | onSave && onSave(htmlContent) 62 | } 63 | 64 | const handleEditorChange = (editorState) => { 65 | setEditorState(editorState) 66 | if(onSave) { 67 | const htmlContent = editorState.toHTML() 68 | onSave(htmlContent) 69 | } 70 | } 71 | 72 | useEffect(() => { 73 | const htmlContent = defaultValue 74 | setEditorState(BraftEditor.createEditorState(htmlContent)) 75 | }, []) 76 | return 83 | }) 84 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Radar/index.tsx: -------------------------------------------------------------------------------- 1 | import { Chart } from '@antv/f2'; 2 | import React, { memo, useEffect, useRef } from 'react'; 3 | // import { uuid } from 'utils/tool'; 4 | import AreaImg from '@/assets/leida.png'; 5 | 6 | import styles from './index.less'; 7 | import { IChartConfig } from './schema'; 8 | 9 | interface XRadarProps extends IChartConfig { 10 | isTpl: boolean; 11 | } 12 | 13 | const XRadar = (props: XRadarProps) => { 14 | const { isTpl, data, color, size, paddingTop, title } = props; 15 | const chartRef = useRef(null); 16 | useEffect(() => { 17 | if (!isTpl) { 18 | const chart = new Chart({ 19 | el: chartRef.current || undefined, 20 | pixelRatio: window.devicePixelRatio, // 指定分辨率 21 | }); 22 | 23 | // step 2: 处理数据 24 | const dataX = data.map(item => ({ ...item, value: Number(item.value)})); 25 | 26 | // Step 2: 载入数据源 27 | chart.coord('polar'); 28 | chart.source(dataX, { 29 | score: { 30 | nice: true, 31 | tickCount: 5 32 | } 33 | }); 34 | 35 | chart.axis('name', { 36 | label: function label(text, index, total) { 37 | if (index === total - 1) { 38 | return null; 39 | } 40 | return { 41 | top: true 42 | }; 43 | }, 44 | grid: function grid(text) { 45 | if (text === '120') { 46 | return { 47 | lineDash: null 48 | }; 49 | } 50 | }, 51 | line: { 52 | top: false 53 | } 54 | }); 55 | chart.area().position('name*value') 56 | .animate({ 57 | appear: { 58 | animation: 'groupWaveIn' 59 | } 60 | }); 61 | chart.line().position('name*value') 62 | .animate({ 63 | appear: { 64 | animation: 'groupWaveIn' 65 | } 66 | }); 67 | chart.point().position('name*value') 68 | .style({ 69 | stroke: '#fff', 70 | lineWidth: 1 71 | }) 72 | .animate({ 73 | appear: { 74 | delay: 300 75 | } 76 | }); 77 | chart.render(); 78 | } 79 | }, [data, isTpl]); 80 | return ( 81 |
82 |
83 | {title} 84 |
85 | {isTpl ? dooring chart : } 86 |
87 | ); 88 | }; 89 | 90 | export default memo(XRadar); 91 | -------------------------------------------------------------------------------- /src/components/PanelComponents/MutiText/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useState, useEffect } from 'react'; 2 | import { Input, Button, Popconfirm } from 'antd'; 3 | import { MinusCircleOutlined } from '@ant-design/icons'; 4 | import styles from './index.less'; 5 | import { TMutiTextDefaultType } from '../FormEditor/types'; 6 | 7 | type MultiTextProps = { 8 | onChange?: (v: TMutiTextDefaultType) => void; 9 | value?: TMutiTextDefaultType; 10 | }; 11 | 12 | export default memo(function MutiText(props: MultiTextProps) { 13 | const { value, onChange } = props; 14 | const [valueList, setValueList] = useState(value || []); 15 | const handleAdd = () => { 16 | setValueList(prev => { 17 | return [...prev, '新增项']; 18 | }); 19 | }; 20 | 21 | const handleDel = (index: number) => { 22 | setValueList(prev => { 23 | let newList = prev.filter((_item, i) => i !== index); 24 | onChange && onChange(newList); 25 | return newList; 26 | }); 27 | }; 28 | 29 | const handleChange = (index: number, e: React.ChangeEvent) => { 30 | const { value } = e.target; 31 | setValueList(prev => { 32 | let newList = prev.map((item, i) => (i === index ? value : item)); 33 | onChange && onChange(newList); 34 | return newList; 35 | }); 36 | }; 37 | 38 | useEffect(() => { 39 | window['currentCates'] = valueList; 40 | return () => { 41 | window['currentCates'] = null; 42 | }; 43 | }, [valueList]); 44 | 45 | return ( 46 |
47 | {valueList.length ? ( 48 | valueList.map((item, i) => { 49 | return ( 50 |
51 | handleChange(i, e)} /> 52 | handleDel(i)} 55 | placement="leftTop" 56 | okText="确定" 57 | cancelText="取消" 58 | > 59 | 60 | 61 | 62 | 63 |
64 | ); 65 | }) 66 | ) : ( 67 |
68 | 69 |
70 | )} 71 | {valueList.length < 3 && ( 72 |
73 | 76 |
77 | )} 78 |
79 | ); 80 | }); 81 | -------------------------------------------------------------------------------- /src/components/PanelComponents/Color/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SketchPicker, ColorResult } from 'react-color'; 3 | import { rgba2Obj } from '@/utils/tool'; 4 | // import styles from './index.less' 5 | 6 | export type ColorConfigType = string; 7 | 8 | //value 初始值传来,onchange item给的回调 9 | interface ColorProps { 10 | value?: ColorConfigType; 11 | onChange?: (v: ColorConfigType) => void; 12 | } 13 | 14 | class colorPicker extends React.Component { 15 | state = { 16 | displayColorPicker: false, 17 | color: rgba2Obj(this.props.value), 18 | }; 19 | 20 | handleClick = () => { 21 | this.setState({ displayColorPicker: !this.state.displayColorPicker }); 22 | }; 23 | 24 | handleClose = () => { 25 | this.setState({ displayColorPicker: false }); 26 | }; 27 | 28 | handleChange = (color: ColorResult) => { 29 | this.setState({ color: color.rgb }); 30 | this.props.onChange && 31 | this.props.onChange(`rgba(${color.rgb.r},${color.rgb.g},${color.rgb.b},${color.rgb.a})`); 32 | }; 33 | 34 | render() { 35 | return ( 36 |
37 |
48 |
56 |
57 | {this.state.displayColorPicker ? ( 58 | 59 |
65 | 66 |
67 |
78 | 79 | ) : null} 80 |
81 | ); 82 | } 83 | } 84 | 85 | export default colorPicker; 86 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Carousel/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IDataListConfigType, 3 | IRadioConfigType, 4 | ISwitchConfigType, 5 | TDataListDefaultType, 6 | TRadioDefaultType, 7 | TSwitchDefaultType, 8 | } from '@/components/PanelComponents/FormEditor/types'; 9 | 10 | export type CarouselDirectionKeyType = 'top' | 'bottom' | 'left' | 'right'; 11 | 12 | export type TCarouselEditData = Array< 13 | IRadioConfigType | ISwitchConfigType | IDataListConfigType 14 | >; 15 | export interface ICarouselConfig { 16 | dotPosition: TRadioDefaultType; 17 | autoPlay: TSwitchDefaultType; 18 | imgList: TDataListDefaultType; 19 | tplImg: string; 20 | } 21 | 22 | export interface ICarouselSchema { 23 | editData: TCarouselEditData; 24 | config: ICarouselConfig; 25 | } 26 | 27 | const Carousel: ICarouselSchema = { 28 | editData: [ 29 | { 30 | key: 'dotPosition', 31 | name: '方向', 32 | type: 'Radio', 33 | range: [ 34 | { 35 | key: 'top', 36 | text: '顶部', 37 | }, 38 | { 39 | key: 'left', 40 | text: '左部', 41 | }, 42 | { 43 | key: 'bottom', 44 | text: '底部', 45 | }, 46 | { 47 | key: 'right', 48 | text: '右部', 49 | }, 50 | ], 51 | }, 52 | { 53 | key: 'autoPlay', 54 | name: '是否自动播放', 55 | type: 'Switch', 56 | }, 57 | { 58 | key: 'imgList', 59 | name: '图片列表', 60 | type: 'DataList' 61 | }, 62 | ], 63 | config: { 64 | dotPosition: 'left', 65 | autoPlay: false, 66 | imgList: [ 67 | { 68 | id: '1', 69 | title: '趣谈小课1', 70 | desc: '致力于打造优质小课程', 71 | link: 'xxxxx', 72 | imgUrl: [ 73 | { 74 | uid: '001', 75 | name: 'image.png', 76 | status: 'done', 77 | url: 'http://h5.dooring.cn/uploads/1_1740bd7c3dc.png', 78 | }, 79 | ], 80 | }, 81 | { 82 | id: '2', 83 | title: '趣谈小课1', 84 | desc: '致力于打造优质小课程', 85 | link: 'xxxxx', 86 | imgUrl: [ 87 | { 88 | uid: '001', 89 | name: 'image.png', 90 | status: 'done', 91 | url: 'http://h5.dooring.cn/uploads/2_1740bd8d525.png', 92 | }, 93 | ], 94 | }, 95 | ], 96 | tplImg: 'http://h5.dooring.cn/uploads/carousal_17442e1420f.png', 97 | }, 98 | }; 99 | export default Carousel; 100 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Divider/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ISelectConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TSelectDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TOrientationSelectKeyType = 'left' | 'right' | 'center'; 13 | 14 | export type TTypeSelectKeyType = 'horizontal' | 'vertical'; 15 | 16 | export type TTextEditData = Array< 17 | ITextConfigType | 18 | IColorConfigType | 19 | ISelectConfigType | 20 | ISelectConfigType | 21 | ISelectConfigType 22 | >; 23 | 24 | export type TDashedSelectKeyType = 0 | 1; 25 | 26 | export interface ITextConfig { 27 | text: TTextDefaultType; 28 | color: TColorDefaultType; 29 | dashed: TSelectDefaultType; 30 | orientation: TSelectDefaultType; 31 | type: TSelectDefaultType; 32 | } 33 | 34 | export interface ITextSchema { 35 | editData: TTextEditData; 36 | config: ITextConfig; 37 | } 38 | const Text: ITextSchema = { 39 | editData: [ 40 | { 41 | key: 'text', 42 | name: '文字', 43 | type: 'Text', 44 | }, 45 | { 46 | key: 'color', 47 | name: '标题颜色', 48 | type: 'Color', 49 | }, 50 | { 51 | key: 'dashed', 52 | name: '是否虚线', 53 | type: 'Select', 54 | range: [ 55 | { 56 | key: 0, 57 | text: '否', 58 | }, 59 | { 60 | key: 1, 61 | text: '是', 62 | } 63 | ], 64 | }, 65 | { 66 | key: 'orientation', 67 | name: '分割线标题位置', 68 | type: 'Select', 69 | range: [ 70 | { 71 | key: 'left', 72 | text: '左', 73 | }, 74 | { 75 | key: 'center', 76 | text: '中', 77 | }, 78 | { 79 | key: 'right', 80 | text: '右', 81 | }, 82 | ], 83 | }, 84 | { 85 | key: 'type', 86 | name: '分割线方向', 87 | type: 'Select', 88 | range: [ 89 | { 90 | key: 'horizontal', 91 | text: '水平', 92 | }, 93 | { 94 | key: 'vertical', 95 | text: '垂直', 96 | } 97 | ], 98 | }, 99 | ], 100 | config: { 101 | text: '', 102 | color: 'rgba(60,60,60,1)', 103 | dashed: 0, 104 | orientation: 'center', 105 | type: 'horizontal' 106 | }, 107 | }; 108 | export default Text; 109 | -------------------------------------------------------------------------------- /src/pages/editor/components/Header/index.less: -------------------------------------------------------------------------------- 1 | .header { 2 | position: relative; 3 | z-index: 210; 4 | padding: 10px 30px; 5 | box-sizing: content-box; 6 | display: flex; 7 | align-items: center; 8 | height: 56px; 9 | background: #fff; 10 | box-shadow: 0 2px 4px rgba(0,0,0,0.1); 11 | .logoArea { 12 | width: 300px; 13 | .backBtn { 14 | display: inline-block; 15 | padding: 12px 10px; 16 | margin-right: 22px; 17 | cursor: pointer; 18 | } 19 | .logo { 20 | display: inline-block; 21 | width: 120px; 22 | overflow: hidden; 23 | border-radius: 3px; 24 | vertical-align: middle; 25 | img { 26 | width: 100%; 27 | } 28 | } 29 | } 30 | .controlArea { 31 | flex: 1; 32 | text-align: center; 33 | 34 | } 35 | .btnArea { 36 | width: 250px; 37 | text-align: right; 38 | } 39 | } 40 | 41 | .saveForm { 42 | padding-top: 10px; 43 | .formIpt { 44 | margin-bottom: 22px; 45 | &>span { 46 | display: inline-block; 47 | padding-bottom: 6px; 48 | } 49 | img { 50 | max-height: 252px; 51 | object-fit: contain; 52 | border: 1px solid #ccc; 53 | } 54 | &.imgWrap { 55 | text-align: center; 56 | } 57 | } 58 | } 59 | .userWrap { 60 | display: inline-block; 61 | margin-left: 20px; 62 | margin-right: 20px; 63 | cursor: pointer; 64 | .user { 65 | margin-right: 10px; 66 | display: inline-block; 67 | width: 40px; 68 | height: 40px; 69 | line-height: 40px; 70 | text-align: center; 71 | white-space: nowrap; 72 | overflow: hidden; 73 | font-size: 12px; 74 | color: #fff; 75 | vertical-align: middle; 76 | border-radius: 50%; 77 | background-color: #2F54EB; 78 | } 79 | } 80 | 81 | .userList { 82 | width: 120px; 83 | padding: 10px; 84 | background-color: #fff; 85 | box-shadow: 2px 0 8px rgba(0,0,0,.2); 86 | .userItem { 87 | font-size: 14px; 88 | line-height: 2.4em; 89 | cursor: pointer; 90 | &:hover { 91 | color: #2F54EB; 92 | } 93 | } 94 | } 95 | 96 | .pageConfig { 97 | width: 300px; 98 | .formControl { 99 | position: relative; 100 | padding-bottom: 10px; 101 | display: flex; 102 | .label { 103 | display: inline-block; 104 | flex-shrink: 0; 105 | width: 76px !important; 106 | } 107 | } 108 | } 109 | 110 | .saveWrap { 111 | width: 360px; 112 | .label { 113 | padding-top: 12px; 114 | display: inline-block; 115 | } 116 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > 让PC制作像搭积木一样简单! 2 | 3 |

Welcome to PC-Dooring 👋

4 |

5 | Version 6 | 7 | Documentation 8 | 9 | 10 | license:GPL3.0 11 | 12 |

13 | 14 | ### 🏠 [Homepage](http://h5.dooring.cn/pc_plus/editor?tid=6AC322B0) 15 | 16 | ### 📦 doc(文档) [PC-Dooring Document](http://h5.dooring.cn/doc) 17 | 18 | ### [视频教程 | Video tutorial](https://www.zhihu.com/zvideo/1326300284608417792) 19 | 20 | 21 | 相关产品: 22 | - [V6.Dooring | 大屏可视化编辑器](https://github.com/MrXujiang/v6.dooring.public) 23 | - [H5-Dooring | H5可视化编辑器](https://github.com/MrXujiang/h5-Dooring) 24 | - - [Nocode/WEP | 可视化文档编辑器](https://github.com/MrXujiang/Nocode-Wep) 25 | 26 | 27 | ## Show your support 28 | 29 | Give a ⭐️ if this project helped you! 30 | 31 | 如果本开源项目对您有帮助, 请点个`star`, 支持开源. 32 | 33 | ## Install(安装) 34 | 1. 下载代码 | Download the code 35 | ```sh 36 | git clone git@github.com:MrXujiang/pc-Dooring.git 37 | ``` 38 | 2. 进入项目目录 | Go to the project catalog 39 | ```sh 40 | cd ./pc-Dooring 41 | ``` 42 | 43 | 3. 安装依赖包 | Install the dependency package 44 | ```sh 45 | yarn install 46 | or 47 | cnpm install 48 | ``` 49 | 50 | ## Usage 51 | 52 | 本地启动应用 | Launch the app locally 53 | ```sh 54 | yarn start 55 | or 56 | cnpm run start 57 | ``` 58 | 59 | 如发现本地启动后组件拖拽遇到奇怪的报错, 是应为第三方组件在开发环境的bug, 可以采用一下方式解决: 60 | > If you find that the local start-up component drag encountered strange errors, is a bug that should be a third-party component in the development environment, can be resolved in a way: 61 | 62 | ```sh 63 | yarn dev 64 | or 65 | cnpm run dev 66 | ``` 67 | 前提是先安装http-server模块. 68 | 69 | ## 本地服务器部署 70 | 1. 打包 71 | ```sh 72 | yarn build 73 | ``` 74 | 2. 启动服务 75 | ```sh 76 | node server.js 77 | # 或者pm2(需要先全局安装pm2) 78 | pm2 start server.js 79 | ``` 80 | 3. 访问地址 81 | ```sh 82 | 服务器ip + 3000 (默认为3000, 也可以改成80, 具体参考server.js) 83 | ``` 84 | 85 | 86 | ## 更新日志 | Update the log 87 | 1. 添加数据可视化模块 88 | 2. 添加表单设计器模块 89 | 3. 添加pc端组件库 90 | 91 | 92 | ## 持续升级 | Continuous upgrades 93 | 如果您有更好的想法, 欢迎和我们一起共建, 让国内开源更强大. 94 | 95 | ## 赞助 | Sponsored 96 | 开源不易, 有了您的赞助, 我们会做的更好~ 97 | 98 | 99 | 100 | ## 技术反馈和交流群 | Technical feedback and communication 101 | 微信:beautifulFront 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/LongText/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ISelectConfigType, 5 | ITextAreaConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TSelectDefaultType, 9 | TTextAreaDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | export type TLongTextSelectKeyType = 'left' | 'center' | 'right'; 12 | 13 | export type TLongTextEditData = Array< 14 | | ITextAreaConfigType 15 | | IColorConfigType 16 | | INumberConfigType 17 | | ISelectConfigType 18 | >; 19 | export interface ILongTextConfig { 20 | text: TTextAreaDefaultType; 21 | color: TColorDefaultType; 22 | fontSize: TNumberDefaultType; 23 | indent: TNumberDefaultType; 24 | lineHeight: TNumberDefaultType; 25 | textAlign: TSelectDefaultType; 26 | bgColor: TColorDefaultType; 27 | padding: TNumberDefaultType; 28 | radius: TNumberDefaultType; 29 | } 30 | 31 | export interface ILongTextSchema { 32 | editData: TLongTextEditData; 33 | config: ILongTextConfig; 34 | } 35 | 36 | const LongText: ILongTextSchema = { 37 | editData: [ 38 | { 39 | key: 'text', 40 | name: '文字', 41 | type: 'TextArea', 42 | }, 43 | { 44 | key: 'color', 45 | name: '标题颜色', 46 | type: 'Color', 47 | }, 48 | { 49 | key: 'fontSize', 50 | name: '字体大小', 51 | type: 'Number', 52 | }, 53 | { 54 | key: 'indent', 55 | name: '首行缩进', 56 | type: 'Number', 57 | range: [0, 100], 58 | }, 59 | { 60 | key: 'textAlign', 61 | name: '对齐方式', 62 | type: 'Select', 63 | range: [ 64 | { 65 | key: 'left', 66 | text: '左对齐', 67 | }, 68 | { 69 | key: 'center', 70 | text: '居中对齐', 71 | }, 72 | { 73 | key: 'right', 74 | text: '右对齐', 75 | }, 76 | ], 77 | }, 78 | { 79 | key: 'lineHeight', 80 | name: '行高', 81 | type: 'Number', 82 | step: 0.1, 83 | }, 84 | { 85 | key: 'bgColor', 86 | name: '背景颜色', 87 | type: 'Color', 88 | }, 89 | { 90 | key: 'padding', 91 | name: '填充间距', 92 | type: 'Number', 93 | }, 94 | { 95 | key: 'radius', 96 | name: '背景圆角', 97 | type: 'Number', 98 | }, 99 | ], 100 | config: { 101 | text: '我是长文本有一段故事,dooring可视化编辑器无限可能,赶快来体验吧,骚年们,奥利给~', 102 | color: 'rgba(60,60,60,1)', 103 | fontSize: 14, 104 | indent: 20, 105 | lineHeight: 1.8, 106 | textAlign: 'left', 107 | bgColor: 'rgba(255,255,255,0)', 108 | padding: 0, 109 | radius: 0, 110 | }, 111 | }; 112 | 113 | export default LongText; 114 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "umi dev", 5 | "build": "umi build", 6 | "postinstall": "umi generate tmp", 7 | "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", 8 | "test": "umi-test", 9 | "test:coverage": "umi-test --coverage" 10 | }, 11 | "gitHooks": { 12 | "pre-commit": "lint-staged" 13 | }, 14 | "lint-staged": { 15 | "*.{js,jsx,less,md,json}": [ 16 | "prettier --write" 17 | ], 18 | "*.ts?(x)": [ 19 | "prettier --parser=typescript --write" 20 | ] 21 | }, 22 | "dependencies": { 23 | "@ant-design/icons": "^4.2.1", 24 | "@antv/f2": "^3.7.7", 25 | "@umijs/plugin-sass": "^1.1.1", 26 | "@umijs/preset-react": "1.x", 27 | "@umijs/test": "^3.2.19", 28 | "antd": "^4.2.3", 29 | "antd-img-crop": "^3.10.0", 30 | "axios": "^0.19.2", 31 | "braft-editor": "^2.3.9", 32 | "chatbot-antd": "^0.6.0", 33 | "codemirror": "^5.57.0", 34 | "dom-to-image": "^2.6.0", 35 | "file-saver": "^2.0.2", 36 | "lint-staged": "^10.0.7", 37 | "node-sass": "^4.14.1", 38 | "prettier": "^1.19.1", 39 | "qrcode.react": "^1.0.0", 40 | "react": "^16.12.0", 41 | "react-audio-player": "^0.14.0", 42 | "react-codemirror2": "^7.2.1", 43 | "react-color": "^2.18.1", 44 | "react-contexify": "^4.1.1", 45 | "react-dnd": "^11.1.3", 46 | "react-dnd-html5-backend": "^11.1.3", 47 | "react-dom": "^16.12.0", 48 | "react-draggable": "^4.4.3", 49 | "react-grid-layout": "^1.0.0", 50 | "react-hotkeys-hook": "^2.3.1", 51 | "react-text-loop": "^2.3.0", 52 | "redux-undo": "^1.0.1", 53 | "sass-loader": "^9.0.3", 54 | "socket.io-client": "^2.3.0", 55 | "umi": "^3.2.19", 56 | "video-react": "^0.14.1", 57 | "xlsx": "^0.16.7", 58 | "yh-react-popover": "^0.3.0", 59 | "yorkie": "^2.0.0", 60 | "zarm": "^2.5.1", 61 | "koa": "^2.13.0", 62 | "koa-body": "^4.2.0", 63 | "koa-logger": "^3.2.1", 64 | "koa-static": "^5.0.0", 65 | "koa2-cors": "^2.0.6" 66 | }, 67 | "devDependencies": { 68 | "@types/classnames": "^2.2.10", 69 | "@types/codemirror": "^0.0.98", 70 | "@types/events": "^3.0.0", 71 | "@types/file-saver": "^2.0.1", 72 | "@types/node": "^14.6.2", 73 | "@types/react-color": "^3.0.4", 74 | "@types/react-grid-layout": "^1.1.1", 75 | "@types/redux-logger": "^3.0.8", 76 | "@typescript-eslint/eslint-plugin": "4.1.1", 77 | "@typescript-eslint/parser": "4.1.1", 78 | "babel-eslint": "10.x", 79 | "babel-plugin-import": "^1.13.0", 80 | "eslint": "6.x", 81 | "eslint-config-react-app": "^5.2.1", 82 | "eslint-plugin-flowtype": "4.x", 83 | "eslint-plugin-import": "2.x", 84 | "eslint-plugin-jsx-a11y": "6.x", 85 | "eslint-plugin-react": "7.x", 86 | "eslint-plugin-react-hooks": "2.x", 87 | "redux-logger": "^3.0.6" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Form/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useCallback } from 'react'; 2 | import { Button } from 'antd'; 3 | import BaseForm from './BaseForm'; 4 | import req from 'utils/req'; 5 | import { IFormConfig } from './schema'; 6 | import logo from '@/assets/03-表单.png'; 7 | import styles from './index.less'; 8 | 9 | interface FormPropTypes extends IFormConfig { 10 | isTpl: boolean; 11 | } 12 | 13 | const FormComponent = (props: FormPropTypes) => { 14 | const { title, bgColor, fontSize, titColor, titWeight, btnColor, btnTextColor, api, formControls } = props; 15 | 16 | const formData: Record = {}; 17 | formControls.map(item => { 18 | if(item.type !== 'MyTextTip') { 19 | formData[item.label] = '' 20 | } 21 | }) 22 | 23 | const handleChange = useCallback( 24 | (item, v) => { 25 | if (item.options) { 26 | formData[item.label] = v; 27 | return; 28 | } 29 | formData[item.label] = v; 30 | }, 31 | [formData], 32 | ) 33 | 34 | const handleSubmit = () => { 35 | console.log(formData) 36 | let isPass = Object.values(formData).every(item => !!item); 37 | if(isPass) { 38 | if (api) { 39 | fetch(api, { 40 | body: JSON.stringify(formData), 41 | cache: 'no-cache', 42 | headers: { 43 | 'content-type': 'application/json', 44 | }, 45 | method: 'POST', 46 | mode: 'cors', 47 | }); 48 | }else { 49 | req.post(`/vip/h5/form/post${location.search}`, formData) 50 | } 51 | }else { 52 | alert('请将表单填写完整') 53 | } 54 | } 55 | 56 | return props.isTpl ? ( 57 |
58 | 59 |
60 | ) : ( 61 |
62 | {title && ( 63 |
64 | {title} 65 |
66 | )} 67 |
68 | {formControls.map(item => { 69 | const FormItem = BaseForm[item.type]; 70 | return ; 71 | })} 72 |
73 | 87 |
88 |
89 |
90 | ); 91 | }; 92 | 93 | export default memo(FormComponent); 94 | -------------------------------------------------------------------------------- /src/components/BasicShop/VisualComponents/Pie/index.tsx: -------------------------------------------------------------------------------- 1 | import { Chart } from '@antv/f2'; 2 | import React, { memo, useEffect, useRef } from 'react'; 3 | // import { uuid } from 'utils/tool'; 4 | import PieImg from '@/assets/pie.png'; 5 | 6 | import styles from './index.less'; 7 | import { IChartConfig } from './schema'; 8 | 9 | interface XChartProps extends IChartConfig { 10 | isTpl: boolean; 11 | } 12 | 13 | interface DataMap { 14 | [name:string]: number | string; 15 | } 16 | 17 | const XLine = (props: XChartProps) => { 18 | const { isTpl, data, color, size, paddingTop, title } = props; 19 | const chartRef = useRef(null); 20 | useEffect(() => { 21 | if (!isTpl) { 22 | const chart = new Chart({ 23 | el: chartRef.current || undefined, 24 | pixelRatio: window.devicePixelRatio, // 指定分辨率 25 | }); 26 | 27 | // step 2: 处理数据 28 | const dataX = data.map(item => ({ ...item, value: Number(item.value), a: '1' })); 29 | 30 | // Step 2: 载入数据源 31 | chart.source(dataX, { 32 | percent: { 33 | formatter: function formatter(val) { 34 | return val * 100 + '%'; 35 | } 36 | } 37 | }); 38 | 39 | // 获取数据的map类型,用以展示图例说明 40 | const dataMap:DataMap = dataX.reduce((prev:any, cur) => { 41 | return prev.name ? {[prev.name]: prev.value, ...{[cur.name]: cur.value}} : {...prev, ...{[cur.name]: cur.value}} 42 | }) 43 | 44 | chart.legend({ 45 | position: 'right', 46 | itemFormatter: function itemFormatter(val) { 47 | return val + ' ' + dataMap[val] + '%'; 48 | } 49 | }); 50 | chart.tooltip(false); 51 | chart.coord('polar', { 52 | transposed: true, 53 | radius: 0.85 54 | }); 55 | chart.axis(false); 56 | chart.interval() 57 | .position('a*value') 58 | .color('name', [ 59 | '#1890FF', 60 | '#13C2C2', 61 | '#2FC25B', 62 | '#FACC14', 63 | '#00CC99', 64 | '#CC3366', 65 | '#CC6600', 66 | '#CC66CC', 67 | '#FF3366', 68 | '#0066CC' 69 | ]) 70 | .adjust('stack') 71 | .style({ 72 | lineWidth: 1, 73 | stroke: '#fff', 74 | lineJoin: 'round', 75 | lineCap: 'round' 76 | }) 77 | .animate({ 78 | appear: { 79 | duration: 1200, 80 | easing: 'bounceOut' 81 | } 82 | }); 83 | 84 | chart.render(); 85 | } 86 | }, [data, isTpl]); 87 | return ( 88 |
89 |
90 | {title} 91 |
92 | {isTpl ? dooring chart : } 93 |
94 | ); 95 | }; 96 | 97 | export default memo(XLine); 98 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Tab/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | IDataListConfigType, 4 | IMutiTextConfigType, 5 | INumberConfigType, 6 | TColorDefaultType, 7 | TDataListDefaultType, 8 | TMutiTextDefaultType, 9 | TNumberDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TTabEditData = Array< 13 | IMutiTextConfigType | IColorConfigType | INumberConfigType | IDataListConfigType 14 | >; 15 | export interface ITabConfig { 16 | tabs: TMutiTextDefaultType; 17 | color: TColorDefaultType; 18 | activeColor: TColorDefaultType; 19 | fontSize: TNumberDefaultType; 20 | imgSize: TNumberDefaultType; 21 | sourceData: TDataListDefaultType; 22 | } 23 | 24 | export interface ITabSchema { 25 | editData: TTabEditData; 26 | config: ITabConfig; 27 | } 28 | 29 | const Tab: ITabSchema = { 30 | editData: [ 31 | { 32 | key: 'tabs', 33 | name: '项目类别', 34 | type: 'MutiText', 35 | }, 36 | { 37 | key: 'activeColor', 38 | name: '激活颜色', 39 | type: 'Color', 40 | }, 41 | { 42 | key: 'color', 43 | name: '文字颜色', 44 | type: 'Color', 45 | }, 46 | { 47 | key: 'fontSize', 48 | name: '文字大小', 49 | type: 'Number', 50 | }, 51 | { 52 | key: 'imgSize', 53 | name: '图片大小', 54 | type: 'Number', 55 | }, 56 | { 57 | key: 'sourceData', 58 | name: '数据源', 59 | type: 'DataList', 60 | }, 61 | ], 62 | config: { 63 | tabs: ['类别一', '类别二'], 64 | color: 'rgba(153,153,153,1)', 65 | activeColor: 'rgba(0,102,204,1)', 66 | fontSize: 16, 67 | imgSize: 100, 68 | sourceData: [ 69 | { 70 | id: '1', 71 | title: '趣谈小课1', 72 | desc: '致力于打造优质小课程', 73 | link: 'xxxxx', 74 | type: 0, 75 | imgUrl: [ 76 | { 77 | uid: '001', 78 | name: 'image.png', 79 | status: 'done', 80 | url: 'http://h5.dooring.cn/uploads/1_1740c6fbcd9.png', 81 | }, 82 | ], 83 | }, 84 | { 85 | id: '2', 86 | title: '趣谈小课2', 87 | desc: '致力于打造优质小课程', 88 | link: 'xxxxx', 89 | type: 0, 90 | imgUrl: [ 91 | { 92 | uid: '001', 93 | name: 'image.png', 94 | status: 'done', 95 | url: 'http://h5.dooring.cn/uploads/2_1740c7033a9.png', 96 | }, 97 | ], 98 | }, 99 | { 100 | id: '3', 101 | title: '趣谈小课3', 102 | desc: '致力于打造优质小课程', 103 | link: 'xxxxx', 104 | type: 1, 105 | imgUrl: [ 106 | { 107 | uid: '001', 108 | name: 'image.png', 109 | status: 'done', 110 | url: 'http://h5.dooring.cn/uploads/1_1740c6fbcd9.png', 111 | }, 112 | ], 113 | }, 114 | ], 115 | }, 116 | }; 117 | 118 | export default Tab; 119 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Card/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ITextConfigType, 5 | ITextAreaConfigType, 6 | IUploadConfigType, 7 | TColorDefaultType, 8 | TNumberDefaultType, 9 | TTextDefaultType, 10 | TUploadDefaultType, 11 | TTextAreaDefaultType, 12 | } from '@/components/PanelComponents/FormEditor/types'; 13 | 14 | export type TCardEditData = Array< 15 | IUploadConfigType | ITextConfigType | IColorConfigType | INumberConfigType | ITextAreaConfigType 16 | >; 17 | export interface ICardConfig { 18 | width: TNumberDefaultType; 19 | bgColor: TColorDefaultType; 20 | bgPadding: TNumberDefaultType; 21 | cardRadius: TNumberDefaultType; 22 | link: TTextDefaultType; 23 | imgUrl: TUploadDefaultType; 24 | title: TTextDefaultType; 25 | desc: TTextAreaDefaultType; 26 | titColor: TColorDefaultType; 27 | titFontSize: TNumberDefaultType; 28 | descColor: TColorDefaultType; 29 | descFontSize: TNumberDefaultType; 30 | } 31 | 32 | export interface ICardSchema { 33 | editData: TCardEditData; 34 | config: ICardConfig; 35 | } 36 | 37 | const Card: ICardSchema = { 38 | editData: [ 39 | { 40 | key: 'width', 41 | name: '卡片宽度', 42 | type: 'Number', 43 | }, 44 | { 45 | key: 'bgColor', 46 | name: '卡片背景颜色', 47 | type: 'Color', 48 | }, 49 | { 50 | key: 'bgPadding', 51 | name: '内容边距', 52 | type: 'Number', 53 | }, 54 | { 55 | key: 'cardRadius', 56 | name: '卡片圆角', 57 | type: 'Number', 58 | }, 59 | { 60 | key: 'link', 61 | name: '点击跳转链接', 62 | type: 'Text', 63 | }, 64 | { 65 | key: 'imgUrl', 66 | name: '选择图片', 67 | type: 'Upload', 68 | isCrop: true, 69 | cropRate: 1, 70 | }, 71 | { 72 | key: 'title', 73 | name: '标题', 74 | type: 'Text', 75 | }, 76 | { 77 | key: 'titColor', 78 | name: '标题颜色', 79 | type: 'Color', 80 | }, 81 | { 82 | key: 'titFontSize', 83 | name: '标题大小', 84 | type: 'Number', 85 | }, 86 | { 87 | key: 'desc', 88 | name: '描述', 89 | type: 'TextArea', 90 | }, 91 | { 92 | key: 'descColor', 93 | name: '描述颜色', 94 | type: 'Color', 95 | }, 96 | { 97 | key: 'descFontSize', 98 | name: '描述文字大小', 99 | type: 'Number', 100 | } 101 | ], 102 | config: { 103 | width: 220, 104 | imgUrl: [ 105 | { 106 | uid: '001', 107 | name: 'image.png', 108 | status: 'done', 109 | url: 'http://h5.dooring.cn/uploads/card@2x_175d58cb3a1.png', 110 | }, 111 | ], 112 | title: '卡片标题', 113 | desc: '卡片描述', 114 | titColor: 'rgba(0,0,0,1)', 115 | titFontSize: 18, 116 | descColor: 'rgba(0,0,0,.6)', 117 | descFontSize: 14, 118 | bgColor: 'rgba(255,255,255,1)', 119 | bgPadding: 16, 120 | cardRadius: 6, 121 | link: '' 122 | }, 123 | }; 124 | 125 | export default Card; 126 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/List/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | IDataListConfigType, 4 | INumberConfigType, 5 | ISelectConfigType, 6 | TColorDefaultType, 7 | TDataListDefaultType, 8 | TNumberDefaultType, 9 | TSelectDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | export type TListSelectKeyType = '60' | '80' | '100' | '120' | '150'; 12 | export type TListEditData = Array< 13 | IColorConfigType | IDataListConfigType | INumberConfigType | ISelectConfigType 14 | >; 15 | export interface IListConfig { 16 | sourceData: TDataListDefaultType; 17 | padding: number; 18 | round: TNumberDefaultType; 19 | imgSize: TSelectDefaultType; 20 | fontSize: TNumberDefaultType; 21 | color: TColorDefaultType; 22 | } 23 | 24 | export interface IListSchema { 25 | editData: TListEditData; 26 | config: IListConfig; 27 | } 28 | 29 | const List: IListSchema = { 30 | editData: [ 31 | { 32 | key: 'sourceData', 33 | name: '数据源', 34 | type: 'DataList', 35 | cropRate: 1 36 | }, 37 | { 38 | key: 'padding', 39 | name: '列表项间距', 40 | type: 'Number', 41 | }, 42 | { 43 | key: 'round', 44 | name: '圆角', 45 | type: 'Number', 46 | }, 47 | { 48 | key: 'imgSize', 49 | name: '图片大小', 50 | type: 'Select', 51 | range: [ 52 | { 53 | key: '60', 54 | text: '60 x 60', 55 | }, 56 | { 57 | key: '80', 58 | text: '80 x 80', 59 | }, 60 | { 61 | key: '100', 62 | text: '100 x 100', 63 | }, 64 | { 65 | key: '120', 66 | text: '120 x 120', 67 | }, 68 | { 69 | key: '150', 70 | text: '150 x 150', 71 | }, 72 | ], 73 | }, 74 | { 75 | key: 'fontSize', 76 | name: '文字大小', 77 | type: 'Number', 78 | }, 79 | { 80 | key: 'color', 81 | name: '文字颜色', 82 | type: 'Color', 83 | }, 84 | ], 85 | config: { 86 | sourceData: [ 87 | { 88 | id: '1', 89 | title: '趣谈小课', 90 | desc: '致力于打造优质小课程', 91 | link: 'xxxxx', 92 | imgUrl: [ 93 | { 94 | uid: '001', 95 | name: 'image.png', 96 | status: 'done', 97 | url: 'http://h5.dooring.cn/uploads/1_1740c6fbcd9.png', 98 | }, 99 | ], 100 | }, 101 | { 102 | id: '2', 103 | title: '趣谈小课', 104 | desc: '致力于打造优质小课程', 105 | link: 'xxxxx', 106 | imgUrl: [ 107 | { 108 | uid: '002', 109 | name: 'image.png', 110 | status: 'done', 111 | url: 'http://h5.dooring.cn/uploads/1_1740c6fbcd9.png', 112 | }, 113 | ], 114 | }, 115 | ], 116 | padding: 16, 117 | round: 0, 118 | imgSize: '80', 119 | fontSize: 16, 120 | color: 'rgba(153,153,153,1)', 121 | }, 122 | }; 123 | 124 | export default List; 125 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Form/BasePopoverForm.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactText } from 'react'; 2 | import { Button } from 'antd'; 3 | import { 4 | baseFormDateTpl, 5 | baseFormMyRadioTpl, 6 | baseFormMyCheckboxTpl, 7 | baseFormMySelectTpl, 8 | baseFormNumberTpl, 9 | baseFormTextAreaTpl, 10 | baseFormTextTpl, 11 | baseFormUnionType, 12 | baseFormTextTipTpl 13 | } from '@/components/PanelComponents/FormEditor/types'; 14 | 15 | // 维护表单控件, 提高form渲染性能 16 | 17 | type TBaseForm = { 18 | [key in baseFormUnionType]: any; 19 | }; 20 | 21 | const BaseForm: TBaseForm = { 22 | Text: (props: baseFormTextTpl & { onChange: (v: string | undefined) => void }) => { 23 | const { label, onChange } = props; 24 | return ( 25 | 36 | ); 37 | }, 38 | Textarea: (props: baseFormTextAreaTpl & { onChange: (v: string | undefined) => void }) => { 39 | const { label, onChange } = props; 40 | return ( 41 | 44 | ); 45 | }, 46 | Number: (props: baseFormNumberTpl & { onChange: (v: string | undefined | number) => void }) => { 47 | const { label, onChange } = props; 48 | return ( 49 | 52 | ); 53 | }, 54 | MyRadio: (props: baseFormMyRadioTpl & { onChange: (v: string | undefined | number) => void }) => { 55 | const { label, onChange } = props; 56 | return ( 57 | 60 | ); 61 | }, 62 | MyCheckbox: ( 63 | props: baseFormMyCheckboxTpl & { onChange: (v: Array | undefined) => void }, 64 | ) => { 65 | const { label, onChange } = props; 66 | return ( 67 | 70 | ); 71 | }, 72 | Date: (props: baseFormDateTpl & { onChange: (v: Date) => void }) => { 73 | const { label, onChange } = props; 74 | return ( 75 | 78 | ); 79 | }, 80 | MySelect: ( 81 | props: baseFormMySelectTpl & { onChange: ((v: Record) => void) | undefined }, 82 | ) => { 83 | const { label, onChange } = props; 84 | return ( 85 | 88 | ); 89 | }, 90 | MyTextTip: (props: baseFormTextTipTpl) => { 91 | const { label } = props; 92 | return ( 93 | 103 | ); 104 | }, 105 | }; 106 | 107 | export default BaseForm; 108 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Form/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | IFormItemsConfigType, 4 | INumberConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TFormItemsDefaultType, 8 | TNumberDefaultType, 9 | TSelectDefaultType, 10 | ISelectConfigType, 11 | TTextDefaultType, 12 | } from '@/components/PanelComponents/FormEditor/types'; 13 | 14 | export type TTextWeightSelectKeyType = '300' | '400' | '500' | '600'; 15 | 16 | export type TFormEditData = Array< 17 | ITextConfigType | 18 | INumberConfigType | 19 | IColorConfigType | 20 | ITextConfigType | 21 | IFormItemsConfigType | 22 | ISelectConfigType 23 | >; 24 | export interface IFormConfig { 25 | title: TTextDefaultType; 26 | fontSize: TNumberDefaultType; 27 | titColor: TColorDefaultType; 28 | titWeight: TSelectDefaultType; 29 | bgColor: TColorDefaultType; 30 | btnColor: TColorDefaultType; 31 | btnTextColor: TColorDefaultType; 32 | api: TTextDefaultType; 33 | formControls: TFormItemsDefaultType; 34 | } 35 | 36 | export interface IFormSchema { 37 | editData: TFormEditData; 38 | config: IFormConfig; 39 | } 40 | 41 | const Form: IFormSchema = { 42 | editData: [ 43 | { 44 | key: 'title', 45 | name: '标题', 46 | type: 'Text', 47 | }, 48 | { 49 | key: 'fontSize', 50 | name: '标题大小', 51 | type: 'Number', 52 | }, 53 | { 54 | key: 'titColor', 55 | name: '标题颜色', 56 | type: 'Color', 57 | }, 58 | { 59 | key: 'titWeight', 60 | name: '标题粗细', 61 | type: 'Select', 62 | range: [ 63 | { 64 | key: '300', 65 | text: '300 x 300', 66 | }, 67 | { 68 | key: '400', 69 | text: '400 x 400', 70 | }, 71 | { 72 | key: '500', 73 | text: '500 x 500', 74 | }, 75 | { 76 | key: '600', 77 | text: '600 x 600', 78 | }, 79 | ], 80 | }, 81 | { 82 | key: 'bgColor', 83 | name: '背景色', 84 | type: 'Color', 85 | }, 86 | { 87 | key: 'btnColor', 88 | name: '按钮颜色', 89 | type: 'Color', 90 | }, 91 | { 92 | key: 'btnTextColor', 93 | name: '按钮文本颜色', 94 | type: 'Color', 95 | }, 96 | { 97 | key: 'api', 98 | name: '表单Api地址', 99 | type: 'Text', 100 | }, 101 | { 102 | key: 'formControls', 103 | name: '表单控件', 104 | type: 'FormItems', 105 | }, 106 | ], 107 | config: { 108 | title: '表单定制组件', 109 | fontSize: 18, 110 | titColor: 'rgba(60,60,60,1)', 111 | titWeight: '400', 112 | bgColor: 'rgba(255,255,255,1)', 113 | btnColor: 'rgba(20,54,226,100)', 114 | btnTextColor: 'rgba(255,255,255,1)', 115 | api: '', 116 | formControls: [ 117 | { 118 | id: '1', 119 | type: 'Text', 120 | label: '姓名', 121 | placeholder: '请输入姓名', 122 | }, 123 | { 124 | id: '2', 125 | type: 'Number', 126 | label: '年龄', 127 | placeholder: ' 请输入年龄', 128 | }, 129 | { 130 | id: '4', 131 | type: 'MySelect', 132 | label: '爱好', 133 | options: [ 134 | { label: '选项一', value: '1' }, 135 | { label: '选项二', value: '2' }, 136 | { label: '选项三', value: '3' }, 137 | ], 138 | }, 139 | ], 140 | }, 141 | }; 142 | export default Form; 143 | -------------------------------------------------------------------------------- /src/layouts/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useState, useEffect } from 'react'; 2 | import { library, generateRespones, RenderList, useRegister } from 'chatbot-antd'; 3 | import { IRouteComponentProps, history } from 'umi'; 4 | import { Button, Modal } from 'antd'; 5 | import { CustomerServiceOutlined } from '@ant-design/icons'; 6 | 7 | library.push( 8 | //语料库,push进去,也可以不用 9 | { 10 | text: '我是机器人', 11 | reg: '你是谁', 12 | }, 13 | { 14 | text: ( 15 |
16 | @徐小夕 17 | @yehuozhili 18 |
19 | ), 20 | useReg: /(.*?)作者是谁(.*?)/, 21 | }, 22 | ); 23 | 24 | export default function Layout({ children }: IRouteComponentProps) { 25 | const [modalOpen, setModalOpen] = useState(false); 26 | const callb = useCallback((v: RenderList) => { 27 | setTimeout(() => { 28 | //使用settimeout 更像机器人回话 29 | let returnValue = generateRespones(v); 30 | if (returnValue) { 31 | //排除null 32 | setList(prev => [...prev, { isUser: false, text: returnValue }]); 33 | } 34 | }, 500); 35 | // eslint-disable-next-line react-hooks/exhaustive-deps 36 | }, []); 37 | // 注册 38 | const [render, setList] = useRegister( 39 | modalOpen, 40 | callb, 41 | { 42 | onOk: () => setModalOpen(false), 43 | onCancel: () => setModalOpen(false), 44 | title: 'h5-Dooring机器人客服', 45 | width: 420 46 | }, 47 | {}, 48 |
49 | welcome!欢迎使用h5-Dooring,你有任何问题,都可以咨询我哦~ 50 |
51 |
【dooring指南】
52 |
53 | 54 | 55 |
  3. 如果复制/删除组件不生效, 请先点击需要复制/删除组件, 再右键删除/复制
56 |
  4. dooring开源交流群(微信:Mr_xuxiaoxi)
57 | 58 |
59 |
60 |
, 61 | ); 62 | 63 | useEffect(() => { 64 | setInterval(() => { 65 | const timeout = +localStorage.getItem('tt') 66 | if(timeout && timeout < Date.now()) { 67 | localStorage.removeItem('tt'); 68 | Modal.info({ 69 | title: 'Dooring温馨提示', 70 | content: ( 71 |
72 | 您的登录已过期, 请点击确认按钮重新登录 73 |
74 | ), 75 | okText: '确认', 76 | onOk() { 77 | localStorage.removeItem('rp'); 78 | localStorage.removeItem('nickname'); 79 | history.push('/login'); 80 | }, 81 | }); 82 | } 83 | }, 1000 * 15) 84 | }, []) 85 | return ( 86 |
87 |
96 | 99 |
100 | {render} 101 | {children} 102 |
103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /src/components/PanelComponents/DataList/editorModal.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo, useEffect, FC } from 'react'; 2 | import { Form, Select, Input, Modal } from 'antd'; 3 | import Upload from '../Upload'; 4 | import { Store } from 'antd/lib/form/interface'; 5 | import { TDataListDefaultTypeItem } from '../FormEditor/types'; 6 | // import styles from './index.less'; 7 | const normFile = (e: any) => { 8 | if (Array.isArray(e)) { 9 | return e; 10 | } 11 | return e && e.fileList; 12 | }; 13 | 14 | const { Option } = Select; 15 | 16 | const formItemLayout = { 17 | labelCol: { span: 6 }, 18 | wrapperCol: { span: 14 }, 19 | }; 20 | 21 | export type EditorModalProps = { 22 | visible: boolean; 23 | onCancel: ((e: React.MouseEvent) => void) | undefined; 24 | cropRate: number | undefined; 25 | item?: TDataListDefaultTypeItem; 26 | onSave: Function; 27 | }; 28 | 29 | const EditorModal: FC = props => { 30 | const { item, onSave, visible, cropRate, onCancel } = props; 31 | const onFinish = (values: Store) => { 32 | onSave && onSave(values); 33 | }; 34 | const handleOk = () => { 35 | form 36 | .validateFields() 37 | .then(values => { 38 | console.log(values); 39 | if (item) { 40 | values.id = item.id; 41 | onSave && onSave(values); 42 | } 43 | }) 44 | .catch(err => { 45 | console.log(err); 46 | }); 47 | }; 48 | 49 | const [form] = Form.useForm(); 50 | 51 | useEffect(() => { 52 | return () => { 53 | form.resetFields(); 54 | }; 55 | }, [item]); 56 | 57 | return ( 58 | <> 59 | {!!item && ( 60 | 68 |
75 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | {!!window['currentCates'] && ( 89 | 94 | 103 | 104 | )} 105 | 106 | 112 | 113 | 114 |
115 |
116 | )} 117 | 118 | ); 119 | }; 120 | 121 | export default memo(EditorModal); 122 | -------------------------------------------------------------------------------- /src/global.css: -------------------------------------------------------------------------------- 1 | html, body, #root { 2 | height: 100%; 3 | overflow: hidden; 4 | } 5 | 6 | body { 7 | margin: 0; 8 | } 9 | 10 | :root { 11 | --sk-size: 40px; 12 | --sk-color: #06c; 13 | } 14 | 15 | .ant-btn-primary { 16 | color: #fff; 17 | background: #2F54EB !important; 18 | border-color: #2F54EB !important; 19 | } 20 | 21 | .ant-btn.ant-btn-link { 22 | color: #2F54EB !important; 23 | } 24 | 25 | .ant-btn-background-ghost.ant-btn-primary { 26 | color: #2F54EB !important; 27 | border-color: #2F54EB !important; 28 | } 29 | 30 | .ant-btn-link[disabled], .ant-btn-link[disabled]:active, .ant-btn-link[disabled]:focus, .ant-btn-link[disabled]:hover { 31 | color: rgba(0,0,0,.25) !important; 32 | background: transparent !important; 33 | border-color: transparent !important; 34 | } 35 | 36 | .react-grid-item{ 37 | overflow: hidden; 38 | } 39 | .ant-tabs-left.editorTabclass 40 | .ant-tabs-nav , .ant-tabs-left.editorTabclass .ant-tabs-nav-list{ 41 | width: 40px!important; 42 | min-width: 40px!important; 43 | } 44 | .ant-tabs-left.editorTabclass{ 45 | height: 100%; 46 | } 47 | 48 | .ant-tabs-left.editorTabclass > .ant-tabs-nav .ant-tabs-tab{ 49 | padding:20px 0px !important; 50 | 51 | } 52 | .ant-tabs-left.editorTabclass .ant-tabs-tab{ 53 | margin:auto!important; 54 | } 55 | .ant-tabs-left.editorTabclass .ant-tabs-tab div{ 56 | display: inline-block; 57 | } 58 | .ant-tabs-left.editorTabclass .ant-tabs-content-holder{ 59 | overflow: auto; 60 | padding:8px; 61 | } 62 | .ant-tabs-left.editorTabclass .ant-tabs-tabpane{ 63 | display: flex; 64 | flex-wrap: wrap; 65 | padding-left: 10px!important; 66 | padding-right: 10px!important; 67 | padding-bottom: 40px; 68 | } 69 | #form_editor { 70 | margin-bottom: 100px; 71 | } 72 | #form_editor .ant-form-item-label > label { 73 | position: fixed; 74 | color: #4A4A4A; 75 | } 76 | #form_editor .ant-form-item-label { 77 | text-align: start 78 | } 79 | #form_editor .ant-form-item-control { 80 | text-align: end; 81 | } 82 | #form_editor .ant-form-item { 83 | margin-bottom: 20px; 84 | } 85 | #form_editor .ant-input-number-handler-wrap { 86 | width: 12px; 87 | } 88 | #form_editor .ant-input, #form_editor .ant-input-number,#form_editor .ant-select-selector { 89 | background-color: #f6f6f6; 90 | } 91 | #form_editor .ant-form-item .ant-select { 92 | text-align: center; 93 | } 94 | #form_editor .ant-radio-wrapper { 95 | margin-right: 0; 96 | } 97 | #form_editor .ant-input-number { 98 | width: 42px; 99 | /* height: 24px; */ 100 | border: 1px solid #DBDBDB; 101 | } 102 | #form_editor .ant-input-number-handler-down-inner { 103 | text-align: right; 104 | transform: translateY(-50%) translateX(13%); 105 | } 106 | #form_editor .ant-input-number-handler-up-inner { 107 | text-align: right; 108 | transform: translateY(-26%) translateX(15%); 109 | } 110 | #form_editor .ant-input-number-input { 111 | padding-left: 4px; 112 | } 113 | #form_editor .ant-form-item-control-input-content { 114 | line-height: 14px; 115 | } 116 | #form_editor .ant-form-item-label > label::after { 117 | content: ''; 118 | } 119 | #form_editor .za-cell:after { 120 | border-top: 0px; 121 | } 122 | #form_editor .za-cell__content { 123 | font-size: 14px; 124 | } 125 | #form_editor .za-select--arrow .za-select__input:after { 126 | display: none; 127 | } 128 | #form_editor .ant-upload-list-picture-card-container { 129 | display: flex; 130 | margin-left: 30px; 131 | } 132 | 133 | @import '~react-grid-layout/css/styles.css'; 134 | @import '~react-resizable/css/styles.css'; 135 | @import '~zarm/dist/zarm.min.css'; 136 | 137 | @import '~codemirror/lib/codemirror.css'; 138 | @import '~codemirror/theme/material.css'; 139 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Icon/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ICardPickerConfigType, 3 | IColorConfigType, 4 | INumberConfigType, 5 | ISwitchConfigType, 6 | TCardPickerDefaultType, 7 | TColorDefaultType, 8 | TNumberDefaultType, 9 | TSwitchDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TIconEditData = Array< 13 | IColorConfigType | INumberConfigType | ISwitchConfigType | ICardPickerConfigType 14 | >; 15 | export interface IIconConfig { 16 | color: TColorDefaultType; 17 | size: TNumberDefaultType; 18 | spin: TSwitchDefaultType; 19 | type: TCardPickerDefaultType; 20 | } 21 | 22 | export interface IIconSchema { 23 | editData: TIconEditData; 24 | config: IIconConfig; 25 | } 26 | 27 | export type IconTypes = 28 | | 'AccountBookTwoTone' 29 | | 'AlertTwoTone' 30 | | 'ApiTwoTone' 31 | | 'AppstoreTwoTone' 32 | | 'AudioTwoTone' 33 | | 'BankTwoTone' 34 | | 'BellTwoTone' 35 | | 'BookTwoTone' 36 | | 'BugTwoTone' 37 | | 'BuildTwoTone' 38 | | 'BulbTwoTone' 39 | | 'CalculatorTwoTone' 40 | | 'CalendarTwoTone' 41 | | 'CameraTwoTone' 42 | | 'CarTwoTone' 43 | | 'CarryOutTwoTone' 44 | | 'CiCircleTwoTone' 45 | | 'CloudTwoTone' 46 | | 'CodeTwoTone' 47 | | 'CrownTwoTone' 48 | | 'CustomerServiceTwoTone' 49 | | 'DollarCircleTwoTone' 50 | | 'EnvironmentTwoTone' 51 | | 'ExperimentTwoTone' 52 | | 'FireTwoTone' 53 | | 'GiftTwoTone' 54 | | 'InsuranceTwoTone' 55 | | 'LikeTwoTone' 56 | | 'LockTwoTone' 57 | | 'MailTwoTone' 58 | | 'MessageTwoTone' 59 | | 'PhoneTwoTone' 60 | | 'PictureTwoTone' 61 | | 'PlaySquareTwoTone' 62 | | 'RedEnvelopeTwoTone' 63 | | 'ShopTwoTone' 64 | | 'TrademarkCircleTwoTone' 65 | | 'StarTwoTone' 66 | | 'SafetyCertificateTwoTone' 67 | | 'SettingTwoTone' 68 | | 'RocketTwoTone'; 69 | 70 | const Icon: IIconSchema = { 71 | editData: [ 72 | { 73 | key: 'color', 74 | name: '颜色', 75 | type: 'Color', 76 | }, 77 | { 78 | key: 'size', 79 | name: '大小', 80 | type: 'Number', 81 | }, 82 | { 83 | key: 'spin', 84 | name: '旋转动画', 85 | type: 'Switch', 86 | }, 87 | { 88 | key: 'type', 89 | name: '图标类型', 90 | type: 'CardPicker', 91 | icons: [ 92 | 'AccountBookTwoTone', 93 | 'AlertTwoTone', 94 | 'ApiTwoTone', 95 | 'AppstoreTwoTone', 96 | 'AudioTwoTone', 97 | 'BankTwoTone', 98 | 'BellTwoTone', 99 | 'BookTwoTone', 100 | 'BugTwoTone', 101 | 'BuildTwoTone', 102 | 'BulbTwoTone', 103 | 'CalculatorTwoTone', 104 | 'CalendarTwoTone', 105 | 'CameraTwoTone', 106 | 'CarTwoTone', 107 | 'CarryOutTwoTone', 108 | 'CiCircleTwoTone', 109 | 'CloudTwoTone', 110 | 'CodeTwoTone', 111 | 'CrownTwoTone', 112 | 'CustomerServiceTwoTone', 113 | 'DollarCircleTwoTone', 114 | 'EnvironmentTwoTone', 115 | 'ExperimentTwoTone', 116 | 'FireTwoTone', 117 | 'GiftTwoTone', 118 | 'InsuranceTwoTone', 119 | 'LikeTwoTone', 120 | 'LockTwoTone', 121 | 'MailTwoTone', 122 | 'MessageTwoTone', 123 | 'PhoneTwoTone', 124 | 'PictureTwoTone', 125 | 'PlaySquareTwoTone', 126 | 'RedEnvelopeTwoTone', 127 | 'ShopTwoTone', 128 | 'TrademarkCircleTwoTone', 129 | 'StarTwoTone', 130 | 'SafetyCertificateTwoTone', 131 | 'SettingTwoTone', 132 | 'RocketTwoTone', 133 | ], 134 | }, 135 | ], 136 | config: { 137 | color: 'rgba(74,144,226,1)', 138 | size: 36, 139 | spin: false, 140 | type: 'CarTwoTone', 141 | }, 142 | }; 143 | 144 | export default Icon; 145 | -------------------------------------------------------------------------------- /src/utils/tool.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useLayoutEffect, RefObject } from 'react'; 2 | import { RGBColor } from 'react-color'; 3 | 4 | // 生成uuid 5 | function uuid(len: number, radix: number) { 6 | let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( 7 | '', 8 | ); 9 | let uuid = [], 10 | i; 11 | radix = radix || chars.length; 12 | 13 | if (len) { 14 | for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)]; 15 | } else { 16 | let r; 17 | uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; 18 | uuid[14] = '4'; 19 | 20 | for (i = 0; i < 36; i++) { 21 | if (!uuid[i]) { 22 | r = 0 | (Math.random() * 16); 23 | uuid[i] = chars[i === 19 ? (r & 0x3) | 0x8 : r]; 24 | } 25 | } 26 | } 27 | 28 | return uuid.join(''); 29 | } 30 | 31 | // 将rgba字符串对象转化为rgba对象 32 | function rgba2Obj(rgba = '') { 33 | let reg = /rgba\((\d+),(\d+),(\d+),(\d+)\)/g; 34 | let rgbaObj: RGBColor = { r: 0, g: 0, b: 0, a: 0 }; 35 | 36 | rgba.replace(reg, (_m, r, g, b, a) => { 37 | rgbaObj = { r, g, b, a }; 38 | return rgba; 39 | }); 40 | return rgbaObj; 41 | } 42 | 43 | export { uuid, rgba2Obj }; 44 | 45 | export function useGetRect() { 46 | const [rect, setRect] = useState({ width: 0, height: 0 }); 47 | useEffect(() => { 48 | setRect({ 49 | width: window.innerWidth, 50 | height: window.innerHeight, 51 | }); 52 | }, []); 53 | return rect; 54 | } 55 | 56 | export function useGetScrollBarWidth(ref: RefObject) { 57 | const [width, setWidth] = useState(0); 58 | useLayoutEffect(() => { 59 | if (ref.current) { 60 | const diff = ref.current.offsetWidth - ref.current.clientWidth; 61 | setWidth(diff); 62 | } 63 | }, [ref]); 64 | return width; 65 | } 66 | 67 | export function useAnimation(state: boolean, delay: number) { 68 | const [display, setDisplay] = useState(false); 69 | useEffect(() => { 70 | let timer: number; 71 | if (state && display === true) { 72 | setDisplay(false); 73 | } else if (!state && display === false) { 74 | timer = window.setTimeout(() => { 75 | setDisplay(true); 76 | }, delay); 77 | } 78 | return () => { 79 | window.clearTimeout(timer); 80 | }; 81 | }, [delay, display, state]); 82 | return [display, setDisplay]; 83 | } 84 | 85 | export function unParams(params = '?a=1&b=2&c=3') { 86 | let obj: any = {}; 87 | params && 88 | params.replace(/((\w*)=([\.a-z0-9A-Z]*)?)?/g, (m, a, b, c): any => { 89 | if (b || c) obj[b] = c; 90 | }); 91 | return obj; 92 | } 93 | 94 | export function throttle(fn: Function, delay: number) { 95 | let flag = true; 96 | return (...args: any) => { 97 | if (flag) { 98 | flag = false; 99 | fn(...args); 100 | setTimeout(() => { 101 | flag = true; 102 | }, delay); 103 | } 104 | }; 105 | } 106 | 107 | export function formatTime(fmt: string, dateObj: any) { 108 | const date = dateObj || new Date(); 109 | const o: any = { 110 | 'M+': date.getMonth() + 1, //月份 111 | 'd+': date.getDate(), //日 112 | 'h+': date.getHours(), //小时 113 | 'm+': date.getMinutes(), //分 114 | 's+': date.getSeconds(), //秒 115 | 'q+': Math.floor((date.getMonth() + 3) / 3), //季度 116 | S: date.getMilliseconds(), //毫秒 117 | }; 118 | if (/(y+)/.test(fmt)) { 119 | fmt = fmt.replace( 120 | RegExp.$1, 121 | (date.getFullYear() + '').substr(4 - RegExp.$1.length), 122 | ); 123 | } 124 | for (var k in o) { 125 | if (new RegExp('(' + k + ')').test(fmt)) { 126 | fmt = fmt.replace( 127 | RegExp.$1, 128 | RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length), 129 | ); 130 | } 131 | } 132 | return fmt; 133 | } 134 | 135 | export const isDev = process.env.NODE_ENV === 'development'; 136 | 137 | export const serverUrl = isDev 138 | ? 'http://localhost:3000' 139 | : 'http://h5.dooring.cn'; 140 | 141 | export const _gaw = (w: number) => { 142 | return (window.innerWidth / 1024) * w; 143 | }; 144 | -------------------------------------------------------------------------------- /src/components/ModalTpl/index.js: -------------------------------------------------------------------------------- 1 | import React, { memo, useState, useEffect } from 'react' 2 | import { Modal, Button, Tabs, Empty } from 'antd' 3 | import Loading from '../LoadingCp' 4 | import req from 'utils/req' 5 | import cateTpl from './cate' 6 | import styles from './index.less' 7 | 8 | const { TabPane } = Tabs; 9 | 10 | export default memo(function ModalTpl(props) { 11 | const { showModalTpl, onSelectTpl, onCloseModalTpl } = props 12 | const [isLoading, setLoading] = useState(false) 13 | const [tpls, setTpls] = useState([]) 14 | const [cates, setCates] = useState({}) 15 | 16 | const useTpl = (tid) => { 17 | const config = { 18 | title: 'dooring温馨提示', 19 | content: ( 20 |
21 | 导入模板会覆盖画布已有的数据,确认要导入吗? 22 |
23 | ), 24 | okText: '确定', 25 | cancelText: '取消', 26 | onOk() { 27 | setLoading(true) 28 | req.get(`/visible/tpl/get?tid=${tid}`).then(res => { 29 | res && onSelectTpl && onSelectTpl({tpl: res.tpl, pageConfig: res.pageConfig}) 30 | setLoading(false) 31 | onCloseModalTpl() 32 | }).catch(err => { 33 | setLoading(false) 34 | }) 35 | } 36 | }; 37 | Modal.confirm(config); 38 | } 39 | 40 | const handlePaneChange = (key) => { 41 | setTpls(cates[key] || []) 42 | } 43 | 44 | useEffect(() => { 45 | setLoading(true) 46 | req.get('/visible/tpls/free').then(res => { 47 | // 对模版数据进行分类 48 | if(res) { 49 | const cateObj = {}; 50 | res.forEach(item => { 51 | const cate = item.cate; 52 | if(!cate) { 53 | cateObj['其他'] ? cateObj['其他'].push(item) : (cateObj['其他'] = [item]) 54 | return 55 | } 56 | cateObj[cate] ? cateObj[cate].push(item) : (cateObj[cate] = [item]) 57 | }) 58 | setCates(cateObj) 59 | setTpls(cateObj['其他'] || []) 60 | } 61 | setLoading(false) 62 | }).catch(err => { 63 | setLoading(false) 64 | }) 65 | }, []) 66 | 67 | return 75 |
76 | { 77 | !isLoading ? 78 | 79 | { 80 | cateTpl.map((item, i) => { 81 | return 82 |
83 | { 84 | tpls.length ? tpls.map((item,i) => { 85 | return
86 | dooring可视化 87 |
88 |
89 | }) :
90 | } 91 |
92 | 93 |
94 | }) 95 | } 96 |
97 | : 98 | 99 | } 100 |
101 |
102 | }) -------------------------------------------------------------------------------- /src/components/PanelComponents/FormItems/EditorModal.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, memo, useEffect } from 'react'; 2 | import { Form, Select, Input, Modal, Button, InputNumber } from 'antd'; 3 | import Color from '../Color'; 4 | import { baseFormOptionsType } from '../FormEditor/types'; 5 | 6 | const { Option } = Select; 7 | 8 | const formItemLayout = { 9 | labelCol: { span: 6 }, 10 | wrapperCol: { span: 14 }, 11 | }; 12 | 13 | interface EditorModalProps { 14 | item: any; 15 | onSave: (data: any) => void; 16 | visible: boolean; 17 | } 18 | 19 | const EditorModal: FC = props => { 20 | const { item, onSave, visible } = props; 21 | 22 | const onFinish = (values: any) => { 23 | onSave && onSave(values); 24 | }; 25 | 26 | const handleOk = () => { 27 | form 28 | .validateFields() 29 | .then(values => { 30 | values.id = item.id; 31 | onSave && onSave(values); 32 | }) 33 | .catch(err => { 34 | console.log(err); 35 | }); 36 | }; 37 | 38 | const [form] = Form.useForm(); 39 | 40 | useEffect(() => { 41 | if (form && item && visible) { 42 | form.resetFields(); 43 | } 44 | }, [form, item, visible]); 45 | 46 | return ( 47 | <> 48 | {!!item && ( 49 | 53 | 56 |
57 | } 58 | forceRender 59 | visible={visible} 60 | onOk={handleOk} 61 | closable={false} 62 | > 63 |
70 | { 71 | 74 | } 75 | {!!item.label && ( 76 | 81 | 82 | 83 | )} 84 | {!!item.fontSize && ( 85 | 90 | 91 | 92 | )} 93 | {!!item.color && ( 94 | 99 | 100 | 101 | )} 102 | {!!item.placeholder && ( 103 | 104 | 105 | 106 | )} 107 | {!!item.options && ( 108 | 113 | 128 | 129 | )} 130 |
131 | 132 | )} 133 | 134 | ); 135 | }; 136 | 137 | export default memo(EditorModal); 138 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Text/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IColorConfigType, 3 | INumberConfigType, 4 | ISelectConfigType, 5 | ITextConfigType, 6 | TColorDefaultType, 7 | TNumberDefaultType, 8 | TSelectDefaultType, 9 | TTextDefaultType, 10 | } from '@/components/PanelComponents/FormEditor/types'; 11 | 12 | export type TTextSelectKeyType = 'left' | 'right' | 'center'; 13 | 14 | export type TTextTipSelectKeyType = 'left' | 'right' | 'bottom' | 'top'; 15 | export type TTextEditData = Array< 16 | ITextConfigType | 17 | IColorConfigType | 18 | INumberConfigType | 19 | ISelectConfigType | 20 | ISelectConfigType | 21 | ISelectConfigType 22 | >; 23 | export type TTextWeightSelectKeyType = '300' | '400' | '500' | '600'; 24 | 25 | export interface ITextConfig { 26 | text: TTextDefaultType; 27 | color: TColorDefaultType; 28 | fontSize: TNumberDefaultType; 29 | align: TSelectDefaultType; 30 | lineHeight: TNumberDefaultType; 31 | fontWeight: TSelectDefaultType; 32 | bgColor: TColorDefaultType; 33 | padding: TNumberDefaultType; 34 | radius: TNumberDefaultType; 35 | link: TTextDefaultType; 36 | textTip: TTextDefaultType; 37 | textTipPosition: TSelectDefaultType; 38 | } 39 | 40 | export interface ITextSchema { 41 | editData: TTextEditData; 42 | config: ITextConfig; 43 | } 44 | const Text: ITextSchema = { 45 | editData: [ 46 | { 47 | key: 'text', 48 | name: '文字', 49 | type: 'Text', 50 | }, 51 | { 52 | key: 'color', 53 | name: '标题颜色', 54 | type: 'Color', 55 | }, 56 | { 57 | key: 'fontSize', 58 | name: '字体大小', 59 | type: 'Number', 60 | }, 61 | { 62 | key: 'align', 63 | name: '对齐方式', 64 | type: 'Select', 65 | range: [ 66 | { 67 | key: 'left', 68 | text: '左对齐', 69 | }, 70 | { 71 | key: 'center', 72 | text: '居中对齐', 73 | }, 74 | { 75 | key: 'right', 76 | text: '右对齐', 77 | }, 78 | ], 79 | }, 80 | { 81 | key: 'lineHeight', 82 | name: '行高', 83 | type: 'Number', 84 | }, 85 | { 86 | key: 'fontWeight', 87 | name: '文字粗细', 88 | type: 'Select', 89 | range: [ 90 | { 91 | key: '300', 92 | text: '300 x 300', 93 | }, 94 | { 95 | key: '400', 96 | text: '400 x 400', 97 | }, 98 | { 99 | key: '500', 100 | text: '500 x 500', 101 | }, 102 | { 103 | key: '600', 104 | text: '600 x 600', 105 | }, 106 | ], 107 | }, 108 | { 109 | key: 'bgColor', 110 | name: '背景颜色', 111 | type: 'Color', 112 | }, 113 | { 114 | key: 'padding', 115 | name: '填充间距', 116 | type: 'Number', 117 | }, 118 | { 119 | key: 'radius', 120 | name: '背景圆角', 121 | type: 'Number', 122 | }, 123 | { 124 | key: 'link', 125 | name: '链接地址', 126 | type: 'Text', 127 | }, 128 | { 129 | key: 'textTip', 130 | name: ' 文字提示', 131 | type: 'Text', 132 | }, 133 | { 134 | key: 'textTipPosition', 135 | name: '文字提示方向', 136 | type: 'Select', 137 | range: [ 138 | { 139 | key: 'left', 140 | text: '左', 141 | }, 142 | { 143 | key: 'right', 144 | text: '右', 145 | }, 146 | { 147 | key: 'top', 148 | text: '上', 149 | }, 150 | { 151 | key: 'bottom', 152 | text: '下', 153 | }, 154 | ], 155 | }, 156 | ], 157 | config: { 158 | text: '我是文本', 159 | color: 'rgba(60,60,60,1)', 160 | fontSize: 18, 161 | fontWeight: '400', 162 | align: 'center', 163 | lineHeight: 2, 164 | bgColor: 'rgba(255,255,255,0)', 165 | padding: 0, 166 | radius: 0, 167 | link: '', 168 | textTip: '', 169 | textTipPosition: 'bottom' 170 | }, 171 | }; 172 | export default Text; 173 | -------------------------------------------------------------------------------- /src/components/BasicShop/BasicComponents/Image/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | INumberConfigType, 3 | IUploadConfigType, 4 | TNumberDefaultType, 5 | TUploadDefaultType, 6 | IColorConfigType, 7 | TColorDefaultType, 8 | ISelectConfigType, 9 | TSelectDefaultType, 10 | IPosConfigType, 11 | TPosDefaultType, 12 | TTextDefaultType, 13 | ITextConfigType 14 | } from '@/components/PanelComponents/FormEditor/types'; 15 | 16 | export type TTextSelectKeyType = 'left' | 'right' | 'center'; 17 | export type TTextWeightSelectKeyType = '300' | '400' | '500' | '600'; 18 | 19 | export type TImageEditData = Array 23 | | IColorConfigType 24 | | ITextConfigType 25 | >; 26 | 27 | export interface IImageConfig { 28 | translate: TPosDefaultType; 29 | align: TSelectDefaultType; 30 | titText: TTextDefaultType; 31 | titColor: TColorDefaultType; 32 | titFontSize: TNumberDefaultType; 33 | titFontWeight: TSelectDefaultType; 34 | subTitText: TTextDefaultType; 35 | subTitColor: TColorDefaultType; 36 | subTitFontSize: TNumberDefaultType; 37 | subTitFontWeight: TSelectDefaultType; 38 | imgUrl: TUploadDefaultType; 39 | round: TNumberDefaultType; 40 | } 41 | 42 | export interface IImageSchema { 43 | editData: TImageEditData; 44 | config: IImageConfig; 45 | } 46 | 47 | const Image: IImageSchema = { 48 | editData: [ 49 | { 50 | key: 'translate', 51 | name: '文字偏移', 52 | type: 'Pos' 53 | }, 54 | { 55 | key: 'align', 56 | name: '对齐方式', 57 | type: 'Select', 58 | range: [ 59 | { 60 | key: 'left', 61 | text: '左对齐', 62 | }, 63 | { 64 | key: 'center', 65 | text: '居中对齐', 66 | }, 67 | { 68 | key: 'right', 69 | text: '右对齐', 70 | }, 71 | ], 72 | }, 73 | { 74 | key: 'titText', 75 | name: '标题文字', 76 | type: 'Text', 77 | }, 78 | { 79 | key: 'titFontSize', 80 | name: '标题大小', 81 | type: 'Number', 82 | }, 83 | { 84 | key: 'titColor', 85 | name: '标题颜色', 86 | type: 'Color', 87 | }, 88 | { 89 | key: 'titFontWeight', 90 | name: '标题粗细', 91 | type: 'Select', 92 | range: [ 93 | { 94 | key: '300', 95 | text: '300 x 300', 96 | }, 97 | { 98 | key: '400', 99 | text: '400 x 400', 100 | }, 101 | { 102 | key: '500', 103 | text: '500 x 500', 104 | }, 105 | { 106 | key: '600', 107 | text: '600 x 600', 108 | }, 109 | ], 110 | }, 111 | { 112 | key: 'subTitText', 113 | name: '副标题文字', 114 | type: 'Text', 115 | }, 116 | { 117 | key: 'subTitFontSize', 118 | name: '副标题大小', 119 | type: 'Number', 120 | }, 121 | { 122 | key: 'subTitColor', 123 | name: '副标题颜色', 124 | type: 'Color', 125 | }, 126 | { 127 | key: 'subTitFontWeight', 128 | name: '副标题粗细', 129 | type: 'Select', 130 | range: [ 131 | { 132 | key: '300', 133 | text: '300 x 300', 134 | }, 135 | { 136 | key: '400', 137 | text: '400 x 400', 138 | }, 139 | { 140 | key: '500', 141 | text: '500 x 500', 142 | }, 143 | { 144 | key: '600', 145 | text: '600 x 600', 146 | }, 147 | ], 148 | }, 149 | { 150 | key: 'imgUrl', 151 | name: '上传图片', 152 | type: 'Upload', 153 | isCrop: false, 154 | }, 155 | { 156 | key: 'round', 157 | name: '圆角', 158 | type: 'Number', 159 | }, 160 | ], 161 | config: { 162 | translate: [0, 0], 163 | align: 'center', 164 | titText: '', 165 | titFontSize: 20, 166 | titColor: 'rgba(0,0,0,1)', 167 | titFontWeight: '400', 168 | subTitText: '', 169 | subTitFontSize: 16, 170 | subTitColor: 'rgba(0,0,0,1)', 171 | subTitFontWeight: '400', 172 | imgUrl: [ 173 | { 174 | uid: '001', 175 | name: 'image.png', 176 | status: 'done', 177 | url: 'http://h5.dooring.cn/uploads/bg_174e470dc22.png', 178 | }, 179 | ], 180 | round: 0, 181 | }, 182 | }; 183 | 184 | export default Image; 185 | --------------------------------------------------------------------------------