├── .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 |
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 |
9 |
10 | );
11 |
12 | export default memo(function ZanPao() {
13 | return (
14 |
15 |
16 |
17 | 支持作者, 换台好点的服务器?
18 |
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 |
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 |
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 |
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 |
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 |
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 |
18 |
22 |
23 |
3. 编辑器页面使用说明
24 |
25 |
26 |
30 |
31 |
5. 页面管理系统使用
32 |
33 |
34 |
35 |
6. 页面数据分析, 数据收集
36 |
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 ?
:
}
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 | ) :
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 |
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 ?
:
}
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 |
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 ?
:
}
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 ?
:
}
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 ?
:
}
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 |
74 | 添加项目
75 |
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 |
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 |
6 |
7 |
8 |
9 |
10 |
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 |
85 | 提交
86 |
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 ?
:
}
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 | onChange}
33 | >
34 | {label}
35 |
36 | );
37 | },
38 | Textarea: (props: baseFormTextAreaTpl & { onChange: (v: string | undefined) => void }) => {
39 | const { label, onChange } = props;
40 | return (
41 | onChange}>
42 | {label}
43 |
44 | );
45 | },
46 | Number: (props: baseFormNumberTpl & { onChange: (v: string | undefined | number) => void }) => {
47 | const { label, onChange } = props;
48 | return (
49 | onChange}>
50 | {label}
51 |
52 | );
53 | },
54 | MyRadio: (props: baseFormMyRadioTpl & { onChange: (v: string | undefined | number) => void }) => {
55 | const { label, onChange } = props;
56 | return (
57 | onChange}>
58 | {label}
59 |
60 | );
61 | },
62 | MyCheckbox: (
63 | props: baseFormMyCheckboxTpl & { onChange: (v: Array | undefined) => void },
64 | ) => {
65 | const { label, onChange } = props;
66 | return (
67 | onChange}>
68 | {label}
69 |
70 | );
71 | },
72 | Date: (props: baseFormDateTpl & { onChange: (v: Date) => void }) => {
73 | const { label, onChange } = props;
74 | return (
75 | onChange}>
76 | {label}
77 |
78 | );
79 | },
80 | MySelect: (
81 | props: baseFormMySelectTpl & { onChange: ((v: Record) => void) | undefined },
82 | ) => {
83 | const { label, onChange } = props;
84 | return (
85 | onChange}>
86 | {label}
87 |
88 | );
89 | },
90 | MyTextTip: (props: baseFormTextTipTpl) => {
91 | const { label } = props;
92 | return (
93 |
101 | {label}
102 |
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 |
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 | setModalOpen(!modalOpen)}>
97 |
98 |
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 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | {!!window['currentCates'] && (
89 |
94 |
95 | {window['currentCates'].map((v, i) => {
96 | return (
97 |
98 | {v}
99 |
100 | );
101 | })}
102 |
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 |
87 |
useTpl(item.tid)}>立即使用
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 | handleOk()}>
54 | 确定
55 |
56 |
57 | }
58 | forceRender
59 | visible={visible}
60 | onOk={handleOk}
61 | closable={false}
62 | >
63 |
72 |
73 |
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 |
120 | {item.options.map((v: baseFormOptionsType, i: number) => {
121 | return (
122 |
123 | {v.label}
124 |
125 | );
126 | })}
127 |
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 |
--------------------------------------------------------------------------------